MCP server
Letter runs a remote Model Context Protocol
server at https://mcp.letter.app/. Add it to your AI assistant, sign in with
your Letter account, and the assistant can work in your workspace: draft and
publish sequences, schedule broadcasts, build forms and segments, look up
contacts, and check how sends performed.
It acts as you, with your role in the workspace, so it can do what you can do in the dashboard and nothing more.
Wiring Letter into your app’s code with a coding agent? That’s a different job. See Agent setup for the CLI and the local
@letterapp/mcppackage.
| URL | https://mcp.letter.app/ |
| Transport | Streamable HTTP (stateless, JSON responses) |
| Auth | OAuth 2.1: dynamic client registration and PKCE |
| Scopes | workspace:read, workspace:write |
There is no API key to paste and nothing to install. Opening the URL in a browser shows a short “how to connect” page.
Connect
Claude
- Open Customize → Connectors and choose Add custom connector.
- Name it Letter, paste
https://mcp.letter.app/, and click Add. Leave the OAuth client ID and secret empty. - Click Connect, sign in to Letter, and approve.
Turn it on in a chat from the + menu. On Team and Enterprise plans, an owner adds the connector once in Organization settings → Connectors, then each member connects their own Letter account.
ChatGPT
- Open Plugins, click +, and choose Add custom MCP server.
- Name it Letter, paste
https://mcp.letter.app/as the Server URL, and pick OAuth for authentication. - Click Create as a plugin, sign in to Letter, and approve.
Type @ in a chat and pick Letter. ChatGPT confirms with you before any tool that changes something. Whether you can add custom MCP servers depends on your ChatGPT plan and, on business plans, your workspace settings.
GitHub Copilot (VS Code)
- Run MCP: Add Server from the Command Palette and choose HTTP.
- Paste
https://mcp.letter.app/and name the serverletter. - VS Code opens your browser: sign in to Letter and approve.
The tools then show up in Copilot Chat in agent mode. To share the server with
everyone working on a repository, commit it to .vscode/mcp.json:
{
"servers": {
"letter": {
"type": "http",
"url": "https://mcp.letter.app/"
}
}
}
Claude Code
claude mcp add --transport http letter https://mcp.letter.app/
Then run /mcp in Claude Code, pick letter, and sign in.
Cursor and other clients
Any client that supports remote MCP servers with OAuth connects the same way:
give it the URL and sign in when it opens your browser. In Cursor, add this to
.cursor/mcp.json (or ~/.cursor/mcp.json for every project):
{
"mcpServers": {
"letter": { "url": "https://mcp.letter.app/" }
}
}
Sign-in and permissions
When you connect, Letter’s consent screen shows which app is asking, which workspace it will reach, and what it can do. Claude and ChatGPT are recognized by where they send you back to; other apps show the name they registered with.
- Allow changes is the one choice you make. Leave it off for a read-only
connection (
workspace:read): the assistant can look but only sees read tools. Turn it on to also grantworkspace:write. - Your role applies. Tools run as you, so workspace roles work exactly as in the dashboard. Admin-only tools (projects, members and invitations, sending domains, sender identity, sending mode, API keys) say so in their description and refuse for members.
- Sessions refresh on their own. Access tokens last an hour and are renewed automatically. A connection unused for 90 days has to sign in again.
- Tokens only work here. A connector’s token is bound to
https://mcp.letter.app/. It can’t call the Management API directly, and an API key or access token can’t be used as an MCP connection. - Disconnect anytime from Settings → Connected apps (use.letter.app/connected-apps). The next call fails and the assistant asks you to reconnect.
What it can do
The tools cover the whole Management API, plus a few built for chat (Markdown email bodies, contacts by email, CSV imports as text). Read tools are always available; the rest need Allow changes.
| Area | Tools |
|---|---|
| Workspace | list_projects, get_project, create_project, update_project, delete_project, list_members, invite_member, remove_member, list_invitations, create_invitation, revoke_invitation |
| Contacts | get_contact, list_contacts, upsert_contact, import_contacts, get_import_status, get_import_error_report, unsubscribe_contact, resubscribe_contact, delete_contact, delete_contacts, list_accounts, get_account, list_events, list_traits |
| Suppressions | list_suppressions, suppress_email, unsuppress_email |
| Lists | list_lists, create_list, get_list, update_list, delete_list, list_list_members, add_contact_to_list, remove_contact_from_list |
| Segments | list_segments, create_segment, get_segment, update_segment, preview_segment, delete_segment |
| Forms | list_forms, create_form, get_form, update_form, duplicate_form, delete_form, list_form_submissions |
| Sequences | list_sequences, create_sequence, get_sequence, update_sequence, save_sequence_draft, validate_sequence, preview_sequence_email, send_sequence_test, publish_sequence, list_sequence_versions, get_sequence_activity, delete_sequence |
| Broadcasts | list_broadcasts, create_broadcast, get_broadcast, update_broadcast, send_broadcast_test, preflight_broadcast, schedule_broadcast, cancel_broadcast, get_broadcast_activity, delete_broadcast |
| Templates | list_templates, get_template, create_template, update_template, set_default_template, reset_template, remove_template_logo, delete_template |
| Sending | list_domains, add_domain, verify_domain, remove_domain, set_sender_identity, set_sending_mode, list_transactional_messages, get_transactional_message |
| Credentials | list_api_keys, revoke_api_key, list_access_tokens, revoke_access_token |
Some prompts to start with:
- “Draft a three-email onboarding sequence for new trials, then validate it.”
- “How did last week’s broadcast perform?”
- “Build a segment of users who signed up but never invited a teammate.”
- “Did jane@acme.com get her password reset email?”
- “Add a waitlist form that drops signups into the Beta list.”
Good to know
- Projects. Most tools take a
projectslug. It can be left out when the workspace has one project; otherwise the assistant starts withlist_projects, or you can name the project in your request. - Email bodies are Markdown. Sequence emails and broadcasts take
body_markdown, personalized with{{first_name}}or{{first_name|there}}.list_traitsshows which trait keys exist. Letter converts the Markdown to its editor format, so the email stays editable in the dashboard. - Sequences are drafted, then published.
save_sequence_draftreplaces the draft graph and needs thedraft_revisionfromget_sequence, so the assistant can’t overwrite edits someone made in the dashboard meanwhile. Nothing reaches contacts untilpublish_sequence. - Test emails only go to you.
send_sequence_testandsend_broadcast_testalways mail the signed-in user, never an address the assistant picks. - Risky tools are flagged. Tools that email contacts or delete data carry
MCP’s
destructiveHintand read tools carryreadOnlyHint, so clients that support it ask you before acting. The server also tells the assistant to confirm before scheduling a broadcast, publishing a sequence, or deleting anything. - CSV imports are passed as text, header row first, up to 5 MB. The import
runs in the background;
get_import_statusreports progress. - Rate limit: 300 tool calls per minute per connection. Over that, calls
get
429with aRetry-After. - Errors explain themselves. A rejected call returns the API’s error with per-field details, so the assistant can correct its input and retry.
Not available over MCP
- Creating API keys or access tokens. Secrets don’t belong in a chat transcript. You can list and revoke them from chat, but create them in the dashboard or with the CLI.
- Billing and plan changes.
- Uploading a template logo, which is a file upload. Use the dashboard.
- The ingestion API (
identify,track,POST /v1/send). Those authenticate with a project API key from your backend. See API and Transactional email.
Troubleshooting
| Symptom | Fix |
|---|---|
| The assistant says it can’t make changes | You connected read-only. Disconnect in Settings → Connected apps, then connect again with Allow changes on. |
| ”This action requires the admin role” | Your workspace role doesn’t allow it. Ask an admin, or have them run it. |
| The assistant keeps asking you to sign in | The connection was disconnected or unused for 90 days. Reconnect from your assistant’s connector settings. |
| It works on the wrong project | Name the project in your request, or ask it to run list_projects first. |
| Your client asks for an OAuth client ID or secret | Leave them empty. Letter registers clients automatically. |
For MCP client developers
-
Discovery. An unauthenticated
POST https://mcp.letter.app/returns401pointing at the protected resource metadata (RFC 9728):WWW-Authenticate: Bearer realm="letter", resource_metadata="https://mcp.letter.app/.well-known/oauth-protected-resource"It names
https://use.letter.appas the authorization server, whose metadata (RFC 8414) is athttps://use.letter.app/.well-known/oauth-authorization-server. -
Registration. Dynamic client registration (RFC 7591) at
https://use.letter.app/oauth/register. Redirect URIs may behttps,httpon a loopback host, or a private-use scheme. Client ID Metadata Documents aren’t supported. -
Authorization. Authorization code flow with PKCE,
S256only. Authorization responses includeiss(RFC 9207). Theresourceparameter (RFC 8707) is optional; if sent, it must behttps://mcp.letter.app/. Tokens are always bound to that resource. -
Tokens. Access tokens last one hour. Refresh tokens rotate on every use, and a replayed one is rejected. Revoke at
https://use.letter.app/oauth/revoke(RFC 7009). -
Transport. Stateless Streamable HTTP: every request is a
POSTwith a JSON response, with no sessions or SSE stream.GETandDELETEreturn405. Send the token asAuthorization: Bearer ….