Skip to content

MCP server

Give Claude, Cursor or any other Model Context Protocol client the PostStack API as 117 tools, 5 guided prompts and 5 readable resources, either from the hosted endpoint or from a local process.

How it works#

The Model Context Protocol lets an AI client call tools a server exposes. PostStack's server wraps the same REST API the TypeScript SDK calls: every tool call becomes one or more ordinary API requests made with your API key, so the same permissions, rate limits, suppression checks and sending rules apply as if your own code had made them.

HostedLocal (stdio)
Endpointhttps://api.poststack.dev/mcpnpx -y @poststack.dev/mcp
TransportStreamable HTTPstdio
InstallNothingNode.js with npx
AuthAPI key in Authorization: BearerPOSTSTACK_API_KEY environment variable
Usage analyticsRecorded in the dashboardNot recorded

Both serve the identical set of tools, prompts and resources. Create the key under API keys in the dashboard, and read Permissions before choosing its permission level.

Connect the hosted server#

Point any MCP client that speaks Streamable HTTP and lets you set a request header at https://api.poststack.dev/mcp, with the header Authorization: Bearer sk_live_…. The server is stateless: each request builds a fresh server, so there is no session to keep alive or resume. In Claude Code:

bash
claude mcp add --transport http poststack https://api.poststack.dev/mcp \
  --header "Authorization: Bearer sk_live_..."

Requests to the endpoint are limited per API key: 120 a minute on paid plans, 60 on the Free plan, on top of the limits of the API endpoints each tool calls. The current ceiling is in the X-RateLimit-Limit response header. Responses also carry X-Poststack-Mcp: v1.

Run it locally#

The @poststack.dev/mcp package runs as a stdio server that your client starts on demand, so there is nothing to install ahead of time.

  1. Add the server to your client

    {
      "mcpServers": {
        "poststack": {
          "command": "npx",
          "args": ["-y", "@poststack.dev/mcp"],
          "env": { "POSTSTACK_API_KEY": "sk_live_..." }
        }
      }
    }

    Claude Desktop reads ~/Library/Application Support/Claude/claude_desktop_config.json on macOS and %APPDATA%\Claude\claude_desktop_config.json on Windows. Cursor reads ~/.cursor/mcp.json, or .cursor/mcp.json in a project.

  2. Restart the client

    Claude Desktop and Cursor read the file at startup. Once connected the client lists the PostStack tools; ask it to “list my sending domains” to confirm the key works.

npx @poststack.dev/mcp --print-config <target> prints a snippet for claude-desktop, cursor or claude-code, filled with POSTSTACK_API_KEY if it is set. The claude-desktop and cursor output is the whole file shown above, ready to save; the claude-code output is the claude mcp add command.

POSTSTACK_API_KEYstringrequired
The key every tool call authenticates with. Without it the process prints POSTSTACK_API_KEY environment variable is required and exits with status 1.
POSTSTACK_BASE_URLstringdefault https://api.poststack.dev
API origin the tools call.
POSTSTACK_MCP_TRACE"1"
Writes one JSON line per tool call (timestamp, tool, ok, duration, error code) to stderr, never to stdout, so it cannot corrupt the protocol stream.

Permissions#

The server can do exactly what its API key can do. Tools that need more than the key allows return the API's 403 as a tool error instead of acting.

Key permissionWhat the agent can do
full_accessAll 117 tools.
sending_accessThe email tools (send_email, send_batch_emails, list_emails, get_email, cancel_email, reschedule_email, lint_email, preview_email), plus validate_email, validate_email_batch, list_suppressions, add_suppression, remove_suppression, reply_to_inbound_email and forward_inbound_email. Everything else fails with 403 insufficient_scope.

Let an agent experiment safely

Give the agent a test-mode key (sk_test_…). Sends are recorded and reported as delivered, but nothing reaches a mail server. See Test mode.

Tools#

117 tools in 16 groups. Arguments marked ? are optional; the client shows the agent each argument's type and description. Every tool returns a short text summary plus the data as structuredContent. When the API rejects a call the tool returns isError: true with structuredContent of { statusCode, code } and the API's error message, so the agent can tell a validation error from a missing permission.

Emails

ToolArgumentsWhat it does
send_emailfrom, to, subject?, html?, text?, cc?, bcc?, reply_to?, tags?, scheduled_at?, template_id?, variables?, headers?, signature?Send a single transactional email immediately, or schedule it for a future time.
send_batch_emailsemailsSend multiple emails in a single batch request (up to 100 per call).
list_emailspage?, per_page?, status?, domain_id?, to?, tag?, date_from?, date_to?List previously-sent emails with optional filters and pagination.
get_emailidGet full details and event timeline for a specific email by id.
cancel_emailidCancel a scheduled email that has not yet entered the sending pipeline.
reschedule_emailid, scheduled_atReschedule a scheduled email to a new send time.
lint_emailfrom, to, subject, html?, text?Run a Rspamd-backed spam pre-flight on a draft email and return the score, action, and per-rule symbols.
preview_emailfrom, to, subject?, html?, text?, template_id?, variables?Render + lint + measure an email in one shot WITHOUT sending it.

Email validations

ToolArgumentsWhat it does
validate_emailemailCheck whether an email address is safe to send to BEFORE create_contact or send_email.
validate_email_batchemailsValidate up to 100 email addresses in a single call. Same checks as validate_email per address.

Contacts

ToolArgumentsWhat it does
get_contact_activityemail?, id?, limit?, since?Get a contact's recent email-event timeline grouped by event type (sent / delivered / opened / clicked / bounced / complained / failed).
get_engagement_summaryemail?, id?Get a single contact's engagement summary: segment + lifetime counts + open / click rates + last open / click + top tags.
search_contactsquery?, filters?, limit?, page?Search contacts with filters (segment, engagement, unsubscribed) and a fuzzy query across email/first_name/last_name. Each row carries a match_reason.
create_contactemail, first_name?, last_name?, unsubscribed?, properties?Create a new contact (person who can receive emails / broadcasts).
list_contactspage?, per_page?, search?, segment_id?List contacts with optional search and segment filtering.
get_contactidGet full details of a contact by id.
update_contactid, first_name?, last_name?, unsubscribed?, properties?Update an existing contact's name, properties, or subscription state.
delete_contactidPermanently delete a contact (irreversible — use unsubscribe_contact for opt-outs).
unsubscribe_contactidMark a contact as unsubscribed from all email (preserves the contact record).
get_contact_by_emailemailLook up a contact by their email address.

Contact properties

ToolArgumentsWhat it does
create_contact_propertyname, label, type?, options?, required?Define a custom contact property (typed schema for the contact.properties field).
list_contact_propertiesnoneList all custom contact properties defined for this account.
update_contact_propertyid, label?, options?, required?Edit a custom contact property's label, options or required flag.
delete_contact_propertyidRemove a custom contact property definition. Values already stored on contacts are left in place.

Segments

ToolArgumentsWhat it does
create_segmentnameCreate a static contact segment (manually-managed list).
list_segmentsnoneList every contact segment (not paginated).
get_segmentidGet a segment's details and member count.
update_segmentid, nameRename an existing segment.
delete_segmentidDelete a segment definition. Contacts in it are NOT deleted.
add_contacts_to_segmentid, contact_idsAdd one or more contacts to a segment.
remove_contact_from_segmentid, contact_idRemove a single contact from a segment.

Subscription topics

ToolArgumentsWhat it does
create_subscription_topicname, description?Create a subscription topic (named opt-in/opt-out preference like "Product Updates").
list_subscription_topicsnoneList all subscription topics defined for this account.
delete_subscription_topicidPermanently delete a subscription topic. Subscriptions are removed.
get_contact_subscriptionscontact_idList a contact's explicit subscription-topic choices.
subscribe_contact_to_topiccontact_id, topic_idOpt a contact in to a subscription topic.
unsubscribe_contact_from_topiccontact_id, topic_idOpt a contact out of a subscription topic.

Templates

ToolArgumentsWhat it does
create_templatename, subject, html, text?, variables?Create a new email template with {{variable}} placeholders.
list_templatespage?, per_page?List email templates.
get_templateidGet a template's full body, subject and variables.
update_templateid, name?, subject?, html?, text?, variables?Update an existing template's name, subject, body or variable list.
delete_templateidPermanently delete a template (irreversible).
publish_templateidPublish a template. send_email with an unpublished template_id fails with 422.
unpublish_templateidUnpublish a template so sends that reference it fail.
duplicate_templateidCopy a template: new id, same content, named "Copy of <name>", unpublished.
render_templatetemplate_id, variables?Server-side render a template with the provided variables.

Broadcasts

ToolArgumentsWhat it does
create_broadcastsegment_id, topic_id?, from, subject, html?, text?, reply_to?, name?, scheduled_at?Create a draft broadcast targeted at a segment, optionally scoped to a subscription topic.
list_broadcastspage?, per_page?List broadcasts.
get_broadcastidGet a broadcast's details and aggregate delivery stats.
update_broadcastid, segment_id?, topic_id?, from?, subject?, html?, text?, reply_to?, name?, scheduled_at?Edit a draft broadcast in place. Only draft broadcasts can be updated.
send_broadcastidDispatch a draft broadcast to its segment immediately.
resend_broadcastid, target?, subject?Resend a sent broadcast to the subset that didn't open or didn't click — a fresh broadcast is created targeting an auto-built segment of those recipients.
cancel_broadcastidCancel a queued or sending broadcast.
broadcast_performancebroadcast_id?, since?, best_metric?, limit?Get broadcast performance — either for one broadcast (variant breakdown if A/B) or for a leaderboard ranked by a chosen metric.
find_non_clickersbroadcast_id, limit?List contacts who received a broadcast but did NOT click any tracked link in it.

Workflows

ToolArgumentsWhat it does
list_workflowsnoneList all workflows (event-triggered automation pipelines) defined for this team.
get_workflowidGet a workflow's metadata and its graph (nodes + edges).
create_workflowname, trigger_type, trigger_config?Create a draft workflow. Its graph starts with a single trigger node.
update_workflowid, name?, trigger_type?, trigger_config?Update a workflow's name, trigger type, or trigger config.
delete_workflowidPermanently delete a draft or paused workflow, its graph and its run history. Runs still in progress are deleted with it.
add_workflow_nodeid, type, config, after_node?Add a node to a workflow's graph and (by default) wire it after an existing node.
connect_workflow_nodesid, from, to, branch?Add an edge between two nodes.
update_workflow_nodeid, node_id, configReplace a node's config. Cannot change its type or edges (use connect_workflow_nodes / remove_workflow_node).
remove_workflow_nodeid, node_idRemove a node and every edge touching it.
activate_workflowidMove a workflow from draft/paused to active so new trigger events start runs.
pause_workflowidPause an active workflow so new trigger events DO NOT start runs. In-flight runs continue to completion.
trigger_workflowid, contact_idStart a run for one contact. Only for an active workflow whose trigger_type is manual; anything else fails with 422.
post_workflow_eventevent, contact_id?, email?Post an application-defined event, enrolling a contact into every ACTIVE workflow whose trigger is "custom" with a matching event name.

Signup forms

ToolArgumentsWhat it does
list_signup_formspage?, per_page?List embeddable signup forms.
get_signup_formidGet full details of a signup form including its fields, target segment, and submission count.
create_signup_formname, segment_id?, topic_id?, fields, success_message?, redirect_url?Create an embeddable signup form. Submissions create a contact and optionally add them to a segment / subscription topic.
update_signup_formid, name?, segment_id?, topic_id?, fields?, success_message?, redirect_url?, active?Update a signup form's fields, target segment/topic, messaging, or active state.
delete_signup_formidPermanently delete a signup form. Pages that embed it get 404 on submit.

Domains and deliverability

ToolArgumentsWhat it does
get_deliverability_signalsidRead ISP deliverability-intelligence signals for a sending domain.
create_domainname, region?, custom_return_path?, open_tracking?, click_tracking?, tls_mode?Add a new sending domain to PostStack.
list_domainsnoneList sending domains.
get_domainidGet domain details including DNS records and verification status.
verify_domainidTrigger DNS verification for a domain.
update_domainid, open_tracking?, click_tracking?, tracking_domain?, tls_mode?, inbound_enabled?, catch_all?, stream_preference?, bimi_logo_url?Update domain settings (tracking, TLS, inbound, BIMI, sending stream).
delete_domainidPermanently delete a sending domain (irreversible — historical email records remain).
rotate_dkim_keyidStart a DKIM key rotation for a domain.
activate_dkim_rotationid, force?Cut over to the staged DKIM key, once its DNS record is published.
cancel_dkim_rotationidAbandon a staged DKIM rotation and remove its DNS record.
check_deliverabilityfromCheck whether a from-address is safe to send from RIGHT NOW.

Mailboxes

ToolArgumentsWhat it does
create_mailboxdomainId, localPart, password, displayName?, quotaBytes?, webhookEnabled?Provision a mailbox (IMAP/SMTP inbox) on a verified domain.
list_mailboxespage?, per_page?List mailboxes across all domains.
get_mailboxidGet a mailbox's metadata (status, quota, last login).
update_mailboxid, displayName?, quotaBytes?, status?, webhookEnabled?, forwardTo?, forwardMode?, forwardKeepCopy?, signatureHtml?, signatureText?Update a mailbox's display name, quota, status, webhook setting, auto-forwarding, or signature.
list_mailbox_filtersidList a mailbox's server-side mail filters (Sieve rules), in the order they run.
set_mailbox_filtersid, rulesReplace a mailbox's server-side mail filters. Runs on delivery, for every client.
delete_mailboxidPermanently delete a mailbox and all its stored mail.
change_mailbox_passwordid, passwordReset a mailbox's IMAP/SMTP password.
suggest_from_addresspurposeSuggest sensible from-addresses for a given purpose, drawn from the team's verified domains + existing mailboxes.

Inbound emails

ToolArgumentsWhat it does
list_inbound_emailspage?, per_page?, domain?, to?, q?, reports?List inbound emails received on your domains, newest first.
get_inbound_emailidGet a received inbound email's full headers and body.
list_inbound_email_attachmentsidList attachments on an inbound email (filename, size, contentType).
reply_to_inbound_emailid, from, html?, text?, cc?, bcc?, quote_original?Send a reply to an inbound email (subject and threading headers are set automatically).
forward_inbound_emailid, from, to, cc?, bcc?, message?Forward an inbound email to other recipients with an optional cover note.
draft_from_threadinbound_id, tone?Build a reply-draft skeleton for an inbound email — proper threading, quoted original, salutation/sign-off, suggested from + subject. The agent writes the body.

Webhooks

ToolArgumentsWhat it does
create_webhookurl, eventsSubscribe a URL to receive event notifications via signed POST requests.
list_webhooksnoneList configured webhook endpoints.
get_webhookidGet a webhook's details.
update_webhookid, url?, events?, enabled?Edit a webhook's URL, event list, or enabled state.
delete_webhookidPermanently delete a webhook endpoint.
test_webhookidSend a test event to a webhook's URL to verify it's reachable and the signature verifies.
get_webhook_deliveriesid, page?, per_page?List recent delivery attempts for a webhook (status, response code, timestamps).
replay_webhook_deliveryid, delivery_idRe-deliver a single past webhook delivery (e.g. one that failed because the endpoint was down).
batch_replay_webhook_deliveriesid, within_minutes?, event_type?, limit?Re-deliver EVERY failed delivery for a webhook, oldest first.
rotate_webhook_secretid, graceHours?Rotate a webhook's signing secret, keeping the old one valid for a grace window. The new secret is returned ONCE here.

Suppressions

ToolArgumentsWhat it does
list_suppressionspage?, per_page?List suppressed addresses (PostStack will not send to them).
add_suppressionemail, reason?Suppress an address so future sends to it are skipped. reason defaults to manual.
remove_suppressionemailRemove an address from the suppression list (sends will resume).

API keys

ToolArgumentsWhat it does
create_api_keyname, permission, mode?, domain_id?Generate a new PostStack API key. The full key is returned ONCE in this response and cannot be retrieved again.
list_api_keysnoneList every API key (not paginated; only the prefix is returned, never the full secret).
get_api_keyidGet an API key's metadata (the secret is never returned after creation).
revoke_api_keyidPermanently revoke an API key — all subsequent requests using it will fail.
rotate_api_keyidRotate an API key: issue a new secret and invalidate the old one. The new full key is returned ONCE in this response and cannot be retrieved again.

Prompts#

Prompts are playbooks the client offers you by name (in Claude, from the prompt picker). Each tells the agent which tools to call in which order. tone is formal, friendly or casual and defaults to friendly.

PromptArgumentsWhat the agent does
draft_welcome_emailcontact_email, tone?, template_hint?Looks the contact up, picks a published welcome template, renders it, lints it, and sends only if the preview passes.
reengage_dormantsegment_id?, days_dormant?, tone?Sizes the audience of contacts inactive for days_dormant days (default 90), picks a template and stages a broadcast for you to review.
followup_non_clickersbroadcast_id?, since?, tone?Takes the given broadcast, or the best by click rate since the cutoff (default 30 days ago), pulls the recipients who did not click, and drafts a follow-up.
summarize_campaignbroadcast_idA short performance report: headline metrics, the A/B winner if there was a test, and a one-line recommendation.
triage_inboundinbound_id, tone?Reads an inbound email, classifies it (support, sales, billing, spam, other), looks up the sender and proposes the next action, with a reply skeleton when one fits.

Resources#

Read-only JSON documents the client can attach to the conversation before the agent acts.

URIContents
poststack://templatesEvery template: id, name, subject, version, published flag.
poststack://templates/{id}One template by public id: body, subject and variables.
poststack://domainsSending domains: name, status, DNS records, tracking flags.
poststack://segmentsSegments: id, name, contact count.
poststack://brandTeam name, verified-domain count and a recommended default from-address.

Usage analytics#

Every tool call made through the hosted endpoint is recorded and shown on the MCP analytics page of the dashboard: total calls, error rate, p50 and p95 latency, top tools, hourly activity and recent errors, over the last 24 hours, 7 days or 30 days. Calls made through the local stdio server are not recorded.

Each record holds the tool name, duration, success or failure, error code, transport and the API key used, plus a hash of the arguments used to group identical calls. The arguments themselves are never stored. The hash is not cryptographic, so do not rely on it to hide low-entropy values such as an email address. Records older than 30 days are deleted.

Troubleshooting#

The client shows no PostStack tools

For the local server, run POSTSTACK_API_KEY=sk_live_... npx -y @poststack.dev/mcp in a terminal: it should start and wait silently. If it prints POSTSTACK_API_KEY environment variable is required, the env block in your config is missing or misspelled. Restart the client after editing its config.

Every call fails with 401

The key is missing, revoked or mistyped. For the hosted server, check the header is Authorization: Bearer sk_… with a single space and no quotes around the key.

A tool fails with 403 insufficient_scope

The key is a sending_access key and the tool needs more. Use a full_access key for an agent that manages domains, contacts or templates.

Calls fail with 429

The agent is calling faster than the rate limit allows (120 requests a minute to the hosted endpoint on paid plans, 60 on Free, plus each API endpoint's own limit). The error reaches the agent as a tool error; ask it to slow down or batch, e.g. send_batch_emails instead of many send_email calls.

Next steps

Was this page helpful?

Related