AI Mail MCP

Email for agents

Give your agent a real inbox.

AI Mail MCP is an email server you talk to through MCP. Your agent can add a domain, get the exact DNS records, create mailboxes, read one inbox across all of them, wait for a sign-up code, and send or reply in-thread. Every write has a dry run, every call is audited, and every token is scoped.

MCP endpoint  https://aimailmcp.com/mcp

1. Bring a domain

add_domain returns the MX, SPF, DKIM and DMARC records. On domains we manage, apply_dns writes them for you; anywhere else, publish them and call verify_domain.

2. Make mailboxes

create_mailbox for agent@yourdomain.com, plus aliases, a catch-all and forwarding. Each mailbox also gets a route address any mail host can forward to.

3. Read and send

list_messages is one inbox across every mailbox the token can see. send_message, reply and forward go out through Cloudflare Email Sending with Resend as failover.

Connect

Sign in with an emailed code through OAuth (Claude and most MCP clients do this for you), or get a token without a browser.

Claude Code

claude mcp add --transport http aimail https://aimailmcp.com/mcp

Claude Code opens the sign-in page on first use. To use a token instead, add --header "Authorization: Bearer $AIMAIL_TOKEN".

Claude.ai and Claude Desktop

Settings, Connectors, Add custom connector. Paste:

https://aimailmcp.com/mcp

You will be asked for your email and a 6-digit code.

Any MCP client (JSON)

{
  "mcpServers": {
    "aimail": {
      "type": "http",
      "url": "https://aimailmcp.com/mcp",
      "headers": { "Authorization": "Bearer amk_..." }
    }
  }
}

Agents without a browser

curl -X POST https://aimailmcp.com/api/v1/signup \
  -H 'content-type: application/json' \
  -d '{"email":"you@example.com"}'
# then, with the code from the email:
curl -X POST https://aimailmcp.com/api/v1/signup/confirm \
  -H 'content-type: application/json' \
  -d '{"email":"you@example.com","code":"123456"}'

Returns an admin token once. Use create_token to hand narrower tokens to other agents.

Tool reference

Generated from the live registry at /tools.json. Every tool that writes accepts dry_run.

Domains 6

Bring a domain, get the exact DNS records, verify it.

add_domainAdd a domainwrites admin token dry_run

Add a domain (or subdomain) to this account so it can hold mailboxes. Returns the DNS plan. A domain whose zone is on this service's Cloudflare account is "managed" (apply_dns can write its records, internal accounts only); any other domain is "external" and proves ownership with a TXT record, then verify_domain.

ParameterTypeNotes
name requiredstringDomain name, for example example.com or mail.example.com.
dry_runbooleanValidate and report what would happen without changing anything.
get_dns_planGet the DNS planread only

The exact DNS records (MX, SPF, DKIM, DMARC, ownership TXT) a domain needs for receiving and sending, and whether each is in place.

ParameterTypeNotes
domain requiredstringDomain name, for example example.com or mail.example.com.
apply_dnsApply DNS records (managed domains)writes destructive admin token dry_run

Write the DNS plan to a managed domain on this service's Cloudflare account: enables Email Routing (MX + SPF), routes mail to the receiver, registers the domain for sending and adds DKIM and DMARC. Never deletes records. Refuses when the name already has MX records pointing elsewhere unless replace_existing_mx is true. Use dry_run first.

ParameterTypeNotes
domain requiredstringDomain name, for example example.com or mail.example.com.
replace_existing_mxbooleanAllow enabling routing on a name whose MX currently points at another provider.
dry_runbooleanValidate and report what would happen without changing anything.
verify_domainVerify a domainwrites admin token dry_run

Check the domain's live DNS: ownership TXT (external domains), MX, SPF, DMARC and the sending provider's DKIM status. Marks the domain verified when ownership is proven, and registers it for sending once it is.

ParameterTypeNotes
domain requiredstringDomain name, for example example.com or mail.example.com.
dry_runbooleanValidate and report what would happen without changing anything.
list_domainsList domainsread only

Every domain on this account with its status, mode, sending readiness and mailbox count.

No parameters.

remove_domainRemove a domainwrites destructive admin token dry_run

Remove a domain from this account. Refuses while it still has mailboxes unless delete_mailboxes is true (which deletes them and their mail). DNS records are left as they are; the response lists the ones you may want to remove.

ParameterTypeNotes
domain requiredstringDomain name, for example example.com or mail.example.com.
confirm requiredstringRepeat the domain name to confirm.
delete_mailboxesboolean
dry_runbooleanValidate and report what would happen without changing anything.

Mailboxes 6

Addresses that receive and send, plus aliases, catch-alls and forwarding.

create_mailboxCreate a mailboxwrites admin token dry_run

Create a mailbox (an address that receives and sends) on one of this account's domains. Also returns its route address, which any mail host can forward to.

ParameterTypeNotes
address requiredstringThe new address, for example agent@example.com.
display_namestring
dry_runbooleanValidate and report what would happen without changing anything.
list_mailboxesList mailboxesread only

Every mailbox this token can see, with unread counts, last received time, aliases, forwarding and route address. Also describes the calling token (scope, can_send, daily cap).

No parameters.

delete_mailboxDelete a mailboxwrites destructive admin token dry_run

Delete a mailbox. Mail to it is rejected from then on; its stored messages stay readable by owner tokens for audit until the domain is removed.

ParameterTypeNotes
address requiredstringMailbox address (or mailbox id).
confirm requiredstringRepeat the address to confirm.
dry_runbooleanValidate and report what would happen without changing anything.
add_aliasAdd an aliaswrites admin token dry_run

Deliver mail sent to another address on one of this account's domains into an existing mailbox.

ParameterTypeNotes
mailbox requiredstringMailbox address (or mailbox id).
alias requiredstringThe extra address, for example support@example.com.
dry_runbooleanValidate and report what would happen without changing anything.
set_catch_allSet the catch-allwrites admin token dry_run

Deliver mail for any unknown address on a domain into one mailbox, or pass mailbox: null to turn the catch-all off.

ParameterTypeNotes
domain requiredstring
mailbox requiredstring | nullMailbox address (or mailbox id).
dry_runbooleanValidate and report what would happen without changing anything.
set_forwardingSet forwardingwrites admin token dry_run

Also forward every message a mailbox receives to up to 5 external addresses (the original is attached as message/rfc822). Pass an empty list to stop. Forwarded copies never re-forward.

ParameterTypeNotes
mailbox requiredstringMailbox address (or mailbox id).
forward_to requiredstring[]
dry_runbooleanValidate and report what would happen without changing anything.

Read 9

One unified inbox across every mailbox the token can see.

list_messagesList messages (unified inbox)read only

One inbox across every mailbox this token can see, newest first, each item tagged with its mailbox. Filters: mailboxes, folder (inbox, archive, sent, drafts, trash, all), unread_only, label, from, subject_contains, since (default 30 days). Paginate with next_cursor. Sender-controlled text arrives inside an untrusted_external_content envelope.

ParameterTypeNotes
mailboxesstring[]Limit to these mailboxes (addresses or ids). Default: every mailbox this token can see.
folder"inbox" | "archive" | "sent" | "drafts" | "trash" | "all"
unread_onlyboolean
direction"inbound" | "outbound"
labelstring
fromstring
subject_containsstring
sincestringISO date; default 30 days ago.
limitinteger
cursorstring
search_messagesSearch messagesread only

Full-text search over subject, sender, recipients and body across every mailbox this token can see (all folders by default).

ParameterTypeNotes
query requiredstring
mailboxesstring[]Limit to these mailboxes (addresses or ids). Default: every mailbox this token can see.
folder"inbox" | "archive" | "sent" | "drafts" | "trash" | "all"
sincestring
limitinteger
cursorstring
get_messageGet a messageread only

Headers, text body (or text derived from HTML), extracted links, attachment list and threading ids for one message. Body truncated at max_chars (default 20000, max 200000) with a truncated flag. HTML only with include_html.

ParameterTypeNotes
id requiredstring
max_charsinteger
include_htmlboolean
get_threadGet a threadread only

The whole conversation a message belongs to, inbound and outbound, oldest first, as previews.

ParameterTypeNotes
id requiredstringAny message id in the thread.
wait_for_messageWait for a messageread only

Block until a matching message arrives (or timeout_s, max 60, default 25) and return it in full. Built for sign-up codes and magic links: create a mailbox, use it in a form, wait here. since defaults to 60 seconds ago.

ParameterTypeNotes
mailboxstring
fromstring
subject_containsstring
sincestring
timeout_sinteger
max_charsinteger
get_attachmentGet an attachmentread only

One attachment as base64 (up to 5 MB). attachment_id is the index listed by get_message.

ParameterTypeNotes
message_id requiredstring
attachment_id requiredinteger
mark_readMark read or unreadwrites dry_run

Mark up to 100 messages read (or unread with read: false). Ids outside this token's scope come back in not_found_in_scope.

ParameterTypeNotes
ids requiredstring[]Message ids (1 to 100).
readboolean
dry_runbooleanValidate and report what would happen without changing anything.
labelAdd or remove labelswrites dry_run

Add and/or remove labels on up to 100 messages. Labels are free-form short strings; filter on them with list_messages label.

ParameterTypeNotes
ids requiredstring[]Message ids (1 to 100).
addstring[]
removestring[]
dry_runbooleanValidate and report what would happen without changing anything.
archiveArchivewrites dry_run

Move up to 100 messages out of the inbox into archive (or back with unarchive: true).

ParameterTypeNotes
ids requiredstring[]Message ids (1 to 100).
unarchiveboolean
dry_runbooleanValidate and report what would happen without changing anything.

Send 4

Send, reply in-thread, forward and draft, behind per-token gates.

send_messageSend a messagewrites can_send dry_run

Send a new email from a mailbox in scope (or send a saved draft with draft_id). Requires a token with can_send; counts against its daily cap; at most 20 recipients. Use dry_run to check the gates without sending.

ParameterTypeNotes
fromstringMailbox address to send from.
tostring | string[]
ccstring | string[]
bccstring | string[]
subjectstring
textstring
htmlstring
reply_tostring
attachmentsobject[]Up to 10 attachments, 5 MB each, base64.
draft_idstring
dry_runbooleanValidate and report what would happen without changing anything.
replyReplywrites can_send dry_run

Reply in-thread from the mailbox that received the message (In-Reply-To and References set, "Re:" added once, original quoted). reply_all adds the other recipients as cc.

ParameterTypeNotes
message_id requiredstring
textstring
htmlstring
reply_allboolean
ccstring | string[]
attachmentsobject[]Up to 10 attachments, 5 MB each, base64.
dry_runbooleanValidate and report what would happen without changing anything.
forwardForwardwrites can_send dry_run

Forward a message (with its attachments) to new recipients, with an optional note on top. Sends from the receiving mailbox unless from names another mailbox in scope.

ParameterTypeNotes
message_id requiredstring
to requiredstring | string[]
ccstring | string[]
textstring
fromstring
dry_runbooleanValidate and report what would happen without changing anything.
create_draftCreate a draftwrites dry_run

Save a draft in a mailbox (folder drafts) for a human or another agent to review. Send it later with send_message draft_id. Does not need can_send. Pass reply_to_message_id to thread it as a reply.

ParameterTypeNotes
from requiredstring
tostring | string[]
ccstring | string[]
bccstring | string[]
subjectstring
textstring
htmlstring
reply_to_message_idstring
dry_runbooleanValidate and report what would happen without changing anything.

Automation 3

Signed webhooks when mail arrives or a send finishes.

create_webhookCreate a webhookwrites admin token dry_run

POST a signed JSON event to your https URL when mail arrives or a send finishes. Events: message.received, message.sent, message.failed. Each delivery carries X-AIMail-Timestamp and X-AIMail-Signature (v1=hex HMAC-SHA256 of "timestamp.body" with the secret returned once here). Private and loopback targets are refused.

ParameterTypeNotes
url requiredstring
events"message.received" | "message.sent" | "message.failed"[]
mailboxesstring[]
dry_runbooleanValidate and report what would happen without changing anything.
list_webhooksList webhooksread only admin token

Webhooks on this account with their events, scope and last delivery status. Secrets are never shown again.

No parameters.

delete_webhookDelete a webhookwrites admin token dry_run

Stop and remove a webhook.

ParameterTypeNotes
webhook_id requiredstring
dry_runbooleanValidate and report what would happen without changing anything.

Access 4

Scoped tokens for other agents, and the audit log.

create_tokenCreate a tokenwrites admin token dry_run

Mint a token for another agent, scoped to some mailboxes (default: all), with or without send permission and with a daily send cap. A token can never exceed the one that creates it. The token value is returned once and never stored.

ParameterTypeNotes
label requiredstring
mailboxesstring[]
can_sendboolean
daily_send_capinteger
is_adminboolean
expires_in_daysinteger
dry_runbooleanValidate and report what would happen without changing anything.
list_tokensList tokensread only admin token

Tokens on this account: label, prefix, scope, send permission, cap, last use. Never the values.

ParameterTypeNotes
include_revokedboolean
revoke_tokenRevoke a tokenwrites destructive admin token dry_run

Revoke a token by id or prefix. It stops working immediately.

ParameterTypeNotes
token requiredstringToken id or its prefix (amk_...).
dry_runbooleanValidate and report what would happen without changing anything.
audit_logRead the audit logread only admin token

Recent tool calls on this account (every attempt, including refusals and dry runs): tool, outcome, token, time.

ParameterTypeNotes
limitinteger
toolstring

How mail reaches you

Managed domains

When a domain's DNS lives on our Cloudflare account, its MX records point at Cloudflare Email Routing (route1/2/3.mx.cloudflare.net). Routing hands each message to our aimail-receiver worker, which signs the raw message and posts it to this server. We store the original RFC 822 source and parse it into the inbox.

Your own DNS

Keep your MX where it is. Prove ownership with one TXT record (_aimail.yourdomain), add the sending records from get_dns_plan, and forward the addresses you want to each mailbox's route address. Sending works from your domain with your DKIM.

RecordNameValueWhy
MXyourdomainroute1/2/3.mx.cloudflare.netInbound, managed domains only
TXT_aimail.yourdomainaimail-verify=...Ownership, external domains
TXT / MXsend.yourdomainfrom get_dns_planSPF and bounces for sending
TXTresend._domainkey.yourdomainfrom get_dns_planDKIM signature key
TXT_dmarc.yourdomainv=DMARC1; p=nonePolicy; tighten once mail flows

Security model

Scoped tokens

A token sees only its mailboxes. Sending needs can_send and stops at a rolling 24 hour cap. A token can never mint one with more power than itself. Only hashes are stored.

Signed inbound

The receiver worker signs every message with HMAC-SHA256 over a timestamp and the exact body. Unsigned, stale or altered posts are rejected before parsing. Outbound webhooks are signed the same way.

Untrusted content

Everything a sender controls comes back inside an untrusted_external_content envelope with a note that it is data, not instructions. Links are extracted; HTML is returned only on request.

Audit and dry runs

Every tool call writes an audit row, including refusals. Every write accepts dry_run. Destructive tools ask you to repeat the name you are removing.

DNS writes are fenced

apply_dns only works on zones in our own Cloudflare account, for internal accounts, never deletes records, and refuses to take over a domain whose mail goes elsewhere.

Your raw mail

The original message source is kept in private object storage, so nothing is lost to parsing. Bodies are searchable; attachments are served only to tokens that can see the mailbox.