入门 📋 6 个步骤 第 590 / 590 篇

用 mcpify 把任意可脚本化 Mac 应用变成类型化 MCP 工具:字典生成、只读默认与写入闸门实操

提醒事项、日历、音乐、Finder 这些 Mac 应用大多没有 API,但不少自带 AppleScript 字典。mcpify 读字典生成类型化 MCP 工具,并对着真实应用逐条验证:默认只读,写入要一次性验证码,还能用作用域把改动锁在一个清单里。本教程六步落地,含 13 个应用的实测数字与明确的边界清单。

2026.11.02· 11 分钟上手· 约 3365 字· 🍎 mcpify / 🔧 类型化工具

Agent 想帮你管一下提醒事项、翻一下日历、给播放列表加首歌,最大的障碍不是模型不够聪明,而是这些应用根本没有 API。好在 macOS 上大量应用自带一份 AppleScript 字典——一份机器可读的清单,写明它暴露了哪些对象和命令。mcpify 做的事就是把这份字典读出来,为里面的每一个类生成类型化 MCP 工具,再把这些工具对着真实应用逐条跑一遍验证。于是 Reminders 就变成了 list_reminders、get_reminder、create_reminder 这样一组工具,Agent 可以像调任何其它 MCP 工具一样调它们。它的设计取向非常明确:默认只读,写入必须显式开启,而且每写一次都要过一道确认闸门。本教程六步落地:确认环境并先看一眼会生成什么、挂成只读服务器、理解生成规则与 ref 寻址、开写入并走通确认流程、用作用域与演练模式收口、最后看实测数字与边界清单。

🍎 本教程适合:在 macOS 上开发、想让编码 Agent 操作本地应用的开发者。前置条件是 macOS 加 Node 23.6 或更高版本,项目为 MIT 许可。开始前先建立一个正确预期:作者自己在 README 里给出的诚实总结是,对强模型来说类型化工具并不会让它更准,反而更慢——真正买到的是「可控」。只读默认、作用域、确认这三件套,是随手给 Agent 一个 osascript 工具所没有的。

Step 1:确认环境,然后先看一眼会生成什么

1 不装不看,先看清单

动手之前有两件事值得先做:列出你机器上所有可脚本化的应用,以及看一眼目标应用会被生成出哪些工具。这两步都不改任何东西,属于纯探查,能帮你判断这条路对当前应用到底成不成立。

# 列出这台 Mac 上所有可脚本化的应用
npx -y github:abhaymettu/mcpify apps

# 看某个应用会生成哪些工具(不启动服务器)
npx -y github:abhaymettu/mcpify tools Reminders

# 看某个应用暴露了什么,星号表示可写
npx -y github:abhaymettu/mcpify inspect Calendar

inspect 的输出值得细看,它直接告诉你哪些属性是可写的。这一步也是排障的起点:如果某个应用根本不在 apps 列表里,说明它没有字典,mcpify 也就无从生成——它不依赖无障碍 API 去兜底。

# 任何在列表里的应用,用法都一样
mcpify Music
mcpify Finder
mcpify "/Applications/Some App.app"

平台门槛要认:mcpify 需要 macOS 与 Node 23.6 或更高版本。Windows 与 Linux 上跑不了,因为它走的是 sdef 字典加 Apple Events 这套 macOS 原生机制,没有跨平台替代路径。

Step 2:挂成只读服务器:一行命令接入 Claude Code

2 默认形态里,写工具根本不会注册

接入方式就是标准的 MCP 添加命令,把 mcpify 当作一个 stdio 服务器交给客户端启动。加完之后直接在对话里问就行,比如「我的购物清单上还剩什么」。这时会触发一次系统授权弹窗,问你是否允许终端控制该应用;日历、通讯录与照片除了自动化授权,还会另外要数据访问权限。

# 以只读方式接入 Claude Code
claude mcp add reminders -- npx -y github:abhaymettu/mcpify Reminders

# 然后直接问
# 「我的购物清单上还剩什么?」
# 调用时会弹出 macOS 授权:是否允许你的终端控制 Reminders

这里有个容易被忽略但非常重要的特性:不加 --writes 时,任何会改变状态的工具压根不会被注册。也就是说,Agent 在只读模式下连「尝试写一下」的机会都没有——不是靠提示词约束它别写,而是工具列表里就没有写工具。这是与「给 Agent 一个能跑任意 osascript 的工具」最本质的区别,后者的一条 do shell script 就能绕过所有约定。

💡 建议先用只读模式跑几天,确认 Agent 确实能从这些工具里拿到它想要的数据,再考虑开写入。很多场景——查日历、看提醒、翻播放列表——其实根本不需要写权限。

Step 3:理解生成规则:按类生成、四层深度、ref 寻址

3 工具名不是瞎起的,是从字典里推出来的

生成规则是确定性的:从应用对象出发,沿元素图走(默认最多四层,所以会出现 日历 > 事件 > 参与者 这样的层级),为每一个可达的类生成一组工具。理解这个规则之后,你就能预判某个应用大概会生成多少工具,也知道该从哪里裁剪。

按类生成的工具(默认 / 需显式开启)

list_<复数>              默认    父对象下的条目,支持 where 过滤
                                 (文本用 contains,其它用相等或 gt、lt)
                                 支持 fields 与分页,每条结果带一个 ref
get_<类>                 默认    单个对象的全部可读属性,加上它包含的计数
get_application          默认    应用自身的属性,比如 Music 当前播放的曲目
create_<类>              --writes     按字典里的可写属性生成
update_<类>              --writes     同上
应用自带命令              --writes     比如 Music 的 play、Safari 的加入阅读列表
delete_<类>              --allow-delete
破坏性命令                --allow-delete   清空回收站、发送等

对象通过 ref 寻址,形如 calendars[name="Work"]/events[id="1F2E..."]。每条列表结果都会带上 ref,Agent 可以直接把它传回去。这一点在安全上有明确设计:ref 在 Node 侧解析、对照字典校验,然后作为数据传给 osascript 去走,Agent 的文本永远不会变成脚本文本。这从根本上切断了注入路径。

两个生成期的坑:其一,工具数量会膨胀——System Events 的字典会生成 132 个工具,用 --depth 可以裁掉层数。其二,字典说什么就是什么——Photos 把每个元素都标成只读,于是 mcpify 不为它生成 create 工具,尽管 Photos 其实能建相册。字典错,工具就跟着错。

Step 4:开写入:计划、验证码、过期时间,一个都不少

4 一次写入要过两道手

加 --writes 才会注册写工具。而每一次写入,还要再过一道确认:如果客户端支持 MCP elicitation,写入会先弹出询问,展示将要发生的改动;如果不支持,那么调用会先返回一个计划(更新操作会同时给出当前值与新值)外加一个一次性验证码,拿着这个码再调一次才真正执行。验证码与精确的调用参数绑定,10 分钟后失效,且只能用一次。

# 只在一个清单里写入,且带确认
claude mcp add reminders -- npx -y github:abhaymettu/mcpify Reminders \
  --writes --scope 'lists[name="Groceries"]'

# 确认流程(客户端不支持 elicitation 时)
# 1. Agent 调用 create_reminder(...)
# 2. 返回计划 + 一次性验证码(绑定参数,10 分钟有效,仅一次)
# 3. Agent 带上验证码再调一次,改动才落地

# 关掉确认(不推荐)
--confirm none

这套流程看似繁琐,但它把「Agent 改了什么」从黑盒变成了可审计的两段式操作。对那些会被反复调用的场景,你可以在确认无误之后再考虑是否放宽,而不是一开始就把闸门拆掉。

--confirm none 与 --allow-delete 不要一起给不受信任的 Agent:前者拆掉确认,后者放出删除、清空、发送一类破坏性工具。两个叠加之后,一次误判就是不可逆的破坏。仓库里的 test/safety.e2e.test.ts 正是针对这些闸门做的端到端检查,别把它们当可选项。

Step 5:收口:作用域锁死、演练模式、删除单列

5 三道闸门,按从宽到严的顺序收紧

开写入之后还有三道可以单独调节的闸门。作用域 --scope 会把写入限制在某个 ref 之内,任何目标在它之外的写入都会被直接拒绝;按名字与按 id 的 ref 会先各自解析成 id 再比较,所以重命名不会绕过它。--dry-run 则让每次写入只返回计划、什么也不改,适合在打开写入之前先跑一遍看看 Agent 到底想动什么。

# 作用域:只允许写这个清单
--scope 'lists[name="Groceries"]'

# 演练:每次写入只返回计划,什么都不改
--dry-run

# 删除类工具与破坏性命令,要单独开
--allow-delete

# 三者叠加的最小权限用法
claude mcp add reminders -- npx -y github:abhaymettu/mcpify Reminders \
  --writes --scope 'lists[name="Groceries"]' --dry-run

删除是单独一列,这个设计值得表扬:日常自动化里极少真的需要删除,把它从 --writes 里摘出来,等于让「能建能改」与「能删」变成两个不同的信任等级。给 Agent 权限时,按这个阶梯一级一级放,比一次给全要安全得多。

💡 推荐的上线顺序:只读跑几天 → 加 --dry-run 看它想改什么 → 加 --scope 与 --writes 放开有限写入 → 确认万无一失再考虑 --allow-delete。每一步都能停下来回看。

Step 6:看实测数字,以及那份写得很实在的边界清单

6 慢三倍,但可控——这就是它卖的东西

作者在 13 个应用上做了覆盖率实测,并用 31 个任务做了 Agent 对照。看数字之前要记住一句前提:全部结果来自一台机器、一次运行、一个模型(Sonnet),而且当时机器负载很高(平均负载 85 到 120),所以时间是偏慢且有噪声的。这个诚实度本身就值得学习。

覆盖率(13 个应用,98 个类)
  生成 206 个读工具 + 292 个写工具
  实时扫描覆盖到的读工具全部通过,只有一次 Photos 调用超时
  创建 / 更新 / 删除往返(每步用原始 osascript 独立复核)通过 7 个应用:
    Reminders、Notes、Calendar、Contacts、Music、Finder、TextEdit
  Keynote、Numbers、Pages 在负载下建测试文档超时,未测
  Mail 只扫结构,不读邮件

Agent 对照(31 个任务、9 个应用,Keynote 两题两臂都跳过,剩 29 题)
  分组                成功率   测试数据外改动   工具调用中位数   时间中位数
  mcpify 生成工具      29/29        0              3            23.8 秒
  裸 osascript        29/29        0              1             7.9 秒

作者的结论非常坦率:强模型本来就能写好 AppleScript,类型化工具没有让它更准,反而更慢——三次调用顶人家一次,成本约六倍。它买到的是控制力:不传 --writes 就只能读,写入被 --scope 锁住,每次写入都要先给计划再要确认。而 osascript 那一臂什么都没有,它那一个工具可以跑任何脚本,包括 do shell script。想清楚你买的是什么,再决定要不要装。

想自己复现

git clone https://github.com/abhaymettu/mcpify && cd mcpify && npm install
npm run test:unit     # 字典模型与生成器,不碰 Apple Events
npm test              # 真实应用测试:Notes、Reminders(仅测试清单)
npm run coverage      # 对 bench/fixtures.ts 里的应用做实时扫描
npm run bench         # Agent 对照套件(会使用你的 Claude Code 登录态)
node bench/summarize.ts bench/results/agent-*.jsonl

边界清单一定要读完再用,共七条:全部结果来自一台 macOS 26.5 且高负载的机器,单次运行、单模型;每次工具调用都要起一个 osascript 进程,高负载下一次 Apple Event 可能要两分钟;iWork 三件套未测;字典说什么就是什么,字典错工具就错;按名字的 ref 在重命名后失效,没有可读 id 的对象(Safari 文档、Finder 项目)就按名字选;每个应用都要单独授予自动化权限,日历、通讯录、照片还要数据权限;没有字典的应用(比如计算器)它不管,那需要无障碍 API。若终端缺少某应用的自动化权限,可以用 MCPIFY_OSASCRIPT 指向一个有权限的包装脚本。

延迟要提前有心理准备:每个工具调用都会新起一个 osascript 进程,在负载高的机器上单次调用可能慢到难以接受。如果是放在定时任务或长链路里用,务必先量一下端到端耗时,别把二十次工具调用串成一条关键路径。

常见问题 FAQ

问题结论
Windows 或 Linux 能用吗?不能。依赖 macOS 的 sdef 字典与 Apple Events,需要 macOS 加 Node 23.6 或更高版本。
默认会给 Agent 写权限吗?不会。不加 --writes 时写工具根本不注册;删除类还要单独加 --allow-delete。
Agent 文本会变成脚本吗?不会。ref 在 Node 侧解析并对照字典校验,再作为数据传给 osascript,Agent 文本不会成为脚本文本。
它比直接让 Agent 写 osascript 更强吗?不是更强,是更可控。作者自测两臂都是 29/29,但生成工具一臂约慢三倍、成本约六倍,换来只读默认、作用域与确认三道闸门。
工具太多怎么办?用 --depth 裁剪遍历层数。生成规则默认从应用对象走四层,System Events 的字典会生成 132 个工具。
← 返回教程中心