@1amsheldon/mail-mcp
v3.0.0
Published
Email MCP server for Codex and Claude with IMAP, SMTP, Apple Mail, Microsoft, and Mailtrap
Maintainers
Readme
mail-mcp
An email MCP server for Codex, Claude Code, Claude Desktop, and other MCP clients. It connects to IMAP, SMTP, ManageSieve, Apple Mail, Microsoft Graph, Exchange Web Services, and Mailtrap without putting mail credentials in client configuration.
Backends
| Backend | What it supports | Runtime requirement | | --- | --- | --- | | IMAP + SMTP | Cursor-based listing and search, stable message locators, raw RFC 822, folders, copy/move/trash/permanent delete, flags, threads, contacts, MIME drafts and sends, reply-all, attachments, delivery verification | Any provider exposing IMAP and SMTP | | ManageSieve | List, read, create, update, and delete server-side filters | A ManageSieve endpoint on the IMAP account | | Apple Mail | Accounts, mailboxes, messages, raw source, drafts, compose, reply, reply-all, forward, flags, move, trash, and rule management | macOS with Mail.app and Automation permission | | Microsoft Graph | Read by ID, Internet Message-ID lookup, thread lookup, send, reply, inline attachments, and large attachment upload sessions | Pre-provisioned Microsoft OAuth2 credentials | | Exchange Web Services | Search, read, and send through EWS with escaped XML payloads | EWS endpoint and pre-provisioned OAuth2 credentials | | Mailtrap | Send, templates, sandboxes, logs, statistics, inbound streams, domains, suppressions, webhooks, contacts, lists, fields, imports, exports, and campaigns | Mailtrap API token |
Gmail, Google Workspace, Mail.ru, iCloud, Fastmail, Yahoo, Zoho, and other standards-based providers use the IMAP + SMTP backend. POP3 and JMAP are not implemented.
Install
Requirements:
- Node.js 20.19 or newer
- an operating-system credential store supported by
cross-keychain - credentials for at least one backend
Add an account with the interactive backend wizard:
npx -y --prefer-online @1amsheldon/mail-mcp@latest accounts addThe wizard supports IMAP/SMTP, Apple Mail, Microsoft Graph, EWS, and Mailtrap. List or remove configured accounts with:
npx -y --prefer-online @1amsheldon/mail-mcp@latest accounts list
npx -y --prefer-online @1amsheldon/mail-mcp@latest accounts remove ACCOUNT_IDAccount definitions are stored in ~/.config/mail-mcp/accounts.json. Passwords, OAuth2 credentials, and API tokens are stored in the operating-system credential store under com.1amsheldon.mail-mcp.
Codex
npx -y --prefer-online @1amsheldon/mail-mcp@latest --install-codexThe installer updates only [mcp_servers.mail], backs up an existing config, and installs the bundled mail-mcp skill in ~/.codex/skills/mail-mcp.
On Windows, it installs one authenticated Streamable HTTP service at http://127.0.0.1:8765/mcp. Every Codex conversation shares the same process and cached per-account connections, so opening more conversations does not create more mail sessions. A supervised task starts it at sign-in, checks health, and applies later npm releases after a graceful restart.
On macOS and Linux, the installer writes an auto-updating stdio entry. To select stdio explicitly on any platform:
npx -y --prefer-online @1amsheldon/mail-mcp@latest --install-codex-stdioAdd --read-only to either installer command to expose only read tools. Restart Codex after installation.
Claude Desktop
npx -y --prefer-online @1amsheldon/mail-mcp@latest --install-claudeThe installer preserves other MCP servers, creates claude_desktop_config.json.mail-mcp.bak when replacing an existing file, and writes an npx command that checks for the latest release when Claude starts the server. Add --read-only for read-only access, then restart Claude Desktop.
Claude Code
npx -y --prefer-online @1amsheldon/mail-mcp@latest --install-claude-codeThe installer delegates configuration to the official claude mcp CLI, registers mail at user scope, and installs the bundled workflow in ~/.claude/skills/mail-mcp. Replacing an existing mail registration is transactional: the installer backs up ~/.claude.json and restores it byte-for-byte if registration or skill installation fails. Native Windows uses the required cmd /c npx wrapper; macOS, Linux, and WSL use npx directly. Add --read-only for read-only access, then restart Claude Code.
Other MCP clients
Run the server over stdio:
npx -y --prefer-online @1amsheldon/mail-mcp@latest --confirm --audit-log --redactExample Codex-compatible TOML:
[mcp_servers.mail]
command = "npx"
args = ["-y", "--prefer-online", "@1amsheldon/mail-mcp@latest", "--confirm", "--audit-log", "--redact"]
enabled = true
startup_timeout_sec = 30.0
tool_timeout_sec = 300.0Do not put passwords, refresh tokens, client secrets, API tokens, or HTTP bearer tokens in MCP configuration.
Agent workflow
The server publishes the mail://agent-guide resource and the mail_agent_workflow prompt. The Codex installer also installs the same workflow as a skill.
The important rules are short:
- Call
list_accountsand inspect backend capabilities before choosing an operation. - For IMAP/SMTP, call
listFoldersbefore assuming mailbox names. For Apple Mail, useapple.listMailboxes. Microsoft and Mailtrap expose provider resources instead of a universal folder list. - For IMAP/SMTP, start
listMessagesorsearchMessageswithout a cursor, then passnextCursorback unchanged. Use the matching provider-prefixed operation for other backends. Offset pagination is rejected. - Keep the returned locator. It binds the account, mailbox, UIDVALIDITY, and UID so mailbox changes cannot silently retarget an operation.
- Read a message before replying, forwarding, moving, deleting, changing flags, or fetching attachments.
- Prefer a draft when recipients or wording are not final.
- Never retry
smtp_outcome_unknownautomatically. UseverifySentMessagewith the returned Message-ID first.
IMAP cursor snapshots are capped at 10,000 UIDs and hydrate only the requested page. Page size is capped at 100.
Keyword search
Use mail_query with searchMessages for IMAP accounts:
{
"accountId": "work",
"operation": "searchMessages",
"input": {
"folder": "INBOX",
"keywordsAny": ["invoice", "payment overdue"],
"excludeKeywords": ["newsletter"],
"keywordScope": "all",
"unread": true,
"since": "2026-09-01",
"limit": 20,
"headerOnly": true
}
}keywordsAll: every word or phrase must match.keywordsAny: at least one word or phrase must match.excludeKeywords: none may match.keywordScope:body(default),subject, orall(headers and body).unreadandflagged: filter message state;falseselects read or unflagged messages.
Combine these with from, to, cc, subject, messageId, since, and before. Different filter groups are combined with AND. Array elements are literal substrings, not regular expressions or an AI relevance score. Each list accepts up to 20 terms of up to 256 characters. Matching and language behavior depend on the IMAP provider. The existing keywords string searches a literal phrase in the body.
Search runs on the mail server and fetches only the requested result page. Use headerOnly: true to avoid body snippets. To continue, repeat the same filters with cursor set to nextCursor; changing filters requires a new search. Apple Mail and Microsoft use their provider-specific search operations.
MCP surface
The server exposes three tools instead of publishing a separate JSON schema for every operation:
| Tool | Purpose |
| --- | --- |
| list_accounts | Discover configured accounts, backends, and capabilities |
| mail_query | Read, search, inspect, and render mail data |
| mail_mutate | Draft, send, organize, delete, and configure mail |
Operations are routed through those two tools rather than registered as separate tools or servers. Detailed input rules are loaded on demand through the mail://agent-guide resource and the mail_agent_workflow prompt, keeping the initial tool catalog small.
Both routers use the same envelope:
{
"accountId": "work",
"operation": "listMessages",
"input": {
"folder": "INBOX",
"limit": 25
}
}Use mail_query for listMessages, searchMessages, readMessage, listFolders, threads, attachments, statistics, contacts, templates, delivery verification, and filter reads. Use mail_mutate for sends, drafts, replies, forwarding, mailbox changes, labels, flags, Trash, permanent deletion, OAuth registration, and filter changes.
Provider operations use a prefix, for example apple.listMessages, microsoft.searchMessages, or mailtrap.templates. Mailtrap keeps its resource action inside input:
{
"accountId": "mailtrap",
"operation": "mailtrap.templates",
"input": { "operation": "list" }
}microsoft.searchMessages is available through EWS. Microsoft Graph supports ID lookup, Internet Message-ID lookup, and thread lookup instead.
Moving to Trash and permanent deletion remain separate operations: moveToTrash and permanentlyDelete.
Account configuration
The CLI writes secret-free JSON. Optional IMAP/SMTP fields include:
signature: append a signature to sends and drafts.allowedRecipients: exact addresses or domains such as@example.org.allowedAttachmentRoots: real paths from which file attachments may be loaded. Base64 attachments do not use filesystem paths.fromAliases: approved From addresses.smtpSecurity:tls,starttls, orplain; plaintext SMTP is restricted to loopback hosts.sentPolicy:auto,always, orneverfor providers that save Sent mail themselves.sentFolder: explicit Sent-folder override.manageSievePort: enable server-side filter tools.
Apple Mail accounts also accept allowedAttachmentRoots. Path-based attachments are disabled until at least one root is configured, and symlinks cannot escape those roots.
Mail.app automation is useful for native rules and accounts that do not expose server credentials, but AppleScript scans can be slow on large mailboxes. Configure the same mailbox through the IMAP + SMTP backend when fast server-side listing and search are required; use the Apple Mail backend only for Mail.app-specific operations.
IMAP/SMTP OAuth2 uses pre-provisioned XOAUTH2 credentials and refreshes them at runtime. The package does not create provider applications or run browser consent flows.
For Gmail, the shortest personal setup is usually a Google app password with 2-Step Verification enabled:
| Setting | Value |
| --- | --- |
| IMAP host | imap.gmail.com |
| IMAP port | 993 |
| SMTP host | smtp.gmail.com |
| SMTP port | 465 for implicit TLS or 587 for STARTTLS |
| User | full Gmail or Workspace address |
| Password | app password, not the normal account password |
Managed Google environments may require OAuth2 instead.
Guarded writes
mail-mcp --read-only
mail-mcp --allow-tools createDraft,moveMessage
mail-mcp --confirm --audit-log --redact--read-onlyremovesmail_mutate.--allow-toolsnarrowsmail_mutateto named operations. Version 1 internal names remain accepted as CLI selectors during upgrades, but they are not advertised as MCP tools.--confirmrequires a short-lived confirmation ID bound to the exact tool arguments.--audit-logwrites JSONL diagnostics to~/.config/mail-mcp/audit.log.--redactmasks selected sensitive values before content reaches the client.
Nested audit fields are sanitized. Provider errors are reduced to safe codes and request metadata instead of returning raw response bodies or process stderr.
Delivery states
Server drafts
For IMAP/SMTP accounts, createDraft returns a stable draftId. Keep it to edit or send the same draft later:
{"accountId":"work","operation":"updateDraft","input":{"draftId":"returned-id","changes":{"subject":"Revised subject","textBody":"Ready for review."}}}{"accountId":"work","operation":"sendDraft","input":{"draftId":"returned-id"}}Both calls use mail_mutate. Supply locator instead of draftId to use an existing server draft. Updates accept to, cc, bcc, subject, textBody, htmlBody, and attachments. Omitted fields stay unchanged. An attachment list replaces the old list; [] clears it. Changing a body format removes unspecified alternatives so old HTML cannot override new plain text.
Sending reads the latest server content, preserves MIME bodies and reply headers, and excludes Bcc headers from the transmitted message while keeping those recipients in the SMTP envelope. The unchanged draft moves to Trash only after full recipient acceptance and a confirmed Sent copy. Partial or uncertain delivery retains the draft and must not trigger an automatic resend.
Delivery records in ~/.config/mail-mcp/draft-state prevent repeated calls from resending a recorded attempt, including after a restart. Keep this directory when moving an installation. This protection applies to this installation, not independent sends from webmail or another computer. Do not schedule the same draft in both places.
Interrupted replacements and stale process locks require inspection; the service does not guess which copy to delete or resend. IMAP cannot atomically compare and replace a draft, so avoid editing it in webmail during an update or send. If webmail replaces both its UID and Message-ID, select the current draft again.
SMTP results
SMTP acceptance and the Sent-folder copy are independent. Send, reply, and forward return structured results:
| Status | Meaning |
| --- | --- |
| sent_and_saved | SMTP accepted every recipient and IMAP confirmed the Sent copy |
| partially_sent_and_saved | SMTP accepted some recipients and IMAP confirmed the Sent copy |
| smtp_accepted_sent_not_confirmed | SMTP accepted the message, but the Sent copy was not confirmed |
| smtp_partially_accepted_sent_not_confirmed | Partial SMTP acceptance without a confirmed Sent copy |
| smtp_rejected | SMTP rejected every recipient |
| smtp_connection_failed | The connection failed before a confirmed delivery attempt |
| smtp_outcome_unknown | Delivery may have happened; do not retry automatically |
Use retrySafe, messageId, and the verifySentMessage query operation. Absence from Sent is not proof that SMTP delivery failed.
Shared HTTP service
Manual loopback start:
$env:MAIL_MCP_BEARER_TOKEN = '<random-token>'
npx -y --prefer-online @1amsheldon/mail-mcp@latest --http --host 127.0.0.1 --port 8765 --confirm --audit-log --redactGET /health reports version, start time, and active session count. POST /mcp requires the bearer token. Keep the bind address on loopback unless an authenticated reverse proxy protects the server.
If the managed service restarts after sleep or an automatic update, requests carrying an older MCP session ID are recovered without requiring the chat to be reopened. Within each service process, the mail connection cache, pagination snapshots, rate limits, and pending write confirmations are shared by ordinary and recovered requests.
Updates
Managed Codex and Claude installations run @1amsheldon/mail-mcp@latest. Updates replace package code only; account JSON and keychain credentials remain in their existing user directories.
Version 2 changes the MCP tool surface from individual operation names to mail_query and mail_mutate. Restart the MCP client after upgrading so it refreshes the tool list. Stored accounts and credentials do not need migration.
Version 3 keeps the same three-tool interface and adds server draft editing/sending and keyword filters. Accounts and credentials stay in place; draft delivery records are created on first use. Refresh the client's tool catalog or restart the client to discover the new operations.
The Windows service checks npm every six hours. It stops accepting new requests, gives in-flight requests up to eight seconds to finish, and then restarts on the new package. Stdio clients update when the MCP process next starts.
Windows logon and watchdog tasks use a windowless launcher. Managed services created by older versions migrate their scheduled-task action when the updated service next starts; the previous task definition is saved as ~/.config/mail-mcp/service/task-before-windowless.xml.
If a Windows installation predates 2.1.3, run npx -y --prefer-online @1amsheldon/mail-mcp@latest --install-codex once from outside a source checkout to replace the old supervisor as well. The new supervisor installs package updates without lifecycle scripts and launches Node directly, avoiding the npx/cmd.exe process chain that can open Windows Terminal. Accounts and credentials are preserved.
If an older installation pins an exact version, run its installer command again.
Re-running --install-codex on an existing Windows HTTP installation preserves its bearer token. When the connection settings are unchanged, existing chats can continue after the service updates without restarting Codex.
Connection diagnostics
npx -y --prefer-online @1amsheldon/mail-mcp@latest --validate-accountsChecks configured IMAP connections and SMTP hosts without sending mail. Prints a result for each connection and a total of passed, failed, and skipped probes. Exits with code 1 if a probe fails or no IMAP accounts are configured; otherwise exits with 0. This command does not validate Apple Mail, Microsoft API, or Mailtrap accounts.
Develop
git clone https://github.com/1amSheldon/mail-mcp.git
cd mail-mcp
npm ci
npm run release:check
npm pack --dry-runrelease:check builds the package, enforces English repository text, runs the complete test suite, exercises stdio and HTTP transports, checks importability, and runs npm audit.
The default suite uses protocol and provider mocks. It does not access a real mailbox, send mail, call Microsoft or Mailtrap, or automate Mail.app. --validate-accounts opens configured IMAP and SMTP connections without sending a message.
Local Windows build
After building and testing, node dist/index.js --install-codex-local runs that checkout as the shared Windows service. This disables npm refresh and runtime auto-update for that installation. Rebuild and rerun the installer to update it. Use --install-codex to return to the published npm package. Account definitions and credentials are reused.
npm run backup:windows-service backs up the task, service files, and Codex configuration under the local mail-mcp configuration directory. Keep these backups private. npm run probe:draft-workflow -- --check checks the draft operation catalog and lists folders without writing mail. It requires MAIL_MCP_ACCOUNT_ID and MAIL_MCP_BEARER_TOKEN. The separate --prepare mode creates and updates a test draft and must be explicitly requested; it never sends.
Release
Publishing is handled by .github/workflows/publish.yml. Publish a GitHub Release whose tag is v followed by the exact version in package.json. GitHub Actions verifies the tag and publishes to npm through OIDC trusted publishing; the workflow contains no npm token.
License
MIT. See LICENSE.
