API key
For your backend or a headless agent. Send it as a bearer token. Limit it to the accounts it needs; account tokens (wat_) are refused.
A hosted Model Context Protocol server for the Gmail, Microsoft 365 and IMAP accounts you connect. Agents search and read mail, draft replies, manage events and contacts. Scopes decide which tools exist, and sends are confirmed by default.
# Sign in with OAuth: pick the workspace, scopes and accounts
claude mcp add --transport http workwae https://api.workwae.com/mcp Tool calls go through the same API as /v1: scopes, plan limits, rate limits and metering apply as they do there, and every call, reads included, lands in the audit log under the surface MCP.
| Property | Value |
|---|---|
| URL | https://api.workwae.com/mcp |
| Transport | Streamable HTTP, stateless |
| Protocol versions | 2026-07-28, 2025-11-25, 2025-06-18 |
| Authentication | An API key, an MCP key, or OAuth |
| Tools | 32, listed to a client only when its credential's scopes allow them |
| Prompts | 5, offered as slash commands |
| Extensions | MCP Apps views (message review, meeting time picker); MCP Events by webhook on 2026-07-28 |
| Discovery | Server card at /mcp/server-card, readable without a token; listed in the MCP Registry as com.workwae/mcp |
The credential decides the scopes, and the scopes decide which tools the client sees.
For your backend or a headless agent. Send it as a bearer token. Limit it to the accounts it needs; account tokens (wat_) are refused.
For a person with a console login, in Claude, ChatGPT, Claude Code, Cursor or VS Code. The client needs only the URL. The user picks the workspace, the scopes (read, write, send, capped by their role) and the accounts.
For your own users, who have no console login. Mint one per connected account; it works only at /mcp and answers mcp_only_key anywhere else.
claude mcp add --transport http workwae https://api.workwae.com/mcp \
--header "Authorization: Bearer $WORKWAE_API_KEY" // ~/.cursor/mcp.json
{
"mcpServers": {
"workwae": {
"url": "https://api.workwae.com/mcp",
"headers": { "Authorization": "Bearer ${env:WORKWAE_API_KEY}" }
}
}
} # ~/.codex/config.toml
[mcp_servers.workwae]
url = "https://api.workwae.com/mcp"
bearer_token_env_var = "WORKWAE_API_KEY" POST https://api.workwae.com/mcp
Authorization: Bearer <API key or MCP key>
Content-Type: application/json Use an MCP key in place of the API key in any of these. Claude.ai and ChatGPT custom connectors sign in only with OAuth, so they take the URL and no key.
Add the URL without a header. The client sends you to the Workwae console to sign in and consent; Claude, Claude Code, ChatGPT and VS Code publish a client ID metadata document, so they connect without registering. Access tokens last 15 minutes, and every request is checked against the grant and your current role, so revoking it, lowering your role or leaving the workspace takes effect at once. Revoke a grant under Connected apps in the console.
Scopes do not imply each other: a credential that sends mail needs send, and also read if it should find mail first. admin is never granted to an MCP client, and no tool here needs it.
curl -X POST "https://api.workwae.com/v1/accounts/$ACCOUNT_ID/mcp-keys" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{ "name": "Claude", "scopes": ["read", "write"] }'OAuth asks for the same scopes on the consent screen. For an API key, tick them in the console under API keys.
No scope selected, so a client sees no tools.
| Tool | Area | What it does |
|---|---|---|
read Look, never change. | ||
list_accounts | Accounts | The accounts the credential reaches: id, provider, status, time zone, capabilities and send-as addresses. |
get_current_time | Accounts | The current date, weekday and time in a zone. |
list_changes | Accounts | What changed across the accounts (messages, events, contacts, sends), oldest first, by cursor. |
list_mailboxes | An account's folders or Gmail labels, with ids, roles and counts. | |
search_messages | Find or list mail by text, sender, recipient, date, flags, attachments or mailbox. With no account named, searches every account the caller reaches. | |
get_message | One message: body as text, recipients and attachment list. | |
list_threads | Conversations by latest activity, optionally in one mailbox. | |
get_thread | A whole conversation, oldest first, in one call. | |
get_attachment | Downloads an attachment, up to 10 MiB. | |
list_calendars | Calendar | An account's calendars: id, name, primary, editable and time zone. |
list_events | Calendar | Lists or searches events overlapping a range, recurring occurrences included. |
get_event | Calendar | One event in full: description, attendees and their answers, join link, recurrence rule. |
find_availability | Calendar | When the given people and the account are busy, and free slots they share. |
pick_meeting_time | Calendar | find_availability with a slot picker, where the client shows MCP Apps views. |
search_contacts | Contacts | Finds contacts by name, email, phone or organization; returns full records. |
write Change data, and save drafts. Nothing here sends mail by itself. | ||
connect_account | Accounts | A link, valid an hour, where the user signs in to connect another Google, Microsoft 365 or IMAP mailbox. Refused for a key limited to accounts. |
create_draft | Saves a draft in the account's Drafts for the user to review and send; nothing goes out. | |
update_draft | Replaces a draft's whole content; the draft gets a new id. | |
delete_draft | Deletes a draft for good; nothing is sent. | |
mark_messages | Marks up to 50 messages, or a thread, read or unread, flagged or not. | |
move_messages | Moves up to 50 messages, or a thread, to a mailbox (including trash), or changes Gmail labels. | |
create_event | Calendar | Creates an event or recurring series, optionally with an online meeting. Needs send as well when it invites people outside the account. |
update_event | Calendar | Changes the fields given on an event or one occurrence. Needs send as well when it notifies attendees. |
delete_event | Calendar | Deletes an event or a series. Needs send as well when it cancels for attendees. |
respond_to_event | Calendar | Accepts, tentatively accepts or declines an invitation. Needs send as well when it tells the organizer or carries a comment. |
create_contact | Contacts | Adds a contact to the account's address book. |
update_contact | Contacts | Changes the fields given on a contact. |
send Anything that leaves the account as email. | ||
send_message | Sends an email now or at sendAt. Subject to the send policy and the user's confirmation. | |
send_draft | Sends a draft now or at sendAt. Subject to the send policy and the user's confirmation. | |
cancel_send | Cancels a queued send during the account's undo window or before its sendAt. | |
preview_message | Shows a message as it would go out, unsent; in clients with MCP Apps, as a card with Send and Edit. | |
commit_message | The review card's Send: sends what preview_message showed, once, within 10 minutes. | |
preview_message, commit_message and pick_meeting_time back the MCP Apps views; on 2026-07-28 they are offered only to clients that declare the extension. Calendar writes that email anyone outside the account follow the send policy like a send. Tools that act on one account take accountId (its id or email), which a credential limited to one account may leave out.
Clients list these as slash commands. A prompt is offered only to a caller holding every scope it needs, and mentions a writing tool only when the caller has its scope.
| Prompt | Needs | What it does |
|---|---|---|
triage_inbox | read | Sorts recent inbox mail into replies needed, actions, FYI and noise. |
draft_reply | read, write | Drafts a reply to a message for the user to review. |
schedule_meeting | read | Finds times everyone is free and, once the user picks one, books it. |
weekly_agenda | read | The week's events by day, with conflicts, open invitations and free blocks. |
summarize_thread | read | Positions, decisions, open questions and next steps of a conversation. |
The strongest control is the credential: without send, an agent cannot be talked into sending mail or meeting invitations, whatever the model does.
A client sees only the tools its scopes allow. Without send there is no send tool, and a calendar change that emails people outside the account is refused with missing_scope.
The default send policy is confirm: every send needs the user's confirmation of subject, recipients and account, in a form or on the review card. A client that can do neither cannot send, and neither can a headless agent on a backend API key.
cancel_send cancels a queued send during the account's undo window or before its sendAt. An identical send within 10 minutes is refused with duplicate_send, so a retried call does not send twice.
Mail, event and contact text comes back in a separate block fenced by a random marker, labeled untrusted, with invisible characters stripped. It helps the model; it is not a guarantee, so keep confirmation on for agents that read mail from strangers.
An API key or OAuth grant can be limited to specific accounts, and an MCP key is bound to one. A grant is for one workspace.
Every tool call is recorded in the audit log, reads included. Revoke an OAuth grant, an MCP key or an API key at any time.
Owners and admins set it under Settings, Agents (MCP), or with mcpSendPolicy in PATCH /v1/settings. It covers sends and calendar writes that email people through MCP; sends through /v1 are not affected.
| Field | Values | Effect |
|---|---|---|
mode | confirm (default)allowdrafts_only | confirm: every send needs the user's confirmation. allow: agents send, and clients that can ask still confirm each send. drafts_only: agents save drafts and never send. |
allowedRecipientDomains | Up to 100 domains, or null | Recipients must be in these domains, their subdomains or the sending account's own. For a public mailbox provider such as gmail.com, only the account's own addresses. |
dailySendLimit | 1 to 10,000, or null | Sends per credential per UTC day. |
externalRecipientConfirmation | true or false | A send to an address the account has never sent to needs the user's confirmation. |
curl -X PATCH "https://api.workwae.com/v1/settings" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{ "mcpSendPolicy": { "mode": "confirm", "dailySendLimit": 50 } }' A send the policy stops answers send_policy_refused with the rule (drafts_only, recipient_domain, daily_limit, confirm or external_recipient_confirmation) and tells the model to save a draft for the user instead. An MCP key can carry its own send policy on top of the workspace's; it can only tighten it.
admin is never granted over OAuth or to an MCP key.send scope. Nor can it email attendees, or the organizer, from a calendar change.confirm when it cannot ask you. A client with neither forms nor the review card cannot send.allowedRecipientDomains and sends past dailySendLimit are refused./v1 answers mcp_only_key.An illustration, not a recording: tool and argument names are the server's, while ids and values are made up. It assumes a one-account credential with read, write and send, the default send policy, and a client that can show a form.
Find Ada's unread email about the budget and draft a reply saying Thursday works.
search_messages{ "q": "budget", "from": "ada@example.com", "unread": true }
One unread message from Ada Lovelace, subject "Q4 budget", id msg_1.
get_message{ "messageId": "msg_1" }
create_draft{ "reply": { "messageId": "msg_1", "mode": "reply" }, "body": "Thursday works for me." }
Created draft drf_1 in account acc_1. To: ada@example.com From: me@example.com Subject: Re: Q4 budget
Looks right. Send it.
send_draft{ "messageId": "drf_1" }
Send to ada@example.com from me@example.com, subject "Re: Q4 budget"? [ ] Send this message
Tick Send this message, accept.
Queued send out_1 in account acc_1, status …, going out at …. It can be cancelled with cancel_send (outboxId out_1) until ….
Had the credential lacked send, the agent would have had no send_draft to call, and the draft would wait in Drafts. Under drafts_only, the call is refused with send_policy_refused and the same outcome.
Workwae is in early access; tell us what you are building, and read the MCP guide in the meantime.