ByteNoteByteNote
OAuth、多模态、错误策略:MCP 进阶三件事
字

字节笔记本

2026年10月24日 · 约 10 分钟读完

OAuth、多模态、错误策略:MCP 进阶三件事

API中转
¥120

通过 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、多模态、精细的错误处理。


安全:Claude Code 的安全实践

OAuth 2.1 + PKCE

需要认证的 MCP server(Streamable HTTP transport),用 OAuth 2.1 授权码流程 + PKCE(mcp.go:58 的 OAuthHandler):

go
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)处理这种多模态:

go
// 概念示意(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 通道。

非文本块不丢弃

go
// 对于非文本、非图片的 content,JSON 编码后保留
// 绝不丢弃任何 MCP 返回的内容

这是"信息完整性"原则,MCP 返回的任何内容都传给模型,哪怕 agents-go 不认识这个类型,也 JSON 编码后保留。 丢弃可能导致模型丢失关键信息。


错误回喂 vs 中断:MCP vs Sandbox

这是 Part V 一个重要的对比。三种工具的错误策略不同:

工具类型错误策略理由
普通 FunctionTool回喂模型(默认)或中断(FailureErrorFunction=nil)可配
MCP tool回喂模型MCP 错误通常是工具逻辑层,模型可自愈
Sandbox CodeTool代码错回喂,基础设施错中断两种错误语义不同

MCP 为什么都回喂

MCP tool 的错误通常是,参数不合法、权限不够、资源不存在、工具内部逻辑错。这些都是"工具逻辑层"的错误,模型看到错误信息后,可以调整参数重试或换工具。

go
// 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。这里讲清楚缓存和过滤的分层:

text
ListTools 调用时:
  1. 如果 CacheToolsList=true 且已缓存 → 用缓存的工具列表(省 list_tools 网络往返)
     如果没缓存 → 调 server 的 list_tools,获取并缓存

  2. 在(缓存的或新获取的)列表上,应用静态过滤:
     - AllowedTools 白名单
     - BlockedTools 黑名单

  3. 再应用动态过滤:
     - ToolFilter 函数(每轮都跑,因为可能依赖 RunContext)

缓存只省 list_tools 的网络往返,静态/动态过滤每轮都跑。 为什么动态过滤每轮跑?因为它可能依赖 RunContext(比如"管理员才看到删除工具"),RunContext 每轮可能变。


小结

读完这一章,你应该理解 MCP 的三个进阶:

  1. OAuth 2.1 + PKCE,连接需要认证的 MCP server,PKCE 防授权码拦截
  2. 多模态回传,MCP 图片转 ToolOutputImage,非文本不丢弃
  3. 错误策略对比,MCP 回喂(工具逻辑层错误),Sandbox 分两种(代码错 vs 基础设施错)

核心洞察,不同类型的工具,错误策略不同。 普通工具可配,MCP 默认回喂,Sandbox 分代码错和基础设施错。理解这些差异,才能正确设计工具的错误处理。


下一篇

第 30 章,Skills:渐进式披露的 Agent 能力。Skill 是"固定的 Agent 工作方法",但不是全塞进 prompt,而是只放索引,让模型按需读全文。这是控制上下文窗口的重要范式。


本文是《从 0 写 Agent》系列第 29 章,案例代码基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版)。全系列目录见总览。

相关文章

分享: