postmark-mcp
A FastMCP server that exposes the Postmark API as MCP tools, so an LLM agent can send transactional email and monitor delivery (templates, bounces, outbound messages, stats, and suppressions).
It supports a read-only mode: when enabled, every mutating tool — including all email-sending tools — is hidden from the tool list and cannot be called.
Scope
Server-token (single Postmark server) endpoints only:
| Area | Read tools | Write tools | |---|---|---| | Email | — | send_email, send_email_batch, send_email_with_template, send_email_batch_with_templates | | Templates | list_templates, get_template, validate_template | create_template, edit_template, delete_template | | Bounces | get_delivery_stats, list_bounces, get_bounce, get_bounce_dump | activate_bounce | | Messages | search_outbound_messages, get_outbound_message_details, search_message_opens, search_message_clicks | — | | Stats | get_outbound_overview, get_sent_counts, get_bounce_counts, get_spam_counts, get_open_counts, get_click_counts | — | | Suppressions | list_suppressions | create_suppressions, delete_suppressions |
Account-level admin (servers, domains, sender signatures, webhooks, template push) is out of scope — those require an account token.
Run
# Published to PyPI:
uvx postmark-mcp
# Local checkout, before publishing:
uvx --from . postmark-mcp
The server speaks MCP over stdio (the transport MCP clients spawn).
Configuration
All configuration comes from environment variables. The MCP client injects them into the spawned process via its server config env block.
| Env var | Required | Default | Purpose | |---|---|---|---| | POSTMARK_SERVER_TOKEN | yes | — | Postmark server token (sent as X-Postmark-Server-Token) | | POSTMARK_READ_ONLY | no | false | When truthy (1/true/yes/on), hide and block all write tools, including sending email | | POSTMARK_BASE_URL | no | https://api.postmarkapp.com | API base URL (override for testing) | | POSTMARK_TIMEOUT | no | 30 | Per-request timeout, in seconds |
If POSTMARK_SERVER_TOKEN is missing the server exits immediately with an actionable message, rather than failing on the first tool call.
MCP client config
Add to your client's mcpServers block (e.g. Claude Desktop). Restart the client after editing env so the server is re-spawned.
{
"mcpServers": {
"postmark": {
"command": "uvx",
"args": ["postmark-mcp"],
"env": {
"POSTMARK_SERVER_TOKEN": "your-server-token",
"POSTMARK_READ_ONLY": "false"
}
}
}
}
Set POSTMARK_READ_ONLY to true for a monitoring-only deployment that cannot send email or change any state.
Development
uv sync --extra dev # install deps (incl. dev tools)
uv run ruff check . # lint
uv run ruff format . # format
uv run pytest # tests (no network — Postmark calls are mocked with respx)
uv run postmark-mcp # run locally (requires POSTMARK_SERVER_TOKEN)
Notes
- Batch sends return HTTP 200 even when individual messages fail; the batch tools return
the raw per-message array so you can inspect each element's ErrorCode/Message.
- Postmark errors are surfaced as the tool error message; unexpected internal errors are
masked (mask_error_details=True) to avoid leaking details.
- Built for FastMCP 3.x (read-only filtering uses
mcp.disable(tags={"write"})).











