// 00 — MCP references
MCP references
wspc exposes its agent surface — todo, calendar, email, projects, org — through a single remote MCP server at https://mcp.wspc.ai/mcp. Any host that speaks MCP (Claude Desktop, Hermes, Cursor, a custom orchestrator) can register the URL, complete the OAuth handshake once, and then read and write the same workspace a human would through the web console.
This page is a quick reference: which tools exist, what each does in one line, and what a typical agent call looks like. The live tools/list response is the authoritative schema — fetch it any time the prose here disagrees.
// 01 — Connection
Connection
Point your MCP host at the server below. Step-by-step setup instructions for each client are in QUICKSTART.
| Field | Value |
|---|---|
| Name | wspc |
| URL | https://mcp.wspc.ai/mcp |
| Transport | Streamable HTTP (over HTTPS) |
| Auth | OAuth 2.1 Remote Flow |
The server advertises tools capabilities only. List tools with the standard tools/list request once the host has a valid bearer token.
// 02 — Authentication
Authentication
Every MCP request carries Authorization: Bearer <token>. Tokens come from the OAuth 2.1 remote flow — dynamic client registration, browser-driven authorization, and an email magic code for proof of identity. The MCP host runs the dance on first connect and again whenever the access token expires.
When a request arrives without a token, or with one the server cannot verify, the response is 401 Unauthorized with a WWW-Authenticate header pointing at https://mcp.wspc.ai/.well-known/oauth-protected-resource. MCP hosts that support OAuth discovery use that URL to relaunch the flow automatically.
The single issued scope is wspc:full — root-level access for the authenticated user. Finer-grained scopes are not available yet.
// 03 — Tools
Tools
Seventy-seven tools across seven domains. Each one accepts a JSON object input and returns either a structured result or an isError payload with a stable error code. The summaries below are intentionally short — fetch tools/list for full input schemas, required fields, and the exact error codes each tool can return.
Todo
Tasks with hierarchy, status, due dates, optimistic locking, and project-scoped types with custom fields.
| Tool | Summary |
|---|---|
todo_get | Get one todo by id. |
todo_list | List todos. Filter by status, due window, project, parent, user. |
todo_create | Create a todo. Parent, due date, status, type, custom fields, optional idempotency_key for safe retries. |
todo_update | Update a todo. Partial; optional expected_version optimistic lock. |
todo_delete | Soft-delete a todo. Optional cascade to descendants. |
todo_restore | Restore a soft-deleted todo. Optional cascade. |
todo_type_create | Create a todo type with optional custom-field schema. |
todo_type_list | List todo types in a project. |
todo_type_get | Get one type with its schema and version. |
todo_type_update | Update a type’s label, hidden core fields, or custom-field schema. |
todo_type_delete | Soft-delete a type. The current default type cannot be deleted. |
todo_type_restore | Restore a soft-deleted type. |
todo_rule_create | 建立 recurring rule。可用 assignee_user_id 指定目前 Workspace Member;省略時使用 creator。建立後不能修改 assignee,所有 occurrences 與 subtasks 都使用固定 assignee。支援 type_id;省略時使用 project default type。 |
todo_rule_get | Get one recurrence rule with its template todo snapshot (type_id + custom fields) and the count of materialized instances. |
todo_rule_list | 列出指定 project 的 rules,包含 type_id、creator user_id、assignee_user_id 與 assignee_status。user_id 篩選仍代表 creator。 |
todo_rule_delete | Delete a recurrence rule. Stops future materialization and soft-deletes the template; already-materialized instances are kept. Optional expected_version optimistic lock. Destructive. |
todo_comment_create | Add a comment to a todo. Fields: todo_id, content (max 10 000 chars). Author is the authenticated user. |
todo_comment_list | List comments on a todo. Fields: todo_id, optional order (asc | desc, default asc), optional include_deleted. |
todo_comment_update | Edit a comment’s content. Fields: id, content. |
todo_comment_delete | Soft-delete a comment. Fields: id. No restore path. |
Calendar
calendar_list_events 的 q 以 literal substring 搜尋 title、description、location,支援長字串與 UTF-8。ASCII 比對不分大小寫;%、_ 是一般字元,不是 wildcard。
Imported Event 與 Agenda single item 的 invitation 保留外部 UID/Organizer。calendar_update_event 對 Imported Event 回 IMPORTED_EVENT_READ_ONLY;delete/restore 只切換 Calendar Trash,不寄 attendee 通知。email_get 保留 optional calendar_sync,只有具備 Calendar read 權限才包含 event_id。同步結果描述該封通知當時的處理,不代表 Event 目前狀態。此版收件同步預設停用,待 provider 驗證後才啟用。
Calendar 支援 attendees、ISO 8601 時間、跨 DST 的 IANA time-zone Series、iCalendar 匯出與 optimistic locking.
Todo, project, calendar, and email tool calls participate in the same per-database consistency bookmark contract as their HTTP APIs. MCP clients should store and resend each returned bookmark header independently: x-cb-todo for todo/project tools, x-cb-cal for calendar tools, and x-cb-email for email tools. The bookmark values are opaque transport headers; they are not included in MCP tool output bodies.
| Tool | Summary |
|---|---|
calendar_get_event | Get one calendar event by id. |
calendar_list_events | List events. Filter by date range, free-text query, deleted, past. |
calendar_list_event_occurrences | Expand one recurring series in a required bounded half-open window. |
calendar_list_agenda | Merge overlapping single Events and recurring Occurrences in a bounded view-zone window. |
calendar_update_event_occurrence | Reschedule one immutable Recurrence ID. |
calendar_cancel_event_occurrence | Cancel one Occurrence without cancelling its Series Master. |
calendar_restore_event_occurrence | Remove one Occurrence Exception and inherit the Series Master again. |
calendar_get_event_ics | Return RFC 5545 iCalendar text for one event. |
calendar_create_event | Create an event or recurring series. ISO 8601 times, attendees, all-day, optional RRULE and idempotency_key. |
calendar_update_event | Update an event or whole series. Empty recurrence_rule clears recurrence; optional optimistic lock. |
calendar_delete_event | Soft-delete an event. Recoverable. |
calendar_restore_event | Restore a soft-deleted event. |
私人 Calendar 訂閱
使用 calendar_get_subscription 讀取目前狀態,calendar_create_subscription 建立或重新取得相同的有效 URL。兩者不需要輸入;結果為 { enabled, version, url },停用時 url 是 null。calendar_reset_subscription 與 calendar_disable_subscription 需要 { expected_version },且標為 destructive。版本過時回 VERSION_CONFLICT;不確定上次操作結果時,先 get 最新狀態再重試。管理操作只使用目前帳號及 Workspace,不接受 caller 指定 owner。
URL 可讀取該使用者在目前 Workspace 的所有過去與未來 Events,含取消狀態及 recurrence,排除 Trash;取得 URL 的人都能讀取完整活動內容,請勿公開分享。Reset 立即使舊 URL 失效並要求外部 calendar 重新訂閱;disable 停止存取,但無法刪除外部已保存的副本。訂閱 token 不能作為一般 REST/MCP Bearer credential。功能未啟用時回 SUBSCRIPTION_UNAVAILABLE。
Google Calendar 電腦版使用 Other calendars → From URL 貼上私人 URL。外部更新時間由 calendar app 決定,可能延遲;下載再 import 檔案只會產生一次性副本。
Inbound mail under user-controlled aliases, outbound send with attachments, batch read / unread / delete.
| Tool | Summary |
|---|---|
email_list | List received emails. Cursor pagination (default 20, max 100). |
email_get | Get one email by id. Text body by default; pass include_html for HTML source. |
email_mark_read | Mark one or more emails as read. |
email_mark_unread | Mark one or more emails as unread. |
email_delete | Batch soft-delete emails (move to trash). |
email_restore | Batch restore soft-deleted emails. |
email_attachment_get | Download an attachment by index. Returns base64; max 5 MB. |
email_send | Send a plain-text email from a user alias. Fresh sends accept distinct to, cc, and bcc recipient arrays; replies reject non-empty cc or bcc. Verified custom-domain aliases route through their active provider (Resend for new domains; pete-mail for existing Pete bindings). New, reply, or re-attach inbound files. |
email_get 與 email_send 都會回傳 { email, attachments, attachment_availability };email_send 另含 idempotent_replay。每個 attachment item 只含 idx、filename、mime_type 與 size_bytes,不會暴露 storage key 或 digest。當保存的 metadata 數量與 email.attachment_count 不一致時,attachment_availability 是 unavailable 且 attachments 是空陣列;仍可用 email_attachment_get 嘗試讀取特定 received 或 sent attachment 的內容。
Email aliases
Platform Email Domain @wspc.app 的地址依 Current Workspace tier 計算容量:Free 3、Personal 10、Startup 40、Business 200。同一上限也適用於 Workspace 的 rolling 30-day creation budget;刪除釋放 active capacity,但不會重設建立額度,restore 也不算新建立。額度耗盡時回 ALIAS_CREATION_LIMIT_EXCEEDED;custom-domain aliases 不使用此額度,維持每位使用者最多 10 個 active aliases。Alias identifier 是完整 email address,可使用 @wspc.app 或同 organization 已完整 verified 且 enabled 的 Custom Email Domain。平台 local part 維持 5–32 字元;custom domain 接受 1–64 字元,例如 me@example.com。兩者皆以英數開頭,僅允許英數、點、底線與連字號,並轉成 lowercase;reserved words 只限制平台地址。Custom domain 未註冊於目前 organization 時回 ALIAS_DOMAIN_NOT_FOUND;verification 尚未完整或 domain 受限時回 ALIAS_DOMAIN_NOT_READY。Billing 失敗時回 EMAIL_ENTITLEMENTS_UNAVAILABLE,不建立地址。刪除後停止收取新信、釋放容量並保留歷史郵件。
| Tool | Summary |
|---|---|
email_alias_create | Create an alias under @wspc.app or a fully verified and enabled organization custom domain. Platform addresses use the Workspace tier capacity and rolling creation budget. |
email_alias_list | List the user’s aliases. Active only by default. |
email_alias_delete | Soft-delete an alias. Stops accepting new mail; existing emails preserved. |
email_alias_restore | Re-enable a deleted alias. Counts toward the current Workspace tier capacity. |
Email domains
Startup and Business Workspaces can register one unverified custom-domain candidate at a time, manage DNS verification records, and delete unused domains. Free and Personal cannot register custom domains. New domains use Resend for sending and receiving; existing Pete bindings remain on pete-mail. Verification does not by itself guarantee mail use: the domain must also be enabled by the effective Workspace entitlement. Entitlement loss restricts excess domains without deleting domains, addresses, or messages. Inbound delivery is handled by an operator-managed provider webhook; there is no MCP tool for webhook registration.
DNS records may include a purpose hint such as identity_verification, dkim, mail_from, receiving_mx, or dmarc so operators know why each record is required.
| Tool | Input | Summary |
|---|---|---|
email_domain_create | { domain } | Register a domain and return the DNS records the organization must set. |
email_domain_list | {} | List registered domains for the caller’s organization. |
email_domain_get | { domain } | Get one registered domain, including cached DNS records and status. |
email_domain_verify | { domain } | Ask the provider to verify current DNS records and return the result. |
email_domain_delete | { domain } | Delete a domain when no active aliases use it. |
Drive
Organization-scoped file libraries: store, edit, move, version, and search text files and images.
| Tool | Summary |
|---|---|
drive_library_list | List drive libraries in the caller’s organization (cursor-paginated). |
drive_library_get | Get one drive library by id. |
drive_library_create | Create a drive library (an org-scoped file container). |
drive_library_update | Rename a drive library. |
drive_library_delete | Delete a drive library. The library must be empty; the server rejects deletion of a non-empty library. |
drive_file_list | List files in a drive library. Use path_prefix to scope to a folder. Returns paths, sizes, and versions, not contents. |
drive_file_read | Read a file’s current (or a specific historical) version. Text files return UTF-8 text; images return an image; other binaries return metadata only. |
drive_file_write | Create or overwrite a whole text file. Blind last-write-wins by default; prior versions are retained and restorable. |
drive_file_edit | Edit a text file by replacing old_string with new_string. old_string must match exactly and uniquely; set replace_all to replace every occurrence. Fails (EDIT_NO_MATCH) if the text isn’t found — re-read the file and retry. |
drive_file_move | 依原 entry_id、from_path、expected_entry_version 移至 to_path,保留內容與 history。目的 path 占用回 PATH_CONFLICT。 |
drive_file_delete | 依原 entry_id、path、expected_entry_version 建立 tombstone,保留 history;僅確認目前 tombstone 版本才回 unchanged。 |
drive_file_history | 讀取單一 active File entry 的保留版本,newest-first。Optional entry_id 鎖定選取的 identity;省略時只查當下 active path。不符回 VERSION_CONFLICT,不得省略 ID 重試;missing/deleted-only 回 NOT_FOUND,invalid input 回 VALIDATION_ERROR。回 path、entry_id、entry_version、current_version_id、versions,均來自同一 snapshot。此 read 不構成 restore 授權。 |
drive_file_restore | 以已確認的 entry identity 與 Entry Version 還原歷史 bytes,回傳 updated 或 unchanged。 |
drive_search | 搜尋 library 的文字檔案,回傳 matches(path/snippet)及 optional next_cursor;帶 cursor 續頁,末頁省略 next_cursor。 |
Move/delete 的 confirmation 全部 required,版本須為大於等於 1 的 safe integer。缺失/無效欄位回 VALIDATION_ERROR;Library 不可見回 NOT_FOUND;來源 identity、path 或版本改變回 VERSION_CONFLICT;適用 purge claim 回 PURGE_IN_PROGRESS。以刪除前版本重送不是 unchanged。Adapter 原樣轉送確認,conflict 後須重新檢視並取得使用者確認,不得以 fresh lookup 替換 identity/版本自動重送。Response 遺失時先按 identity 讀回,無法判定就保留 unknown。
Drive 歷史還原
drive_file_restore 的 required fields 為 library_id、entry_id、path、expected_entry_version、version_id。Entry Version 必須是大於等於 1 的 safe integer;它不是 retained version 的 ID 或 version_number。使用 manifest 中經使用者確認的 entry identity/version,不得自動讀最新值補上缺欄位,舊格式會被拒絕。
VERSION_CONFLICT 表示確認後 entry 已改變、移動、刪除或被另一 entry 取代,需重新檢視並取得確認;同 hash 也不豁免。Source 必須屬於同一 entry/Library/Workspace;不存在回 FILE_NOT_FOUND,已有 purge claim 回 PURGE_IN_PROGRESS。成功結果只可能是 updated 或 unchanged,不會建立或復活 entry。先確認 live tools/list 的 required fields,再啟用依賴此保護的 flow。Timeout 後讀回 identity、Entry Version、current version 與 hash 判斷結果,不自動換新確認重送。
Drive 搜尋續頁
drive_search 首次傳 { "library_id": "<id>", "query": "launch plan", "limit": 20 },續頁加入前頁 next_cursor 作為 cursor。保持 Workspace、library 與 query 逐字相同;沒有 next_cursor 就停止。limit 為 optional integer,預設 20、夾至 1–50,可在續頁調整;非整數或錯誤型別回 input validation error。
Search Cursor 無 TTL,與 D1 bookmark 不可互換;沿用 x-cb-drive bookkeeping。索引不變時按 relevance/內部 file ID 翻頁不重不漏;包括其他 library 在內的索引變動可能造成重複或遺漏,這不是 snapshot 或完整匯出。時間或 anchor 刪除不使 cursor 失效。VALIDATION_ERROR 應重新搜尋,NOT_FOUND 應停止並處理 library 存取權;每頁都重新檢查目前權限。網路錯誤保留舊結果與 cursor,成功才追加;不要無限 retry。HTTP 使用方式見 CLI 文件的 HTTP example。
Projects
Org-scoped containers for todos. Each project defines a default todo type for new work.
| Tool | Summary |
|---|---|
todo_project_get | Get one project by id. |
todo_project_list | List projects in the caller’s organization. |
todo_project_create | Create a project in the caller’s organization. |
todo_project_update | Update project attributes (name, default todo type). |
todo_project_delete | Archive the project and active user-visible todos atomically. Keep recurrence templates. |
todo_project_restore | Restore the project. Keep todos in Trash and resume future recurrence. |
Org
Organization metadata. In v1 every user belongs to a single personal organization.
| Tool | Summary |
|---|---|
auth_me | Get the authenticated user’s own identity (user_id, email, display_name). |
auth_org_get | Get metadata for the authenticated user’s organization. |
auth_org_members_list | List organization members (paginated). v1 returns just the caller. |
auth_org_rename | Rename the authenticated user’s organization. |
auth_org_invite_create | Invite an email to the Workspace. Pending invites do not use member capacity; create can fail when the Workspace Member Limit is reached. |
auth_org_invites_list | List invites issued by your organization. |
auth_org_invite_revoke | Revoke a pending invite. |
auth_invites_list | List invites addressed to your email. |
auth_invite_accept | Accept an invite and switch Workspace. Capacity is checked atomically; a limit failure leaves the invite available to retry. |
auth_invite_reject | Reject an invite. |
auth_org_member_remove | Remove a member from the organization (or leave). |
Member removal tool details
- Tool Name:
auth_org_member_remove - Description: Remove a member from the authenticated user’s organization, or leave the organization (self-removal).
- Input Parameters:
id(string, required): The ID of the user to be removed (e.g.usr_01KT4FF78A1F9CT5SWN5HK392F).
- Usage & Safety Warning:
- Revert Behavior: Removing a member reverts their active organization back to their previous organization (
previous_org_id). - Self-Removal (Leave): Any user (except the creator of the organization) can remove themselves, which is semantically equivalent to leaving the organization.
- Creator Protection: The creator of the organization (
created_by_user_id) cannot be removed. - Revert Constraint: A user cannot be removed if their
previous_org_idis null, protecting against “homeless” states.
- Revert Behavior: Removing a member reverts their active organization back to their previous organization (
Guide
| Tool | Summary |
|---|---|
guide_start | Return the guided-tour script; call it when the user asks to start the wspc tour. |
// 04 — Examples
Examples
Three sketches of what an agent does when a human asks it something natural. The shape — utterance, tool call, a note — repeats. The tool names and field names track real MCP inputs; the exact JSON the host sends is what tools/list describes.
Schedule a lunch
“Book lunch with Alice tomorrow at 12:30, one hour, at Taipei HQ. Her email is alice@example.com.”
calendar_create_event({
title: "Lunch with Alice",
start: "2026-05-27T12:30:00+08:00",
end: "2026-05-27T13:30:00+08:00",
location: "Taipei HQ",
attendees: [{ email: "alice@example.com", display_name: "Alice" }],
idempotency_key: "lunch-alice-20260527-1230"
})
start and end are ISO 8601 datetimes with offset. The agent resolves the user’s “tomorrow” against the local timezone before constructing the call; the server itself only accepts ISO. An iCalendar invite is dispatched to the attendee on creation. For create retries, send a stable idempotency_key; the same key with the same payload returns the existing event, while a changed payload returns IDEMPOTENCY_KEY_REUSED.
For a recurring series, pass recurrence_rule as an RFC 5545 RRULE value without the RRULE: prefix. All-day series require date-only start/end and DATE UNTIL. Timed series may use UTC, or provide time_zone with matching offset-bearing local start/end; timed UNTIL remains UTC. The server returns a canonical rule and canonical IANA zone. On update, omit the fields to preserve them, pass an empty recurrence_rule to clear recurrence and its zone, or pass an empty time_zone to preserve the instants and return the series to UTC semantics. Update, cancel, delete, and restore affect the whole series; list calls return the persisted series master without expanding occurrences.
Triage the open backlog
“What todos do I still have open this week?”
todo_list({
status: ["open", "in_progress"],
due_after: "2026-05-26",
due_before: "2026-06-02"
})
The agent maps “this week” to a half-open [due_after, due_before) window and filters by the two active statuses. Both bounds are date-only ISO strings; due_after is inclusive, due_before is exclusive.
Clear the unread queue
“Mark all my unread emails as read.”
email_list({ unread_only: true })
// → [{ id: "em_abc123", ... }, { id: "em_def456", ... }]
email_mark_read({ ids: ["em_abc123", "em_def456"] })
email_list returns unread items in cursor-paginated batches (default 20, max 100). email_mark_read takes a batch of ids in one call. If the inbox has more than limit unread emails, the agent re-calls email_list with the returned next_cursor until exhausted.
Recurring task with a checklist
“Every morning I do a standup: write notes, then post the summary. Set that up as a daily task.”
todo_rule_create({
title: "Daily standup",
rrule: "FREQ=DAILY",
dtstart: "2026-06-10",
project_id: "prj_abc"
})
// → { rule: { id: "tdr_xyz" }, template_todo_id: "tod_tmpl", ... }
todo_create({ title: "Write notes", parent_id: "tod_tmpl", project_id: "prj_abc" })
todo_create({ title: "Post summary", parent_id: "tod_tmpl", project_id: "prj_abc" })
A rule owns a hidden template todo. To make the same subtasks appear on every occurrence, the agent attaches them to the template — not to one day’s instance. The template id comes back as template_todo_id from todo_rule_create (or as template.id from todo_rule_get). Adding a child under the template re-materializes future occurrences so each daily todo carries the checklist. Subtasks are one level deep; attaching to a specific day’s instance would only affect that day.
// 05 — Reference
Reference
- Live
tools/list—POST https://mcp.wspc.ai/mcpwith atools/listJSON-RPC body. Returns the canonical name, description, and JSON Schema input for every registered tool. Treat the response as the source of truth whenever this page disagrees. - HTTP equivalents — every MCP tool here maps to one or more REST endpoints documented in API REFERENCES.
- CLI equivalents — the same surface from a terminal lives in CLI REFERENCE.
- Setup walkthrough — setup steps for each client are in QUICKSTART.
// 06 — Troubleshooting
Troubleshooting
401 Unauthorizedon every call — the request carries no bearer token, an expired one, or a token issued for a different resource. The response includes aWWW-Authenticateheader pointing athttps://mcp.wspc.ai/.well-known/oauth-protected-resource; OAuth-aware hosts use it to relaunch the flow. If your host does not auto-reconnect, remove and re-add the server to redo the handshake.- Authorization never completes — the flow finishes in the browser with an email magic code. Make sure pop-ups are allowed for your host and that you can receive mail at the address you entered; enter the code to finish. Tokens expire, so an idle connection eventually re-prompts.
405 Method Not AllowedonGET /mcp— expected. The server is stateless and exposes no server-to-client SSE stream, so it answersGETwith405. UsePOSTfor JSON-RPC; compliant clients do this automatically.- A tool returns
isError— tools return a structured payload with a stablecode(for exampleVERSION_CONFLICT,INVALID_INPUT,NOT_FOUND) instead of throwing. Read thecodeto decide how to retry; forVERSION_CONFLICT, re-read the item and retry without a staleexpected_version. - A tool or field looks different from this page — the live
tools/listresponse is authoritative. Fetch it and trust it over the prose here.
Still stuck? Email contact@wspc.ai.
Recurring rule 的 assignee_status 為 valid、not_member 或 unknown,todo_rule_get 也會回傳。後兩者暫停產生與重建 occurrences,保留既有 Todos。建立時指定非成員會回傳 INVALID_ASSIGNEE;membership 驗證失敗則回傳 ASSIGNEE_CHECK_UNAVAILABLE,不建立 rule。MCP 沒有修改 rule assignee 的 input。