Apple Mail Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Apple Mail Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A minimal, read-only MCP (Model Context Protocol) server that lets Claude Desktop interact with Apple Mail on macOS. Uses AppleScript via subprocess — no third-party email libraries, no network calls.
Current: 1.2.1
Versioning follows Semantic Versioning:
| Tool | Description |
|---|---|
mail_list_mailboxes | List every account and mailbox configured in Apple Mail |
mail_search_emails | Search emails by keyword across all mailboxes (uses Mail's native search index); optional account and mailbox_name filters for scoped searches |
mail_read_email | Read the full content of a specific email by its opaque ID |
git clone https://github.com/androidua/apple-mail-mcp.git
cd apple-mail-mcppython3 -m venv venv
venv/bin/pip install -r requirements.txtvenv/bin/python apple_mail_mcp.pyPress Ctrl-C to stop. If no errors appear, the server is ready.
The first time the server runs, macOS will ask whether this process may control Apple Mail. Click OK. You can manage this later in:
System Settings → Privacy & Security → Automation
Open (or create) ~/Library/Application Support/Claude/claude_desktop_config.json and add the block shown below under "mcpServers".
{
"mcpServers": {
"apple_mail": {
"command": "/path/to/apple-mail-mcp/venv/bin/python",
"args": [
"/path/to/apple-mail-mcp/apple_mail_mcp.py"
]
}
}
}Replace /path/to/apple-mail-mcp with the absolute path to the directory where you cloned the repository (e.g. /Users/yourname/projects/apple-mail-mcp).
Restart Claude Desktop after saving the file.
Once connected, you can ask Claude things like:
mail_search_emails uses AppleScript's whose clause — a declarative predicate evaluated by Mail's Objective-C runtime — to filter messages by subject and sender. This is fast even on accounts with hundreds of thousands of messages, and works correctly on macOS 26 / Mail 16 (which removed the older search <mailbox> for <keyword> AppleScript command). System mailboxes (Trash, Junk, Spam) are skipped by default.
To further scope a search, pass the optional account and/or mailbox_name parameters — e.g. restrict to account="Yahoo" and mailbox_name="INBOX" to avoid scanning all accounts.
| Symptom | Fix |
|---|---|
AppleScript failed … not allowed to send Apple events | Go to System Settings → Privacy & Security → Automation and enable Mail for your Python process. |
No mailboxes found | Open Apple Mail and ensure at least one account is signed in. |
| Tool times out | Use account and/or mailbox_name to scope the search, or reduce limit. |
Invalid email_id | Always pass the email_id back exactly as returned by mail_search_emails. |
apple-mail-mcp/
├── apple_mail_mcp.py # MCP server — single file, all tools
├── requirements.txt # Pinned dependencies
├── README.md # This file
└── venv/ # Local virtual environment (not committed)proc.stdout.close() / proc.stderr.close() — asyncio.StreamReader has no .close() method; calling it on timeout caused AttributeError that crashed the tool and surfaced as the "StreamReader object has no attribute 'close'" error users saw for slow IMAP accountsasyncio.gather(return_exceptions=True) — a slow or offline account (Yahoo!, Hotmail, etc.) can no longer block or crash results from other accounts; each account gets an independent 45-second timeoutaccount is provided the original single-script path is preserved (60 s timeout); parallel path is used only for cross-account searchesmail_search_emails now accepts since_days (integer, 1–365) to filter emails by date received — supports natural queries like "last 7 days", "yesterday", "past month"keyword is now optional in mail_search_emails — browse recent mail without a search term (e.g. since_days=1 returns today's mail)keyword="invoice" + since_days=30 returns invoice emails from the past monthwhose content contains forces a full body download for every message, making it impractically slow on real mailboxes_script_read_email to proven account/mailbox iteration — direct AppleScript addressing (mailbox X of account Y) was unreliable for non-standard account types (Gmail, Exchange, shared accounts)mail_search_emails JSON output to flat array [...] — the {"results": [...]} wrapper introduced in v1.1.3 broke Claude AI's ability to extract email_id values from resultsawait proc.wait() on timeout — prevents file descriptor accumulation under repeated Mail.app timeouts---BODY_START--- split to a leading newline — prevents a subject line containing that exact string from corrupting header parsing in mail_read_email\x1f byte in a subject no longer shifts sender/date/is_read columns_CTRL_STRIP_RE to cover C1 controls U+0080–U+009F (including U+0085 NEL which Python's splitlines() treats as a line terminator)osascript stderr internally; return a generic error string to callers instead of forwarding script fragments_script_read_email with direct AppleScript object addressing (mailbox X of account Y) — O(1) lookup instead of O(accounts × mailboxes) name scanwhose docstring — it is O(n) per mailbox, not indexed; low limit does not reduce scan cost\) in the_script_read_email AppleScript template with sequential assignments — AppleScript uses ¬ for continuation, not \; the invalid characters caused all mail_read_email calls to fail with AppleScript syntax error -2741
search <mailbox> for <keyword> AppleScript command with awhose clause filter — the search command was removed in Mail 16 (macOS 26) and caused all mail_search_emails calls to fail with an AppleScript syntax error
mail_search_emails now uses Apple Mail's native indexedsearch (search <mailbox> for <keyword>) instead of brute-force message iteration — dramatically faster on large mailboxes (e.g. Yahoo with 20+ years of email)
account and mailbox_name parameters tomail_search_emails for scoped searches (e.g. search only Yahoo / INBOX)
skipped automatically unless explicitly targeted via mailbox_name
mail_list_mailboxes, mail_search_emails, mail_read_email~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.