// 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 scanning surface: which tools exist, what each does in one line, 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-four 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 | Create a recurring todo rule from an RFC 5545 RRULE. Optional type_id; omit to adopt the project’s default type. The type’s custom-field defaults are copied onto every instance; a required field with no default fails with MISSING_REQUIRED_FIELD. |
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 | List active recurring rules. Each item includes type_id (derived from the rule’s template). |
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
Events with attendees, ISO 8601 datetimes, RFC 5545 iCalendar export, and 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_get_event_ics | Return RFC 5545 iCalendar text for one event. |
calendar_create_event | Create an event. ISO 8601 times, attendees, all-day, optional idempotency_key for safe retries. |
calendar_update_event | Update fields of an event. Optional expected_version optimistic lock. |
calendar_delete_event | Soft-delete an event. Recoverable. |
calendar_restore_event | Restore a soft-deleted event. |
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. 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 aliases
Up to 10 active aliases per account. Alias identifiers are full email addresses under @wspc.app or a fully verified organization custom domain. Custom-domain alias creation returns ALIAS_DOMAIN_NOT_FOUND when the domain is not registered to the caller’s organization and ALIAS_DOMAIN_NOT_READY when sending or receiving verification is incomplete. Deletion stops new mail; history is preserved.
| Tool | Summary |
|---|---|
email_alias_create | Create an alias under @wspc.app or a fully verified organization custom domain. Up to 10 active aliases per account. |
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 10-alias limit. |
Email domains
Register organization custom domains, manage DNS verification records, and delete unused domains. New domains use Resend for sending and receiving; existing Pete bindings remain on pete-mail. Custom domains can be used for aliases once status, sending_status, and receiving_status are all verified. 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 | Move or rename a file within a library. Content and version history follow the file; fails with PATH_CONFLICT if the target path is occupied. |
drive_file_delete | Delete (tombstone) a file. Version history is retained. |
drive_file_history | List the version history of a file, newest first. |
drive_file_restore | Restore a previous version of a file as a new current version. |
drive_search | Full-text search file contents within a library (text files only). Returns matching paths with snippets; read a file for full content. |
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 | Soft-delete a project. |
todo_project_restore | Restore a soft-deleted project. |
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 your organization (sends an invite email). |
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 (switches your organization; previous-org data becomes invisible). |
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.
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.