Postmark MCP Server

by ActiveCampaign

279 downloads
Not rated
GitHub

About

An MCP server for sending emails via the Postmark service, configured through environment variables.

Details

Author
ActiveCampaign
Downloads
279
Categories
Communication, Automation, API, Other

- Exposes an MCP server for sending emails via Postmark
- Simple configuration via environment variables
- Comprehensive error handling and graceful shutdown
- Secure logging practices (no sensitive data exposure)
- Automatic email tracking configuration

Setting up with Highlight

This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:

  1. Download and install Highlight from highlightai.com/download
  2. Navigate to the plugins tab and select "Add Custom Plugin"
  3. Configure the plugin with the settings below
    Plugin Name Postmark MCP Server
    Command (node, npx, python, etc.)

    Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.

  4. Enable "Start Automatically" if you want the plugin to start when Highlight launches

From the repository

Configure the server via environment variables. Requires Node.js v16 or higher and a Postmark account with a server token.

Claude Desktop / Cursor

Paste into your MCP client config file to install this server.

{
    "mcpServers": {
        "postmark mcp server": {
            "postmark": {
                "command": "node",
                "args": [
                    "path/to/postmark-mcp/index.js"
                ],
                "env": {
                    "POSTMARK_SERVER_TOKEN": "your-postmark-server-token",
                    "DEFAULT_SENDER_EMAIL": "your-sender-email@example.com",
                    "DEFAULT_MESSAGE_STREAM": "your-message-stream"
                }
            }
        }
    }
}

McpServers

{
    "postmark": {
        "command": "node",
        "args": [
            "path/to/postmark-mcp/index.js"
        ],
        "env": {
            "POSTMARK_SERVER_TOKEN": "your-postmark-server-token",
            "DEFAULT_SENDER_EMAIL": "your-sender-email@example.com",
            "DEFAULT_MESSAGE_STREAM": "your-message-stream"
        }
    }
}

Send emails with Postmark using Claude and other MCP-compatible AI assistants.

- Exposes a Model Context Protocol (MCP) server backed by yourPostmark account
- 24 tools spanning email sending (single + batch), templates (CRUD + validation), message search, delivery diagnostics, bounces, suppressions, stats, server info, and webhooks
- MCP tool annotations (readOnlyHint,destructiveHint) let supporting clients auto-approve safe reads and require confirmation before mutating or destructive operations
- Simple configuration via environment variables
- Comprehensive error handling and graceful shutdown
- Structured JSON logging to stderr with optional log-file persistence; email addresses are partially masked by default
- HTTPS enforcement and optional domain allowlist for webhook registration
- Automatic open/click tracking on every send

- πŸ“’ API Documentation
-
πŸ”Ž API Explorer
-
πŸ“– Engineering Articles
-
πŸ“ Changelogβ€” what's new in each release

We'd love to hear from you! Please share your feedback and suggestions using ourfeedback form.

- Node.js v20 or higher
- A
Postmark accountand server token

git clone https://github.com/ActiveCampaign/postmark-mcp cd postmark-mcp

Create your own environment file from the example

Edit your.envto contain your Postmark credentials and settings.

Important:This is intended for local development purposes only. Secrets should never be stored in version control and.envtype files should be added to.gitignore.

npm start # or yarn start # or bun start

The repo ships two smoke-test example files. Copy each to its non-example name (which is gitignored) before running, so your local edits β€” including any verified-sender addresses β€” never end up committed.

# Read-only suite (25 checks). Optionally edit RECIPIENT_WITH_HISTORY. cp smoke-test.example.mjs smoke-test.mjs npm run smoke # Mutating suite (full lifecycles + real email sends). # REQUIRED: edit SENDER and RECIPIENT to two of your verified addresses. cp smoke-test-mutating.example.mjs smoke-test-mutating.mjs node smoke-test-mutating.mjs

The read-only suite spawns the server over stdio and exercises every read tool against your Postmark account, plus the validation paths foreditTemplateandcreateWebhook. Does not send mail or mutate state.

The mutating suite runs full create→edit→delete lifecycles for templates (including layout binding), webhooks, and suppressions, and sends real emails between the two addresses you configure. It cleans up after itself. The script refuses to run while the placeholder values are still in place.

After installing the MCP, update your configuration to set:

- POSTMARK_SERVER_TOKEN
- DEFAULT_SENDER_EMAIL
- DEFAULT_MESSAGE_STREAM(default:outbound)

Using npx (recommended β€” no clone required)

Install directly from npm without managing a local copy:

{ "mcpServers": { "postmark": { "command": "npx", "args": ["-y", "@activecampaign/postmark-mcp"], "env": { "POSTMARK_SERVER_TOKEN": "your-postmark-server-token", "DEFAULT_SENDER_EMAIL": "your-sender-email@example.com", "DEFAULT_MESSAGE_STREAM": "outbound" } } } }
{ "mcpServers": { "postmark": { "command": "node", "args": ["/absolute/path/to/postmark-mcp/index.js"], "env": { "POSTMARK_SERVER_TOKEN": "your-postmark-server-token", "DEFAULT_SENDER_EMAIL": "your-sender-email@example.com", "DEFAULT_MESSAGE_STREAM": "outbound" } } } }

Both snippets work withClaude Desktop(~/Library/Application Support/Claude/claude_desktop_config.json),Cursor(.cursor/mcp.json), and any other MCP client that accepts the standard JSON configuration format.

This section provides a complete reference for the Postmark MCP server tools including example prompts and payloads. The server registers24 toolsorganized into eight categories.

- Email

- sendEmail
-
sendEmailWithTemplate
-
sendBatch
-
sendBatchWithTemplate

- listTemplates
-
getTemplate
-
createTemplate
-
editTemplate
-
deleteTemplate
-
validateTemplate

- searchOutboundMessages
-
getMessageDetails

- searchBounces
-
getBounceDump
-
activateBounce

- listSuppressions
-
createSuppressions
-
deleteSuppressions

- listWebhooks
-
createWebhook
-
deleteWebhook

Sends a transactional email to one recipient or up to 50 recipients.

Send an email using Postmark to recipient@example.com with the subject "Meeting Reminder" and the message "Don't forget our team meeting tomorrow at 2 PM."
{ "to": "recipient@example.com", "subject": "Meeting Reminder", "textBody": "Don't forget our team meeting tomorrow at 2 PM.", "htmlBody": "<p>Don't forget our team meeting tomorrow at 2 PM.</p>", "from": "sender@example.com", "cc": "manager@example.com", "bcc": "archive@example.com", "replyTo": "support@example.com", "tag": "meetings" }

toaccepts a single address or an array of up to 50 addresses.htmlBody,from,cc,bcc,replyTo, andtagare optional. Iffromis omitted,DEFAULT_SENDER_EMAILis used.

Email sent successfully! MessageID: 0a1b2c3d-... To: recipient@example.com Subject: Meeting Reminder

Sends an email using a Postmark template.

Send the "welcome" template to customer@example.com with name "John Doe" and login_url "https://myapp.com/login".
{ "to": "customer@example.com", "templateAlias": "welcome", "templateModel": { "name": "John Doe", "login_url": "https://myapp.com/login" }, "from": "sender@example.com", "tag": "onboarding" }

ProvideeithertemplateId(number)ortemplateAlias(string), not both.

Template email sent successfully! MessageID: 0a1b2c3d-... To: customer@example.com Template: welcome

Sends up to 500 emails in a single API call. Each message is fully independent β€” its own recipient, subject, and body. This wraps Postmark'ssynchronousbatch endpoint (POST /email/batch), which returns immediate per-message results.

Note:Postmark also offers an asynchronousbulk email API(POST /email/bulk) for large-volume jobs with no message-count cap and a 50 MB payload limit. That endpoint uses a submit-and-poll workflow and is not currently wrapped by this MCP server.

{ "messages": [ { "to": "alice@example.com", "subject": "Order #1234 confirmed", "textBody": "Thanks Alice β€” your order is on its way.", "tag": "order-confirmation" }, { "to": "bob@example.com", "subject": "Order #1235 confirmed", "textBody": "Thanks Bob β€” your order is on its way.", "tag": "order-confirmation" } ] }

Per-message fields:to,subject,textBodyare required.htmlBody,from,cc,bcc,replyTo, andtagare optional. Iffromis omitted on a message,DEFAULT_SENDER_EMAILis used.

Sent 2/2 successfully Successes: - alice@example.com β€” abc-123-def - bob@example.com β€” abc-456-ghi

When some messages fail at submission (e.g., suppressed recipients), failures are listed first with theirErrorCodeand reason:

Sent 8/10 successfully (2 failed) Failures: - blocked@example.com β€” 406: Address has been suppressed. - bad@example.com β€” 300: Inactive recipient ...

Sends up to 500 templated emails β€” same template, per-recipient template models. Ideal for "render this onboarding template for each new user" flows.

{ "templateAlias": "welcome", "from": "hello@yourapp.com", "tag": "onboarding", "recipients": [ { "to": "alice@example.com", "templateModel": { "name": "Alice", "plan": "Pro" } }, { "to": "bob@example.com", "templateModel": { "name": "Bob", "plan": "Free" } } ] }

ProvideeithertemplateId(number)ortemplateAlias(string). Top-levelfromandtagapply to all recipients but can be overridden per-recipient. Each recipient also accepts optionalcc,bcc, andreplyTo.

Lists saved templates on this server. Returns the first 100 templates; if a server has more than 100, pagination is not yet supported and the response will indicate that results are truncated.

Found 2 templates: β€’ Welcome - ID: 12345678 - Alias: welcome - Subject: Welcome to {{product_name}}

Retrieves a single template's full content (HTML body, text body, subject, type).

Payload:{ "templateIdOrAlias": "welcome" }β€” accepts numeric ID or string alias.

Creates a new template. Requiresname. At least one ofhtmlBodyortextBodymust be provided.

subjectis required for Standard templates and must beomittedfor Layout templates β€” Postmark rejects the field on Layouts.

layoutTemplate(Standard only) binds the new template to an existing Layout by alias. Without it, the new template renders unwrapped (no chrome from any layout).

{ "name": "Order Confirmation", "subject": "Your order #{{order_id}} is confirmed", "htmlBody": "<h1>Thanks {{name}}</h1>", "textBody": "Thanks {{name}}", "alias": "order-confirmation", "templateType": "Standard", "layoutTemplate": "basic" }

templateTypemay be"Standard"(default) or"Layout".

Updates an existing template. RequirestemplateIdOrAliasplusat least oneupdated field (name,subject,htmlBody,textBody,alias, orlayoutTemplate).

Pass"layoutTemplate": nullto unbind a template from its current Layout (the MCP translates this to the empty-string the Postmark API requires for clearing the association).

Permanently deletes a template by ID or alias. Layout templates cannot be deleted while Standard templates are still bound to them β€” unbind viaeditTemplatefirst.

Payload:{ "templateIdOrAlias": "order-confirmation" }

Validates template content (Mustachio syntax, undefined variables) without saving. At least one ofsubject,htmlBody, ortextBodyis required.

{ "subject": "Order #{{order_id}}", "htmlBody": "<p>Thanks {{name}}</p>", "textBody": "Thanks {{name}}", "testRenderModel": { "order_id": 42, "name": "John" }, "templateType": "Standard" }

Expected Payload (all filters optional):

{ "recipient": "user@example.com", "fromEmail": "sender@example.com", "tag": "marketing", "subject": "Welcome", "status": "sent", "messageStream": "outbound", "fromDate": "2025-05-01", "toDate": "2025-05-15", "count": 50, "offset": 0 }

statusis one ofqueued,sent,processed.countis 1–500 (default 50).

Retrieves full details and event timeline for a single outbound message.

Payload:{ "messageId": "0a1b2c3d-..." }

Composite triage tool. Answers "did my email reach X, and if not, why?" by running message search, suppression check, and bounce history lookups in parallel against a recipient address, then synthesizing a plain-English recommendation.

This is adiagnostictool: it composes multiple Postmark API calls into a single coherent answer rather than mirroring a single endpoint.

Did my email to recipient@example.com get delivered? If not, what should I do?
{ "recipient": "recipient@example.com", "messageId": "0a1b2c3d-...", "fromDate": "2026-04-21", "toDate": "2026-04-28", "messageStream": "outbound" }

All fields exceptrecipientare optional. IfmessageIdis omitted, the most recent message to the recipient is used. The default search window is the last 7 days.

Delivery Diagnosis: recipient@example.com ──────────────────────────────────────────────── Suppression: not suppressed on stream "outbound" Most recent message: MessageID: fadeae4e-fb04-4102-9303-9876078c7b81 Subject: Welcome to MyApp Sent: 2026-04-27T18:42:19.0000000-04:00 Status: Sent Events: Delivered, OpenedΓ—2, Clicked Bounce history: none Recommended action: Email was delivered. If recipient says they didn't see it, check their spam folder or ask them to whitelist the sender domain.

When the recipient is suppressed, the recommendation differs based on reason:SpamComplaintis permanent,HardBouncemay be reactivatable,ManualSuppressioncan be deleted viadeleteSuppressions.

Searches the bounce log with optional filters by type, recipient, tag, message ID, message stream, date range, and active/inactive status.

{ "type": "HardBounce", "inactive": true, "emailFilter": "@example.com", "tag": "marketing", "messageID": "0a1b2c3d-...", "messageStream": "outbound", "fromDate": "2025-05-01", "toDate": "2025-05-15", "count": 50, "offset": 0 }

Returns the raw SMTP dump for a bounce. Bounce dumps are retained for 30 days.

Payload:{ "bounceId": 123456 }

No reviews yet β€” be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.