A Claude Desktop extension that gives Claude fast access to Apple Mail on macOS. Reads come straight from Mail.app's local message store — searching a 250k-message mailbox takes milliseconds, not tens of seconds — while Mail.app remains the sync and auth engine (iCloud, Gmail, anything Mail supports), so no IMAP credentials or passwords are ever handled. Writes (drafts, flags) go through Mail.app's native scripting interface.
Packaged as an MCPB desktop extension with the Apple Mail icon and one-click install.
| Tool | Description |
|---|---|
get_stats |
Total messages, unread count, mailbox and account counts |
list_mailboxes |
Every account/folder with message counts |
search_emails |
Rich search: free text, sender, recipient (To/CC), subject, date range, read/flagged status, attachments. Every result includes clickable open-in-Mail links |
get_email |
Full email with decoded plain-text body, recipients, flag color, and metadata |
get_email_link |
Get a message:// URL that opens the email directly in Mail.app |
open_email_in_mail |
Open an email directly in Mail.app (for chat UIs that block message:// links) |
get_selected_emails |
The message(s) currently selected in Mail.app's viewer — id, subject, sender, mailbox, and open-in-Mail links |
get_email_html |
HTML body of a message |
get_thread |
All messages in a conversation thread |
list_email_attachments |
Enumerate attachments for any email |
get_email_attachment |
Retrieve attachment content (base64) |
create_email_draft |
Create a draft email saved to Mail.app's Drafts mailbox, returns a message:// link to open it |
create_email_reply_draft |
Reply to an existing message — preserves In-Reply-To/References headers so the reply threads correctly in the recipient's client |
get_email_flag |
Get the flag status and color (e.g. "orange") for an email |
set_email_flag |
Set or remove a color flag on an email (red/orange/yellow/green/blue/purple/gray, or null to remove) |
Mail.app remains the sync and auth engine — it holds your Apple ID / iCloud credentials natively and continuously mirrors every account to disk. This server has two engines on top of that:
Reads are served directly from Mail.app's local message store:
~/Library/Mail/V*/MailData/Envelope Index— Mail's SQLite index of every message (subjects, senders, recipients, dates, read/flag state). Searches complete in milliseconds instead of tens of seconds..emlxfiles — raw RFC 2822 messages on disk, parsed for bodies, headers, HTML, and attachments.
No credentials are ever handled: the server is a read-only consumer of data Mail.app has already synced. The store is opened read-only (PRAGMA query_only) and never mutated. The only extra requirement is Full Disk Access, granted once in System Settings — see Permissions for the uv gotcha.
The schema of the Envelope Index varies across macOS releases, so the server introspects it at runtime and adapts (falling back to the documented flags bitfield when dedicated columns are absent). Account UUIDs in mailbox URLs are resolved to display names ("iCloud", "Work Gmail") via the system accounts store. Run uv run python -m apple_mail_mcp.selftest from a terminal with Full Disk Access to verify the fast path on your machine.
Every search/thread/email result carries two links:
mail_link— the rawmessage://<Message-ID>URL. Works in Terminal (open '<url>'), Notes, Reminders, task managers, and Safari — but most chat UIs (including Claude Desktop and Claude Code) block custom URL schemes in rendered links.open_link—http://127.0.0.1:<port>/open/<id>?t=<token>. Chat UIs open http links fine: the click routes through your browser to a tiny localhost-only server inside the extension, which tells macOS to front Mail.app on that message. Requests require a per-install random token (persisted, so links in old conversations keep working); the endpoint's only capability is focusing Mail — it never serves message content.
There is also an open_email_in_mail tool so Claude can jump to a message directly without any clicking.
When Full Disk Access is missing, reads transparently fall back to the original JXA (JavaScript for Automation) bridge (the two-round bulk-fetch search, ~37s across 60k+ messages). Search results include an engine field ("sqlite" or "applescript") so you can tell which path served them. Set APPLE_MAIL_MCP_DISABLE_FAST=1 to force the JXA path.
Writes — drafts, reply drafts, flag changes — always go through Mail.app scripting (Automation permission), so Mail.app owns every mutation and syncs it back to the server (e.g. iCloud) itself.
- macOS 13 Ventura or later
- Apple Mail running with at least one configured account
- Python 3.11+
- Claude Desktop (with extension support)
Download the latest .mcpb from Releases, then double-click to install.
Or build from source:
git clone https://github.com/falconbradley/claude-connector-apple-mail.git
cd claude-connector-apple-mail
./build.shThen double-click dist/apple-mail.mcpb (or drag it into Claude Desktop).
The extension appears in Settings > Extensions with the Apple Mail icon.
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"apple-mail": {
"command": "uv",
"args": ["run", "--project", "/path/to/apple-mail-mcp", "apple-mail-mcp"]
}
}
}Two macOS permissions matter:
- Full Disk Access (for the fast read path): System Settings > Privacy & Security > Full Disk Access > enable uv. Claude Desktop launches extension servers through a helper that makes the spawned process itself responsible for permissions, so macOS attributes FDA to the
uvlauncher binary — enabling Claude Desktop alone is not sufficient. Ifuvisn't in the list, add it with + (press Cmd+Shift+G):~/.local/bin/uvand/or~/Library/Application Support/Claude/uv-runtime/<version>/uv. Then disable/re-enable the extension. Without FDA, reads still work via the slow AppleScript fallback. (Enable your terminal app too if you want to run the selftest.) - Automation (for writes and the fallback): Mail.app must be running; macOS prompts automatically on first use — click OK. If the prompt doesn't appear, check System Settings > Privacy & Security > Automation.
Once installed, just ask Claude naturally:
- "Show me my unread emails from this week"
- "Search for emails from alice@example.com about the Q4 budget"
- "Show me emails sent to bill@example.com in the last month"
- "What attachments are in the last email from my accountant?"
- "Summarise the email thread about the contract renewal"
- "Find flagged emails with PDF attachments"
- "Draft a reply to John's email about the project update"
- "Reply-all to that thread saying I'll review by EOD"
- "Create a draft email to the team announcing Friday's meeting"
- "Flag this email as orange"
- "What color is the flag on that email from Sarah?"
# Install mcpb CLI (one time)
npm install -g @anthropic-ai/mcpb
# Build the extension
./build.sh
# Or manually:
mcpb validate manifest.json
mcpb pack . dist/apple-mail.mcpbapple-mail-mcp/
├── manifest.json # MCPB desktop extension manifest
├── icon.png # Apple Mail icon (512x512)
├── icons/ # Multi-size icons
│ ├── icon-128.png
│ ├── icon-256.png
│ └── icon-512.png
├── pyproject.toml # Python package + dependencies
├── build.sh # Validate + pack build script
└── src/
└── apple_mail_mcp/
├── __init__.py
├── server.py # MCP tools (FastMCP)
├── applescript.py # JXA bridge to Mail.app
├── emlx.py # MIME body extraction utilities
└── models.py # Pydantic data models
Search performance depends on mailbox size and which filters are active. Bulk property fetches are conditional — only the properties needed for active filters are fetched.
| Scenario | Approx. time |
|---|---|
| Init (one-time mailbox prescan) | ~12s |
| Search with date filter only | ~14s |
| Search with text (subject/sender) | ~24s |
| Search with recipient (To/CC) filter | ~68s |
| Full search (all filters) | ~91s |
Times measured against ~61K messages across 7 mailboxes. Searches without optional filters add zero overhead for those properties.
Phase 1 — Read
- List mailboxes and accounts
- Search emails (subject, sender, recipient, date, flags, attachments)
- Read full message body (plain text + HTML)
- Thread view
- List and retrieve attachments
-
message://links to open emails in Mail.app
Phase 2 — Write (in progress)
- Create draft emails (saved to Drafts with a
message://link to open) - Reply to a thread (preserves
In-Reply-To/Referencesheaders) - Set, change, or remove color flags on emails
- Mark as read / unread
- Move to folder
- Delete (move to Trash)
- Read operations never modify your mail: the Envelope Index is opened with
PRAGMA query_onlyand.emlxfiles are only ever read. Write operations go through Mail.app scripting and are limited to: creating drafts (saved locally, never sent automatically) and setting/removing flags on messages. - No data leaves your machine — this is a local MCP server. Mail.app keeps sole custody of account credentials (iCloud sign-in, OAuth, etc.).
- The open-in-Mail link redirector binds to 127.0.0.1 only, requires a per-install random token on every request, and can only focus Mail.app on a message — it never serves message content.
- Full Disk Access (read-only usage) powers the fast search path; without it the extension degrades to Automation-only scripting.
- macOS-only (
"platforms": ["darwin"]in manifest). - Attachment data is returned as base64 only when explicitly requested.
"Mail.app is not running" Open Mail.app before using the extension. It must be running for JXA scripting to work.
"Automation permission denied" Go to System Settings > Privacy & Security > Automation and ensure Claude Desktop (or Terminal) is allowed to control Mail.app. Then restart Claude Desktop.
Search is slow or times out
Large mailboxes (50k+ messages) take longer. Use date filters (since) to narrow the search window. The first search after startup includes a one-time ~12s mailbox prescan.
Extension doesn't appear after install Make sure you're running a recent version of Claude Desktop that supports MCPB extensions. Restart Claude Desktop after installing.