时间:2026 年 9 月 29 日

来源:Earendil 工程 <rfc@earendil.com>
Pi 开发者平台(pi.dev)此前曾明确声明不支持 MCP(Model Context Protocol),其团队在多次播客访谈及马里奥撰写的文章中均表达了对该协议的保留态度。然而,随着 Pi 的最新版本升级,MCP 已成为核心支持功能。这一转变并非简单的妥协,而是基于技术演进与架构重构的深思熟虑之举。
从边缘扩展到核心集成
尽管世界和技术环境处于动态变化中,但将 MCP 纳入核心并非仅仅因为协议本身的演变。Pi 原本拥有完善的扩展生态系统,理论上 MCP 完全可以作为第三方扩展存在,甚至可能获得 Earendil 官方认可。最终决定将其整合进核心,是团队重新评估架构后的结果。
引入 MCP 的核心驱动力在于其与 Pi 内部组件 Jev 的协同效应。通过适配 MCP,Jev 在 Pi 中的使用变得更加便捷。本质上,Pi 所需的功能与 MCP 的目标高度重合:即提供一个以翻译形式运行的沙箱环境。
MCP 的现状与挑战
虽然 MCP 的多个方面有所改进,但其开发体验依然复杂。即便引入了 Codemode——一种允许编写工具调用的轻量级沙箱机制——MCP 仍未完全解决这一痛点。问题的根源不在于 MCP 协议本身,而在于现有的 MCP 服务器实现方式及其与不同方法的兼容性。
目前,许多 MCP 服务器仍倾向于简单地将工具倾倒至环境中,并通过返回文本优化 Token 效率。理想的 MCP 架构应更接近 OpenAPI 标准:工具需返回结构化数据,并通过文档和描述实现自我发现。代理和模型通常依赖高效的 Bash 脚本进行连接,但这并非唯一路径。参考 Codex 等工具的做法,可以通过 JavaScript 沙箱暴露这些工具,从而提升灵活性。
Codemode 与 MCP 的深度结合
为何不直接构建一个无 MCP 支持的 Codemode?这涉及 Pi 近期对工具表示形式的重构。过去几个月,团队致力于让 Pi 更好地适应新模型,但工具装备尚未完全同步升级。
在 Codemode 架构下,需要明确区分工具是供 LLM 直接调用,还是仅用于 Codemode 部分。传统的 MCP 扩展缺乏足够的元数据,无法支持 Pi 的工具加载机制以实现流畅体验。因此,必须确保工具可配置为延迟加载或特定于 Codemode 的模式。
团队认为,通过 Codemode 支持 MCP 能解决传统集成中的诸多问题。拥抱而非旁观,积极参与现代 MCP 生态的完善,比单纯批评更能推动技术进步。尽管当前 MCP 优于旧版,但服务器模式和规范仍有改进空间。
解析 Codemode
Codemode 是一种特殊的执行机制。当 Harness 执行工具时,通常涉及两个信任层级不同的侧面:Harness 代理循环运行在可信环境中,而工具通常在低信任沙箱中执行。Codemode 的独特之处在于它运行在 Harness 侧,充当编排和协调工具调用的机制。
这种设计允许代理以更灵活的方式发起工具调用,并利用 JavaScript 进行组合。由于 Codemode 运行在 Harness 侧,其状态作为会话记录的一部分存储,而非文件系统。理论上任何语言均可实现,但选择 JavaScript 是因为其小型化版本可作为 WASM 二进制文件分发。
在 Pi 中,一旦配置 MCP,Codemode 会自动加载并默认启用。用户无需额外操作,只需重新配置 Pi 即可激活代码模式。这不仅限于 MCP,例如在登录提供 "Jev" 服务的服务器时,可通过以下指令触发:
通过 Codemode 使用 TypeSafe/Jev 找到 20 个最沮丧的评论者
该指令能巧妙结合 Linear MCP 和 Jev,直接在 Pi 内部进行分析,且不占用上下文窗口。
> Use typesafe/jev via codemode to find the most frustrated people on our issue tracker
I'll pull the open issues from Linear and let Jev rate the tone of each thread, four at a time.
codemode
const { issues } = await tools.mcp__linear__list_issues({
team: "Pi", state: "open", limit: 250,
});
const jev = await models.getModelOfType(
"classifier", "cloudflare-workers-ai", "typesafe/jev",
);
const questions = {
frustration: {
type: "choice",
instructions: "Judge ONLY the emotional tone of the people writing. " +
"Ignore how severe the bug is.",
criteria: {
none: "Neutral, factual, or friendly, even about a serious bug",
mild: "Explicit annoyance, impatience, or disappointment",
high: "Clearly angry, exasperated, sarcastic, or fed up",
},
},
};
const results = [];
let next = 0;
async function worker() {
while (next < issues.length) {
const issue = issues[next++];
const { comments } = await tools.mcp__linear__list_comments({
issueId: issue.identifier,
});
const c = await models.classify(jev, { state: { ...issue, comments }, questions });
results.push({ id: issue.identifier, title: issue.title, ...c.answers.frustration });
}
}
await Promise.all([worker(), worker(), worker(), worker()]);
store("frustration", results);
const score = (r) => r.probabilities.mild * 0.5 + r.probabilities.high;
const counts = {};
for (const r of results) counts[r.choice] = (counts[r.choice] ?? 0) + 1;
const flagged = results.filter((r) => r.choice !== "none");
flagged.sort((a, b) => score(b) - score(a));
return {
total: results.length,
counts,
flagged: flagged.map((r) => `${r.id} ${r.title}`),
};
... (331 earlier calls)
✓ mcp__linear__list_comments {"issueId":"PI-4714"} 255ms
✓ mcp__linear__list_comments {"issueId":"PI-3200"} 354ms
✓ models.classify cloudflare-workers-ai/typesafe/jev 756ms
✓ models.classify cloudflare-workers-ai/typesafe/jev 750ms
{
"total": 167,
"counts": {
"none": 156,
"mild": 11
},
"flagged": [
"PI-7740 TUI after /reload does not follows custom tool's re…",
"PI-9566 context size defaults to 128k despite the real size…",
"PI-10031 Pi sporadically stuck in \"Working...\" when thinking…",
"PI-4714 [feature] /update TUI command instead of pi update",
"PI-5916 Support provider extensions with model aliases and…",
"PI-7547 [Windows] [sink-thread] How do you use Pi on window…",
"PI-6907 Add an Installation section to the README",
"PI-6212 Proposal: Bedrock path should honor `compat.forceAd…",
"PI-9932 before_agent_start: forced system prompt keeps tool…",
"PI-7730 High CPU usage on Mac OS with long session",
"PI-6600 pi update --extensions and npm scripts: npm 11.16.0…"
]
}
Most of the tracker is calm. Jev rated 156 of the 167 open issues as neutral, 11
as mildly frustrated and none as highly frustrated.
The clearest cases:
- PI-6907 no installation section in the README (“It's frustrating”) - PI-10031
Pi gets stuck on “Working...” after pressing Esc during thinking - PI-4714 a
/update command request (“a pain in the butt”) - PI-7730 high CPU usage on macOS
in long sessions
The per-issue verdicts are stored in codemode under frustration, so I can dig
into any of them without fetching the issues again. 此次调整体现了 Pi 在面对不断发展的技术环境时,如何通过深思熟虑的架构更新来保持适应性。





