Skip to main content
Glama
dh-repo

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+

  • 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

git clone https://github.com/dh-repo/apple-messages-mcp.git
cd apple-messages-mcp
npm install
npm test

The server speaks MCP over stdio (leave this to Cursor; do not write logs to stdout):

npm run build
node 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). Returns truncated.

messages_send

Registered only when ENABLE_SEND=1. 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, send a one-line test, confirm it in Messages.app. 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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoDefault 50.
beforeNoPage older than this message_id or ISO timestamp.
handleNoPhone number, email, or chat guid of a participant / thread.
chat_idNochat.ROWID. Prefer guid from a previous response to remember a thread.
to_dateNoISO-8601 inclusive upper bound.
from_dateNoISO-8601 inclusive lower bound.
include_reactionsNoInclude tapback rows. Default false.

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does meaningful work: page ordering, attachment metadata present but bytes absent, and tapbacks omitted unless include_reactions is true. It omits operational traits such as pagination termination and error behavior when the chat cannot be resolved, so it is strong but not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The opening sentence is front-loaded and efficient, but the second sentence ('No MESSAGES_SCOPE and MESSAGES_ALLOW_UNSCOPED is unset; list/search/thread/send/watch return SCOPE') is cryptic jargon-heavy shorthand that resists parsing and interrupts the flow between purpose and identity resolution.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter, all-optional, no-output-schema, no-annotation tool, the description covers identity resolution, pagination, date filtering, and reaction handling adequately. It leaves a gap around what happens on unresolved chats or empty results, but nothing essential for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, and the description earns credit by clarifying that 'before' accepts either a message_id or an ISO timestamp and that from_date/to_date filter on the converted Apple date. It also spells out that guid can arrive via the handle field or chat_id, adding meaning beyond the schema labels.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Return messages for one chat') plus an ordering guarantee ('oldest-first within the page'), which is enough to distinguish it from messages_list_chats. However, it never explicitly contrasts itself with messages_search, the sibling most likely to be confused with a message-retrieval tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives conditional guidance for identity resolution (chat_id, guid, or participant handle) and the single-chat fallback when nothing is supplied, which is genuinely useful. It does not say when to prefer this tool over messages_search or messages_list_chats, so alternative selection is left implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax chats to return. Default 30.
queryNoFilter by display name, chat identifier, guid, or handle.

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does reasonably well: it discloses the local-database source, the SCOPE failure condition tied to MESSAGES_SCOPE / MESSAGES_ALLOW_UNSCOPED, the returned fields, and an explicit exclusion ('Does not return attachment bytes'). It stops short of permissions or pagination behavior beyond the limit parameter.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded and the return-value sentence earns its place, but the SCOPE sentence ('No MESSAGES_SCOPE and MESSAGES_ALLOW_UNSCOPED is unset') is grammatically broken and ambiguous, and the enumeration of sibling tools adds noise rather than routing guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no annotations, the description compensates by naming the returned fields and the SCOPE error condition, which is enough for an agent to call it and interpret results. Minor gaps remain around ordering of 'recent' conversations and the absence of explicit sibling routing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% (limit and query both documented in-schema), so the baseline is 3. The description adds no additional meaning for either parameter, for example what counts as a match for 'query'.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List recent conversations') and scopes it precisely to the local Messages database covering iMessage and SMS. It alludes to sibling tools ('list/search/thread/send/watch') but does not name them directly, so differentiation from messages_search and messages_get_thread requires inference.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Offers one concrete usage tip ('Prefer guid to remember a thread across launches') but never says when to choose this tool over messages_search or messages_get_thread. Usage is implied by the 'recent conversations' framing rather than stated as an explicit rule.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does real work: it discloses that message bodies are never returned, that it surfaces a likely-missing Full Disk Access condition, and that 'unscoped' is gated on MESSAGES_ALLOW_UNSCOPED=1. It omits cost/latency and read-only confirmation, but for a zero-parameter status probe this is solid coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core verb ('Report whether...') and packs many diagnostics into one tight sentence, followed by the usage cue and two exclusion/semantics notes. Every clause carries information, though the MESSAGES_ALLOW_UNSCOPED sentence is a fine-grained detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description must describe what comes back — and it enumerates the returned status fields explicitly. For a no-parameter, no-annotation diagnostic tool, nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes no parameters, so there is no parameter semantics to document. Baseline 4 applies for a zero-parameter definition.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Report') and enumerates the exact diagnostics it returns: db readability, Full Disk Access status, macOS hints, send/redact flags, snapshot size/age, and inbox scoping. This is clearly distinct from the sibling list/search/thread tools, which handle message content rather than health status.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Call this first when anything fails' gives an explicit, actionable trigger condition. It doesn't name specific alternatives or state when-not-to-use, but the diagnostic nature makes the boundary with the content-reading siblings self-evident.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 4 tool updatesv0.1.0
    • First observedmessages_get_thread
    • First observedmessages_list_chats
    • First observedmessages_search
    • First observedmessages_status

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

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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.
    210
    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