MCP 协议断裂式升级
MCP 协议断裂式升级
2026 年 7 月 28 日,MCP 协议发布了一次断裂式升级。新版规范把协议从"有会话的握手协议"改成了"无状态的纯 HTTP 协议":initialize 握手被移除,Mcp-Session-Id 被移除,ping 被移除,Sampling、Roots、Logging 三个特性进入弃用倒计时。
如果你手里的 MCP Server 是照着 2025 年的教程写的,那么新版客户端连上来,第一步就会撞墙。没有渐进路径,只能升级。
这个时间点,我正好在做一个把企业 REST API 批量翻译成 MCP Server 的开源平台。协议一变,平台的地基跟着变了。怎么选,成了立项第一天就必须回答的问题。
一、这次升级,到底改了什么
协议文档给出了从 2025-11-25 到 2026-07-28 的完整变更日志,改动集中在几条主线。
官网文档地址:https://modelcontextprotocol.io/specification/2026-07-28/changelog
1. 协议无状态化,砍掉握手
这是最根本的一条。旧版协议里,客户端要先 initialize 协商版本,服务端返回 session ID,后续请求都带着它。新版直接移除了这套机制(SEP-2575、SEP-2567):
| 旧形态 | 新形态 |
|---|---|
initialize / notifications/initialized 握手 |
移除,每次请求在 _meta 里携带协议版本与客户端能力 |
Mcp-Session-Id 头 |
移除,任何请求可以落在集群任意节点 |
| 服务端能力靠握手时宣告 | 新增 server/discover,服务端自报版本、能力与身份,客户端可先探测再干活 |
工程上的含义很直接:MCP Server 从"需要会话亲和"变成了"可以跑在普通轮询负载均衡后面"的设备。横向扩展从需要专门设计,变成了天然能力。
2. list 类响应可以缓存了
新版给 tools/list、prompts/list、resources/list 等响应统一加了缓存提示(SEP-2549):返回 ttlMs 与 cacheScope,告诉客户端这份清单多久内不用重新拉。规范还建议 tools/list 按确定性顺序返回工具,配合客户端缓存能提升 LLM 提示词缓存的命中率。
这条对工具量大的 Server 是实打实的利好。一个挂了上千个工具的 Server,客户端每次会话都全量拉一遍工具清单,token 开销很可观。有了缓存提示,清单拉一次能复用很久。
3. 请求头路由,网关不再需要懂协议
新版要求 Streamable HTTP 的 POST 请求必须带 Mcp-Method、Mcp-Name 头(SEP-2243)。网关层只读 HTTP 头就能把请求分到对应处理逻辑,不需要解析 JSON-RPC 请求体。
4. 弃用大扫除,给了 12 个月窗口
Roots、Sampling、Logging 三个特性进入弃用状态,新实现不应该再支持。HTTP+SSE 传输方式也被正式弃用,统一迁到 Streamable HTTP。规范还确立了特性生命周期策略:Active、Deprecated、Removed 三态,弃用窗口最短 12 个月。
5. 错误码划了规范保留区
JSON-RPC 的 server-error 区间被重新划分:-32020 到 -32099 保留给 MCP 规范使用。几个错误码随之重编号,其中 UnsupportedProtocolVersion 变成了 -32022。
6. 其他几条
多轮往返请求(MRTR)模式引入,服务端需要补充信息时返回 resultType: "input_required",客户端带齐信息后重试原请求。实验性的 tasks 能力移出核心协议,成为官方扩展。SSE 流不再支持断点续传,断了就重发。
二、变化背后,是一个明确的信号
把这些改动放在一起看,方向很清楚:MCP 在把自己打磨成"标准 HTTP 基础设施就能托管"的协议。
无状态化意味着服务端不需要共享会话存储,部署形态向普通 Web 服务看齐。请求头路由意味着网关不需要理解 MCP 语义,流量治理可以交给现成的网关设施。
缓存提示意味着工具清单这种高频大响应有了流量出口。弃用窗口意味着生态在主动收缩复杂度,逼着新实现走干净的路。
对 Server 作者来说,这个版本最值得注意的一点是:版本差异不再是"平滑兼容",而是"硬断裂"。旧客户端连新服务端会收到明确的 -32022,新客户端连旧服务端同样失败。
三、我做的决定:只实现 2026-07-28
我的平台叫 MCP Bridge,目标是把企业存量 REST API(Swagger / OpenAPI 2.0 与 3.x)批量翻译成可治理、可托管、可多集群发布的 MCP Server。立项那天,我面对的第一道选择题就是:新旧两版协议都还有存量客户端,要不要都支持?
我查了一圈市面上的方案。单文件转换器和单实例代理已经不少,但它们基本都停在"开发者本地跑一个进程"的形态,而且大多还在追旧协议。真正缺的,是跟上新协议、并且能扛住企业治理需求的东西。
在"兼容旧版"和"只做新版"之间,我最终选了后者,并且是没有任何过渡期的后者。这个决定写在项目的架构决策记录里(ADR-0001),理由可以摊开讲。
双协议入口的方案被否了。 分流点必须解析请求体才能判断版本,这直接抵消了请求头路由的价值,网关又变回"必须理解 MCP 语义"的设备。更麻烦的是要维护两套并行的错误语义与会话语义,测试矩阵翻倍。
双栈加弃用公告的方案也被否了。 它是最容易被接受的方案,也最容易变成永久债务。一旦有生产客户端跑在兼容层上,摘除动作就有了阻力,"摘除日"通常不会到来。
协议在 2026 年 7 月发布正式版,我 9 月立项,没有"用户来不及升级"的历史包袱。兼容层只会是我自己背上去的。
所以平台的协议策略一句话讲完:全平台只实现并只应答 MCP 2026-07-28,对 legacy 客户端显式拒绝,不做双栈、不做兼容转换、不预留适配器。
拒绝不是冷冰冰的。平台的数据面(Executor)在进入任何业务逻辑之前,先跑一道协议守卫,识别五种 legacy 形态:
| 特征 | 对应旧协议 |
|---|---|
请求带 initialize / notifications/initialized |
旧握手 |
请求带 Mcp-Session-Id 头 |
旧会话 |
| 版本头不等于 2026-07-28 | 旧版本声明 |
请求体 params.protocolVersion 不是 2026-07-28 |
旧版本声明 |
_meta.sessionId 存在 |
会话残留 |
命中任意一条,返回固定的 -32022,错误体的 data 里带齐上下文:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32022,
"message": "Unsupported Protocol Version: 本平台仅支持 MCP 2026-07-28",
"data": {
"supportedProtocolVersion": "2026-07-28",
"detectedProtocolVersion": "2025-11-25",
"reason": "initialize 握手已在 2026-07-28 规范中移除",
"upgradeUrl": "https://modelcontextprotocol.io/specification/2026-07-28",
"legacySupported": false
}
}
}
legacySupported: false 是给客户端的机器可读承诺,五种形态各有可区分的 reason。不然用户只会看到"连不上",然后花一整天抓包。现在客户端可以把整段错误直接显示给用户,告诉他该升级了。
这个决定的回报,恰好踩在新版协议的设计意图上:Executor 完全无状态,可以跑在普通轮询负载均衡后面,任意节点服务任意请求,不需要会话存储。Redis 只承载三类应用层共享状态(上游令牌缓存、刷新锁、失效广播),不承载路由。Redis 挂了,端点仍然能服务,只是退化成每节点各自换令牌。
四、平台长什么样
协议地基定了,上面的治理链路才能谈。平台的完整形态是控制面加数据面两个平面:
控制面负责"编译",数据面负责"执行"。两者之间只有一条通道:Executor 用节点令牌轮询拉取发布快照,不直连控制面数据库。因此私有集群可以部署在与控制面网络隔离的环境里,只要它能出站访问控制面 API。
一条 REST 服务从注册到被 AI 客户端调用,走的是完整治理链路:
注册即解析。 支持 URL、上传文件、粘贴内容三种方式收进 Swagger 文档,解析成可治理的结构。每个 operation 自动变成一个 MCP 工具,命名有规约,入参 Schema 从 parameters 和 requestBody 推导。
解析出致命错误时不生成 Server,只留诊断列表。一个不完整的 Server 比一个明确的失败更难处理。
精修不碰原稿。 原始 Swagger 文档只读存储,加 sha256 双保险,读取时重算哈希,不一致直接拒绝。你对工具做的任何精修(改名、改 Schema、启停用)都存在独立的覆盖层里。
将来上游文档升级,按锚点对账。锚点消失的覆盖进挂起区,等人工确认后再决定去留。
发布即快照。 发布会把此刻的生效模型序列化进版本化的发布记录,标记 Server 为已发布只是副作用。发布之后无论原稿和覆盖层怎么改,已发布的端点行为都不变,直到下一次显式发布。
回滚就是用历史快照创建新版本,历史链条完整可审计。
调用即透传。 客户端请求到达 Executor 后,协议守卫、下行鉴权、工具定位、参数映射、上行鉴权、负载均衡、熔断重试,十步走完,把上游 REST 的响应包装成 MCP 工具结果返回。
鉴权是双跳模型,两跳完全隔离:
| 跳 | 方向 | 模式 |
|---|---|---|
| Auth-D | MCP 客户端到 Executor | NONE / STATIC_BEARER(OAuth2 未实现前显式拒绝,不给"假装受保护"的假象) |
| Auth-B | Executor 到上游 REST | API_KEY / HTTP(Bearer、Basic) / OAuth2 Client Credentials / CUSTOM_HEADER |
上行凭据用 AES-256-GCM 加密托管,控制面只回显掩码。下行令牌只存哈希,比对用常量时间,鉴权失败一律回同一句话,不给攻击者枚举的机会。
五、接入一个 AI 助手,只需要一个 URL
发布完成后,任何支持 MCP Streamable HTTP 的客户端都能接入。以 Codex 为例,配置就两样东西:一个 URL,一个令牌。
[mcp_servers.order]
url = "http://your-executor/mcp/order"
bearer_token_env_var = "MCP_BEARER_TOKEN"
或者直接用命令:
codex mcp add order --url http://your-executor/mcp/order
重启 Codex,会话里 /mcp 能看到 Server 处于 connected。之后你对 Codex 说"帮我查一下订单 1024 的状态",它就会调用这个由你部门 REST 接口自动翻译出来的工具,而不是靠幻觉编一个答案。平台侧的审计日志里,能看到这次来自 Codex 的 tools/call 记录。
整个平台 Docker Compose 一条命令起全套(PostgreSQL、Redis、控制面、两个 Executor 节点、管理控制台),项目已开源,MIT 协议。
六、为什么我赌 Modern-only
说回协议。我知道会有人问:企业里旧客户端那么多,你这么激进,不怕没人用吗?
我的判断是,这次断层的方向和以往不同。以往协议升级是叠加新能力,旧客户端最多少用几个功能。这次是删除基础机制,旧形态连不上新世界,新形态也不愿意为旧形态保留负担。
协议自己都给了 12 个月弃用窗口,官方想表达的态度很明确:快速收敛,别留双轨。
与其做一层迟早要拆的兼容垫片,不如把精力全押在正确的协议上,把拒绝做得干净、可解释。等存量客户端陆续升级上来,一个只认 2026-07-28 的 Server,反而成了最不需要操心的那个。
如果你想围观这个决定怎么落地,代码、架构决策记录(ADR)、踩坑记录都在仓库里,欢迎 star 和提 issue。下一篇我会接着写平台里最有意思的一个取舍:覆盖层怎么做到"原稿永不改动",欢迎关注。