为什么无状态化:旧会话模型的工程债
按官方版本约定,MCP 当前协议版本为 2026-07-28,上一个版本是 2025-11-25。这次修订最直观的变化是两个沿用了两年多的机制被移除:initialize 握手与 Mcp-Session-Id 请求头。
旧模型把 MCP 设计成类似「打电话」:客户端与服务器先互报版本与能力,服务器发放会话 ID,后续所有请求都靠这个 ID 维持上下文。这种设计在单机开发场景没有问题——一个客户端对一个服务器,连接生命周期与开发会话一致。但把它搬进生产环境,工程债立刻显现:
| 旧会话模型的问题 | 具体表现 | 受影响场景 |
|---|---|---|
| 水平扩容困难 | 同一用户的请求必须落到同一服务器实例,否则「不认识这个会话」 | 多实例负载均衡部署 |
| 粘性会话依赖 | 需要在负载均衡器上配置会话粘连,或引入 Redis 等外部状态存储 | 高可用架构设计 |
| 重启即失联 | 服务器重启或滚动发布时,会话状态丢失,通信中断 | 持续部署、弹性伸缩 |
| 网关盲区 | 中间层无法仅凭 HTTP 头判断请求意图,路由与审计需解析 JSON 正文 | 企业网关、WAF、合规审计 |
值得强调的是:旧规范本身并未强制要求粘性会话或 Redis,是「会话」这个隐含假设在多实例部署下自然引出了这些补丁。新规范的做法不是继续打补丁,而是把这个假设从协议里整个拿掉。
每个请求必须「自我介绍」:协议版本、客户端能力、客户端身份随每个请求携带,任何一个服务器实例都能独立处理任何一个请求。状态没有被消灭,而是被赶出了协议层。
新规范核心机制逐项拆解
自描述请求:_meta 与版本头
新规范要求每个请求通过 _meta 字段携带 io.modelcontextprotocol/protocolVersion 键声明协议版本;在 Streamable HTTP 传输上,同一取值还须通过 MCP-Protocol-Version 请求头传递。服务器对每个请求独立接受或拒绝:版本不匹配时直接返回 UnsupportedProtocolVersionError,并列出自己支持的版本,客户端可换版本重试或向上抛错。
server/discover:握手的一次性替代
对于需要提前获知服务器能力的客户端,新规范提供了 server/discover 调用:一次请求返回服务器支持的协议版本、能力清单与身份信息。它的定位值得注意——这是一个强制实现的 RPC,但调用本身是可选的:客户端完全可以跳过它直接发业务请求,出错后再处理版本错误。换言之,旧规范里「必须先握手才能干活」的强耦合被解开了。
强制方法头:Mcp-Method 与 Mcp-Name
新规范将 Mcp-Method 与 Mcp-Name 请求头设为强制。这两个头把「这次调用的是什么方法、哪个工具」提升到 HTTP 头层面,网关或 WAF 不打开 JSON 正文就能完成路由、度量与过滤。这是协议首次明确为「中间基础设施」而不是只为两端设计。
| 机制 | 旧规范(2025-11-25 及更早) | 新规范(2026-07-28) |
|---|---|---|
| 连接建立 | initialize 握手 + notifications/initialized | 废除握手,请求各自携带 _meta 声明 |
| 会话标识 | Mcp-Session-Id 请求头(服务器可选发放) | 移除;状态改为显式 handle(见下节) |
| 版本协商 | 握手时一次性协商,全程生效 | 逐请求声明 + 逐请求拒绝,server/discover 可选预查 |
| 方法可见性 | 仅存在于 JSON 正文 | Mcp-Method / Mcp-Name 头强制 |
| 结果类型 | 无强制区分 | resultType(complete / input_required)成为强制字段 |
状态去哪了:handle 显式化与 MRTR
handle:从协议隐含状态到工具显式参数
购物车是文档与社区讨论中最常用的例子:往购物车里加商品,天然需要「知道之前加了什么」。旧架构下这类连续性由会话在协议层隐式维持;新架构下,改为由服务器在首次调用时返回一个显式标识(例如 basket_id),后续调用由模型把它作为普通工具参数传入。
这个转移的语义差异比听起来大:状态从「连接断了就没了」变成「只要拿得到标识就能续上」。规范原文的表述是,状态没有消失,而是从协议背后浮现到工具的输入输出之中——跨连接续接一个中断的操作,在新模型下是自然的,在旧模型下需要会话还活着。
MRTR:两阶段往返取代悬挂连接
旧规范允许服务器在处理中途「反问」客户端(例如请求确认授权),这隐含依赖一条常驻流。新规范引入 MRTR(Multi Round-Trip Requests)机制,把反问改成显式的两阶段:
- 服务器不挂起等待,而是返回一个携带必要查询的
resultType: "input_required"结果; - 客户端准备好答案后,重新发送初始请求并附上答案。
配套地,resultType(complete 或 input_required)成为所有结果的强制字段。如果存量服务器未返回该字段,客户端必须按 complete 处理——这是一条明确写入规范向后兼容规则。
对智能体工程来说,MRTR 意味着「人类确认」不再需要维持一条随时待命的连接:审批请求变成一次可排队、可转发、可超时的结构化结果。这与近两年框架层把中断恢复做进核心(长时任务、断点续跑)的方向完全同频。
部署形态的变化:负载均衡、网关与缓存
负载均衡回归朴素
无状态化后,任意实例可处理任意请求,前置负载均衡器使用标准的轮询或最少连接策略即可,无需粘性会话配置,也无需为会话共享引入外部存储。对运维团队而言,滚动发布与弹性伸缩不再需要考虑「正在进行的会话怎么办」——不存在协议级会话了。
网关从旁观者变成一等公民
Mcp-Method 与 Mcp-Name 头的组合,使企业网关可以在不解析正文的情况下完成:按工具粒度的路由、限流与审计采样。对于把 MCP 纳入数据出口治理(例如禁止某些工具访问外网)的企业,这是一次合规成本的实际下降。
缓存提示:ttlMs
新规范为可缓存的响应(工具列表、资源列表及读取等)增加了 ttlMs 字段:服务器以此毫秒值告诉客户端「这条结果在多久内可以复用」。客户端无需为保持新鲜度而维持长连接轮询,离线缓存变得有据可依。
迁移路径与向后兼容
MCP 的版本策略是日期字符串标识「最后一次不向后兼容变更的日期」;向后兼容的改进不升版本。新规范对 2025-11-25 及更早的握手式实现提供了向后兼容章节,官方废弃政策规定:被废弃的特性保留至少十二个月(加急移除例外为九十天)才可移除。
对服务器作者,迁移工作大致分四类:
- 无状态服务器:多数简单工具服务器几乎零成本——它们本来就不依赖会话,只需补齐 _meta 声明与 resultType 字段;
- 有状态服务器:需要把会话内隐含的状态重构为显式 handle,写入工具入参出参,并设计 handle 的过期与并发策略;
- 使用服务器反问的服务器:改造为 MRTR 两阶段流程,注意客户端重发请求时上下文的幂等性;
- 多实例部署方:可以拆除粘性会话与会话共享存储,但需检查监控与审计管道是否已适配新请求头。
需要指出:目前官方及行业暂未披露主流客户端适配新版规范的统一时间表,后续将持续跟进迭代动态。企业在升级前应以所用客户端的实际支持情况为准。
对智能体客户端与工具设计的影响
MCP 的价值主张是「一次实现,多端可用」——同一个 MCP 服务器可被 Claude Code、Cursor、Codex CLI、Gemini CLI 等主流编码智能体直接调用。协议层的这次修订对这一生态有三层影响:
- 企业部署门槛下降:过去把 MCP 服务器推向生产时最棘手的多实例与粘性问题被移除,自托管网关 + 无状态服务器的组合成为更自然的默认架构;
- 工具设计被推向「意图级」:handle 显式化后,把低层 API 原样包成 MCP 工具的做法代价上升(模型要自己管理一串 handle)。社区讨论中反复出现的建议是构建「能力服务器」——让工具表达领域意图,把 API 编排收进工具内部;
- 人在环流程标准化:MRTR 让「服务器向客户端确认」从各客户端的私有实现变成协议能力,跨客户端的审批流有了统一形状。
辩证看待:收益与代价
无状态化不是免费午餐,三处代价值得正视:
- 复杂度上移而非消失:状态管理从服务器协议层转移到了模型与工具层。模型需要正确携带 handle、理解 input_required 并构造重发请求——出错面从「连接层」转移到了「推理层」;
- 请求变长、重复声明:每个请求携带版本与能力信息,在高频调用量大的场景下有可度量的字节开销,对极端成本敏感的调用方并不友好;
- 存量迁移成本:依赖会话语义的既有服务器需要真正的重构而非改配置,十二个月废弃窗口对大型企业内部的 MCP 资产盘点来说并不宽裕。
但从协议演进的整体方向看,这次修订与 MCP 从开发工具协议走向企业基础设施的定位是一致的:它牺牲了一点单机开发场景的简洁,换取了多实例部署、网关治理与断点续跑三个生产级能力的标准化。对把智能体当基础设施建设的团队,这是一个值得跟进的版本。