MCP 转向无状态:把服务端迁移到 2026-07-28 规范
2026-07-28 版 Model Context Protocol 规范已于 7 月 28 日发布,在协议层把 MCP 变为无状态。initialize 握手与会话头被移除,elicitation 变成带 requestState 的多轮往返请求,路由改由 Mcp-Method 头承载,列表结果可缓存,一级 SDK 同步更新。本文给出迁移路径,附前后对比代码。
协议层的无状态化
一、头条:无状态的协议内核
MCP 最初是双向、有状态的协议:客户端初始化会话、协商能力,并在多次调用之间保持该会话存活。2026-07-28 规范把它变成请求/响应式协议。维护者将其描述为开发者呼声最高的一项改动——人们想要更好的可靠性与可扩展性——并且特意选择了一次干净的分割,让未来的修订无需重写传输层或生命周期代码即可演进。发布说明还引入了正式的弃用策略与扩展框架。实际效果是:水平扩展不再需要粘性路由与共享会话存储,而这两者恰是 MCP 部署中最常见的隐蔽生产事故来源之一。
// BEFORE 2026-07-28: a stateful initialize handshake
const session = await client.initialize({
protocolVersion: "2025-06-18",
clientInfo: { name: "my-agent", version: "1.0.0" },
capabilities: { elicitation: {} },
});
// client and server capabilities live on `session`
const tools = await session.request("tools/list", {});二、握手没了
initialize 握手被移除。客户端能力改为在每次请求中通过 _meta 与请求头声明,服务端能力则通过 server/discover 调用获取,而不再在连接时一次性协商。这是服务端作者最大的一处代码改动,而且是机械性的:你不再把协商状态存在会话对象上,而是从每次请求的元数据里读取协议版本与客户端信息。代码示例 1 是旧形态,代码示例 2 是新形态。回报是:同一客户端的两个请求可以落到不同实例而不出问题,因为已经没有会话可丢。如果你的服务端此前在调用之间保留了什么内存状态,现在它应该放进请求里、放进以请求为键的缓存里,或者放进外部存储。
// AFTER 2026-07-28: no handshake, per-request metadata
const meta = {
"protocol-version": "2026-07-28",
"client-info": { name: "my-agent", version: "1.0.0" },
"client-capabilities": { elicitation: {} },
};
const server = await client.request("server/discover", {}, { meta });三、Elicitation 变成多轮往返
交互式提示过去默认存在一个活跃会话。新规范用多轮往返请求流取代 elicitation。服务端不再保持会话开启,而是返回一个 InputRequiredResult 和一个唯一的 requestState 标识;客户端收集到用户输入后,发送一个携带原请求 id 与所收 requestState 的新请求,服务端据此重建自己当时在做什么。代码示例 3 勾勒了这一交互。它比实时提示多了一点仪式感,却稳健得多:这次交互能挺过服务端重启、滚动发布,或者负载均衡把后续请求送到另一个实例——这些都是有状态提示挺不过去的。
// Elicitation is now a multi round-trip request, not a live session.
const first = await client.request("tools/call", {
name: "book_flight",
arguments: { from: "SFO" },
});
if (first.InputRequiredResult) {
const { requestState, prompt } = first.InputRequiredResult;
const answer = await askUser(prompt);
const done = await client.request("tools/call", {
name: "book_flight",
arguments: { from: "SFO", returnDate: answer },
requestState, // server reconstructs its own state
requestId: first.id,
});
}四、请求头路由与可缓存列表
有两个特性悄悄改变了部署方式。第一,请求携带 Mcp-Method 头,并可通过 Mcp-Name 做基于名称的路由,网关因此无需解析请求体即可按方法转发;你的服务端应当接受并校验这些头。第二,列表结果现在可缓存:在 tools/list 与资源读取上输出 ttlMs 与 cacheScope,客户端与网关即可缓存。代码示例 4 给出了形态。两者结合,让一队完全相同的无状态实例可以坐在普通负载均衡之后,并把大部分发现类流量从缓存中服务出去。这正是这个版本真正关乎的运维差异:MCP 服务端现在长得像你其余的网络基础设施,而不再像一个只有某位工程师才看得懂的特殊长生命周期进程。
// Emit ttlMs and cacheScope so gateways and clients can cache discovery.
return {
tools: TOOLS,
ttlMs: 60_000,
cacheScope: "public",
};五、迁移你的服务端
升级分四步。第一,更新到会说 2026-07-28 的 SDK 版本;TypeScript、Python、Go、C# 的一级 SDK 已随规范同步更新。第二,删除握手与会话处理,改为每次请求从 _meta 读取协议版本与客户端信息。第三,在列表操作上输出 ttlMs 与 cacheScope,并校验 Mcp-Method 与 Mcp-Name。第四,把任何内存中的会话状态替换为请求作用域数据或外部缓存。代码示例 5 把整个服务端压缩为一个无状态处理器。破坏性变更是真实的:请把它当成「准备就绪」而非「今天全部拆掉」,在过渡窗口内同时运行两个协议版本,并借助新的弃用策略规划其余部分。授权加固也一并落地,所以顺手把 OAuth 流程也复查一遍。
// A whole MCP server in a Worker: no session store, no sticky routing.
export default {
async fetch(req, env) {
const method = req.headers.get("Mcp-Method");
const name = req.headers.get("Mcp-Name");
const meta = JSON.parse(req.headers.get("Mcp-Meta") || "{}");
const body = await req.json();
const handler = ROUTES[method];
if (!handler) return new Response("unknown method", { status: 404 });
return Response.json(await handler({ name, meta, body, env }));
},
};六、为什么这不只关乎 MCP
无状态是一种成熟度信号。一个需要粘性会话的协议,是你在边缘跑不了、无法廉价扩缩、出事时也说不清楚的协议。MCP 用了约十八个月成为 AI 与工具集成的事实标准,而这个版本正是标准长大成人后要做的事:删掉那些不能扩展的聪明设计。同一直觉也适用于你自己的 Agent 基础设施:如果技术栈中某个组件必须在调用之间记住什么,先问它是否真的应该。2026 年 7 月 28 日之后,答案越来越多是「不应该」。服务端渲染界面与长时间运行 Tasks 也作为正式扩展到来,这意味着 MCP 服务端真正有趣的表面已经变成用户界面与持久化工作,而不再是连接管理。
负载均衡后平滑扩缩
握手已成历史
📌 常见问题 FAQ
2026-07-28 版 MCP 规范是什么?
自发布以来最大的一次修订:在协议层把 MCP 变为无状态,移除 initialize 握手,新增多轮往返请求、基于请求头的路由、可缓存列表结果、授权加固、正式扩展框架与弃用策略。
它是破坏性变更吗?
是。规范于 2026 年 7 月 28 日发布且包含破坏性变更,因此早期文章应视为「准备就绪」的指引,而非要求你今天就把一切拆掉。
什么取代了 elicitation?
多轮往返请求流:服务端返回带唯一 requestState 标识的 InputRequiredResult,客户端再发送携带原请求 id 与该状态的后继请求,服务端据此重建自己的工作。
客户端现在如何声明能力?
通过在每次请求中携带 _meta 与请求头,而非一次性握手;服务端能力则通过 server/discover 调用获取。
哪些 SDK 已更新?
TypeScript、Python、Go、C# 的一级 SDK 已随规范发布;MCP 服务端现在可以只跑在一个 Cloudflare Worker 里,无需任何有状态基础设施。