MCP

Carte is a Model Context Protocol server. Connect it to Claude, ChatGPT, Cursor or any MCP client and ask questions of your inbox, open files, and send or reply on your behalf. Two ways in: the hosted server with OAuth (for people), and the stdio package with an API key (for scripts and agents).

Hosted server (OAuth)

Paste this URL into your assistant. It will send you to carte.sh to sign in and approve access, then connect. Reads use the business you own; sends and replies are attributed to you, the signed-in person, and note which app sent them.

url
https://api.carte.sh/mcp

Claude

Settings → Connectors → Add custom connector, paste the URL. Claude.ai, Desktop and mobile share the connection. In Claude Code:

sh
claude mcp add --transport http carte https://api.carte.sh/mcp

ChatGPT

Settings → Apps & Connectors → Create (developer mode), paste the URL as the MCP server, choose OAuth. Or add Carte from the app directory.

What you approve

  • read: list the inbox, read messages and threads, download files.
  • write: send messages and replies as you.

Disconnect at any time from Connected apps; the app's tokens stop working immediately. If you have not claimed a handle yet, read tools explain how; sending works for Workspace accounts, exactly as in the dashboard.

For implementers: the server is OAuth 2.1 with PKCE, RFC 9728 protected resource metadata at https://api.carte.sh/.well-known/oauth-protected-resource, RFC 8414 metadata at carte.sh/.well-known/oauth-authorization-server, Client ID Metadata Documents (preferred) and dynamic client registration, refresh-token rotation, and a resource parameter that must equal the URL above. Machine-to-machine grants are not offered; use an API key.

API key (stdio or hosted)

For programmatic use, mint a key at Agents & keys: create an agent for the assistant (say, claude) and a key under it. Sends and replies are attributed to that agent. A read key is enough unless the agent should send.

@carte/mcp runs over stdio with npx and needs Node 20+. CARTE_API_KEY is required; CARTE_API_URL defaults to https://api.carte.sh/v1.

claude_desktop_config.json / .cursor/mcp.json
{
  "mcpServers": {
    "carte": {
      "command": "npx",
      "args": ["-y", "@carte/mcp"],
      "env": { "CARTE_API_KEY": "ck_live_…" }
    }
  }
}
Claude Code (stdio)
claude mcp add carte -e CARTE_API_KEY=ck_live_… -- npx -y @carte/mcp

The hosted URL also accepts a key as a bearer header, for clients that speak Streamable HTTP but not OAuth:

sh
claude mcp add --transport http carte https://api.carte.sh/mcp --header "Authorization: Bearer ck_live_…"

Tools

ToolArgumentsReturns
list_inboxsince?, from?, to?, type?, thread?, limit? (1–200, default 20){ data: Envelope[], next_cursor } with download URLs stripped
get_messageidFull envelope, including short-lived file download URLs
get_threadthread_id, limit? (1–200, default 50){ thread_id, data: Envelope[] (oldest first), next_cursor, hidden } with download URLs stripped
get_file_urlfile_id{ download_url, expires_at }
read_file_textfile_id{ file_id, content_type, size_bytes, text } for text files under 2 MB
send_messageto (handle or handle/segment), type?, subject?, note?, json?The receipt envelope
reply_to_messageid, note?, json?, subject?, type?The receipt envelope of the reply, sent to the other party of message `id` in its thread
list_contactsq?, saved?, type?, cursor?, limit? (1–200, default 50){ data: Contact[], next_cursor }: every party the business has exchanged messages with, plus saved ones, with label, notes, can_initiate, blocked and ledger activity
get_contacthandle | domainOne Contact: what the business knows about that party
save_contacthandle | domain, label?, notes?The saved Contact (idempotent per party)
find_handleq, limit? (1–20, default 8){ data: [{ handle, display_name }] } from the handle directory

Read tools are annotated read-only; send_message and reply_to_message write but never delete (nothing on Carte does). read_file_text accepts text/*, JSON, CSV, Markdown, XML and YAML; binary files and files over 2 MB return an error pointing to get_file_url. reply_to_message never takes a recipient: the reply goes to the other party of the message and joins its thread. Errors come back as tool errors in the form Error <code> (<status>): <message>, with plain-language guidance where there is something to do.

Example prompts

  • "What did I receive today?"
  • "What did northwind send me this week?"
  • "Which invoices in my inbox failed validation, and why?"
  • "Read the September price list CSV and tell me which SKU is most expensive."
  • "Open the August statement thread and summarise the conversation."
  • "Reply to the latest invoice from northwind saying it is approved."
  • "Send acme a message of type po with this JSON."

Privacy

The server only ever returns messages addressed to, or sent by, the account that connected. It reads nothing from your assistant beyond the tool arguments. See the privacy policy and terms.

Running from source

sh
bun install
bun run --filter @carte/mcp build
CARTE_API_KEY=ck_live_… node packages/mcp/dist/index.js