上一期聊 Pi 的时候,还提到它没有内置 MCP,想用得自己装扩展。现在,这一点得更新了:Pi 已经原生支持 MCP。
官方还专门写了一篇 《You Said No MCP!》,标题翻过来就是“你不是说不做 MCP 吗?”这篇文章发在 2026 年 9 月 29 日。
两天后,Pi 1.0 发布,原生 MCP 支持也被列进了主要更新。
原来放在扩展里也能用,为什么现在要做到核心里?这次可以顺着 Codemode 看看,Pi 准备怎么让模型使用工具。
Codemode 是怎么工作的
假设我想让 Pi 帮忙查两个项目里的问题:找出优先级高、还没有负责人接手的,把编号和标题列出来。
通过 MCP,Pi 可以调用项目管理工具。工具返回的数据里,除了我要的字段,可能还有问题描述、标签、评论,以及各种时间信息。如果直接把这些内容交给模型,模型得从中找出符合条件的记录,再整理答案。
这当然能做。但这个任务的条件已经很明确了:优先级是不是高,负责人是不是为空。交给一小段程序就能判断,没有必要让模型逐条读完问题描述。
Pi 的 Codemode 允许模型写 JavaScript 来组织工具调用。工具返回的数据可以先留在脚本中,筛选、合并、整理好以后,再输出给模型。
下面用一组虚构数据说明:两个项目一共 12 条问题,其中 3 条符合条件。
假设两个项目的问题已经查完,也已经根据工具的返回格式整理成了 projectA 和 projectB 两个数组,筛选部分可以这样写:
const selected = [...projectA, ...projectB]
.filter(issue =>
issue.priority === "high" && issue.assignee === null
)
.map(({ id, title }) => ({ id, title }));
text(selected);
这里的 text() 是 Codemode 提供的输出方法。这段代码只展示筛选逻辑,实际接 MCP 工具时,还需要核对返回结构、错误状态和分页结果,不能把任意工具的响应直接当成数组。
按这组示例数据,最后留下三条:
| 编号 | 标题 |
|---|---|
| A-01 | 登录失败提示缺失 |
| A-06 | 导出文件为空 |
| B-04 | 重复提交未拦截 |
原来的 12 条记录照样要查出来。变化发生在查询之后:先由脚本处理,模型接下来读到的是整理好的结果。
如果工具本身就支持按优先级和负责人筛选,还能只返回指定字段,那更应该先把查询条件传进去。Codemode 比较适合补上跨工具合并、返回结果整理这些工作,不需要为了用它,特意把服务端已经能做的筛选搬回来。
在 Mac 上接起来试试
我在 Mac 上接了一个 filesystem MCP,用本地 JSON 文件代替项目管理系统,沿用前面的筛选条件做了一次演示。
这里另外准备了 12 条示例问题。
先看命令入口。终端里的 pi mcp --help 已经列出了添加、删除、检查连接和登录等命令,也说明了全局配置与项目配置的位置。
接着在 Pi 里打开 /mcp,可以看到 filesystem 已连接,列出 14 个工具。这张截图中的提供方式是 direct,配置来自当前项目。
先让 Pi 通过 MCP 读取文件,再找出优先级高、负责人为空的问题。最终回答列出了三条:A01「登录接口偶发超时」、A06「订单导出失败」和 B04「库存同步延迟」。截图下方还保留了路径纠正:实际文件在 Downloads 下的演示目录里,最初给出的路径不对。
再让 Pi 用 Codemode 完成相同任务。这次并不是一次就跑通:先遇到了变量重名错误,随后又把 MCP 返回的 content 当成字符串解析,出现了 JSON 解析错误。查看实际返回结构后,模型改成从 content 数组的文本块里取出文本,再解析 JSON,执行 filter 和 map,最终输出了同样的三条记录。
这也说明,前面那段筛选代码只是其中一环,接上真实工具后,还得处理返回格式。这次调试过程中,模型曾打印返回对象来检查结构,所以不能把整个会话描述成“模型始终只看到三条结果”。最终成功的那次脚本完成了筛选和字段提取,但这组截图没有完整的 Token、耗时或费用对照,不能用来证明更快或更省。
哪些事交给代码哪些事交给模型
上面的例子有个前提:判断规则已经写清楚了。
换一个问题,“这两个项目里,哪些问题最值得先解决?”这时候,只保留编号和标题就可能不够。还得看影响了多少用户、有没有临时解决办法、会不会造成数据丢失。把相关描述提前删掉,模型就少了判断依据。
所以,写脚本之前,得先想好最后要回答什么。按字段筛选,可以交给程序;需要理解语义、比较影响的内容,就得保留下来。
两个项目的查询互不依赖,也可以在脚本里并行执行。不过,普通工具调用同样可以支持并行,不能把这项能力都归功于 Codemode。这里更值得关注的是:中间结果能在程序里继续处理,不必每一步都把整份数据发给模型。
Pi 为什么要把 MCP 内置进来
按官方在那篇文章里的解释,他们这次还想调整工具的加载和调用方式。有些工具一开始就告诉模型,有些等用到再查;放进 Codemode 后,还要考虑脚本怎样发现和调用它们。这些都需要配套的信息。
原来的 MCP 扩展拿不到足够的工具元数据,很难把这些方式配合好。继续补扩展接口当然也是一条路,官方这次选择把相关能力一起做到核心里。
这也体现在现在的 MCP 配置 上。工具可以设置不同的提供方式:
| 方式 | 模型怎样使用 |
|---|---|
direct | 直接看到工具声明,也可以通过 Codemode 调用 |
deferred | 通过 tool_search 搜索并为下一次模型请求加载声明,也能从 Codemode 调用 |
codemode | 在脚本里发现工具、查看参数并调用,是 MCP 工具的默认方式 |
hidden | 不提供给模型,也不能从 Codemode 调用 |
默认方式下,不会一开始就把每个 MCP 工具的完整说明全列出来。脚本可以通过 searchTools() 找工具,用 describeTool() 查看具体参数,再调用它。
这些设置决定工具默认怎样提供给模型,并不完全划分调用渠道。codemode 和 deferred 工具都可以从脚本调用,也都可以由 tool_search 加载声明;相应的入口需要启用。
接的工具多了,这种安排就比较容易理解:当下只需要查问题列表,就先找到相关工具,没必要同时读完其他工具的说明。
MCP 负责接入外部工具,Codemode 则提供了用代码组合调用、处理数据的方式。两者可以一起用。
脚本报错后之前的操作会不会撤销
Codemode 的 JavaScript 在 QuickJS 沙箱中执行,没有 Node 的文件系统、网络等接口。但脚本可以调用工具,而工具可能有读写文件、修改项目数据的能力。
比如,脚本先调用工具改了一个文件,随后处理另一份数据时报错。前面的文件修改不会自动撤销。
这里说的是工具已经产生的外部改动。Codemode 自带的 store() 用来保存供后续脚本读取的小份 JSON 状态,这类写入只在脚本成功后保存,不能与工具写文件、改项目数据混为一谈。
如果根据报错直接把整段脚本重跑一遍,前面已经成功的操作还可能再执行一次。做批量写入时,需要知道哪些步骤已经完成、重复执行会发生什么;给工具的权限,也要按实际任务来定。
至于能省多少 Token、能快多少,目前这篇文章没有同任务的对照测试,不能仅凭设计就给出数字。生成脚本、执行工具和错误重试都有开销,最后还得看完整任务的结果。
如果你平时经常让 Agent 批量查数据,再做筛选或统计,可以先选一个只读的小任务试试:核对有没有漏查、筛选结果对不对,以及最终交给模型的内容是否足够回答问题。把这些看清楚,再决定要不要用到更复杂的流程里。
总结
Pi 把 MCP 纳入核心,是为了让工具接入、按需发现和 Codemode 组合调用配合起来。MCP 连接外部工具,Codemode 让模型写 JavaScript 编排调用,在中间结果进入模型上下文之前,先完成筛选、合并和统计。工具多、返回数据量大的任务,是这种设计比较适合的场景。
使用时,要分清哪些判断能按明确规则交给代码,哪些仍需要模型理解语义,保留回答问题所需的信息。QuickJS 沙箱限制了脚本直接访问外部环境,但工具已经完成的写入不会因后续报错自动撤销。至于能省多少 Token、快多少,需要同任务对照才能确认,本文的示例数据和本地演示说明了处理过程,也保留了实际报错与修正;没有据此测出性能或费用收益。
⭐️推荐阅读:
- 后端开发学习 + 面试指南:覆盖 Java、计算机基础、数据库、框架、系统设计等后端开发核心知识与面试内容。
- AI 应用开发学习 + 面试指南:覆盖 LLM、RAG、Agent、MCP、Prompt、评测、系统设计等 AI 应用开发知识与面试内容。
- AI 编程实战指南:覆盖 Claude Code、Cursor、Codex、Trae 等工具的使用技巧与面试内容。