Skip to main content
Glama
Apple Messages MCP

Apple Messages MCP

by dh-repo

apple-messages-mcp

Local MCP server that lets a Cursor or Grok Bot agent read your Messages.app database on the user's Mac (iMessage + SMS), and optionally send a reply through AppleScript.

There is no cloud inbox and no extra phone number. Reads stay on localhost. Read-only by default. Scoped shut by default. With no MESSAGES_SCOPE and no MESSAGES_ALLOW_UNSCOPED=1, list/search/thread/send/watch return SCOPE. A personal bot that wants the whole inbox sets one line: MESSAGES_ALLOW_UNSCOPED=1.

See DESIGN.md for schema notes, the threat model, Phase 2 watcher hook, and agent instructions.

Requirements

  • macOS with Messages.app (the live chat.db path)

  • Node.js 22 (engines: >=22 <23)

  • Full Disk Access for the app that launches this server (Cursor, or Terminal if you test from a shell)

  • Automation permission for Messages only if you turn send on

Linux CI uses a tiny fixture SQLite database. You do not need Messages.app to run npm test.

Related MCP server: iMessage MCP Server

Install (pinned 0.2.0)

This is meant to be cloned, built, and launched as node /ABS/PATH/dist/index.js — not npx'd unpinned from a random machine. Requires Node 22. bin is dist/index.js.

git clone https://github.com/dh-repo/apple-messages-mcp.git
cd apple-messages-mcp
# stay on a commit whose package.json version you intend to run (0.2.0+)
npm install
npm test
npm run build
node dist/index.js

The server speaks MCP over stdio (leave this to Cursor; do not write logs to stdout). The example Cursor config stays node /ABS/PATH/TO/apple-messages-mcp/dist/index.js.

Dev: npx tsx src/index.ts. Same binary is a local CLI for the smoke checklist and the Phase 2 watcher:

npx tsx src/index.ts status
npx tsx src/index.ts list
npx tsx src/index.ts thread --chat-id 12 --limit 20
npx tsx src/index.ts search snacks
npx tsx src/index.ts watch --interval 3000

Full Disk Access

  1. Open System Settings → Privacy & Security → Full Disk Access.

  2. Enable Cursor (and Terminal if you smoke-test here).

  3. Quit and reopen that app. TCC is applied at launch.

  4. If messages_status returns fda_likely_missing: true or PERMISSION, this step is not done.

Without FDA, macOS returns EPERM for ~/Library/Messages/chat.db even to the file's owner.

Cursor / Claude Desktop config

Cursor Add MCP server / mcp.json (same shape):

{
  "mcpServers": {
    "apple-messages": {
      "command": "node",
      "args": ["/ABS/PATH/TO/apple-messages-mcp/dist/index.js"],
      "env": {
        "MESSAGES_DB_MODE": "copy",
        "ENABLE_SEND": "0",
        "REDACT_PREVIEWS": "0"
      }
    }
  }
}

A checked-in copy lives at examples/cursor-mcp.json. Point args at your clone.

Claude Desktop: paste the same mcpServers block into claude_desktop_config.json.

Dev without a build: npx tsx src/index.ts.

Environment

Default is scoped shut. Pick one:

"MESSAGES_SCOPE": "Family, 12"
"MESSAGES_ALLOW_UNSCOPED": "1"

Variable

Default

Purpose

MESSAGES_SCOPE

unset

Comma/semicolon tokens: display name, chat_id, guid, handle.

MESSAGES_ALLOW_UNSCOPED

0

1 is the only way to open the whole inbox.

MESSAGES_DB_PATH

~/Library/Messages/chat.db

Override for fixtures.

MESSAGES_DB_MODE

copy

Copy + WAL replay. direct opens the live file.

ENABLE_SEND

0

1 enables messages_send (real texts).

REDACT_PREVIEWS

0

Bodies become [redacted N chars].

MESSAGES_WAKE_HOOK

unset

Watcher runs this executable with one JSON event on stdin.

WATCH_INCLUDE_PREVIEW

0

Watcher includes a redacted preview.

Tools

Tool

What it does

messages_status

Can we read chat.db? FDA missing? Scoped shut or unscoped? Snapshot size/age?

messages_list_chats

Recent chats, or only the allowlist if one is set.

messages_get_thread

Messages for a chat_id / guid / handle (required unless the allowlist is exactly one chat).

messages_search

Two-phase substring search (plain text LIKE, then a bounded Tahoe decode). Uses a sidecar of (message_id, decoded_text) so old blobs stay findable after they leave the scan window. Returns truncated when a row has not been decoded.

messages_send

Registered only when ENABLE_SEND=1. dry_run: true returns { to, chat_id, guid, body } and does not call osascript. A real send still requires confirm: true.

IDs in responses are SQLite ROWIDs: chat_id, message_id, handle_id, attachment_id. Handles are phone numbers and emails, not Contacts names.

Copy this into the agent's MCP instructions: the block in DESIGN.md §8.

Watcher (Phase 2)

Not push, and not part of the MCP stdio stream. A second process polls chat.db (and optionally fs.watchs the WAL) for new ROWIDs — all chats, or only the allowlist if one is set — then writes JSON lines:

npx tsx src/index.ts watch --interval 3000
# {"type":"messages.ready","chat_id":…,"newest_message_id":…}
# {"type":"messages.new","chat_id":…,"count_new":1,"preview":null}

Pipe that into your own wake script, or set MESSAGES_WAKE_HOOK to examples/wake-hook.sh. The host should start an agent turn and call messages_get_thread. Do not treat this as a Slack-style channel.

Smoke-test checklist (Mac)

  1. npm test passes (fixture-only; safe on any machine).

  2. Grant FDA, restart Cursor, reload the MCP server.

  3. Call messages_status.

    • readable: true

    • fda_likely_missing: false

    • unscoped: false unless you set MESSAGES_ALLOW_UNSCOPED=1

    • Optional: set MESSAGES_SCOPE and confirm scope.active: true and scope.chats matches

  4. messages_list_chats without scope env returns SCOPE and scope.candidates.

  5. messages_get_thread with a chat_id from a scoped or unscoped list returns recent lines. At least some text_source values may be attributedBody on current macOS. text_source: "guess" means the decoder fell back to printable bytes.

  6. messages_search with a word you know exists returns hits across chats, or empty + truncated: true when the Tahoe window missed.

  7. messages_send is absent from tools/list while ENABLE_SEND is unset.

  8. Optional: ENABLE_SEND=1, Automation allowed. dry_run: true returns { to, chat_id, guid, body } without sending. A real send still needs confirm: true. Leave send off afterward.

  9. npx tsx src/index.ts status (CLI) matches the MCP messages_status payload.

  10. npx tsx src/index.ts watch --interval 2000 prints one messages.ready line and, on a new message, messages.new without plaintext.

Inspector, if you want a GUI without Cursor:

npx @modelcontextprotocol/inspector npx tsx src/index.ts

What this is not

  • Not Inkbox / Linq / a hosted agent number.

  • Not a push notification service. The Phase 2 watch process polls / fs.watchs chat.db and wakes the host — see DESIGN.md.

  • Not a way to dump chat.db to a server.

License

MIT. See LICENSE.

Available Tools

4 tools
messages_get_threadGet threadA

Return messages for one chat, oldest-first within the page. No MESSAGES_SCOPE and MESSAGES_ALLOW_UNSCOPED is unset; list/search/thread/send/watch return SCOPE. Identify the chat with chat_id (chat.ROWID), guid (via handle field or chat_id), or a participant handle (phone/email). If omitted and the allowlist has exactly one chat, that chat is used. before paginates: a message_id or ISO-8601 timestamp. from_date / to_date filter on the converted Apple date. Attachment metadata is included; file bytes are not. Tapbacks are omitted unless include_reactions is true.

Inputs: limit, before, handle, chat_id, +3 more.

messages_list_chatsList chatsA

List recent conversations from the local Messages database (iMessage and SMS). No MESSAGES_SCOPE and MESSAGES_ALLOW_UNSCOPED is unset; list/search/thread/send/watch return SCOPE. Returns chat_id (chat.ROWID), guid, chat_identifier, handles (phone/email), and a last-message preview. Prefer guid to remember a thread across launches. Does not return attachment bytes.

Inputs: limit, query.

messages_statusMessages statusA

Report whether chat.db is readable, whether Full Disk Access is likely missing, macOS version hints, send/redact flags, snapshot size/age, and whether the inbox is scoped shut. Call this first when anything fails. Does not return message bodies. unscoped is true only when MESSAGES_ALLOW_UNSCOPED=1.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.8/5.0

    Scored across 4 tools

    Disambiguation4/5

    Each tool has a distinct role: messages_status is diagnostic, messages_list_chats enumerates conversations, messages_get_thread reads a single thread, and messages_search finds text. Minor overlap exists since list_chats returns a last-message preview and get_thread/search both return message content, but the intended usage is clear enough to avoid misselection.

    Naming Consistency4/5

    All tools share the messages_ prefix and mostly follow a verb_noun pattern (list_chats, get_thread, search). messages_status is a noun-only outlier, and messages_search lacks a noun object, but the prefix convention is consistent and readable.

    Tool Count4/5

    Four tools is on the lean side but reasonable for a focused, read-oriented Messages server covering diagnostics, listing, reading, and searching. It is slightly thin given lifecycle operations appear to exist elsewhere.

    Completeness3/5

    The surface covers discovery, read, and search but exposes no send/redact operations, even though messages_status references send/redact flags and descriptions mention send/watch returning SCOPE. These references imply tools that are not present, leaving a notable gap for a messaging domain.

    Maintenance

    ActivityStale
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables reading, searching, and sending iMessages directly from MCP-compatible clients by accessing the local macOS iMessage database, supporting conversations, attachments, and both individual and group chats.
      674 npm
      10
      MIT
    • F
      license
      A
      quality
      D
      maintenance
      Enables reading, searching, and sending iMessages on macOS by accessing the local messages database and utilizing AppleScript. Users can list conversations, search message history, and send messages to individuals or group chats directly through the Model Context Protocol.
      6
      -
    • A
      license
      A
      quality
      B
      maintenance
      Enables reading the local iMessage database and sending messages through Messages.app on macOS, with both local stdio and remote HTTP access.
      7
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to read the entire Apple Messages (iMessage/SMS) history on a Mac through a read-only, batched tool that supports listing chats, retrieving transcripts, polling recent messages, and searching message bodies via REST or streamable HTTP MCP.
      MIT