mcpField
A
Apple Messages

Apple Messages

本地服务
cupertino.mgcrea.io通信协作5今天更新

关于 Apple Messages

Apple Messages 的 macOS MCP 服务,可搜索和读取 iMessage、SMS 与 RCS 对话,并解码归档消息文本。读取需要完整磁盘访问权限;发送默认关闭且需显式启用。

主要功能

  1. 01搜索、列出并读取消息
  2. 02解码归档消息正文
  3. 03按日期、聊天或联系人统计
  4. 04将号码解析为通讯录姓名
  5. 05可选提取一次性验证码
  6. 06显式启用后发送消息

适用场景

  1. 01检索历史对话中的内容
  2. 02统计各联系人或时间段的消息量
  3. 03查找近期收到的验证码
  4. 04在确认并启用写入后发送消息

MCP 相关

作为 MCP 服务,向客户端提供诊断、聊天与消息检索、统计等工具;验证码读取和消息发送分别通过独立设置启用。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"mgcrea-mcp-apple-messages": {
"command": "npx",
"args": [
"-y",
"@mgcrea/mcp-apple-messages"
],
"env": {
"APPLE_MESSAGES_ALLOW_WRITES": "<APPLE_MESSAGES_ALLOW_WRITES>",
"APPLE_MESSAGES_ALLOW_CODES": "<APPLE_MESSAGES_ALLOW_CODES>",
"APPLE_MESSAGES_ALLOW_FILE_SEND": "<APPLE_MESSAGES_ALLOW_FILE_SEND>"
}
}
}
}
启动命令npx -y @mgcrea/mcp-apple-messages
需要配置的环境变量
APPLE_MESSAGES_ALLOW_WRITESRegister the mutating tools. Off by default, and the gate decides whether the write tools exist at all rather than merely refusing — an agent with writes off cannot see them.
APPLE_MESSAGES_ALLOW_CODESRegister apple_messages_find_codes, which reads one-time codes out of recent messages. Its own gate rather than allowWrites: it is a read, and the one read most worth an explicit decision.
APPLE_MESSAGES_ALLOW_FILE_SENDLet apple_messages_send_message attach a local file. Off by default even with writes on, because a path argument is a way to send anything the server can read.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@mgcrea/mcp-apple-messages1.31.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

@mgcrea/mcp-apple-messages

适用于 macOS 的 Apple Messages — iMessage、SMS 和 RCS — Model Context Protocol 服务。

非官方。 与 Apple 无关联。它会读取 Mac 上已有的消息存储。

没有第二条通路的界面

这个系列中的其他服务都有两种接入方式,并会选择更快的那种。这个服务只有一种,而且这是经过实测的结论,而非臆测:通过 Messages 的脚本字典进行的所有读取都会失败。 即使 NSRunningApplication 报告它正在运行,它仍会返回 Application isn't running,因为它是一个无窗口的后台进程,不会响应脚本调用而唤醒。

因此,完全磁盘访问权限是必需的,并非可选升级。 没有这项权限,服务就完全无法工作——没有可供回退的降级模式。

Apple Events 仍然可用,但只支持一个动词。send 是字典中唯一有效的命令,因此这里是唯一一个 Apple Events 只能用于写入、不能用于其他操作的界面——也因此,写入门禁同时也是权限边界。关闭 APPLE_MESSAGES_ALLOW_WRITES 后,不会发送任何 Apple Event,也不会请求任何自动化授权。不会有读取操作绕过门禁泄露数据,因为这里根本没有读取功能。

SQL 无法看到三十二条消息中的一条

chat.db 会在两个位置保存消息文本,而你通常会尝试读取的那个位置其实是_不完整的_。在一个包含 97,416 条消息的真实存储中进行实测:

消息97,416
有普通 text 列94,043
有 attributedBody blob97,094
仅有 blob — 没有文本3,051 (3.1%)

这个 blob 是一个 NSArchiver typedstream,无论执行多少 SQL 都无法读取其内部内容。只查询 text 的服务会静默漏掉三十二条消息中的一条,而且不会报错提醒你。

而且这个比例只是历史平均值。 通过此服务对实时存储进行实测:2016 年至 2025 年,约 99% 的消息都有普通 text,随后这一情况戛然而止——从 2026 年 3 月起,每条消息都只有 blob。 Apple 似乎在 2 月下旬到 3 月下旬之间移除了该列。因此,解码器不是用来处理边缘情况的;它是读取近期消息的唯一方法,没有解码器的服务会报告对话在 2 月就停止了。

此软件包可以解码该内容。解码器依据真实数据编写——NSArchiver 仍随 macOS 26 一同提供,也是这些 blob 的生成工具,因此可以通过归档已知字符串生成无需猜测的测试样本——随后又使用 text 作为 94,043 条已标注记录的判定基准,对整个存储进行了验证:

准确率为 100.000%。97,094 个 blob 全部成功解码,没有失败。 每千条约需 2 ms。

软件包根目录导出了 decodeAttributedBody()。

名称来自 Contacts

chat.db 只会将联系人记录为 +15551234567,不会提供其他信息,因此此服务依赖 @mgcrea/mcp-apple-contacts 将其转换为姓名。每个联系人记录都会包含这两项,以及一个 resolution 状态。

unknown 是正常情况,不是错误。 对真实通讯录进行实测:过去一年的消息中,97.6% 可以解析;但在最常联系的 25 位联系人中,只有约 84% 可以解析——也就是说,你最常联系的人中,大约每六位就有一位没有联系人卡片。任何将无法解析的标识视为失败的代码,在真实收件箱中都会频繁出错。

如果尚未授予 Contacts 权限,系统会跳过解析,并原样返回标识。这种能力降级会通过 diagnostics 报告,不会抛出异常。

查询会使用 Contacts 软件包的默认设置。APPLE_CONTACTS_* 设置用于配置 Contacts 服务,而不是此服务:在此处设置它们不会产生任何效果,Messages 唯一会读取的相关开关是 APPLE_MESSAGES_RESOLVE_CONTACTS。

工具

读取:diagnostics、list_chats、list_messages、search_messages、get_message、 count_messages。

count_messages 是算术通道:提供总数及发送/接收拆分,还可通过 groupBy 按天、月、聊天、句柄或方向统计数量。它会在 SQL 中汇总所有匹配项,因此,“我最常给谁发消息”只需一个小型结果,无需手动逐页统计对话;按 handle 分组还能跨越同一个人所在的多个聊天,而 list_chats 的每个聊天总数无法合并统计。它有意仅处理元数据:2026 年起的消息正文存放在归档 blob 中,SQL 无法读取,因此在此处使用文本筛选会返回一个看似正确、却悄悄漏掉存储中近期一半内容的数字。

选择启用的读取功能:find_codes,由 APPLE_MESSAGES_ALLOW_CODES 控制——参见配置。默认关闭,且不受写入门控限制。

写入:send_message,这就是全部的命令集合。sdef 列出三个命令——send、login 和 logout——其中另外两个会让用户在其拥有的所有设备上退出 iMessage。没有可公开使用的编辑、删除、标记为已读或回应命令,因此此服务能向你展示的一切,都无法由它更改。

send 的直接参数类型为 file 或 text,两种形式各有两条通道,限制也各不相同。attachmentId 会转发此 Mac 的 Messages 存储中已有的文件——使用与 save_attachment 相同的 guid,并经过相同的“必须位于 ~/Library/Messages 内,否则拒绝”边界——因此无法访问任意路径,除 ALLOW_WRITES 外无需其他标志。filePath 指定任意本地文件,是一种数据外泄原语;与文本形式不同,它的影响范围不受模型能够说出的内容限制。只有启用 APPLE_MESSAGES_ALLOW_FILE_SEND 后,此参数才会存在;它默认关闭。它有意不限制目录范围——关于为何这种限制只是披着边界外衣的减速带,请参见 docs/messages.md。

一次调用只发送一项内容,因此带说明文字的照片需要调用两次。

发送消息及其发送结果的报告方式

优先使用 list_chats 返回的 chatRef,而不是原始句柄。Messages 不允许脚本枚举参与者,因此只有现有对话才能作为可靠的目标——但 chat 携带 id,而存储中为每个对话都保存了 guid。读取通道选择目标,写入通道则通过 id 指定目标;这是唯一能让两条通道都不必执行其无法做到之事的安排。

随后,Apple Events 不会返回任何内容——连所发送消息的标识符也没有。因此,客户端会在发送前记录时间戳,然后轮询 chat.db 查找发出的消息行:

reconciliation含义
matched找到了对应行;结果包含真实的消息引用
pendingMessages 已接受发送请求,但尚未写入消息行
unavailable没有可供查找的现有聊天

pending 不表示失败。不要重试——消息已经发送,重试会导致发送两次。

迄今为止,还没有通过实时 Messages 执行过任何发送操作。 这条通道基于命令字典和存储实现,其测试有意使用虚假的 osascript 运行。docs/messages.md 详细记录了仍有哪些问题尚未验证;安全的测量方式是给自己的句柄发送消息。

配置

变量默认值
APPLE_MESSAGES_RESOLVE_CONTACTSon在通讯录中查找姓名。
APPLE_MESSAGES_INDEX_MODEautoauto | ro | immutable | off。
APPLE_MESSAGES_STOREauto指定存储路径。
APPLE_MESSAGES_DEFAULT_RANGE_DAYS30仅提供开始时间时使用的时间范围。
APPLE_MESSAGES_MAX_RESULTS50默认分页大小。
APPLE_MESSAGES_ALLOW_WRITESoff是否注册 send_message。
APPLE_MESSAGES_ALLOW_CODESoff是否注册 find_codes。
APPLE_MESSAGES_ALLOW_WATCHESoff是否注册 watch 和 unwatch。
APPLE_MESSAGES_ALLOW_EVENTSoff是否提供实验性的 messages.received 事件(仅限 2026-07-28 版 MCP Events 草案扩展)。
APPLE_MESSAGES_ALLOW_FILE_SENDoff为 send_message 添加 filePath。
APPLE_MESSAGES_SEND_RECONCILE_MS5000等待已发送消息记录的时长。

APPLE_MESSAGES_ALLOW_CODES 控制 find_codes,该工具会从最近收到的消息中提取一次性 2FA 验证码。它属于读取操作,因此刻意不纳入 ALLOW_WRITES:通过写入权限门禁来获取读取能力,意味着必须授予发送消息的权限。

之所以要设置门禁,是因为它会与其他能力组合。此服务已保存对话历史,而 @mgcrea/mcp-apple-mail 则保存收件箱——两者合起来就覆盖了密码_重置_渠道。再加入实时身份验证代码,就凑齐了账户接管所需的完整能力;这些组成部分单独来看都合情合理,因此该功能默认关闭,且两个门禁彼此独立。

验证码是根据特征匹配的,而不是通过 \d{4,8} 正则表达式匹配;每个结果都会附带 confidence 和 matched,说明匹配结果的置信度及匹配方式。任何低于 high 的结果都应在使用前与消息正文核对。参见 @mgcrea/mcp-apple-core 中的 src/codes.ts——当 @mgcrea/mcp-apple-safari 成为第二个调用方时,启发式逻辑便移到了那里;另请参阅仓库中的 docs/passwords.md,了解为何无法访问 Passwords 应用,以及此处提供的替代方案。

需要特别注意的问题

  • 日期无法存入 JavaScript 数字。 每个日期列都是自 2001 年以来的纳秒数——共 18 位,超出了 Number.MAX_SAFE_INTEGER——而 node:sqlite 会直接_抛出异常_,不会截断。若用 try/catch 吞掉异常,效果和“此列为空”完全一样;曾有一次探测因此报告 97,414 条消息中的 7 个日期列都“没有日期”。这里的每个查询都会在 SQL 中进行除法运算,因此整数不会传到 JavaScript。
  • Tapback 是消息表中的行。 在测量的存储库中有 2,788 条;如果读取器不筛选它们,就会把 Liked "see you at 8" 显示得像是有人输入的消息。默认情况下,它们不会纳入对话,而是显示在其对应的目标消息上。
  • 搜索涵盖仅存于 blob 中的消息。 先对该列执行 LIKE 搜索(遍历 97,416 行耗时 16 毫秒),再解码 SQL 无法查看的 3,051 行(约 6 毫秒)。结果发现,确保完整性几乎不费成本;而搜索若悄悄漏掉 3% 的内容,代价可就不小了。
  • 引用使用 GUID,而非 rowid。 SQLite 会复用已删除行的 id,而 Messages 会不断删除数据,因此某一轮分配的 rowid,两轮之后可能就指向另一条消息——看似合理,实际错误,而且不会发出提示。
  • 发送无法撤回,这里也不会假装可以。 字典中没有撤回、草稿或预览功能;send 会直接发送。该工具受 ALLOW_WRITES 控制,要求 confirm: true,其描述也明确说明了这一切,因为调用它的模型是措辞与收件人之间的最后一道关卡。

许可证

MIT。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。