2026-07-28 无状态模型与版本协商
学习目标
- 明确 2026-07-28 的现代 MCP 没有 initialize/initialized 握手和协议 Session。
- 说明每请求元数据如何让 HTTP 请求在副本之间自由路由。
- 区分协议版本协商、能力声明和可选扩展协商。
- 了解双时代实现如何兼容旧版 initialize 客户端,同时不把旧流程写成现代核心。
前置知识
需要阅读 JSON-RPC 请求、响应、通知与 _meta,知道 params._meta 是每请求的协议信封。
核心知识点
现代无状态到底意味着什么
2026-07-28 的协议规定:Server 处理一个请求时所需的协议上下文必须在该请求中提供。协议不从同一连接、同一进程或同一 HTTP worker 的先前请求推断:
- 版本在 io.modelcontextprotocol/protocolVersion 中;
- Client 能力在 io.modelcontextprotocol/clientCapabilities 中;
- Client 身份可在 io.modelcontextprotocol/clientInfo 中;
- 业务状态必须通过显式参数、句柄或安全的 requestState 传递。
无状态不代表业务系统不能有数据库、购物车或缓存。它只表示这些状态不是隐藏在 MCP 协议连接里的。Server 可以发一个不透明的 basket_id,让模型在下一次 tools/call 的 arguments 中带回来,再按当前请求的授权主体验证这个句柄。
明确不再使用的现代握手
现代 2026-07-28 的正常数据流不包含:
- Client 先发送 initialize;
- Server 回复 initialize;
- Client 发送 initialized;
- 后续请求携带协议 Session。
initialize、initialized 和 Mcp-Session-Id 属于旧版兼容时代。双时代 SDK 可以在检测到旧 Server 时回退,但新设计不应要求一条现代请求必须先经过这些步骤。
版本协商的两个入口
现代 Client 有两种合法策略:
- 先发现:先发送 server/discover,读取 supportedVersions,再选择共同版本;
- 内联尝试:直接发送业务请求;若收到 UnsupportedProtocolVersionError(-32022),从 supported 列表挑一个版本并重试。
server/discover 是 Server 必须实现的方法,但 Client 是否先调用它是可选的。stdio 双时代 Client 通常应先探测,因为 stdio 没有 HTTP 状态码;HTTP 可以根据 400 响应中的现代 JSON-RPC 错误判断。
每请求版本与 HTTP 头
Streamable HTTP 的每个 POST 都要带 MCP-Protocol-Version,并且必须与 JSON body 中 _meta 的 protocolVersion 完全一致。现代请求还使用 Mcp-Method,以及 tools/call、resources/read、prompts/get 等方法需要的 Mcp-Name。body 是语义来源,header 只是可路由的镜像;不一致时 Server 返回 400 和 HeaderMismatch(-32020)。
现代、旧版和双时代
| Client | Server | 结果 |
|---|---|---|
| Modern | Modern | 每请求元数据,server/discover 可选 |
| Modern | Legacy | 现代请求失败;Client 应给出可诊断错误 |
| Dual-era | Modern | 走现代请求 |
| Dual-era | Legacy | 探测失败后回退 initialize |
| Legacy | Modern-only | 失败;旧 Client 没有向前升级机制 |
旧客户端仍然有价值,但文章中的新代码应以现代路径为默认,把 fallback 写成兼容层。
无状态路由图
这张图的关键是 B 不需要读取 A 的“连接 Session”。若请求要继续一个任务,参数中必须有业务句柄或经过保护的 requestState;不能指望负载均衡器把它送回 A。
用纯函数选择共同版本
用途:代码模拟一个现代 Server 给出的 supportedVersions 和 Client 的候选版本。真正的传输、重试和 HTTP 头校验由 SDK 或网关实现,这里只验证协商决策。
def choose_version(requested: str, supported: list[str]) -> str:
if requested in supported:
return requested
raise ValueError(
"UnsupportedProtocolVersionError: "
f"requested={requested}, supported={supported}"
)
server_versions = ["2026-07-28", "2025-11-25"]
print(choose_version("2026-07-28", server_versions))
try:
choose_version("2030-01-01", server_versions)
except ValueError as exc:
print(exc)输出:
2026-07-28
UnsupportedProtocolVersionError: requested=2030-01-01, supported=['2026-07-28', '2025-11-25']注意:收到 -32022 后,Client 应从服务端提供的 supported 列表选择,而不是猜一个版本或把失败请求当作成功。
Tasks 的位置
Tasks 是 2026-07-28 的可选扩展 io.modelcontextprotocol/tasks,不是本文的核心生命周期。需要长时间运行时,Client 和 Server 必须显式声明扩展,按 tasks/get、tasks/update、tasks/cancel 的扩展语义工作;不声明时不要把 task 当成普通 complete 结果。MCP 的核心仍然是一次请求、一次结果或 MRTR。
术语与白话
Stateless
术语:每个请求独立携带协议上下文,Server 不依赖前一请求建立语义。 白话:每张快递单都写清收件地址和内容,换一个分拣员也能处理。
Protocol Session
术语:旧版 MCP 用 initialize 和 Session ID 建立的连接级协议状态。 白话:旧版的“先办会员卡再取货”;2026-07-28 现代核心不再办这张卡。
Version Negotiation
术语:Client 与 Server 从支持集合中选择共同协议版本。 白话:先确认双方说哪种方言,不是确认业务是否有权限。
Extension
术语:在核心协议之外通过 capabilities 显式声明的可选功能。 白话:双方都装了同一个插件才能用;只一方支持时要降级或拒绝。
易混点
无状态不是“禁止存储”
数据库、缓存、显式状态句柄都可以存在。禁止的是把连接、进程或隐含 Session 当作请求之间唯一关联。
server/discover 不是现代 initialize
server/discover 是一个普通 Request,拥有自己的 _meta 和 id。它可以帮助 Client 预先了解版本、能力和 instructions,但不建立协议 Session,也不是必需的前置握手。
能力声明不是版本协商
版本回答“消息字段和语义如何解释”;capabilities 回答“这次请求允许用哪些功能”。即使双方版本相同,未声明的能力也不能使用。
HTTP stateless_http 不是协议定义
某个 SDK 的 stateless_http 配置可能影响旧版 HTTP Session 的服务方式;现代 2026-07-28 请求在协议层天然不依赖 Mcp-Session-Id。不要把一个 SDK 配置名误当作协议语义。
课后小问(含解析)
问题 1:现代 Client 能不能先发 initialize 再调用工具?
解析:如果 Server 是双时代实现,可能接受 initialize 并进入旧时代;但这不是现代 2026-07-28 核心流程。现代 Client 应发送带 _meta 的请求,或使用 server/discover 探测。
问题 2:为什么版本不支持时不能静默使用最新版本?
解析:版本差异可能包含错误码、Session、服务器反向请求、字段和传输头的重大变化。必须使用 Server 声明的 supported 列表,否则会把不同语义的消息当成同一种。
小结
2026-07-28 的现代 MCP 取消了连接握手和协议 Session:每个请求携带版本、Client 能力和可选身份,Server 可被普通负载均衡器横向路由。server/discover 是可选的主动发现入口,-32022 允许内联协商;旧 initialize 只保留在双时代兼容路径。
快速回顾
- 现代核心:无 initialize/initialized、无 Mcp-Session-Id、每请求自描述。
- 业务状态用显式句柄或保护后的 requestState。
- server/discover 必须实现,Client 调用可选。
- HTTP body 的协议版本必须和 MCP-Protocol-Version 一致。
- Tasks 是可选 extension,不是核心生命周期。
阿源的知识库