MCP 与外部工具集成的重点不是“多接几个 API”,而是把外部能力包装成可描述、可审计、可授权的 agent tools。每个工具都要回答:来自哪个 server、能读还是能写、需要哪些凭据、是否有外部副作用、日志里能不能暴露结果。
Pi 源码快照 61babc2 明确没有内置 MCP。它的路线是保持核心小,把这类工作流放到 extensions、skills、packages 或外部工具里。因此本课不是寻找一个不存在的 mcp.ts,而是学习如何用 Pi 的 extension tool、active tools、permission gate 和 resource discovery,设计一个 MCP adapter 层。
企业里的 coding agent 通常不能只读本地文件。它还需要:
- 读 issue tracker,例如 Jira、Linear、GitHub Issues。
- 查 CI 状态和失败日志。
- 搜索内部文档系统。
- 创建修复分支、更新 issue、评论 PR。
- 遵守不同系统的权限、审计和 secret 隔离要求。
问题是:这些能力一旦暴露给模型,就可能造成外部副作用。MCP server 只是协议入口,真正的设计难点是权限边界。
把外部工具集成拆成五层:
- MCP server 层:负责和外部系统通信,例如 issue tracker、CI、文档系统。
- adapter 层:把 MCP tool 转成 Pi
ToolDefinition或 extension-registered tool。 - permission layer:把工具标记为 read、write、external-side-effect,并决定是否需要确认。
- context layer:把 issue、CI、文档摘要放入 agent 可理解的上下文,而不是把 token 暴露给模型。
- audit layer:记录调用参数、调用结果摘要、审批人、时间和任务 id。
核心原则:模型看到的是工具描述和结果摘要;凭据、网络细节、审批策略留在 adapter 和宿主里。
1. 先承认 Pi 没有内置 MCP
Section titled “1. 先承认 Pi 没有内置 MCP”源码和文档里没有内置 MCP server/client 实现。packages/coding-agent/README.md 的 philosophy 部分明确说 Pi 核心不内置 MCP,而是建议通过 extension 或外部工具构建。
这对课程很重要:不要在源码锚点里编造 mcp-server.ts。我们要做的是一个 adapter:
MCP server tool -> adapter metadata -> Pi ToolDefinition -> AgentSession active tools2. 定义工具元数据
Section titled “2. 定义工具元数据”课程 starter /Users/ryanchen/codespace/pi-agent-course-lab/src/16-mcp-integration.ts 已经给出最小类型:
type McpTool = { server: string; name: string; description: string; permission: "read" | "write" | "external-side-effect";};permission 是本课关键。读 issue 和更新 issue 不是同一个风险等级;查 CI 和 rerun CI 也不是同一个风险等级。
3. 生成 Pi 工具名
Section titled “3. 生成 Pi 工具名”starter 的 exposeMcpTool() 会把 server 和 name 合成工具名:
name: `mcp_${tool.server}_${tool.name}`;这个命名策略解决两个问题:
- 避免不同 server 的
read_issue重名。 - 让 review/audit 日志一眼看出工具来源。
生产系统还需要做字符规范化,避免 server 名称里出现空格、斜杠或其他不适合 tool name 的字符。
4. 做权限隔离
Section titled “4. 做权限隔离”starter 用一行规则表达默认策略:
requiresApproval: tool.permission !== "read";这不是完整权限系统,但方向正确:read 工具可以自动调用,write 和 external-side-effect 默认需要确认。企业环境还要继续细分:
- read:读 issue、读 CI、搜文档。
- write:评论 issue、更新标签、创建分支。
- external-side-effect:rerun CI、部署、修改权限、触发生产操作。
Pi 的 permission-gate.ts 示例展示了如何在 tool_call 事件里拦截危险 bash。MCP adapter 也应该在同一类边界上做审批,而不是只靠工具描述提醒模型。
5. 接入 issue tracker、CI 和文档系统
Section titled “5. 接入 issue tracker、CI 和文档系统”三个系统的工具形态不同:
- issue tracker:
read_issue、search_issues、comment_issue、transition_issue。 - CI:
get_run_status、get_failed_logs、rerun_job。 - 文档系统:
search_docs、read_doc、suggest_doc_update。
课堂上要让学生比较:哪些是 read,哪些是 write,哪些是 external-side-effect。比如 get_failed_logs 是 read,但日志可能含 secret,需要结果脱敏;comment_issue 是 write,需要确认;rerun_job 会消耗 CI 资源,也需要确认。
6. 控制 active tools
Section titled “6. 控制 active tools”外部工具不应该一次性全暴露给模型。Pi 的 AgentSession 有 getAllTools()、setActiveToolsByName() 和 allowed tools 逻辑,可以把工具按任务阶段启用:
- planning 阶段:启用
read_issue、search_docs。 - implementation 阶段:启用本地文件和测试工具。
- review 阶段:启用
get_ci_status、review_diff。 - handoff 阶段:在用户确认后启用
comment_issue或create_pr。
这样模型的可行动面会小得多,审计也更清楚。
本课实践入口:
/Users/ryanchen/codespace/pi-agent-course-lab/src/16-mcp-integration.ts
starter 演示了把 MCP-like tool 暴露成 agent 可理解的工具描述:
const piTool = exposeMcpTool({ server: "linear", name: "read_issue", description: "Read an issue from Linear", permission: "read",});输出里的变量流是:
server进入工具名前缀,解决来源和命名冲突。name保留外部工具语义。description进入模型可见工具描述。permission决定requiresApproval。
下一步可以把 execute() 接到真实 MCP client。但课堂先不接网络,先把权限模型和工具元数据设计对。
课堂代码推演
Section titled “课堂代码推演”课堂上把 adapter 推演成两段:先转换 metadata,再包装执行:
function exposeMcpTool(tool: McpTool): ToolDefinition { const safeName = normalize(`mcp_${tool.server}_${tool.name}`);
return defineTool({ name: safeName, label: `${tool.server}:${tool.name}`, description: `${tool.description} Permission: ${tool.permission}.`, parameters: tool.inputSchema, async execute(_id, params, signal, _onUpdate, ctx) { if (tool.permission !== "read") { const ok = await ctx.ui.confirm("Allow external action?", `${tool.server}.${tool.name}`); if (!ok) throw new Error("Blocked by user"); }
const result = await mcpClient.callTool(tool.server, tool.name, params, { signal }); return redactAndSummarize(result); }, });}这段推演要讲三个控制点:
第一,normalize() 是安全边界的一部分。工具名会进入模型工具调用协议,不能直接信任 server 返回的名字。
第二,ctx.ui.confirm() 只在 write 或 external-side-effect 时触发,read 工具仍然可以自动化,但结果要做脱敏。
第三,redactAndSummarize() 把外部系统返回值变成 agent 可消费内容,避免把 token、cookie、内部个人信息或超长日志原样塞回上下文。
最后把它接入阶段四项目:
issue id -> read_issue -> agent plans fixCI failure -> get_failed_logs -> agent reproduces locallydocs search -> search_docs -> agent follows internal conventionuser confirms -> comment_issue / create_pr/Users/ryanchen/codespace/pi-agent-course-lab/src/16-mcp-integration.ts:本课 lab starter,包含McpTool和exposeMcpTool()。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/README.mdat61babc2:Philosophy 部分说明 Pi 核心不内置 MCP,应通过 extension、skills 或 packages 构建。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/docs/usage.mdat61babc2:Design Principles 说明 MCP、sub-agent、permission popup、background bash 等属于外层 workflow。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/extensions/types.tsat61babc2:ToolDefinition、defineTool()、registerTool()、ResourcesDiscoverEvent是 MCP adapter 接入 Pi 的主要类型边界。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/extensions/wrapper.tsat61babc2:把 extension tool 包装成 agent-core tool,并传入 extension context。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/examples/extensions/dynamic-tools.tsat61babc2:展示 session 启动后动态注册工具,适合模拟按 MCP server 动态暴露工具。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/examples/extensions/permission-gate.tsat61babc2:展示在tool_call事件中做危险动作审批。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/examples/extensions/github-issue-autocomplete.tsat61babc2:展示通过 GitHub CLI 读取 issue 并接入交互输入,可类比 issue tracker 集成。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/agent-session.tsat61babc2:getAllTools()、setActiveToolsByName()、allowedToolNames和_refreshToolRegistry()支撑按任务阶段启用外部工具。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/resource-loader.tsat61babc2:extendResources()和 ResourceLoader 机制可用于通过企业 extension 动态提供工具、skills 和 prompts。
学完本课后,你应该能做到:
- 说明 Pi 当前没有内置 MCP,为什么课程要用 adapter/extension 方式接入。
- 把 MCP server tool 映射成带命名空间、描述、参数 schema 和权限等级的 agent tool。
- 区分 read、write、external-side-effect 三类工具风险。
- 设计 issue tracker、CI、文档系统的最小工具集,并说明哪些需要确认。
- 解释凭据为什么必须留在 adapter/server 侧,不能进入模型上下文。
- 能基于
/Users/ryanchen/codespace/pi-agent-course-lab/src/16-mcp-integration.ts扩展出一个企业工具接入清单。
- 实践题:扩展
/Users/ryanchen/codespace/pi-agent-course-lab/src/16-mcp-integration.ts,新增ci.get_failed_logs、ci.rerun_job、docs.search_docs三个工具,并按 read/write/external-side-effect 标记审批策略。 - 思考题:如果一个 MCP 工具会读取 CI 日志,而日志里可能包含 token 或用户数据,你会在 MCP server、adapter、agent prompt、还是 PR body 生成阶段做脱敏?说明你的分层方案。