imessage-mcp
# imessage-mcp
A local MCP (Model Context Protocol) server that lets Claude Code read your
macOS Messages history directly, so you stop copy-pasting text threads into
chat by hand.
It reads `~/Library/Messages/chat.db` and your local Contacts database
directly with Python's stdlib `sqlite3`. There are no native dependencies and
nothing leaves your Mac: every tool is a local, read-only SQL query plus a
small typedstream decoder for the message bodies that Messages stores as
binary blobs.
## What it gives Claude
- `list_chats(query, limit)`: recent chats, with names resolved from Contacts.
- `read_thread(chat, since, limit)`: a thread in order, with sender names,
attachment paths, and tapback reactions folded into the message they landed
on.
- `search(text, chat, since, limit)`: full text search over decoded message
bodies.
- `whats_new(mark_seen)`: everything new across every chat since the last
call, using a small cursor file at `~/.imessage-mcp/cursor.json`.
- `send(chat, text)`: sends a message through Messages.app. Disabled unless
you explicitly turn it on, see "Sending" below.
## Requirements
- macOS with Messages.app signed in and syncing.
- Full Disk Access granted to whatever process runs this server (your
terminal, or Claude Code itself, depending on how you launch it). Without
it, `~/Library/Messages/chat.db` will fail to open even in read-only mode.
Grant it under System Settings, Privacy and Security, Full Disk Access.
- Python 3.12 or newer, managed with `uv`.
## Install
```
cd /Users/clint/Projects/imessage-mcp
uv sync
```
## Register with Claude Code
```
claude mcp add --scope user imessage -- uv run --directory /Users/clint/Projects/imessage-mcp python -m imessage_mcp.server
```
That registers the server once, for every project, over stdio.
## Sending is off by default
`send(chat, text)` only works when the server process has the environment
variable `IMESSAGE_SEND=1` set. Without it, the tool returns a plain message
saying sending is disabled, and does nothing else.
When enabled, sending goes through `osascript` and Messages.app:
- a one on one chat sends with `send text to buddy handle of (service 1
whose service type is iMessage)`.
- a group chat sends with `send text to chat id "<guid>"`.
The first send will prompt macOS for Automation permission for whatever
process is driving Messages.app. Treat this as a real send: nothing here
double checks with you before the message goes out, so only turn
`IMESSAGE_SEND` on in a session where you are prepared to review the exact
text before asking the tool to send it.
## Privacy
Everything runs locally. The server opens `chat.db` and your Contacts
database read only (`sqlite3` URI `mode=ro`), decodes text and attachment
paths in process, and returns plain text to Claude. Nothing is uploaded
anywhere, and no network calls happen anywhere in this codebase.
## Tests
```
uv run pytest
```
The tests read your live `chat.db` and Contacts database and skip themselves
cleanly if either is unreadable (for example, no Full Disk Access, or run on
a machine with no Messages history).
## Layout
- `imessage_mcp/typedstream.py`: decodes the `attributedBody` blob that
Messages uses instead of plain `text` for most rows.
- `imessage_mcp/db.py`: all the read only SQL against `chat.db` and Contacts.
- `imessage_mcp/server.py`: the FastMCP server and tool definitions.
- `tests/`: pytest tests against the live database.
TDQS
Scored across 5 tools
Each tool targets a distinct action: listing chats, reading a thread, searching messages, incremental polling, and sending. Even though read_thread and search both return messages, their purposes are clearly separated and easy to tell apart.
list_chats and read_thread follow a verb_noun pattern, while search and send are bare verbs and whats_new is a phrase. All names use lower_snake_case and are readable, but the naming is not fully uniform.
Five tools is a well-scoped set for an iMessage bridge. It covers the main interactions without unnecessary redundancy or surface-area bloat.
The tool set covers the core messaging workflow: enumerate chats, read conversations, search content, poll for new messages, and send replies. There are no obvious dead ends or missing operations for the stated domain.