Give your coding agent the tools to work with email.
Connect ZapSMTP through MCP, add Agent Skills, and read documentation in Markdown. Build with context from the real API.
The MCP server, the CLI and the skills are being published. llms.txt and the Markdown pages are available now.
An MCP server with one tool per API operation.
zapsmtp-mcp turns the API’s 112 operations into tools, generated from the same description as the API reference, plus send_test_email for the simulator and verify_webhook_signature. The default toolsets give 76 tools; --read-only leaves 41.
Ask in plain words; the assistant picks the tool, and the result is the API’s own JSON.
You: Which templates take an order_id?
Assistant calls templates_list · read-only
{
"object": "list",
"has_more": false,
"data": [
{
"id": "tpl-order",
"name": "Order confirmation",
"alias": "order-confirmation",
"status": "published",
"variable_names": ["name", "order_id", "order_url", "items", "subtotal", "shipping", "total", "address"]
},
{
"id": "tpl-shipped",
"name": "Order shipped",
"alias": "order-shipped",
"status": "published",
"variable_names": ["name", "order_id", "items", "tracking_url", "carrier", "eta"]
}
]
}Assistant: Two: Order confirmation and Order shipped, both published.
Set it up in your client.
The server runs next to your editor over stdio with your key in its environment, or as a Streamable HTTP server where every client brings its own key. Claude Code, Claude Desktop, Cursor and VS Code are shown here; Codex, Windsurf, Zed and Gemini CLI are in the guide.
Start with a zap_test_… key: it runs everything and delivers nothing. The zapsmtp-mcp package is being published; these are the commands it will take.
claude mcp add zapsmtp -e ZAP_API_KEY=zap_... -- npx -y zapsmtp-mcp// Settings → Developer → Edit Config (claude_desktop_config.json)
{
"mcpServers": {
"zapsmtp": {
"command": "npx",
"args": ["-y", "zapsmtp-mcp"],
"env": { "ZAP_API_KEY": "zap_..." }
}
}
}// ~/.cursor/mcp.json (every project) or .cursor/mcp.json (one project)
{
"mcpServers": {
"zapsmtp": {
"command": "npx",
"args": ["-y", "zapsmtp-mcp"],
"env": { "ZAP_API_KEY": "zap_..." }
}
}
}// .vscode/mcp.json: VS Code asks for the key once and keeps it
{
"inputs": [
{ "type": "promptString", "id": "zapsmtp-key", "description": "ZapSMTP API key", "password": true }
],
"servers": {
"zapsmtp": {
"command": "npx",
"args": ["-y", "zapsmtp-mcp"],
"env": { "ZAP_API_KEY": "${input:zapsmtp-key}" }
}
}
}The tool catalogue
Tools are grouped in toolsets, named after the Node.js SDK’s methods. Clients that slow down with many tools do best with only what they need: --toolsets emails,simulator is 14 tools.
| Toolset | Tools | Examples |
|---|---|---|
| emails | 12 | emails_send, emails_send_batch, emails_list, emails_get, emails_cancel, emails_spam_check … |
| receiving | 8 | emails_receiving_list, emails_receiving_get, emails_receiving_reply … |
| threads | 3 | threads_list, threads_get, threads_update |
| templates | 12 | templates_list, templates_get, templates_render, templates_publish, templates_versions_restore … |
| variables | 3 | variables_list, variables_set_global, variables_delete_global |
| domains | 11 | domains_list, domains_verify, domains_get_dmarc, domains_get_reputation … |
| webhooks | 12 | webhooks_create, webhooks_test, webhooks_events_replay, verify_webhook_signature … |
| suppressions | 6 | suppressions_list, suppressions_create, suppressions_delete … |
| logs, stats, usage | 7 | logs_list, stats_timeseries, stats_breakdown, usage_get … |
| simulator | 2 | simulator_get, send_test_email |
| inbound, actions, template modules, assets, timers, API keys, SMTP credentials, dedicated IPsoptional | 38 | Off by default: add them with --toolsets |
Decide what an agent may do.
A model does not make an integration safe by itself. These controls limit what the server exposes and help clients flag consequential calls; you choose them, and you review what the agent does.
- Read only.
--read-onlykeeps only tools that change nothing: lists, gets, renders and spam checks. - Sends, changes and deletes are labelled. Sends (
emails_send,emails_send_batch,emails_receiving_reply) are marked open-world; deletes, cancelling a scheduled email, rotating a webhook secret and restoring a template version are marked destructive. Clients that ask before acting use these labels; whether they ask is the client’s setting. - Bounded sends.
--allowed-recipient-domainsrefuses mail to any other domain before a request is made;--sendersets the From. Every send carries an idempotency key, so a retried call within 24 hours is not sent again. - Scoped keys. Give the agent a test key while you try things, and a key limited to sending, one domain or one stream for production.
- The key stays out of the conversation. It is read from the environment and scrubbed from tool results and errors.
Agent Skills that know the API.
Skills are folders an agent loads when a task needs them. The zapsmtp skill routes to six others: sending email, webhooks, templates, receiving email, email best practices, and migrating from Resend, SendGrid, Mailgun, Postmark or Amazon SES. Every code sample in them is checked against the API before release.
Example task: “Send our order confirmation with ZapSMTP when an order is paid.” With the sending and templates skills loaded, expect a server-side send of the order confirmation template with the order’s variables and an idempotency key tied to the order, like this:
// when an order is paid, on your server
await zap.emails.send(
{
from: 'Halden Outfitters <orders@haldenoutfitters.com>',
to: order.email,
template: {
id: 'order-confirmation',
variables: {
name: order.firstName, order_id: order.id, order_url: order.url, items: order.items,
subtotal: order.subtotal, shipping: order.shipping, total: order.total, address: order.address,
},
},
},
{ idempotencyKey: `order-${order.id}-confirmation` }, // a retry within 24 hours sends nothing new
);# in a checkout of the skills repository
DEST=.claude/skills # or ~/.claude/skills for every project
mkdir -p "$DEST/zapsmtp"
cp -R SKILL.md LICENSE references "$DEST/zapsmtp/"
for s in zapsmtp-*/; do cp -R "$s" "$DEST/"; done# .agents/skills is read by Cursor, GitHub Copilot and OpenAI Codex alike
DEST=.agents/skills
mkdir -p "$DEST/zapsmtp"
cp -R SKILL.md LICENSE references "$DEST/zapsmtp/"
for s in zapsmtp-*/; do cp -R "$s" "$DEST/"; doneDocs your model can read.
/llms.txt: what ZapSMTP is, how to authenticate and test, and every docs page with a one-line description./llms-full.txt: every page in one file.- Every docs page as Markdown: add
.mdto its path, such as/docs/sending.md. /openapi.json: the whole API in OpenAPI 3.1.
npm install -g zapsmtp-cli
zapsmtp login # checks the key, keeps it readable by you only
zapsmtp test # an email to the simulator, then its events
zapsmtp send --from "Acme <hello@acme.com>" --to jane@example.org \
--subject "Your receipt" --html-file receipt.html --attach ./invoice.pdf
zapsmtp emails list --status bounced --all --json | jq '.[].to'
zapsmtp domains verify <domain-id>Connect your assistant.
Point your agent at llms.txt today, and add the MCP server and skills as soon as they are published.