
字节笔记本
2026年10月24日 · 约 13 分钟读完
连接外部工具的统一标准:MCP 协议

本文是《从 0 写 Agent》系列第 28 章。这个系列基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版),用 Go 从零实现一个生产级 Agent 运行时;本章讲「MCP 协议:连接外部工具的统一标准」。
到目前为止,Agent 的工具都是手写的 Go 函数。但真实世界有大量现成工具,GitHub 操作、数据库查询、文件系统、浏览器控制,你不可能全自己写。
MCP(Model Context Protocol)是连接外部工具的统一标准。agents-go 的
mcp包是 MCP 客户端,把 MCP server 提供的工具接入 Agent。这一章拆它怎么适配,以及 schema 兼容这个大坑。
引子:为什么需要 MCP
没有 MCP 之前,每个 Agent 框架都要自己写工具集成,Claude Code 写一套、OpenAI 写一套、Cursor 写一套。工具不能复用,每个框架重复造轮子。
MCP 解决这个,一个标准协议,任何 MCP server 提供的工具,任何支持 MCP 的 Agent 都能用。 写一个 "GitHub MCP server",Claude Code、agents-go、Cursor 都能用它操作 GitHub。
agents-go 的 mcp 包就是 MCP 客户端,它连接 MCP server,把 server 的工具转换成 agents-go 的 Tool 接口。

三种 Transport
MCP server 通过三种方式通信(mcp.go:122-144):
| Transport | 怎么通信 | 适合 |
|---|---|---|
| Stdio | 启动子进程,stdin/stdout 通信 | 本地工具(文件系统、git) |
| Streamable HTTP | HTTP 连接(当前规范) | 远程服务、云端工具 |
| SSE(已废弃) | Server-Sent Events | 旧规范,新项目别用 |
// Stdio:启动一个子进程当 MCP server
server, _ := mcp.NewStdioServer(ctx, "filesystem",
exec.Command("npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp"),
mcp.Options{})
// Streamable HTTP:连远程 server
server, _ := mcp.NewStreamableHTTPServer(ctx, "github",
"https://mcp.github.com/sse", mcp.Options{})
// 挂到 Agent 上
agent := &agents.Agent{
Name: "助手",
MCPServers: []agents.MCPServer{server},
}Agent 不关心 transport 细节,MCP server 的工具被统一适配成 Tool 接口。
源码对照:从 MCP tool 到 agents.Tool
toolFor(mcp.go:233)把一个 MCP tool 适配成 agents-go 的 FunctionTool:
// 概念示意(mcp.go:233)
func (s *Server) toolFor(mcpTool) *agents.FunctionTool {
return &agents.FunctionTool{
Name: s.opts.ToolNamePrefix + mcpTool.Name, // 可加前缀防冲突
Description: mcpTool.Description,
ParamsJSONSchema: schemaToMap(mcpTool.Schema), // schema 适配
OnInvoke: func(ctx, tc, argsJSON string) (any, error) {
// 关键:用"原始未加前缀的名字"调用 server
result, err := s.session.CallTool(ctx, mcpTool.Name, argsJSON)
// 错误处理(见下面)
return resultOutput(result), nil
},
}
}注意一个细节,OnInvoke 里调 server 用的是 mcpTool.Name(原始未加前缀的名字),即使对外暴露的名字加了前缀(ToolNamePrefix)。因为 server 不认识你加的前缀,它只认原始名。
大坑:Schema 适配
这是 MCP 集成最难的部分。很多 MCP server 的 schema 和 OpenAI strict mode 不兼容。
OpenAI 的 strict mode 要求 schema 满足一堆约束(additionalProperties: false、所有字段有类型、不允许多余字段)。但很多 MCP server 的 schema 是宽松的,可能有 additionalProperties: true、可能有 union 类型、可能缺字段。
agents-go 的 schemaToMap(mcp.go:281)做 deep-copy + EnsureStrictJSONSchema 重写,把 MCP 的宽松 schema 转成 OpenAI strict 兼容的。
关键,失败不能留下半改写的 schema(mcp.go:238 注释)。如果改到一半失败,要么完整改完,要么用原 schema,不能给模型一个"改了一半"的坏 schema。这是原子性考虑。
Options.Strict(mcp.go:30)可以关闭这个转换,如果你的 MCP server 本来就兼容 strict,或者转换出了问题,关掉它。
Options 的丰富配置
mcp.Options(mcp.go:22)提供了大量配置,几个重点:
AllowedTools / BlockedTools,静态过滤
Options{
AllowedTools: []string{"read_file", "write_file"}, // 只暴露这两个
BlockedTools: []string{"delete_file"}, // 永远不暴露
}白名单/黑名单,在 ListTools 时过滤。
ToolFilter,动态过滤
Options{
ToolFilter: func(ctx, rc, agent, toolName string) bool {
// 根据 RunContext 动态决定
if userIsAdmin(rc) { return true } // 管理员看到所有工具
return toolName != "delete_file" // 普通用户看不到删除工具
},
}比静态过滤更灵活,可以根据运行时上下文决定。 适合权限控制。
CacheToolsList,缓存工具列表
Options{CacheToolsList: true}MCP server 的工具列表第一次获取后缓存,后续 turn 不重新 list_tools(省网络往返)。但静态/动态过滤每轮都跑(在缓存的列表上过滤)。如果 server 的工具变了,调 InvalidateToolsCache。
ToolNamePrefix,防命名冲突
Options{ToolNamePrefix: "github_"}多个 MCP server 可能有同名工具(比如两个 server 都有 "read_file")。加前缀变成 github_read_file 和 fs_read_file,避免冲突。但调用 server 时用原始名(去掉前缀)。
RequireApproval,MCP 工具的 HITL
Options{RequireApproval: func(toolName string) bool {
return toolName == "delete_repo" // 删仓库要审批
}}给 MCP 工具加审批。 把第 12 章的 HITL 扩展到 MCP 工具。
设计权衡:MCP 错误回喂
MCP tool 执行出错,怎么处理?agents-go 的策略(mcp.go:253),错误回喂模型,让模型自愈:
// MCP tool 返回 IsError=true → 通过 DefaultToolErrorFunction 喂回模型
// 而不是中断整个 run这和第 32 章的 Sandbox 相反,Sandbox 的错误处理是"基础设施错 abort,代码错回喂"。MCP 是"MCP 层的错误都回喂"。
为什么不同?因为 MCP tool 的错误通常是工具逻辑层面的(参数不对、权限不够、资源不存在),不是基础设施层的。让模型知道"这个工具失败了,错误是 X",模型可以调整参数重试或换工具。
对比 Sandbox,Sandbox 执行用户代码,代码本身的错误(exit code 非 0)是"正常输出"(喂回模型),但 Docker 容器启动失败(基础设施错)是"该 abort"。两种错误的语义不同,处理也不同。
OAuth 2.1 + PKCE
对于需要认证的 MCP server(比如 GitHub MCP),Options.OAuthHandler(mcp.go:58)支持 OAuth 2.1 授权码 + PKCE + 动态客户端注册。第 29 章详讲。
小结
读完这一章,你应该理解:
- MCP 是什么,连接外部工具的统一标准协议
- 三种 transport,Stdio(本地子进程)、Streamable HTTP(远程)、SSE(废弃)
- Schema 适配是大坑,MCP 宽松 schema 要转成 OpenAI strict,失败不能留半改写
- 丰富配置,静态/动态过滤、缓存、前缀防冲突、HITL 审批
- 错误回喂策略,MCP tool 错误喂回模型自愈,不中断 run
MCP 让 Agent 从"只用自己写的工具"变成"连接整个工具生态"。 下一章讲 MCP 的进阶,OAuth 认证、多模态结果回传。
下一篇
第 29 章,MCP 的进阶:OAuth、缓存、多模态。MCP server 返回的图片怎么传给模型?OAuth 2.1+PKCE 怎么做?ToolFilter 动态过滤的实战场景。
本文是《从 0 写 Agent》系列第 28 章,案例代码基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版)。全系列目录见总览。

