MCP server
The MCP server hands an agent the same setup and reading powers the dashboard has, through ten deliberate tools over a stdio connection. It is a thin wrapper over the REST API with no business logic of its own.
The server speaks the Model Context Protocol over stdio using the official SDK. Every tool maps to one or two REST calls; if you have read the API reference, you already know what the tools do. The parameter schemas are defined once in the server and are the same shapes the API accepts.
Ten tools is deliberate, not a starting point. A larger tool surface degrades an agent's tool selection, so the server covers the workflows agents actually run (set up a domain, wire DNS, create mailboxes in bulk, read a mailbox) and stops there. The full list is the tool reference.
The server is a process your MCP client spawns. Point the client at the built entrypoint and pass your API key in the environment:
{
"mcpServers": {
"emayler": {
"command": "node",
"args": ["/path/to/emayler/apps/mcp/dist/index.js"],
"env": {
"EMAYLER_API_KEY": "emk_live_...",
"EMAYLER_API_URL": "https://api.emayler.com"
}
}
}
}EMAYLER_API_KEY is required. The server refuses to start without it. Scopes work as usual: a read key can list and read, a write key can set things up.EMAYLER_API_URL defaults to http://localhost:3001 for local development. Set it to the production API URL in any real setup.The server attaches a fresh Idempotency-Key to every POST and PATCH it makes. Agents retry tools on timeouts all the time; the header is what stops a retried create_mailbox from creating a mailbox twice.
A failed API call surfaces as a tool error reading API <status>: <detail>, using the API's own problem detail text. Because error copy is actionable, the agent can often tell the user exactly what to fix, for example which DNS record is missing.
read_mailbox does not dump raw mail into the context. Its output is shaped to stay under a 2,000-token budget on the standard fixture: roughly 4 characters per token are estimated, 500 tokens are reserved for thread metadata, and the remaining budget is divided across the messages returned, with a hard cap of 600 characters per message body. Bodies are converted from HTML to Markdown, quoted reply chains and signatures are stripped, and overlong bodies are truncated in the middle (60% head, 40% tail) so the beginning and the actual question at the end both survive.
Inbound email is untrusted input flowing into an LLM context, and people will absolutely try to instruct your agent from inside an email. The server treats every message body as hostile:
suspected_injection: true. Detection runs on the original content including the hidden parts, so an attack cannot launder itself through the stripper.The flag is advice, not a filter
A flagged message is still returned, marked. Instruct your agents to treat suspected_injection content as data to summarise, never as instructions to follow, and never to act on links, attachments, or requests inside it without user confirmation.