MCP server

Let your coding assistant operate the account: add a domain, read the DNS records, verify it, and debug a bounce without opening a dashboard.

The MCP server is developer tooling. It gives the assistant already writing your code a set of tools for the things you would otherwise alt-tab to a dashboard for: adding a sending domain, reading the exact DNS rows to publish, triggering verification, making a key, looking at webhook deliveries, and asking why a message bounced.

It is a thin client over the REST API on the rest of this site. Every tool is one or two calls you could make with curl, which is deliberate: there is no second implementation of sending, validation, or quota to drift from the first.

Connect it

The endpoint is https://mcp.emails.sh and speaks Streamable HTTP. Authenticate with an esh_ key from https://emails.sh/dashboard/api-keys, or authorize over OAuth in a client that supports it, in which case no key is written to disk.

Claude Code
claude mcp add --transport http emailssh https://mcp.emails.sh
.cursor/mcp.json, or .vscode/mcp.json, or .mcp.json
{
  "mcpServers": {
    "emailssh": {
      "type": "http",
      "url": "https://mcp.emails.sh",
      // The key comes from https://emails.sh/dashboard/api-keys.
      "headers": { "Authorization": "Bearer esh_your_key_here" }
    }
  }
}

Cursor reads .cursor/mcp.json, VS Code reads .vscode/mcp.json, and Claude Code reads .mcp.json in the project root. Restart the client after writing the file: a server added to a config a running client already read does not appear until it does.

The key comes from https://emails.sh/dashboard/api-keys and is shown once. Put it in the file your client reads, confirm that file is gitignored, and give it the narrowest scope that lets it do the job.

The tools

ToolWhat it does
send_emailSends one message, the same way POST /v1/emails does. Sending to an address that is not yours is something to be asked about first.
get_emailStatus and the delivery timeline for one id, in prose: when it was accepted, when the receiving server took it, and the bounce if there was one.
list_emailsRecent sends, filterable by status and by tag.
list_domainsSending domains and where each one is in verification.
add_domainAdds a domain and returns the records to publish.
get_domain_recordsThe exact DNS rows for a domain, as type, name, and value, ready to paste into a DNS host.
verify_domainChecks the records now and says which ones have not landed.
list_api_keysKeys and when each was last used. Values are never returned.
create_api_keyMakes a key. The value is in the result and nowhere else.
revoke_api_keyRevokes one, effective on the next request.
list_webhooksEndpoints and the events each is subscribed to.
create_webhookRegisters an endpoint and returns the signing secret once.
get_webhook_deliveriesWhat each attempt got back: status code, response body, duration. This is what tells a broken endpoint apart from a missing event.
test_webhookSends a test event now and reports exactly what your endpoint answered.
replay_webhook_deliverySends a stored delivery again, under its original id, so a receiver that deduplicates recognises the repeat.
why_did_this_bounceReads the delivery log for a message or an address and explains the refusal, including the remote server's own diagnostic.
list_suppressionsAddresses blocked on this workspace, and why each one is there.
remove_suppressionClears one, when you know the address is good again.
check_domain_healthSPF, DKIM, and DMARC as we resolve them right now, rather than as they were meant to be published.
search_docsSearches this documentation, so the assistant answers from the reference rather than from memory.

Every result comes back as prose an assistant can act on rather than a JSON dump, for the same reason the API errors do: the next thing that happens is a decision, and a decision needs a sentence.

Scopes and safety

The key decides what the tools can reach
A key scoped to mail:send exposes the sending tools and nothing else. The domain, key, webhook, and suppression tools need the workspace scope, so a restricted key cannot call them at all rather than calling them and failing.
Destructive tools say so first
Revoking a key, removing a suppression, and sending to an address that is not yours all announce what they are about to do. Approve them the way you approve any other tool call.
Nothing is exclusive to MCP
Every tool maps onto endpoints documented at /docs/rest. If a tool is refused, the same call over HTTPS is refused for the same reason, and /docs/errors explains it.
It does not read your mail
The server operates the account. Received mail is behind /v1/messages and is not part of this tool set.

What it is good for

The pattern worth knowing: ask for the integration and the setup in one sentence. "Add email to the signup flow and get mail.acme.com verified." The assistant writes the send with the skill, then uses add_domain and get_domain_records to hand you the DNS rows, and verify_domain to tell you when they resolved. Nothing in that loop needs a browser except your DNS host.

The other one is debugging. "The receipt to ada@example.com never arrived" turns into get_email, then why_did_this_bounce, then list_suppressions, and ends with a sentence rather than a dashboard tour.