Apple Messages MCP
Integrates with Apple's Messages.app on macOS, providing read access to the local chat database (iMessage + SMS) and optional send capability through AppleScript, gated behind Full Disk Access and Automation permissions.
Allows reading iMessage and SMS conversations from the local Messages.app chat.db on a Mac, including listing chats, retrieving threads, searching message content, and optionally sending replies via AppleScript. Includes a watcher process for polling new messages to wake an agent.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Apple Messages MCPsearch my iMessage history for dinner plans"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.dbpath)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 testThe server speaks MCP over stdio (leave this to Cursor; do not write logs to stdout):
npm run build
node dist/index.jsDev: 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 3000Full Disk Access
Open System Settings → Privacy & Security → Full Disk Access.
Enable Cursor (and Terminal if you smoke-test here).
Quit and reopen that app. TCC is applied at launch.
If
messages_statusreturnsfda_likely_missing: trueorPERMISSION, 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 |
| unset | Comma/semicolon tokens: display name, |
|
|
|
|
| Override for fixtures. |
|
| Copy + WAL replay. |
|
|
|
|
| Bodies become |
| unset | Watcher runs this executable with one JSON event on stdin. |
|
| Watcher includes a redacted preview. |
Tools
Tool | What it does |
| Can we read |
| Recent chats, or only the allowlist if one is set. |
| Messages for a |
| Two-phase substring search (plain |
| Registered only when |
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)
npm testpasses (fixture-only; safe on any machine).Grant FDA, restart Cursor, reload the MCP server.
Call
messages_status.readable: truefda_likely_missing: falseunscoped: falseunless you setMESSAGES_ALLOW_UNSCOPED=1Optional: set
MESSAGES_SCOPEand confirmscope.active: trueandscope.chatsmatches
messages_list_chatswithout scope env returnsSCOPEandscope.candidates.messages_get_threadwith achat_idfrom a scoped or unscoped list returns recent lines. At least sometext_sourcevalues may beattributedBodyon current macOS.text_source: "guess"means the decoder fell back to printable bytes.messages_searchwith a word you know exists returns hits across chats, or empty +truncated: truewhen the Tahoe window missed.messages_sendis absent fromtools/listwhileENABLE_SENDis unset.Optional:
ENABLE_SEND=1, Automation allowed, send a one-line test, confirm it in Messages.app. Leave send off afterward.npx tsx src/index.ts status(CLI) matches the MCPmessages_statuspayload.npx tsx src/index.ts watch --interval 2000prints onemessages.readyline and, on a new message,messages.newwithout plaintext.
Inspector, if you want a GUI without Cursor:
npx @modelcontextprotocol/inspector npx tsx src/index.tsWhat this is not
Not Inkbox / Linq / a hosted agent number.
Not a push notification service. The Phase 2
watchprocess polls /fs.watchschat.dband wakes the host — see DESIGN.md.Not a way to dump
chat.dbto a server.
License
MIT. See LICENSE.
Available Tools
4 toolsmessages_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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Default 50. | |
| before | No | Page older than this message_id or ISO timestamp. | |
| handle | No | Phone number, email, or chat guid of a participant / thread. | |
| chat_id | No | chat.ROWID. Prefer guid from a previous response to remember a thread. | |
| to_date | No | ISO-8601 inclusive upper bound. | |
| from_date | No | ISO-8601 inclusive lower bound. | |
| include_reactions | No | Include tapback rows. Default false. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max chats to return. Default 30. | |
| query | No | Filter by display name, chat identifier, guid, or handle. |
TDQS
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.
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.
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.
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.
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.
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_searchSearch messagesA
Search message text: SQL LIKE on the plain text column, then a bounded decode-scan of empty-text / Tahoe rows. No MESSAGES_SCOPE and MESSAGES_ALLOW_UNSCOPED is unset; list/search/thread/send/watch return SCOPE. Returns truncated:true and scanned when the Tahoe window is exhausted. With no chat_id, searches across all readable chats (or the allowlist). Case-insensitive. Does not search attachment binaries. Does not write FTS into chat.db.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Default 25. | |
| query | Yes | Substring to find. | |
| chat_id | No | Restrict to this chat.ROWID. | |
| to_date | No | ISO-8601 inclusive upper bound. | |
| from_date | No | ISO-8601 inclusive lower bound. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does substantial work: it discloses scope/permission failure behavior ("No MESSAGES_SCOPE... list/search/thread/send/watch return SCOPE"), truncation semantics ("Returns truncated:true and scanned when the Tahoe window is exhausted"), coverage limits (no attachment binaries), and a no-write guarantee ("Does not write FTS into chat.db"). It stops short of stating auth requirements or result ordering, 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with the search semantics and each sentence carries behavioral information rather than repeating the schema. A few internals ("Tahoe rows", "chat.db", "FTS") are implementation jargon that does not help an agent select the tool, which is the main source of bloat.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no annotations and no output schema, the description must supply the full picture, and it covers permission errors, truncation return fields (truncated, scanned), and search coverage. What remains thin is result ordering/pagination shape across large result sets, but for a 5-param search tool this is close to complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description adds genuine meaning on top of the schema by clarifying that matching is case-insensitive (query) and that omitting chat_id widens the search to all readable chats or the allowlist. It does not elaborate on date-bound inclusivity beyond what the schema already says.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
"Search message text" states a specific verb and resource, and the mechanics (LIKE on plain-text column plus decode-scan) make the operation concrete. It never names the sibling tools (messages_get_thread, messages_list_chats), so the agent must infer the boundary between searching messages and fetching a thread, which keeps it out of 5 territory.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives one clear conditional: "With no chat_id, searches across all readable chats (or the allowlist)", which tells the agent how to broaden scope. However, there is no explicit when-to-use-this-vs-messages_get_thread or messages_list_chats guidance, so routing between siblings is only implied.
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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.
4 tool updates
v0.1.0- First observed
messages_get_thread - First observed
messages_list_chats - First observed
messages_search - First observed
messages_status
TDQS
Scored across 4 tools
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.
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.
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.
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
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
MCP connector for iMessage & Contacts via a local Mac agent + Vercel relay
Let ChatGPT, Claude & Cursor use your Mac: email, calendar, iMessage, Teams, files. Local, free.
Search, read, and write your Apple Notes from ChatGPT/Claude via a local Mac agent + MCP relay.
End-to-end encrypted messaging and work coordination for autonomous AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables 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.21010MIT
- FlicenseAqualityDmaintenanceEnables 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-
- AlicenseAqualityBmaintenanceEnables reading the local iMessage database and sending messages through Messages.app on macOS, with both local stdio and remote HTTP access.7MIT
- AlicenseNot gradedqualityCmaintenanceEnables 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