跳转到内容

第 26 课:MCP 与外部工具集成

设计 MCP server、企业内部工具、issue tracker、CI 和文档系统接入 agent 的权限隔离方案。

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 通常不能只读本地文件。它还需要:

  1. 读 issue tracker,例如 Jira、Linear、GitHub Issues。
  2. 查 CI 状态和失败日志。
  3. 搜索内部文档系统。
  4. 创建修复分支、更新 issue、评论 PR。
  5. 遵守不同系统的权限、审计和 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 和宿主里。

源码和文档里没有内置 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 tools

课程 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 也不是同一个风险等级。

starter 的 exposeMcpTool() 会把 server 和 name 合成工具名:

name: `mcp_${tool.server}_${tool.name}`;

这个命名策略解决两个问题:

  • 避免不同 server 的 read_issue 重名。
  • 让 review/audit 日志一眼看出工具来源。

生产系统还需要做字符规范化,避免 server 名称里出现空格、斜杠或其他不适合 tool name 的字符。

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_issuesearch_issuescomment_issuetransition_issue
  • CI:get_run_statusget_failed_logsrerun_job
  • 文档系统:search_docsread_docsuggest_doc_update

课堂上要让学生比较:哪些是 read,哪些是 write,哪些是 external-side-effect。比如 get_failed_logs 是 read,但日志可能含 secret,需要结果脱敏;comment_issue 是 write,需要确认;rerun_job 会消耗 CI 资源,也需要确认。

外部工具不应该一次性全暴露给模型。Pi 的 AgentSessiongetAllTools()setActiveToolsByName() 和 allowed tools 逻辑,可以把工具按任务阶段启用:

  • planning 阶段:启用 read_issuesearch_docs
  • implementation 阶段:启用本地文件和测试工具。
  • review 阶段:启用 get_ci_statusreview_diff
  • handoff 阶段:在用户确认后启用 comment_issuecreate_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。但课堂先不接网络,先把权限模型和工具元数据设计对。

课堂上把 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 fix
CI failure -> get_failed_logs -> agent reproduces locally
docs search -> search_docs -> agent follows internal convention
user confirms -> comment_issue / create_pr
  • /Users/ryanchen/codespace/pi-agent-course-lab/src/16-mcp-integration.ts:本课 lab starter,包含 McpToolexposeMcpTool()
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/README.md at 61babc2:Philosophy 部分说明 Pi 核心不内置 MCP,应通过 extension、skills 或 packages 构建。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/docs/usage.md at 61babc2:Design Principles 说明 MCP、sub-agent、permission popup、background bash 等属于外层 workflow。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/extensions/types.ts at 61babc2ToolDefinitiondefineTool()registerTool()ResourcesDiscoverEvent 是 MCP adapter 接入 Pi 的主要类型边界。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/extensions/wrapper.ts at 61babc2:把 extension tool 包装成 agent-core tool,并传入 extension context。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/examples/extensions/dynamic-tools.ts at 61babc2:展示 session 启动后动态注册工具,适合模拟按 MCP server 动态暴露工具。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/examples/extensions/permission-gate.ts at 61babc2:展示在 tool_call 事件中做危险动作审批。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/examples/extensions/github-issue-autocomplete.ts at 61babc2:展示通过 GitHub CLI 读取 issue 并接入交互输入,可类比 issue tracker 集成。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/agent-session.ts at 61babc2getAllTools()setActiveToolsByName()allowedToolNames_refreshToolRegistry() 支撑按任务阶段启用外部工具。
  • /Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/resource-loader.ts at 61babc2extendResources() 和 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 扩展出一个企业工具接入清单。
  1. 实践题:扩展 /Users/ryanchen/codespace/pi-agent-course-lab/src/16-mcp-integration.ts,新增 ci.get_failed_logsci.rerun_jobdocs.search_docs 三个工具,并按 read/write/external-side-effect 标记审批策略。
  2. 思考题:如果一个 MCP 工具会读取 CI 日志,而日志里可能包含 token 或用户数据,你会在 MCP server、adapter、agent prompt、还是 PR body 生成阶段做脱敏?说明你的分层方案。