本文对应同名视频,建议配合视频观看效果更佳。视频从真实抓包出发,完整演示了握手、工具调用与 Swagger 自动映射三大核心流程。
就在 2026 年 7 月 28 号,MCP 发布了协议诞生以来最大的一次修订——握手机制和 Mcp-Session-Id 被正式砍掉,官方原话是 largest revision of the protocol since launch。
但讽刺的是,Anthropic 今年 6 月才刚公布,MCP 的安装量已经 9700 万。这么大的存量,现网里绝大多数还跑着旧版机制。
有一份第三方基准测试的数字:让大模型直接照着 API 文档自己拼接调用后端接口,复杂任务成功率只有 92.31% ;换成 MCP 协议做中间层之后,成功率到 100% ,调用次数和耗时都少了近 20%。
今天从真实抓包出发,把这套刚被官方判”过时”、但现网还在大规模跑的 MCP 机制,拆给你看。
视频讲解
为什么需要 MCP
大模型调用外部接口,本来只有两条路,都不太舒服。
第一条路:让大模型直接读 API 文档、自己拼请求。但它本质上是个基于概率的文本预测引擎,没有严谨契约约束的时候,参数拼错、长上下文注意力分散引发的幻觉,出错率很难压下去——92.31% 的成功率就是这条路的现实写照。
第二条路:退回到各厂商私有的 Function Calling 格式——准是准了,但每换一种大模型,网关就得重写一遍适配层,陷入生态孤岛。
MCP 协议做的事,是在这两条路之间架一层标准化的桥——大模型只需要认一套协议,剩下的翻译和代理工作,交给网关。
握手:大模型和网关怎么”认识”彼此
JSON-RPC 2.0:MCP 的底层语言
MCP 协议底层就是 JSON-RPC 2.0——一种”动作导向”的轻量级协议,不像 REST 那样纠结 HTTP 动词和路由设计,所有交互都浓缩进一个 JSON 对象,认准四个字段就够:
| 字段 | 含义 |
|---|---|
jsonrpc |
固定值 "2.0",标明协议版本 |
method |
要执行什么动作,如 initialize、tools/call |
params |
传递给 method 的参数 |
id |
请求唯一标识,用于异步场景下配对请求与响应 |
为什么 id 字段必须有
id 这个字段的设计不是偶然的。MCP 底层走的是 SSE 长连接,彻底打破了 HTTP 那种按顺序一问一答的模式——大模型可以连续无阻塞地发出请求1、请求2,但网关处理不同后端接口的耗时不一样,完全可能先推回响应2、再推响应1。客户端就是靠这个 id 字段,在乱序的异步流里把请求和响应精准对上号。
握手流程:initialize → Session-Id → initialized
第一步,大模型客户端发一个 initialize 请求:
1 | curl -i -X POST http://10.1.9.21:11999/ \ |
网关返回响应,头部会带一个 Mcp-Session-Id,后面所有请求都要带着这个凭证:
1 | Mcp-Session-Id: ajnoOA90SgAAAAAA |
响应包体里还有个 capabilities 字段,宣告网关支持哪些能力:
1 | { |
握手拿到 Session-Id 后,这个会话其实只是”半激活“状态——只有等大模型发完 notifications/initialized 这条确认通知,会话才正式生效,在这之前 tools 类的业务请求会被网关直接拒绝。之后如果长时间没有请求,网关也会按超时时间把会话淘汰掉,回收资源。
版本号为什么用日期
MCP 协议的版本号不是常见的 x.y.z,而是用日期,比如 2024-11-05、2025-03-26。设计本意是协议两端能快速、去中心化地演进,用日期做时间线交集比对,省得在次要版本号定义权上扯皮。
但现实是:从 2024 年 11 月发布到现在,一年多总共才出了 5 个正式版本,节奏并不算快。所以现网里大量中间件和网关,包括本文抓包用的矩尺 AI 网关,停留在更早的版本上——这也是为什么理解握手这套”旧”机制,现在依然有实际价值。
工具调用:一句 JSON 是怎么变成 REST 请求的
握手完成后,进入工具调用流程,这才是 MCP 协议的核心价值所在。
第一步:tools/list 发现工具
大模型先要知道网关背后挂了哪些工具,发起 tools/list 查询:
1 | {"jsonrpc":"2.0","method":"tools/list","id":2} |
如果后端接口太多,网关会分页返回,避免一次性把大模型的上下文撑爆:
1 | { |
第二步:tools/call 发起调用
假设列表里有个叫 list_vs 的工具,能查虚拟服务信息。大模型基于用户的自然语言意图,推理出要调用这个工具,传入参数 name: mcp:
1 | { |
第三步:网关翻译成 REST 请求
这条 JSON-RPC 请求到了网关这里,会被翻译成一个真正的 REST 请求——网关把 arguments 里的参数提取出来,重组成 URL 查询参数:
1 | GET /slb/virtual_service/?name=mcp HTTP/1.1 |
后端服务处理完,返回标准的 200 和 JSON 结果,网关再把它包装回 JSON-RPC 的 result 字段,通过同一个会话推回给大模型。
一句自然语言,就这样变成了一次精确的 REST 调用,再原路带着结果回来。
谁来维护工具清单:Swagger → MCP 的自动转换
后端接口天天在变,如果这份工具列表要靠人工维护,运维成本高,还容易滞后出错——接口一改参数,大模型感知不到,调用就会出错。
网关的做法是直接读后端现成的 Swagger 文档,自动生成映射。
tools/list 的数据结构
工具列表返回的结构很规整——一个 tools 数组,加一个分页用的 nextCursor。每个工具描述包含以下字段:
| 字段 | 用途 |
|---|---|
name |
工具名,供大模型调用时引用 |
description / title |
给大模型读的提示词,说明这个工具做什么 |
inputSchema |
遵循 JSON Schema 规范,定义工具能接受什么参数 |
outputSchema |
定义工具返回哪些字段,方便大模型预判后续处理 |
Swagger parameters → inputSchema
inputSchema 的源头是 Swagger 文档里的 parameters 数组。比如 list_vs 工具,在 Swagger 里对应的定义是:GET 方法、/slb/virtual_service/ 路径,参数里声明了一个叫 name 的查询参数。
网关要做的,是把这种扁平的参数列表,重新组装成 JSON Schema 要求的带 properties 的树状结构,同时把参数类型也对齐转换过去:
1 | // Swagger parameters(扁平) |
命名细节:中划线 → 下划线
顺带提一句命名细节——Swagger 里 operationId 是 list-vs,带中划线;网关会把它转成 list_vs,下划线,这是照顾大模型的命名偏好做的转换。
Swagger responses → outputSchema
Swagger 用 responses 字段,按不同 HTTP 状态码分别声明返回格式;网关会取出 200 成功状态码对应的结构,逆向重组成 outputSchema,让大模型不只知道怎么调用,还能提前预判返回结果里有哪些字段,方便规划后续处理逻辑。
$ref 展开:防止大模型推理幻觉
复杂后端服务里,很多参数和返回结构会被多个接口反复复用,统一定义在 Swagger 的 definitions 里,用 $ref 指针互相引用,有的结构内部还嵌套引用着另一个结构。
为了不让大模型在理解深层嵌套关系时产生推理幻觉,网关通常要把这些分散的引用,在转换时完全展开铺平,直接写进最终的工具描述里。
这也导致了一个直观的现象——网关转换后生成的工具列表,体积往往比原始的 Swagger 文档大得多,具体膨胀多少,取决于网关配置的引用展开层数。
总结
回到开头那组数字——92.31% 到 100%,靠的就是这一整条链路:
1 | 握手建立信任 |
这也是这一年 Agent 能大规模接入真实业务系统的底层原因之一。
就在 9 天前,2026 年 7 月 28 号,MCP 发布了协议诞生以来最大的一次修订——握手和 Session-Id 被正式砍掉,改成了无状态架构。讽刺的是,这才是”用日期做版本号是为了快速迭代”这句话,第一次真正兑现。
MCP 协议里的另外两大能力——resources 资源读取、prompts 提示词模板,这次没展开讲,下期接着聊。