MailSyncMCP

MailSyncMCP

MCP server that enables reading, searching, and replying to all Apple Mail accounts from Claude while keeping credentials local, using a two-phase commit for safe sending.

Category
访问服务器

README

MailSyncMCP

Search, read and reply to every account in Apple Mail from Claude, without handing over a single password.

Works with Gmail, Microsoft 365, iCloud and custom-domain IMAP alike, because it reads what Mail has already synced rather than talking to each provider. Runs entirely on your Mac. Nothing is uploaded anywhere.

Why it works this way

Apple Mail already syncs every account you have and already holds their credentials in the Keychain, refreshing them on its own. This server reads Mail's local store and asks Mail to compose. It never sees a password or a token, never stores one, and never triggers a re-authentication. Adding an account means adding it in Mail; nothing here changes.

The alternative, talking to each provider directly, means a Google Cloud project, an Azure app registration, and a refresh token per account. Worse, work and university accounts are usually locked down so that a third-party OAuth app needs administrator consent, which is often simply refused. Reading what Mail has already synced sidesteps all of it.

Safety

Reading and searching are free. Sending is not.

  1. No credentials. Nothing to leak or expire.
  2. Two-phase commit. prepare_* tools validate a request, render a full preview, and return a single-use token that expires in five minutes. They change nothing. commit_action(token) is the only tool that acts, and it takes a token and nothing else, so the message that goes out is always the message that was shown.
  3. Client permission prompt on commit_action.
  4. Untrusted-content fencing. Message bodies come back wrapped and marked as data. Because sending needs a token that only a rendered preview can mint, an instruction buried in an email cannot cause mail to be sent.
  5. Audit log at ~/.local/share/mailsync/audit.jsonl, append-only.

The server speaks stdio only. There is no listener, no daemon and no timer, so it acts only when a tool is called from the chat session.

Set MAILSYNC_READ_ONLY=1 to disable composing entirely for a session.

On AppleScript

Every script in applescript/ is a static file that receives its values through argv. No script text is ever generated, so quotes, backslashes, newlines and AppleScript syntax in an email survive as literal data. tests/test_applescript_safety.py asserts this against hostile payloads. Other Apple Mail MCP servers concatenate message data into script source and defend it with an escaping function; this one has no injection surface to escape.

Tools

Read: list_accounts, search_mail, get_message, get_thread, list_attachments, save_attachment, mailbox_stats, open_in_mail, refresh_index

Compose: prepare_send, prepare_reply, prepare_forward, list_pending, commit_action

There is deliberately nothing that flags, moves, archives or deletes mail. The only thing this server can change is that a new message gets sent or drafted.

Links back to Mail

Every message returned carries a message:// URL built from its RFC-822 Message-ID. Clicking one opens that message in Apple Mail, so answers in chat stay traceable to the real thing. Messages Mail has not stored locally have no Message-ID and so get no link.

Chat clients sanitise link targets to an allowlist of schemes (http, https, mailto), so a message:// link renders as dead text rather than something you can click. The open_in_mail tool exists for that reason: ask to open a message and it goes straight to Mail. The URL is still printed so it can be copied or used outside chat.

open_in_mail takes whole_thread=True, but Mail opens each message in its own window: there is no scriptable threaded view. set selected messages on the main viewer was tested repeatedly and does not work on macOS 26 in conversation mode, returning objects the AppleScript bridge cannot coerce back. get_thread is the better way to read a conversation.

A link always opens a single message, not the conversation around it. Mail registers only three URL schemes (mailto, message, mail-pref-pane) and none of them addresses a thread. Driving Mail's own threaded list through AppleScript was tried and abandoned: on macOS 26 set selected messages either selects the wrong conversation or reports an empty selection, in both a Gmail All Mail mailbox and a plain INBOX. Rather than ship that, get_message lists the rest of the conversation with a link per message, and get_thread renders the whole exchange in order.

Install

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/python -m mailsync --build-index

Registering it

Claude Code and Claude for Desktop are separate apps with separate config files. Registering in one does nothing for the other:

Surface Config file
Claude Code ~/.claude.json, top-level mcpServers
Claude for Desktop ~/Library/Application Support/Claude/claude_desktop_config.json

Add the same entry to whichever you use, pointing at the venv's Python:

{
  "mcpServers": {
    "mailsync": {
      "command": "/absolute/path/to/Apple-Mail-Sync-MCP/.venv/bin/python",
      "args": ["-m", "mailsync"]
    }
  }
}

Because the package is installed into the venv with pip install -e ., the entry does not depend on a working directory. Quit and reopen the app afterwards.

A local server like this one runs as a child process on your Mac, so it is only available to apps running there. claude.ai in a browser cannot reach it.

Each surface starts its own OS process; stdio has no way to share one. That does not fork the setup, because both entries run the same installed package and every process reads and writes the same index at ~/.local/share/mailsync/mailsync.db. Edit the code once and both pick it up on their next restart. Concurrent access is safe: the index runs in WAL mode with a busy timeout, tested with five readers and two simultaneous rebuilds.

Full Disk Access

~/Library/Mail is gated behind Full Disk Access, and an MCP server inherits the grant of whichever app launched it. Claude Code and Claude for Desktop are separate TCC clients, so one can work while the other cannot. Grant it under System Settings > Privacy & Security > Full Disk Access, then quit and reopen the app completely.

Without it, search and reading still work from the local index, which lives outside the protected area. Only refresh_index fails, so the data goes stale. The tool says so plainly rather than failing obscurely.

Composing additionally needs Mail.app running and Automation permission, which macOS prompts for on first use.

Partial messages

Mail keeps header-only copies of messages it has not fully downloaded: about a quarter of them on a typical mailbox. Those stay searchable on sender, subject, date and whatever preview text Mail stored. get_message takes fetch_if_partial=True to ask Mail for the full source, which makes it pull the body from the server. That needs Mail running, so it is opt-in rather than automatic.

How the index works

~/.local/share/mailsync/mailsync.db holds an FTS5 index over message bodies extracted from ~/Library/Mail/V10/**/*.emlx, keyed to Mail's own Envelope Index. Mail's database is copied aside before reading and is never opened writable or locked.

A first build takes about a minute for 12,800 messages. After that a refresh is one to two seconds, since only files whose mtime moved get re-parsed. Searches return in single-digit to low tens of milliseconds.

Bodies are searchable for the roughly 70 percent of messages Mail has downloaded in full. The rest are searchable on sender, subject, date and Mail's own preview text, and get_message falls back to those. Coverage rises on its own as Mail syncs.

Maintenance

Apple's Envelope Index schema is undocumented and can change in a macOS update. All of it is confined to mailsync/index.py, and the body index can be rebuilt from the .emlx files at any time with --build-index --full.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选