iMessage bridge for Claude Code. Reads ~/Library/Messages/chat.db directly for history and new-message polling; sends via AppleScript to Messages.app. macOS only. Built-in access control: inbound messages are gated by an allowlist (default: self-chat only), outbound sends are scoped to the same allowlist. The /imessage:access skill manages allowlists and policy. Requires Full Disk Access and Automation TCC grants — both prompted by macOS on first use. Ships full source — server.ts runs locally via bun, started by the .mcp.json command.
iMessage
Connect iMessage to your Claude Code assistant. Reads ~/Library/Messages/chat.db directly for history, search, and new-message detection; sends via AppleScript to Messages.app. No external server, no background process to keep alive.
macOS only.
Quick setup
Default: text yourself. Other senders are dropped silently (no auto-reply) until you allowlist them. See ACCESS.md for groups and multi-user setups.
1. Grant Full Disk Access.
chat.db is protected by macOS TCC. The first time the server reads it, macOS pops a prompt asking if your terminal can access Messages — click Allow. The prompt names whatever app launched bun (Terminal.app, iTerm, Ghostty, your IDE).
If you click Don't Allow, or the prompt never appears, grant it manually: System Settings → Privacy & Security → Full Disk Access → add your terminal. Without this the server exits immediately with authorization denied.
2. Install the plugin.
These are Claude Code commands — run claude to start a session first.
Install the plugin. No env vars needed.
/plugin install imessage@claude-plugins-official
3. Relaunch with the channel flag.
The server won't connect without this — exit your session and start a new one:
claude --channels plugin:imessage@claude-plugins-official
Check that /imessage:configure tab-completes.
4. Text yourself.
iMessage yourself from any device. It reaches the assistant immediately — self-chat bypasses access control.
The first outbound reply triggers an Automation permission prompt ("Terminal wants to control Messages"). Click OK.
5. Decide who else gets in.
Nobody else's texts reach the assistant until you add their handle:
/imessage:access allow +15551234567
Handles are phone numbers (+15551234567) or Apple ID emails (them@icloud.com). If you're not sure what you want, ask Claude to review your setup.
How it works
| Inbound | Polls chat.db once a second for ROWID > watermark. Watermark initializes to MAX(ROWID) at boot — old messages aren't replayed on restart. |
| Outbound | osascript with tell application "Messages" to send …. Text and chat GUID pass through argv so there's no escaping footgun. |
| History & search | Direct SQLite queries against chat.db. Full history — not just messages since the server started. |
| Attachments | chat.db stores absolute filesystem paths. The first inbound image per message is surfaced to the assistant as a local path it can Read. Outbound attachments send as separate messages after the text. |
Access control
See ACCESS.md for DM policies, groups, self-chat, delivery config, skill commands, and the access.json schema.
Quick reference: IDs are handle addresses (+15551234567 or someone@icloud.com). Default policy is allowlist — this reads your personal chat.db. Self-chat always bypasses the gate.
Tools exposed to the assistant
| Tool | Purpose |
|---|---|
reply |
Send to a chat. chat_id + text, optional files (absolute paths). Auto-chunks text; files send as separate messages. |
chat_messages |
Fetch recent history from a chat (oldest-first). Reads chat.db directly — full native history. Scoped to allowlisted chats. |
What you don't get
AppleScript can send messages but not tapback, edit, or thread — those require Apple's private API. If you need them, look at BlueBubbles (requires disabling SIP).