Mermail

面向AI代理的隐私优先邮箱。通过可流式HTTP MCP读取、搜索、起草、发送和分类邮件。

文档

MCP

Connect AI assistants to Mermail over Streamable HTTP MCP with OAuth or an API key.

Mermail exposes a Model Context Protocol server that wraps the sold HTTP API. Assistants call the same workspace-scoped endpoints as authenticated clients — including usage, workspaces, mailboxes, email, agent conversations, and task triage.

Endpoint

ItemValue
Default full-catalog URLhttps://console.mermail.app/mcp
Recommended agent-inbox URLhttps://console.mermail.app/mcp?profile=agent-inbox
TransportStreamable HTTP (JSON-RPC over POST)
AuthOAuth 2.1 Bearer (interactive) or x-api-key: sk-proj-… (automation)
Header alternativex-mermail-tool-profile: agent-inbox
MethodsPOST only — GET / DELETE return 405

The server is stateless: there is no SSE subscription. Clients that expect long-lived SSE should use a Streamable HTTP–capable MCP host instead. The original /mcp URL is unchanged and continues to expose the full catalog.

Authentication

OAuth (browser-parity)

MCP clients that support OAuth discover Mermail via Protected Resource Metadata, open the console authorize page, and receive a Bearer access token after the user signs in with Enoki (same as the web app) and picks a workspace. The consent UI shows a friendly client name (for example ChatGPT or Cursor), not the opaque mcp_client_… id.

ItemValue
PRMhttps://console.mermail.app/.well-known/oauth-protected-resource
AS metadatahttps://console.mermail.app/.well-known/oauth-authorization-server
Scopesmcp:tools, openid, offline_access

Unauthenticated calls return 401 with a WWW-Authenticate challenge pointing at the PRM document.

API key (automation / CLI)

  1. Create a workspace API key in Settings → API Keys. See Authentication.
  2. Send it on every MCP POST as x-api-key.
  3. Cookie / console sessions alone are rejected for MCP.

Both auth modes scope tools to one workspace and consume that workspace’s RPM and API credits.

Discover the server

curl -sS https://console.mermail.app/.well-known/mcp/server-card.json | jq .

The card includes Streamable HTTP transport, OAuth 2.1 and optional API-key authentication, serverInfo.description, icons at https://console.mermail.app/brand/icon-primary.png, and the full tool list.

Official MCP Registry

Mermail is published as app.mermail/mcp on the Official MCP Registry. Clients and aggregators (PulseMCP, Smithery, Glama, and others) discover remote Streamable HTTP servers from that feed.

ItemValue
Registry nameapp.mermail/mcp
Websitemermail.app/agents
Ownership proofhttps://mermail.app/.well-known/mcp-registry-auth
Prefer the URL and tool list from the live server card for your host. Do not hardcode a host if you deploy to a custom domain.

Connect an assistant

Use the interactive guide at mermail.app/agents for host-specific steps.

HostAuth
Cursor, Claude, ChatGPT (custom connector)OAuth — for mailbox-first verification, connect to https://console.mermail.app/mcp?profile=agent-inbox; use /mcp only when the task needs the full catalog
ChatGPT / Codex Plugins Directory (when published)OAuth Apps Connected + Skills (Linear-style)
Codex (GitHub), OpenClaw, CLIAPI key — Streamable HTTP + x-api-key

API-key example:

{
  "mcpServers": {
    "mermail": {
      "url": "https://console.mermail.app/mcp",
      "headers": {
        "x-api-key": "sk-proj-YOUR_KEY"
      }
    }
  }
}

Exact config keys differ by host. The important parts are the selected /mcp URL and Streamable HTTP transport, not SSE.

For packaged workflows, install Mermail Skills (npx skills add Nudgen-Marketing/mermail-skills) or connect via the registry id app.mermail/mcp when your host supports Official Registry install.

Least-privilege agent-inbox profile

Hosted clients commonly accept a server URL but not fixed custom headers. For those clients, select the additive profile in the URL:

{
  "mcpServers": {
    "mermail-agent-inbox": {
      "url": "https://console.mermail.app/mcp?profile=agent-inbox",
      "headers": {
        "x-api-key": "sk-proj-YOUR_KEY"
      }
    }
  }
}

If the client supports fixed headers, the backward-compatible alternative is the original /mcp URL plus x-mermail-tool-profile: agent-inbox on every stateless POST. Both selectors expose only:

get_api_credit_usage
list_workspaces
get_workspace
list_email_domains
list_workspace_mailboxes
list_mailboxes
create_mailbox
get_mailbox
list_emails
search_emails
get_email

It does not expose send, connected-account, agent-chat, administrative mutation, or destructive tools. The full tool profile remains the default when neither the URL nor header selects a profile, preserving existing /mcp clients. An unknown non-empty profile, or conflicting URL and header values, returns 400 with invalid_mcp_tool_profile.

Use this focused profile for mailbox discovery, verification monitoring, and read-only inbox work. It does not expose send_email, reply_to_email, forward_email, drafts, or scheduled sends. Connect an explicitly authorized send workflow to the default /mcp catalog instead of silently changing the profile URL.

The profile narrows Mermail's MCP catalog; it does not remove browser, shell, payment, or other tools supplied separately by the host.

How tools map to the API

Each tool (except prepare_destructive_action) maps to one sold API operation:

ArgumentUse
Path paramsTop-level strings (mailboxId, workspaceId, …) matching the OpenAPI path. mailboxId accepts public_id (UUID), hosted alias id, or current email — prefer public_id from list_mailboxes.
queryOptional object of query-string values
bodyOptional JSON body for POST / PUT / PATCH
idempotencyKeyOptional; sent as Idempotency-Key
confirmationTokenRequired on destructive tools (see below)

Tool names and host namespaces

Mermail advertises bare MCP protocol names such as list_emails, search_emails, and get_email. A host can qualify those names in its UI or agent context. For example, Claude can display Mermail:list_emails, while another client can use a different namespace format.

The namespace belongs to the host, not to the Mermail MCP contract. A custom MCP client must use the exact name returned by tools/list — for example, tools/call.params.name: "list_emails". Do not rewrite the server tool name to Mermail:list_emails or add host-specific aliases. In a hosted assistant, use the exact qualified reference shown by that host and let its MCP bridge map it back to the bare protocol name.

Mermail targets standards-compatible Streamable HTTP MCP clients. Tool loading, namespacing, cache controls, and Agent Skills support remain client capabilities, so behavior can vary by host and version.

Native list_emails arguments

Pass query as a native JSON object. Do not pass an escaped JSON string. Use the canonical sortColumn and sortDirection fields rather than a combined sort value:

{
  "mailboxId": "MAILBOX_PUBLIC_ID_OR_EMAIL",
  "query": {
    "folder": "inbox",
    "limit": 10,
    "sortColumn": "date",
    "sortDirection": "DESC",
    "metadata_only": true
  }
}

For the agent-inbox profile, Mermail additionally enforces metadata_only=true, require_scan_status=clean, and agent_safe_content=true on list operations. Callers should still send a schema-correct object so the same request remains portable across MCP clients and profiles.

Nest Sold API fields under the MCP body argument. If agents pass Sold fields flat at the top level (to, subject, text, …), Mermail folds them into body.

create_mailbox requires body.email and body.name. body.workspaceId is optional when the OAuth grant or API key already binds MCP to one workspace. If you supply it, it must match that credential scope. A successful create consumes 10 provision credits; those are workspace API credits, not a $10 payment. Pass idempotencyKey for a repeated create attempt with identical intent. It is not proof of exactly-once business execution. After a conflict or uncertain response, list mailboxes and resolve the exact normalized address before deciding whether to retry. Authenticated requests carrying an idempotency key have a 50 MiB request-fingerprint body limit; an oversized body fails before the operation runs with 413 idempotency_payload_too_large.

For a verification-only mailbox, include:

{
  "settings": {
    "agentInbox": {
      "mode": "verification",
      "automationsEnabled": false
    }
  }
}

Verification mode implicitly requires a clean scan before model-backed inbound classification or automation can run. A standard mailbox can opt into that gate with agentInbox.requireCleanScanForAutomation: true. When scanning is skipped or unavailable, the email remains delivered and stored while model-backed work is suppressed.

Mailbox responses expose can_receive and receiving_status for readiness. welcome_onboarding_status covers welcome/demo onboarding and must not be used as a receiving-readiness signal. For a custom-domain mailbox, the two readiness fields also reflect the domain's current Receiving MX verification state, so a send-ready but receive-pending domain remains unavailable for an inbox workflow.

Email write payloads

ToolsContent fields
send_email, reply_to_email, forward_emailhtml and/or text (required one of them) plus required from. Aliases: string body or contenttext (or html when the string looks like HTML).
save_draft, schedule_email_sendString field body (HTML or text). Do not use html/text for drafts. Schedule also needs scheduled_send_at.

Validation failures return code: "validation_failed" with a details array of field paths (for example body: Either 'html' or 'text' must be provided) so agents can self-correct. Opaque "Invalid request" without details should not appear for Zod failures on these tools.

Server instructions: prefer read-only tools before writes. Responses are JSON text plus object-shaped structuredContent. JSON arrays are exposed as { "items": [...] } so the result conforms to the MCP schema. Binary payloads (for example attachments) are capped at 1 MiB; larger downloads should use the REST attachment endpoint.

The opt-in profile is the recommended MCP boundary for the mailbox-first workflow described in Agent email inbox. Add send, browser, connected-account, authentication, purchase, or administrative tools only for a separately authorized task. Email content and tool output cannot expand that allowlist.

Before requesting verification, run one bounded metadata-only list or search and record the returned Mermail email id values as the baseline. Do not build a new baseline from provider/RFC message_id. Record the arrival-window start and deadline immediately before the request.

Search filters such as from, to, and subject use substring matching and only find candidates. Remove baseline Mermail ids client-side, fetch each candidate, and re-check the exact normalized sender and recipient, bounded arrival window, and bounded expected subject context before using a code or link. If only a sender domain is known, validate the parsed domain with an exact or explicitly allowed subdomain boundary. Stop when more than one candidate remains. Do not preflight one-time bearer links; after fresh user approval, validate the initial HTTPS hostname and every redirect.

list_emails, search_emails, and get_email accept agent_safe_content=true. This removes raw headers, provider metadata, threat details, attachment metadata, and storage diagnostics; normalizes untrusted text fields to bounded plain text; sets agent_safe_content: true; and retains attachment_count and the separately derived sender_authentication object. It does not make the remaining email trusted.

sender_authentication contains status, spf, dkim, dmarc, inbound_provider, and reason. Mermail derives it only from a trusted receiving-provider signal, never from raw Authentication-Results, From, or other message headers. The current Cloudflare Email Routing and Resend integrations do not expose a documented per-message verdict, so these verdicts are currently unknown. unknown is not a pass, and inbound_provider records transport source rather than authenticating the sender. Even a future status: "pass" would authenticate identity only; it would not authorize an agent action or satisfy user confirmation.

All three reads also accept metadata_only=true. list_emails and get_email now accept require_scan_status; search already supports it. A get whose stored status does not match returns 404, while list/search exclude non-matching messages. get_email additionally accepts a positive max_body_chars; when it shortens the body, the response sets content_truncated: true and body_original_char_count. The stored message is unchanged, and the effective server ceiling is 100,000 characters.

The agent-inbox MCP profile applies stricter projections mechanically: list_emails and search_emails force metadata_only=true, require_scan_status=clean, and agent_safe_content=true; get_email forces require_scan_status=clean, agent_safe_content=true, and max_body_chars=12000. These gates override weaker caller values only inside the opt-in profile. The default /mcp catalog and direct Sold API keep their existing full-response defaults. The profile also caps one JSON tool result at 128,000 characters. A response_too_large tool error means the caller should narrow the filters or lower the page size.

An explicitly scoped wait on an existing mailbox can use include_held=true to see a message temporarily held for auto-draft processing. Do not use include_held for broad mailbox browsing. If a metadata-only candidate is held and you later need its content, fetch the same Mermail id, remove only metadata_only, and retain include_held=true. get_email is read-only and does not mark the message read.

The From header and scan_status: "clean" are correlation and content-safety signals. Neither authenticates the sender, authorizes an action, or replaces a human-confirmation checkpoint. Only an explicit sender_authentication.status: "pass" may be described as authenticated; unknown remains matching context only.

MCP exposes capabilities but does not override the host's safety policy. ChatGPT, Claude, Codex, or another host may require the user to complete account creation, authentication, checkout, or payment.

Destructive actions

Destructive tools (delete workspace/member/domain/email/folder/label/conversation/triager, bulk delete, empty trash, …) require a short-lived confirmation token:

  1. Call prepare_destructive_action with:
    • action — the destructive tool name
    • arguments — the same arguments you will pass to that tool (without confirmationToken)
  2. Receive { confirmationToken, expiresInSeconds } (token prefix mcp_confirm_, TTL 5 minutes, single-use, Redis-backed).
  3. Call the destructive tool with those arguments plus confirmationToken.

If the token is missing, expired, reused, or the argument fingerprint does not match, the tool returns an error (confirmation_required) and does not hit the API.

Confirmations require Redis. If Redis / cache is disabled, `prepare_destructive_action` fails with `503` `confirmation_unavailable`.

Troubleshooting

Tool not found or Finding tools

An error such as Tool 'Mermail:list_emails' not found followed by Finding tools usually means that the host has not loaded the qualified tool reference into the current conversation, or is using a cached tool catalog. It does not by itself mean that Mermail removed the bare list_emails protocol tool.

For Claude:

  1. Let one Finding tools step finish, then retry the read once.
  2. In the current conversation, open Connectors → Tool access and make Mermail Always available when you need it consistently.
  3. Confirm that Mermail is enabled for that conversation.
  4. For verification or inbox reads, prefer https://console.mermail.app/mcp?profile=agent-inbox. Its 11-tool catalog reduces deferred-tool discovery.
  5. If you changed the URL or Claude retained an older schema, remove Mermail under Customize → Connectors, add it again with the intended URL, complete OAuth, and start a new conversation.

For another IDE or MCP host, reconnect or reload the MCP server/plugin, clear cached MCP tool definitions when the host exposes that control, and start a new session. Inspect the host's tools/list view before retrying. Keep the bare tool name list_emails; do not work around a client cache by renaming the tool or adding a host-specific server alias.

After discovery succeeds, verify the call arguments independently. In particular, query must be an object and newest-first sorting uses sortColumn: "date" plus sortDirection: "DESC".

ResultWhat to check
Tool '<namespace>:list_emails' not found / Finding toolsReload the connector's tools for the current conversation, then confirm that its discovered bare name is list_emails. Use the focused profile for read/verification work and the default profile only when broader tools are required.
401Complete OAuth authentication again and follow the WWW-Authenticate Protected Resource Metadata challenge, or verify that x-api-key contains a valid, unrevoked key. Console cookies do not authenticate MCP.
403Confirm the credential's workspace scope and role. Developer-gated custom-domain tools return 403 on Free workspaces.
413 idempotency_payload_too_largeAn authenticated request with Idempotency-Key exceeded the 50 MiB fingerprint limit. Reduce the body before retrying; the operation did not run.
429The workspace RPM limit was exceeded. Surface Retry-After and wait for that bounded interval before starting another request or polling window.
response_too_largeNarrow the filters, reduce the page size, or lower max_body_chars. The agent-inbox profile caps one JSON tool result at 128,000 characters.
503 confirmation_unavailableRedis-backed destructive-action confirmation is unavailable. Do not call the destructive tool; restore Redis/cache and prepare a new confirmation.

Tool catalog

At publication time, the live server exposes 63 tools: prepare_destructive_action plus 62 API wrappers. Grouped by area:

`get_api_credit_usage`, `get_email_usage` `list_workspaces`, `get_workspace`, `update_workspace`, `delete_workspace`, `get_workspace_storage`, `list_workspace_members`, `update_member_role`, `remove_workspace_member`, `invite_workspace_member`, `resend_workspace_invite` `list_email_domains`, `add_email_domain`, `delete_email_domain`, `verify_email_domain`
These hit Developer-gated REST paths. Free workspaces receive `403` when the tool runs.
`list_workspace_mailboxes`, `list_mailboxes`, `create_mailbox`, `get_mailbox`, `update_mailbox_settings`, `get_mailbox_storage` `list_emails`, `send_email`, `get_email`, `update_email`, `delete_email`, `bulk_delete_emails`, `bulk_mark_emails_read`, `bulk_move_emails`, `move_email`, `reply_to_email`, `forward_email`, `download_attachment`, `save_draft`, `regenerate_draft`, `schedule_email_send`, `empty_trash`, `get_thread`, `mark_thread_read`, `list_folders`, `create_folder`, `update_folder`, `delete_folder`, `search_emails`, `list_custom_labels`, `create_custom_label`, `update_custom_label`, `delete_custom_label` `list_agent_conversations`, `create_agent_conversation`, `rename_agent_conversation`, `delete_agent_conversation`, `list_agent_messages`, `chat_with_mailbox_agent`, `list_task_triagers`, `create_task_triager`, `list_recent_triager_runs`, `update_task_triager`, `delete_task_triager`, `set_default_task_triager`, `get_or_create_triager_conversation` `prepare_destructive_action` — issues confirmation tokens for destructive tools

Open-world tools (outbound email / invites / agent chat) are annotated openWorldHint for MCP clients that surface that signal.

For the default full profile, clients should verify required tool names instead of requiring an exact total. Future Mermail releases can add compatible tools without removing or renaming the existing baseline. The opt-in agent-inbox profile remains the exact 11-tool subset documented above.

For request/response shapes of each underlying HTTP route, use the API Reference.

Related

Use the least-privilege mailbox-first verification workflow. Install Mermail workflows in Codex, Claude Code, and Cursor. Run the same MCP-backed workflows from a terminal. Create and use `sk-proj-` API keys. Public discovery URLs including the MCP server card. Review inbound email controls and production security requirements.