MCP 协议最大改版:无状态化到底改变了什么
MCP 2026-07-28 规范将协议从有状态彻底重构为无状态——Session 消失、握手取消、Extensions 成为一等公民。这篇文章聊聊这次改版的实际影响,以及你的 MCP Server 需要改什么。
上个周末我在重构一个 MCP Server。原先跑在单机上一直好好的,但一放到负载均衡后面就各种诡异问题——session 过期、sticky session 不稳定、共享 session store 的引入让整个架构重了三倍。我跟同事吐槽这事,他说:"等等,MCP 马上要大改版了,session 整个没了。"
我去看了 2026-07-28 规范的 Release Candidate,确实。这不是一次小修小补,而是 MCP 自发布以来最大的一次架构变更。核心思路一句话就能概括:把协议层的状态全部干掉,让 MCP 变成真正的无状态协议。 最终规范将在 7 月 28 日正式发布,但 RC 已经锁定了所有变更,现在就可以动手评估迁移了。
Session 没了,然后呢?
在 2025-11-25 版本中,MCP 的典型调用流程是这样的:客户端先发一个 initialize 请求建立 session,服务器返回一个 Mcp-Session-Id,之后所有请求都得带上这个 header。这意味着负载均衡器必须把同一个 session 的所有请求路由到同一台服务器实例上——sticky session 成了刚需。如果你想做水平扩展,要么引入共享 session store(Redis 之类),要么接受单点瓶颈。
新版本直接把整个 session 概念删了。initialize 握手取消,Mcp-Session-Id 取消,客户端信息、协议版本、capabilities 这些原本在握手阶段交换的东西,现在全塞进每个请求的 _meta 字段里随身携带。
效果立竿见影:任意请求落到任意服务器实例都能被独立处理。你的 MCP Server 从"需要 sticky session + 共享存储"降级为"一个普通的轮询负载均衡就够了"。
// 旧:必须先 initialize,然后每次带 session ID
POST /mcp
Mcp-Session-Id: 1868a90c-3a3f-4f5b
{"jsonrpc":"2.0","method":"tools/call",...}
// 新:一个自包含请求,扔给谁都能处理
POST /mcp
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
{"jsonrpc":"2.0","method":"tools/call","params":{...,"_meta":{...}}}
这还带来了一个附带好处:Mcp-Method 和 Mcp-Name 被提升为必需的 HTTP header。网关和负载均衡器可以直接按 header 做路由、限流、审计,不用再解析 JSON body。运维友好度直接拉满。
状态去哪了?显式句柄模式
协议层不管理状态了,不代表你的应用就不能有状态。官方推荐的替代方案叫"显式句柄模式"(explicit handle pattern),思路很简单:服务器从 tool 调用中生成一个显式的 ID(比如 basket_id、browser_id),模型把这个 ID 当作普通参数在后续调用中传递。
// 步骤 1: 创建购物车,服务器返回 basket_id
tools/call: create_basket
→ { "basket_id": "basket_abc123" }
// 步骤 2: 添加商品,模型把 basket_id 当参数传回去
tools/call: add_item
→ { "basket_id": "basket_abc123", "item": "...", "quantity": 1 }
我一开始觉得这只是"把 session 的活丢给应用层"的偷懒做法。但仔细想了下,这个模式其实更强——状态对模型是可见的。模型可以跨 tool 组合句柄,可以在多步之间灵活传递,甚至可以推理和判断下一步该用哪个句柄。而原来藏在 transport 元数据里的 session 状态,模型根本感知不到。
Extensions 成为一等公民
Extensions 在 2025-11-25 版本就存在了,但那会儿没有正式的治理流程,更像是个占位符。新版通过 SEP-2133 给 Extensions 定了规矩:反向 DNS 命名、独立的 ext-* 仓库、独立版本号、从实验到正式的晋升路径。这意味着 MCP 的进化不再需要每次改核心规范,新能力可以通过 Extension 机制独立发布和迭代。
这个版本引入了两个正式 Extension。
MCP Apps:服务器可以返回交互式 HTML 界面,host 在沙盒 iframe 里渲染。工具可以提前声明 UI 模板让 host 预取、缓存和安全审查。UI 触发的操作走的是跟普通 tool 调用完全相同的 JSON-RPC 通道——意味着所有 UI 操作都会经过同样的审计和授权路径。简单说,你的 MCP tool 不再只能返回 markdown 文本,它可以返回一个可排序的表格、一个表单、一个数据可视化面板。
Tasks:耗时任务从实验性功能正式升级为 Extension。旧版 Tasks 依赖 SSE 长连接来驱动生命周期,新版则完全适配了无状态模型:tools/call 返回 task handle,客户端通过 tasks/get、tasks/update、tasks/cancel 来驱动。tasks/list 被移除了——在无 session 的情况下,没法安全地限定列表的范围。
Breaking Changes:你的代码要改什么
这次改版有 breaking changes,而且不是可选的。以下是必须处理的:
| 变更 | 影响 | 动作 |
|---|---|---|
Mcp-Session-Id 移除 |
所有读这个 header 的代码失效 | 改为显式参数传递 |
initialize 握手取消 |
capabilities 协商逻辑失效 | 改为 _meta 随请求携带 + server/discover |
| Streamable HTTP 新增必需 header | 没带 Mcp-Method / Mcp-Name 的请求会被拒绝 |
在所有请求中加上这两个 header |
| Tools list 缓存策略 | 不能再假设客户端会实时拉取 | 根据数据新鲜度声明合理的 ttlMs |
| W3C Trace Context 标准化 | 分布式追踪的 key 名被锁定 | 按新规范更新 traceparent 等字段 |
还有一个值得注意的:inputSchema 和 outputSchema 升级到了完整的 JSON Schema 2020-12。现在支持 oneOf、anyOf、allOf、$ref、$defs 等组合和引用能力。outputSchema 不再限制为 object 类型,structuredContent 可以是任意 JSON 值。这对定义复杂 tool 参数的人来说是个不小的提升。
另外三个功能被标记为弃用:Roots、Sampling、Logging。弃用不是移除——它们在当前版本和未来 12 个月内都能正常工作,但你得开始规划替代方案了。
迁移时间线
- 2026-05-21:RC 发布,变更锁定
- 2026-07-28:正式规范发布,Tier 1 SDK 预期在此前完成适配
- 2026.07-08:主流 SDK 陆续更新
- 2027+:旧版本支持逐步下线,12 个月弃用窗口结束
如果你现在就开始评估迁移,时间完全够。建议优先做三件事:
- 把 session 依赖的代码全部找出来,按显式句柄模式重写
- 检查所有 Streamable HTTP 端点,确保处理好
Mcp-Method和Mcp-Nameheader - 如果用了实验性 Tasks API,对照新 lifecycle 做适配
最后
这次改版给我的感觉是:MCP 从"能跑就行"进入了"能规模化部署"的阶段。无状态化、Extensions 机制、弃用策略,每一项都在解决实际生产中的痛点。协议层的复杂度被大量削减,换来的是部署简单性和扩展灵活性。
对自建 MCP Server 的团队来说,迁移成本不算低,但值得。一个能用普通 HTTP 基础设施水平扩展的 MCP Server,跟一个需要 sticky session 和共享存储的 MCP Server,运维成本差了一个数量级。
我喜欢这次改版的一个细节:Mcp-Method header 的设计。这个改动很小,但意味着你的 MCP 流量可以用现成的 API 网关工具来治理,不需要自建任何专用中间件。这就是好的协议设计——用已有的轮子,而不是造新轮子。