
dev.primitive/email is a MCP server that lets AI agents send, receive, search, and reply to email over Model Context Protocol.
About
dev.primitive/email is a Model Context Protocol server that gives AI agents first-class email: send messages, ingest inbound mail, search history, and draft replies through tools your agent already understands. developers shipping agents, internal ops bots, or SaaS with transactional email can register the remote endpoint instead of stitching together SMTP libraries, OAuth for Gmail, and ad-hoc parsing in every project. The catalog positions it in Build → Integrations because it is infrastructure you attach while wiring the product, not a one-off research skill. It pairs naturally with CRM, ticketing, or notification MCPs once you move toward Ship and Grow. Expect to bring your own Primitive account or credentials as Primitive’s hosted offering matures; the registry emphasizes the remote MCP URL and agent-oriented description rather than a long tool list in the snippet provided.
- Send, receive, search, and reply to email over MCP from Claude Code, Cursor, or Codex
- Hosted streamable HTTP remote at primitive.dev—no local mail server to operate
- Version 0.1.0 registry entry with GitHub source in primitivedotdev/primitive-mono-repo
- Designed as email infrastructure for AI agents rather than a human-only inbox UI
- Composable with other MCP tools for support, onboarding, and lifecycle messaging
Email by the numbers
- Exposes 28 verified tools (MCP introspection)
- Data as of Jul 7, 2026 (Skillselion catalog sync)
claude mcp add --transport http email https://www.primitive.dev/mcpAdd your badge
Show developers this MCP server is listed on Skillselion. Paste this into your README.
| Transport | HTTP |
|---|---|
| Auth | None |
| Tools | 28 |
What it does
Wire your coding agent into real email so it can send updates, read replies, and search threads without custom SMTP code.
Who is it for?
Best when you're adding agent-driven support, onboarding sequences, or inbox triage without owning full mail infrastructure.
Skip if: Skip if you only need a static contact form or already run a hardened ESP pipeline with strict compliance you cannot route through a third-party MCP.
What you get
After you register the Primitive email remote, your agent can handle mailbox operations through standard MCP tools while you focus on product logic.
- MCP-configured agent that can invoke email send, receive, search, and reply tools
- Reduced custom mail-stack code in your agent or backend integration layer
By the numbers
- Registry version 0.1.0
- 1 streamable-http remote: https://www.primitive.dev/mcp
- 4 email capabilities stated: send, receive, search, reply
Tools 28
Public tool metadata - what this server can do for an agent.
listDomainsList all inbound domains for the organization, both verified and unverified. Each domain includes its verification status and DNS records. Use before addDomain to check whether a domain is already claimed.
addDomain1 paramClaim a new domain and receive the DNS records to publish. Returns dns_records with the exact records to add. If the domain has an mx_conflict (existing mail provider), re-call with confirmed: true to proceed. After publishing DNS records, call verifyDomain to complete setup.
requestBodyobjectrequired
verifyDomain1 paramCheck DNS records for a domain claim (MX, TXT, SPF, DKIM, DMARC). On success the domain becomes verified and starts receiving mail. On failure, returns which checks passed and which still need attention. If DNS propagation is incomplete, wait a few minutes and retry.
idstringrequiredDomain ID returned by addDomain or listDomains.
downloadDomainZoneFile2 paramsDownload a BIND-format DNS zone file for a domain. Useful when users want to import all required DNS records at once rather than copying them individually. Returns plain text in BIND zone file format.
idstringrequiredDomain ID from listDomains or addDomain.outbound_onlybooleanWhen true, include only outbound DNS records (SPF, DKIM, DMARC). Defaults to all records for unverified domains, outbound-only for verified.
getAccountUse this when you need the authenticated Primitive account summary, including email, plan, onboarding state, and webhook secret rotation time.
getInboxStatusUse this when the user asks whether inbound email is ready or needs setup. Returns domains, routes, deployed Functions, and recent inbound activity.
getOutboundStatusWhat can I send FROM? Lists this account's verified outbound (sendable) domains plus any domains still pending DNS verification, with next actions. Call this BEFORE sendEmail to pick a valid `from` domain — the account email is not necessarily sendable. The same sendable list is echoed in a cannot_send_from_domain error.
listEmails7 paramsUse this when you need to browse inbound emails received at verified domains with cursor pagination, status filters, date filters, or sender/recipient search.
cursorstringPagination cursor from a previous response's `meta.cursor` field.limitnumberMaximum number of emails to return (1–100, default 50).domain_idstringFilter to emails received on a specific verified domain.statusstringFilter to emails in this processing status.searchstringFree-text search across sender, recipient, and subject.date_fromstringInclusive lower bound on received_at (ISO 8601).date_tostringInclusive upper bound on received_at (ISO 8601).
searchEmails18 paramsUse this when you need to find inbound emails with structured filters or full-text matching. Use sort=received_at_asc plus date_from for new-mail polling.
qstringFull-text query matched across subject, body, sender, and recipient.fromstringFilter to emails from this sender address or domain.tostringFilter to emails addressed to this recipient.subjectstringFilter to emails whose subject contains this string.bodystringFilter to emails whose body contains this string.domain_idstringFilter to emails received on a specific verified domain.reply_to_sent_email_idstringFilter to inbound emails that are replies to a specific sent email ID.statusstringFilter to emails in this processing status.date_fromstringInclusive lower bound on received_at (ISO 8601).date_tostringInclusive upper bound on received_at (ISO 8601).has_attachmentstringFilter to emails that have ("true") or lack ("false") attachments.spam_score_ltnumberFilter to emails with a spam score strictly below this value.spam_score_gtenumberFilter to emails with a spam score at or above this value.sortstringSort order. Use received_at_asc with date_from for polling new mail.cursorstringPagination cursor from a previous response's `meta.cursor` field.limitnumberMaximum number of emails to return (1–100, default 50).snippetstringInclude a short body snippet in each result (default true).include_facetsstringInclude aggregated facet counts (sender, domain, status) in the response (default true).
getEmail1 paramUse this when you need full details for one inbound email ID, including parsed bodies, threading metadata, SMTP envelope, webhook state, and replies.
idstringrequiredInbound email ID from listEmails or searchEmails.
replyToEmail2 paramsUse this when the user has selected a specific inbound email and confirmed a reply. Sends real outbound email with threading handled server-side.
idstringrequiredInbound email ID to reply to. Threading headers are set server-side.requestBodyobjectrequired
sendEmail2 paramsUse this when the user has confirmed a new outbound email. Sends real email through Primitive's relay and can wait for the first SMTP delivery outcome.
Idempotency-KeystringClient-supplied idempotency key. Retrying with the same key returns the original result without re-sending.requestBodyobjectrequired
sendEmailDemo1 paramSend a SIMULATED email with no account required. Validates the body against the exact same schema as sendEmail, then returns a realistic synthetic success envelope (demo: true) — it never actually sends, queues, or stores anything. Use this to let someone try Primitive and see the response shape before they sign up. To send for real, sign up for an API key and use sendEmail.
requestBodyobjectrequired
listSentEmails7 paramsList outbound emails sent by this org, with cursor pagination and filters. Bodies are omitted from list rows to keep responses small — use getSentEmail to fetch a specific row with full body. Useful for auditing delivery status, finding bounced sends, or checking gate-denied attempts.
cursorstringPagination cursor from a previous response's `meta.cursor` field.limitnumberMaximum number of sent emails to return (1–100, default 50).statusstringFilter to rows in this status.request_idstringFilter to the row matching a specific server-issued request_id from a /send-mail response.idempotency_keystringFilter to rows with the given client idempotency key.date_fromstringInclusive lower bound on created_at (ISO 8601).date_tostringInclusive upper bound on created_at (ISO 8601).
getSentEmail1 paramGet the full record for a single sent email by id, including body_text and body_html. Use to inspect delivery details for a specific send — e.g. the SMTP response on a bounced row, or the gate denial reason on a gate_denied row.
idstringrequiredSent email ID from listSentEmails or a /send-mail response.
awaitReply3 paramsGet the threaded reply to a sent email — the canonical 'did they reply yet?' call. Pass the sent email's id (from a sendEmail response). With wait=true it long-polls up to wait_timeout_ms for the reply to arrive (synchronous agent-to-agent chat); with wait=false (default) it returns immediately with the reply if one has arrived, else reply=null. Matches on the reply's threading, not a from/subject guess.
idstringrequiredThe sent email id to fetch the reply for (from a sendEmail/replyToEmail response).waitstringWhen "true", long-poll until a reply arrives or wait_timeout_ms elapses. Default no-wait.wait_timeout_msnumberHow long to wait when wait=true (1000–30000 ms, default 10000).
getConversation1 paramGet the full conversation an inbound email belongs to as ordered, chat-model-ready turns with bodies. Each message is oldest-first with a direction (inbound/outbound) and a derived role (inbound→user, outbound→assistant). For a brand-new message, returns just that one turn. The response includes a truncated boolean (true when the message cap was reached) and a message_count field.
idstringrequiredID of any inbound email in the conversation.
getThread1 paramGet a conversation thread by id: metadata plus all inbound and outbound messages interleaved oldest-first. Each message has a direction (inbound/outbound) and id; fetch inbound message bodies via getEmail, or outbound bodies via getSentEmail. Discover thread_id from any email or sent-email record. Compare message_count against messages.length to detect truncation.
idstringrequiredThread ID from the thread_id field on any email or sent-email.
downloadEmailAttachments2 paramsDownload all attachments for an inbound email as a gzip-compressed tar archive. Returns the archive as a base64-encoded string along with the attachment count and SHA-256 digest. Prefer getEmail first to check the attachment manifest before downloading.
idstringrequiredInbound email ID.tokenstringSigned download token from a webhook payload. Optional — the bearer token is used when this is omitted.
listEndpointsList all active webhook endpoints for the organization. Each endpoint shows its URL, enabled state, and optional domain restriction.
createEndpoint1 paramCreate a webhook endpoint to receive email.received events. If an endpoint with the same URL already exists but is deactivated, it is reactivated. After creating, call testEndpoint to confirm your signature verifier accepts the payload.
requestBodyobjectrequired
deleteEndpoint1 paramSoft-delete a webhook endpoint. The endpoint will no longer receive deliveries.
idstringrequiredEndpoint ID from listEndpoints or createEndpoint.
testEndpoint1 paramSend a sample email.received event to a webhook endpoint to verify your signature verifier. Rate limited to 4/min and 30/hr. Successful deliveries and verified-domain endpoints are exempt.
idstringrequiredEndpoint ID from listEndpoints or createEndpoint.
listFiltersList all whitelist and blocklist filter rules for the organization.
createFilter1 paramCreate a whitelist or blocklist filter rule. Patterns are stored lowercase. Per-domain filters require a Pro plan.
requestBodyobjectrequired
deleteFilter1 paramDelete a filter rule.
idstringrequiredFilter rule ID from listFilters or createFilter.
listWebhookDeliveries6 paramsList webhook delivery attempts with pagination and filters. Each delivery includes the target endpoint and a nested email object with sender/recipient/subject. Useful for diagnosing delivery failures or confirming a specific email was delivered.
cursorstringPagination cursor from a previous response's `meta.cursor` field.limitnumberMaximum number of delivery records to return (1–100, default 50).email_idstringFilter by inbound email ID.statusstringFilter to deliveries in this status.date_fromstringInclusive lower bound on attempted_at (ISO 8601).date_tostringInclusive upper bound on attempted_at (ISO 8601).
replayWebhookDelivery1 paramRe-send a stored webhook payload from a previous delivery attempt to its original endpoint. Rate limited per org (burst + sustained windows, shared budget with email webhook replays).
idstringrequiredDelivery ID (numeric string from listWebhookDeliveries).
Recommended MCP Servers
How it compares
MCP email transport for agents, not a marketing automation skill or ESP dashboard.
FAQ
Who is dev.primitive/email for?
and small-team developers using Claude Code, Cursor, or similar agents who want programmatic send/receive/search/reply without custom SMTP integrations.
When should I use dev.primitive/email?
Use it during Build when you integrate outbound and inbound email into an agent workflow, support bot, or automated lifecycle messaging.
How do I add dev.primitive/email to my agent?
Add the streamable HTTP remote https://www.primitive.dev/mcp in your MCP client config per Primitive’s docs, then restart the agent session.