apple-mail-mcp
Health Pass
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 24 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
MCP server for Apple Mail: sub-ms search over 300k+ messages via Mail's own SQLite index, verified sends, Exchange body backfill, triage plan/review/apply. 849 tests, MIT.
✉️ apple-mail-mcp
Your Apple Mail, fully agent-operable.
Runs locally on your Mac. It opens the Mail store read-only, returns only
what a client asks for, and sends only when the client explicitly calls the
send tool. No third-party relay, no cloud copy of your mail.
uvx apple-mailbox-mcp setup
Measured on a 300k-message store: addressing one message 0.1 ms via Mail's own
index, against 7 to 10 s for the AppleScript whose-clause; full-text search
p95 under 1 ms. Method and script: docs/benchmarks.md.
Use Claude Code, Claude Desktop, Codex, Cursor, VS Code, or any local MCP client to search,
read, triage, and send email through Apple Mail — your mailbox becomes
something you can
ask, search and delegate to — find anything in seconds, file hundreds of
messages through a reviewed plan, send polished mail as the right identity,
and let Exchange deliver scheduled messages even while your Mac is asleep.

✨ What you can do
🔍 Ask your mailbox questions. "What did Maria send me about the memo
last week?" Search runs at database speed — sender, mailbox, dates, unread,
attachments — and reconstructs whole conversations.
🕳️ Find what Mail itself can't. Mail's built-in search only skims the
first line of most messages. apple-mail-mcp indexes every message body on your
Mac — and for Exchange accounts it even fetches the bodies Mail never
downloaded, straight from your own mailbox on the server. Queries that
returned nothing return twenty.
🎭 Send as the right you. Work mail through the work lane, personal
through Gmail — one parameter picks the identity. Every message is composed
from scratch as clean, standards-correct email that renders everywhere,
including Outlook (the AppleScript compose path that arrives blank in
Outlook is the reason this project exists).
⏰ Schedule like "Send Later", but scriptable. A scheduled message is
frozen in full — attachments, identity, exact text. Exchange can execute it
server-side at the requested time, lid closed; other providers use a local
background sender and deliver on its next pass (or just after the Mac wakes).
🗂️ Triage at scale, without fear. "File these 40 newsletters" becomes a
reviewable plan: nothing moves until it is approved, every message is
re-checked before it is touched, and the result is verified against Mail's
own records afterward. Delete means Mail's Trash — nothing is ever erased.
📝 Draft where your drafts live. Compose into your real Exchange Drafts
folder, ready to open in Outlook or OWA — created, never auto-sent.
⚡ Why it's different
Every other Apple-Mail MCP drives AppleScript for both finding and acting.
This one doesn't — and it shows:
| Operation | AppleScript whose-clause | apple-mailbox-mcp |
|---|---|---|
| 🔍 Search 300k messages | seconds-to-timeout | milliseconds |
| 🎯 Address one message in a 71k mailbox | 7–10 s (measured) | < 0.1 ms |
| ✉️ Send mail | body renders blank in Outlook | renders everywhere, plain+HTML |
| ⏰ Schedule mail | — | server-side on Exchange; reliable local queue everywhere else |
| 🗂️ Bulk triage | one call per message, fire-and-forget | one reviewed plan, one apply, verified |
Every number above was measured on a live ~300,000-message store; the
script and full method are in docs/benchmarks.md.
If the benchmarks hold up on your mailbox, a ⭐ helps others find this.
🛡️ Built to be trusted
- ✅ Plan → review → apply → verify. Bulk actions are frozen into a plan
you can read before anything happens; the outcome is confirmed against
Mail's own store afterward — never assumed. - 🗑️ Nothing is ever erased. "Delete" files into Mail's Trash, and
destructive plans have their own separate, capped door. - 👓 Read-only mail mode. Set
EMAIL_MCP_READ_ONLY=1and only the 11
non-mutating mail tools exist in the session. Search may still maintain its
local body index, and attachment retrieval writes the requested file to the
configured temporary directory. - 💾 A crash-safe scheduled queue. Manifest updates are flushed and
atomically replaced, so an interrupted rewrite keeps the last valid record.
If a file is damaged independently, diagnostics name it instead of claiming
the queue is empty, while healthy scheduled messages keep moving. - 🧾 A local, best-effort activity ledger. Sends, schedules,
cancellations and triage runs are recorded without making an unwritable log
block mail. For reconciliation, the message itself, its Message-ID and its
scheduled record remain authoritative. - 🔒 No third-party mail relay. Mail content stays local except for mail
you send and optional access to your own provider for Exchange/IMAP body
backfill, drafts and server-side scheduling. SMTP passwords stay in the
macOS Keychain or 1Password; Microsoft OAuth tokens live in a private 0600
cache under~/.email-mcp/graph/. - 📜 A written contract. Since v1.0 every tool's shapes, error codes and
caps evolve additively, held in place by 800+ automated tests. - 🤝 Clear to every MCP client. All 21 tools identify what they do, explain
every input, and declare whether they read, change or can remove data. Newer
clients receive structured results; older clients keep the same JSON text.
Both the maintained MCP 1.x line and current MCP 2.x are tested. - 🧱 Built to evolve without breaking your workflow. Email rules are
isolated from MCP, Mail.app, Exchange, delivery, and local storage. Provider
or SDK changes stay at the edge while the 21-tool contract remains stable.
The dependency rules are enforced in CI and explained in the
architecture guide. - 📦 Releases you can verify. Every tagged release is built and installed
in a clean environment before publishing. GitHub includes the wheel, source
archive, SHA-256 checksums and signed build provenance—not just source code. - 🩺 Self-diagnosing.
apple-mail-mcp statusgives one readable readiness,
scheduling and recovery screen.apple-mail-mcp doctorprovides the complete
diagnostic detail and an exact fix for anything red.
🚀 Quick start
Grant Full Disk Access to your terminal app
(System Settings → Privacy & Security → Full Disk Access), then quit and
reopen the terminal. This is Apple's one manual toggle — there is no
pop-up for it.Install and set up:
uvx apple-mailbox-mcp setup # or: pipx install apple-mailbox-mcp # or via Homebrew: brew install parasxos/tap/apple-mail-mcp && apple-mail-mcp setupRegister with your client — one line for Claude Code:
claude mcp add --transport stdio --scope user apple-mail -- uvx apple-mailbox-mcpor the same JSON block for Claude Desktop / Cursor / VS Code
(claude_desktop_config.json/.cursor/mcp.json/.vscode/mcp.json):{ "mcpServers": { "apple-mail": { "command": "uvx", "args": ["apple-mailbox-mcp"] } } }Verify:
uvx apple-mailbox-mcp statusprints one readiness screen —
or just ask your client to run thedoctortool; every red line comes
with its exact fix. The first body-index build on a large mailbox runs in
the background and can take a few minutes; search works immediately and
completes as the index fills.
Before running setup, grant your terminal app Full Disk Access
(System Settings → Privacy & Security → Full Disk Access) — that is how
reading stays fast and local. There is no pop-up for this one; it is Apple's
one manual toggle, and setup walks you to the exact pane if it finds it
missing.
setup asks everything in plain words (bare Enter accepts the recommended
answer), offers a sending identity, builds the body-search index, verifies
the nightly refresh actually runs, and ends by printing the one block you
paste into your MCP client:
{
"mcpServers": {
"apple-mail": { "command": "apple-mail-mcp" }
}
}
Setup ends with a clear ready verdict or numbered recovery steps. Grant
Automation → Mail when triage first asks for it. Check the installation,
the next scheduled message, and failed scheduled sends anytime withapple-mail-mcp status; use apple-mail-mcp doctor for the full technical detail.
💡 New to the terminal? Three things that look wrong and aren't:
brew install pipxwants a typedy(Enter alone is rejected);pipx ensurepathmay print a ⚠️ — the "pipx is ready to go!" line after it
is the verdict; and afterensurepath, close and reopen the terminal once
soapple-mail-mcpis found.
🔌 Works with
Every client below speaks stdio MCP; the command is always uvx apple-mailbox-mcp.
Claude Code
claude mcp add --transport stdio --scope user apple-mail -- uvx apple-mailbox-mcp
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{ "mcpServers": { "apple-mail": { "command": "uvx", "args": ["apple-mailbox-mcp"] } } }
Cursor
Add the same block to ~/.cursor/mcp.json (or per-project .cursor/mcp.json).
VS Code (Copilot MCP)
Add the same block under "servers" in .vscode/mcp.json.
Codex CLI
# ~/.codex/config.toml
[mcp_servers.apple-mail]
command = "uvx"
args = ["apple-mailbox-mcp"]
Any stdio MCP client
Point it at uvx apple-mailbox-mcp. The wire contract is additive-only since v1.0.
🔧 Troubleshooting
| Symptom | Fix |
|---|---|
Client says the server failed to start, but uvx apple-mailbox-mcp works in your terminal |
GUI apps don't inherit your shell PATH. Use the absolute path: "command": "/opt/homebrew/bin/uvx" (find yours with which uvx). |
database is locked or empty results |
Full Disk Access is missing for the app that launches the server (the client, not the terminal). Grant it, then fully quit and reopen that app. |
| Search finds recent mail but not bodies of old mail | The body index is still building — first build on a 100k+ mailbox takes minutes. status shows progress. |
Send fails with transport_unavailable |
Run doctor: it names the failing lane (Keychain item missing, SSH socket cold, SMTP host unreachable) and prints the exact fix. |
| Triage does nothing the first time | Grant Automation → Mail when macOS asks; the prompt appears on first use, not at install. |
🧰 The 21 tools
| Group | Tools |
|---|---|
| 🔍 Read (8) | search_emails (full-body search) · get_email · get_emails_batch · get_thread · list_mailboxes · list_recent · get_attachment · refresh_mail |
| ✉️ Send (6) | send_email · reply_email (threaded, quoted) · create_draft · schedule_email · list_scheduled · cancel_scheduled |
| 🗂️ Triage (5) | triage_plan · triage_plan_delete · triage_apply · mailbox_create · mailbox_delete |
| 🩺 Meta (2) | doctor (full diagnostics with fix-it strings) · audit (the local ledger) |
Attachments both ways, size-budgeted. Replies thread correctly in every
client. Scheduling survives sleep — a message due while the lid was closed
goes out on the first tick after wake, or exactly on time via Exchange.
🎭 Your addresses, your lanes
The From: address decides how mail travels. ~/.email-mcp/identities.toml:
default = "work"
[work] # sent through a host you already trust, over SSH
from_addr = "[email protected]"
driver = "ssh_sendmail"
host = "bastion.example.org" # any login host you already SSH to
[gmail] # classic SMTP — the app password stays in 1Password
from_addr = "[email protected]"
driver = "smtp"
host = "smtp.gmail.com"
op = "op://Personal/gmail app password/password"
Exchange identities can add one sign-in to unlock the extras: drafts filed in
your real Drafts folder, and scheduled sends executed by the server itself —
lid closed, Mac asleep. setup offers it in one plain question. Reading
needs no sending configuration at all.
21 tools · 849 tests · additive wire contract since v1.0
Live-calibrated end-to-end on a 305k-message store.
Built for one Mac — and for anyone else whose Mac runs Mail.app.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found