imessage-mcp
A local MCP server that exposes your Mac's iMessage data to Claude Desktop / Claude Code.Two surfaces:
- SEND via AppleScript →
Messages.app - READ via direct, read-only SQLite access to
~/Library/Messages/chat.db
Everything is local. No API keys, no network calls.
Install
# 1. Clone / copy into ~/mcp-servers/imessage-mcp
cd ~/mcp-servers/imessage-mcp
# 2. Create a Python 3.11+ venv and install deps
python3.12 -m venv .venv
.venv/bin/pip install -e .
Permissions (required)
Full Disk Access — so we can read chat.db
System Settings → Privacy & Security → Full Disk Access → toggle ON for:
- your terminal app (e.g. Terminal, iTerm2, Ghostty), AND
Claude.app(if using Claude Desktop)
Automation — so we can control Messages.app
System Settings → Privacy & Security → Automation → expand your terminal / Claude.appand toggle Messages to ON.
Messages.app must be running and signed into iMessage.
If a permission step is missing, the server errors with the exact settings path to open.
Claude Desktop config
Merge into ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"imessage": {
"command": "/Users/YOUR_USERNAME/mcp-servers/imessage-mcp/.venv/bin/python",
"args": ["-m", "imessage_mcp.server"]
}
}
}
(The absolute path to the venv's python is the safest — it avoids PATHambiguity. Replace YOUR_USERNAME.)
Tools
| Tool | Purpose |
|---|---|
send_imessage(recipient, body, service?) |
Send via Messages.app. recipient is a phone, Apple ID email, or existing buddy name. |
list_recent_chats(limit=20) |
Newest-first chats with last-message preview and unread count. |
get_chat_messages(chat_id?, handle?, limit=50, since?) |
Messages for a chat; provide chat_id or handle. |
search_imessages(query, since?, until?, from_contact?, limit=25) |
Case-insensitive LIKE across message bodies. |
get_my_info() |
Self-handles + DB metadata. |
Dates are ISO8601 in and out. Internally, Apple stores message.date asnanoseconds since 2001-01-01 UTC; the server converts both ways.
Safety invariants
chat.dbis opened withsqlite3.connect("file:...chat.db?mode=ro", uri=True).Any write attempt raisessqlite3.OperationalError: attempt to write a readonly database.send_imessagepasses recipient / body / service as separate osascript argventries — no shell interpolation, no injection.- Attachments return metadata only (filename, mime_type, total_bytes). Blobs arenever read.
Limitations
- Scheduled / delayed sends are not supported.
- Rich / reply-threaded messages may have their text stored in
attributedBody(NSKeyedArchive). We best-effort extract the embedded NSString; messages withmore exotic payloads (images, taps, Apple Pay) will show emptybodybutcorrect metadata. - Delivery confirmation:
send_imessagereports success from osascript's exitcode. To verify the message actually went through, callsearch_imessagesorget_chat_messagesa few seconds later. - This is macOS-only and will not work on sandboxed App Store apps.
Local development
.venv/bin/pytest -q