
字节笔记本
2026年10月24日 · 约 10 分钟读完
OAuth、多模态、错误策略:MCP 进阶三件事

本文是《从 0 写 Agent》系列第 29 章。这个系列基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版),用 Go 从零实现一个生产级 Agent 运行时;本章讲「MCP 进阶:OAuth、多模态、错误策略」。
上一章讲了 MCP 的基础。这一章讲三个进阶话题,OAuth 认证(连接需要授权的 MCP server)、多模态结果回传(MCP 工具返回图片怎么给模型)、以及 MCP 和 Sandbox 错误策略的对比。
引子:真实 MCP server 的复杂性
基础 MCP(本地文件系统 server)很简单,启动子进程就能用。但真实的 MCP server 更复杂:
- GitHub MCP 需要 OAuth 认证才能操作你的仓库
- 数据库 MCP 返回的结果可能含图片(图表截图)
- 浏览器 MCP 可能返回截图
这些场景需要 MCP 的高级能力,OAuth、多模态、精细的错误处理。

OAuth 2.1 + PKCE
需要认证的 MCP server(Streamable HTTP transport),用 OAuth 2.1 授权码流程 + PKCE(mcp.go:58 的 OAuthHandler):
server, _ := mcp.NewStreamableHTTPServer(ctx, "github",
"https://mcp.github.com/sse",
mcp.Options{
OAuthHandler: myOAuthHandler, // 处理 OAuth 流程
})为什么是 OAuth 2.1 + PKCE
MCP 的 HTTP transport 规范要求 OAuth 2.1(不是 2.0)。2.1 相比 2.0 的关键区别,强制 PKCE(Proof Key for Code Exchange)。
PKCE 防止授权码拦截攻击,客户端生成一个 verifier(随机串),算出 challenge(SHA256 of verifier),授权时发 challenge,换 token 时发 verifier 证明身份。即使攻击者截获了授权码,没有 verifier 也换不到 token。
agents-go 把 OAuth 流程委托给 OAuthHandler(auth.OAuthHandler 接口来自 MCP go-sdk),具体实现可参考第 39 章 agents-server 的 ChatGPT OAuth 实现(同样的 PKCE 模式)。
多模态结果回传
MCP 工具可能返回不只文本,还有图片(ImageContent)、嵌入资源(EmbeddedResource)。agents-go 的 resultOutput(mcp.go:353)处理这种多模态:
// 概念示意(mcp.go:353)
func resultOutput(result *mcpsdk.CallToolResult) any {
if hasImageContent(result) {
// 图片转成 agents.ToolOutputImage(模型直接吃原生图片输入)
return agents.ToolOutputImage(...)
}
// 文本和其他类型
return stringifyContent(result)
}关键,MCP 返回的 ImageContent 被转成 agents-go 原生的 ToolOutputImage,让模型直接接收图片输入(而不是把图片 base64 编码塞进文本)。
这呼应第 3 章的工具输出,agents-go 支持 ToolOutputText/ToolOutputImage/ToolOutputFile 三种多模态输出。MCP 的图片结果自动走 Image 通道。
非文本块不丢弃
// 对于非文本、非图片的 content,JSON 编码后保留
// 绝不丢弃任何 MCP 返回的内容这是"信息完整性"原则,MCP 返回的任何内容都传给模型,哪怕 agents-go 不认识这个类型,也 JSON 编码后保留。 丢弃可能导致模型丢失关键信息。
错误回喂 vs 中断:MCP vs Sandbox
这是 Part V 一个重要的对比。三种工具的错误策略不同:
| 工具类型 | 错误策略 | 理由 |
|---|---|---|
| 普通 FunctionTool | 回喂模型(默认)或中断(FailureErrorFunction=nil) | 可配 |
| MCP tool | 回喂模型 | MCP 错误通常是工具逻辑层,模型可自愈 |
| Sandbox CodeTool | 代码错回喂,基础设施错中断 | 两种错误语义不同 |
MCP 为什么都回喂
MCP tool 的错误通常是,参数不合法、权限不够、资源不存在、工具内部逻辑错。这些都是"工具逻辑层"的错误,模型看到错误信息后,可以调整参数重试或换工具。
// MCP tool 返回 {isError: true, content: "参数 city 不能为空"}
// → 通过 DefaultToolErrorFunction 喂回模型
// → 模型看到"city 不能为空",下轮带上 city 重试Sandbox 为什么分两种
Sandbox 执行用户代码。错误分两种:
- 代码错误(exit code 非 0、语法错),这是"正常输出",喂回模型让模型修代码
- 基础设施错误(Docker 容器启动失败、SSH 连不上),这是系统级故障,应该 abort
区分逻辑,代码错误是"模型可以修的"(回喂),基础设施错误是"模型修不了的"(中断)。第 32-33 章详讲。
设计权衡:OAuth 的复杂度
OAuth 让 MCP 能连需要认证的服务,但代价是复杂度暴增:
- 要处理授权码流程(重定向、回调)
- 要存 token、刷新 token
- 要处理 token 过期
- 要处理用户取消授权
agents-go 把这些委托给 OAuthHandler,不自己实现。但如果你要接需要 OAuth 的 MCP server,你得实现或集成一个 OAuthHandler。 agents-server(Part VI)的 MCP OAuth 协调器就是一个参考实现。
这是 MCP 集成的"必要复杂度",认证不是可选的,因为真实服务都要认证。
MCP 的缓存与过滤分层
回忆第 28 章的 CacheToolsList。这里讲清楚缓存和过滤的分层:
ListTools 调用时:
1. 如果 CacheToolsList=true 且已缓存 → 用缓存的工具列表(省 list_tools 网络往返)
如果没缓存 → 调 server 的 list_tools,获取并缓存
2. 在(缓存的或新获取的)列表上,应用静态过滤:
- AllowedTools 白名单
- BlockedTools 黑名单
3. 再应用动态过滤:
- ToolFilter 函数(每轮都跑,因为可能依赖 RunContext)缓存只省 list_tools 的网络往返,静态/动态过滤每轮都跑。 为什么动态过滤每轮跑?因为它可能依赖 RunContext(比如"管理员才看到删除工具"),RunContext 每轮可能变。
小结
读完这一章,你应该理解 MCP 的三个进阶:
- OAuth 2.1 + PKCE,连接需要认证的 MCP server,PKCE 防授权码拦截
- 多模态回传,MCP 图片转
ToolOutputImage,非文本不丢弃 - 错误策略对比,MCP 回喂(工具逻辑层错误),Sandbox 分两种(代码错 vs 基础设施错)
核心洞察,不同类型的工具,错误策略不同。 普通工具可配,MCP 默认回喂,Sandbox 分代码错和基础设施错。理解这些差异,才能正确设计工具的错误处理。
下一篇
第 30 章,Skills:渐进式披露的 Agent 能力。Skill 是"固定的 Agent 工作方法",但不是全塞进 prompt,而是只放索引,让模型按需读全文。这是控制上下文窗口的重要范式。
本文是《从 0 写 Agent》系列第 29 章,案例代码基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版)。全系列目录见总览。



