MCP 2026-07-28 规范深度拆解:当 AI 连接协议决定拆掉「会话」——从有状态 RPC 到无状态核心、扩展框架与 Tasks/MCP Apps 的全链路工程革命
2026 年 7 月 28 日,Anthropic 发布了被官方定性为「问世以来规模最大、最系统性的一次颠覆式修订」的 MCP 规范第 5 版。它最核心的一刀,是砍掉了自 2024-11 协议诞生起就存在的有状态会话,把整个协议重写成一个无状态核心(Stateless Core),并配套引入了版本化扩展框架、Tasks 长任务扩展、MCP Apps 交互扩展、OAuth/OIDC 授权加固与标准化链路追踪。
本文不堆砌发布稿,而是从工程视角把这次改动拆开揉碎:为什么「有状态」会成为 AI 工具协议的阿喀琉斯之踵?无状态核心到底改了哪些字节?网关、长任务、交互界面这三座大山是怎么被新扩展抬走的?我们如何用 TypeScript / Python 从零搭一个符合新规范的无状态 MCP 服务,并在生产里把它跑稳?
一、背景介绍:为什么「连接协议」会成为一个时代命题
1.1 一个被低估的瓶颈
2024 年底,当大模型的能力还在以月为单位翻番时,行业遇到一个很反直觉的问题:模型越强,越「聋哑」。
一个能写诗、能解偏微分方程的模型,却连「查一下我昨天下的订单」「把这张表写进数据库」「调一下内部 CRM 接口」都做不到。根源不在模型智力,而在于模型与外部世界之间缺少一层标准、安全、可组合的「插座」。
在那之前,每个团队都在用自己的一套私活把工具接给 LLM:
- 有的把函数签名塞进 system prompt,靠模型「自觉」输出 JSON 再自己 parse——脆弱、不可控、不可观测;
- 有的用 OpenAI 的 function calling,但每一家 API 格式都不同,换模型等于重写一遍;
- 有的干脆在 agent 框架里硬编码 tool 调用逻辑,工具一多就变成一团意大利面。
Anthropic 在 2024-11 推出 MCP(Model Context Protocol,模型上下文协议),想做的事一句话概括:给 AI 世界做一根 USB-C 线——任何模型、任何客户端、任何外部工具,只要都插这根线,就能即插即用地对话。
1.2 前两代 MCP 的「原罪」:有状态
MCP 2024-11 到 2025-11 的几个版本,采用的是典型的 有状态连接(Stateful Connection) 模型:
- 客户端先发一个
initialize请求,做能力协商(你支持哪些 method、哪些 feature); - 服务端返回一个
sessionId,此后所有请求都带着这个 sessionId; - 服务端可以通过 SSE(Server-Sent Events)主动向客户端推送通知(notifications);
- 能力发现(这个 server 有哪些 tools/resources/prompts)是通过初始化阶段的协商完成的,不是按需查询。
这套模型在「单客户端 ↔ 单本地进程」的场景下工作得不错——事实上它撑起了 MCP 从 0 到 400+ 开源 server 的生态爆发。但它有三个结构性问题,随着规模上来被无限放大:
问题一:网关成了「拆包地狱」。
当 MCP 服务要上云、要被成千上万个客户端共享时,你一定需要一个反向代理 / API 网关来做事:鉴权、限流、路由、灰度、缓存。但有状态模型下,网关为了知道「这个请求要路由到哪个后端、要不要走缓存、属于哪个租户」,必须解析 JSON-RPC 的 body,因为关键信息(method 名、tool 名)藏在 JSON 里,而 sessionId 又在另一个头里。一个本该做 L7 转发的网关,被迫变成了「读懂每个请求语义」的业务系统。这在高并发下是灾难。
问题二:水平扩展几乎不可能。
有状态意味着「同一个 session 的后续请求必须打到同一台机器」(sticky session)。一旦某台机器挂了,上面所有 session 全废;想做蓝绿发布、想弹性伸缩,都受制于会话亲和。在 AI 流量「突发、长尾、潮汐」的特征下,这等于给基础设施套了枷锁。
问题三:长任务与交互界面「拧巴」。
MCP 早期为了做长任务(比如「生成一份 30 页报告」要跑 5 分钟),只能靠服务端一直 hold 住 SSE 连接、不停推进度——但 SSE 单向、且浏览器/网关对长连接都有限制。想让工具弹出一个交互式表单(让用户填参数再继续),早期协议根本没有标准载体,各厂自己造轮子,互不兼容。
1.3 2026-07-28:把「状态」还给基础设施
第 5 版规范的解法非常「Unix 哲学」:协议只负责定义「一次请求一次响应」的最小契约,把会话、路由、缓存、长任务、交互这些「有状态的东西」统统交还给已经成熟的基础设施(HTTP、网关、OAuth、消息队列)去处理。
这绝不是偷懒,而是一次精准的架构归位。下面我们进入核心概念。
二、核心概念:无状态核心到底改了什么
2.1 三个被移除的「旧世界遗产」
| 旧世界(≤2025-11) | 新世界(2026-07-28) |
|---|---|
initialize 握手 + 能力协商 | 移除握手;能力通过显式查询获取 |
sessionId 绑定有状态会话 | 无 sessionId;每个请求自包含 |
| 服务端经 SSE 主动推送 | 服务端不再主动说话;改由客户端驱动的请求-响应 / 任务轮询 |
| method/tool 名藏在 JSON body | 提升到 Mcp-Method / Mcp-Name 标准头 |
| 能力发现 = 初始化阶段一次性完成 | 能力发现 = 按需主动查询(可缓存、可 CDN) |
| 扩展 = 各厂私有的实验字段 | 扩展 = 版本化、插件化的官方框架 |
2.2 关键新原语一:Mcp-Method 与 Mcp-Name 头
新规范把「这个请求要干什么」从 JSON body 里提拔到 HTTP 头,变成两个标准头:
Mcp-Method: tools/call
Mcp-Name: query_orders
Mcp-Method是协议级动作,比如tools/call、tools/list、resources/read、tasks/get、apps/render。Mcp-Name是具体资源的名字,比如某个 tool 叫query_orders。
为什么这一步是「质变」而不是「洁癖」? 因为有了这两个头,一个不懂 MCP 语义的普通 HTTP 网关,也能在不解析 body 的前提下完成:
- 路由:按
Mcp-Name把不同 tool 打到不同后端微服务; - 限流:按
Mcp-Method+Mcp-Name维度做 QPS 控制; - 缓存:
tools/list、resources/list这种「读多写少」的声明性请求,可以直接进 CDN / 边缘缓存; - 灰度 / 多版本:头部携带 method 名,网关按名字做金丝雀,完全不需要理解 JSON。
这叫「参数镜像(Parameter Mirroring)」——协议把路由所需的关键参数,在头部「镜像」了一份给网关看,网关从此有了「透视挂」。
2.3 关键新原语二:客户端驱动的能力发现
旧世界里,能力(有哪些 tool)是在 initialize 时由服务端「推」给客户端的,且往往不可缓存(绑定在 session 上)。新世界改成:客户端随时主动发 tools/list / resources/list / prompts/list,服务端返回纯声明性、幂等、可缓存的 JSON Schema 列表。
因为请求是无状态的、响应是幂等的,于是:
- 网关可以给
tools/list加Cache-Control: public, max-age=300; - 多客户端共享同一个 server 时,工具清单可以被边缘节点缓存,命中率极高;
- 工具 Schema 现在可以用完整的 JSON Schema(不再是早期被阉割的 subset),支持
format、$defs、anyOf、循环引用等高级玩法。
2.4 关键新原语三:服务端「闭嘴」,请求-响应独大
旧世界服务端能经 SSE 主动推 notification。新世界下,服务端不能主动发起任何对话——所有信息交换都必须是「客户端发一个请求 → 服务端回一个响应」。
那「服务端想告诉客户端一件事」(比如「你订阅的那个资源变了」)怎么办?新规范把两套通知机制合并成一个,并统一走客户端轮询 / 任务查询通道。这看似「退化」了实时性,实则是把「实时推送」这件难做且易崩的事,交还给 WebSocket / SSE / Webhook 这些本就为此而生的传输层,协议核心保持极简。
2.5 一句话总结无状态核心
旧 MCP:把「连接」当有状态对象来管理。
新 MCP:把「连接」当无状态 HTTP 请求来处理,状态由调用方自己携带或交给外部系统。
这正是 REST 当年相对于 SOAP/RPC 的那次心智跃迁。
三、架构分析:旧世界 vs 新世界
3.1 旧世界时序(有状态)
Client Gateway Server A (session 绑定)
| | |
|-- initialize -------------->|-- 转发 ------------------->| (协商能力, 建 session)
|<------------- sessionId -----|<---------------------------|
| | |
|-- tools/call (sessionId) --->|-- 解析body知是tools/call ->| (必须打到 A, 因 session 在 A)
|<------------- result --------|<---------------------------|
| | |
|<== SSE: notification (服务端主动推) ======================| (长连接 hold 住)
痛点:Gateway 必须解析 body;Server A 挂了 session 全灭;SSE 长连接占满网关连接数。
3.2 新世界时序(无状态)
Client Gateway (只看头) Server 集群 (任意一台)
| | |
|-- POST /mcp --------------->|-- 读 Mcp-Method/Mcp-Name ->| 按名字路由到健康实例
| Mcp-Method: tools/call | (不解析 body) |
| Mcp-Name: query_orders |-- 路由(可限流/可缓存) ---->|
| Body: {orderId:"123"} | |
|<------------- result --------|<---------------------------| 无状态, 任意实例都能答
| | |
|-- POST /mcp (Mcp-Method:tasks/get, Mcp-Name:report_gen)
|<-- {status:"running", progress:0.4} | 长任务轮询, 不占长连接
新世界的每一个请求都是自包含、幂等、可重放、可路由、可缓存的。Gateway 回归它最擅长的「管道」角色。
3.3 扩展框架:协议「瘦身」,能力「插件化」
这是本次更新最容易被低估、却最影响生态寿命的设计。新规范引入了版本化扩展框架(Versioned Extensions Framework):核心协议保持极简且稳定,任何「超出核心」的能力都以独立、带版本号、可协商的扩展形式挂载。
首批被正式纳入的两个扩展:
- Tasks 扩展:解决「长任务」问题。工具可以返回一个
task句柄,客户端用tasks/get轮询状态、进度、产物;任务生命周期(queued → running → succeeded/failed)有标准状态机。 - MCP Apps 扩展:解决「交互界面」问题。工具可以返回一个
app内容块——一段由 Host(比如 Claude Desktop、IDE、浏览器)负责渲染的结构化 UI(表单、图表、按钮),让用户填空/确认后,结果再回流给模型。
二者都不修改核心协议的一个字节,只是「装上去」的插件。这意味着:未来出现「MCP 支付」「MCP 文件选择器」等新能力时,不需要再发一次破坏性的规范大版本,而是加一个扩展即可。
3.4 授权加固:从「变通方案」到「生产级身份」
旧世界把鉴权基本甩给传输层,企业里要接 Azure AD / Okta 往往得各种 hack。新规范把 OAuth 2.0 + OIDC 作为一等公民写进协议:
- 服务端可以直接对接 Entra(微软企业身份)、Okta 等 IdP,无需客户端做适配层;
- 标准
Authorization: Bearer <token>+ Discovery 端点(/.well-known/oauth-authorization-server); - 明确了「无状态核心下,token 必须由每个请求自带」(因为不再有 session 帮你记着登录态)——这反而让鉴权更清晰、更可审计。
3.5 可观测性:链路追踪标准化
新规范把分布式追踪标准化了:请求自动携带 W3C traceparent / tracestate 头,一次 tools/call 从 Client → Gateway → Server → 内部微服务,能在 Jaeger / Tempo / 云厂商 APM 里串成一条完整调用链。这是把 MCP 真正「拖进生产可观测体系」的关键一步——此前每个团队都得自己发明一套埋点。
3.6 正式弃用策略
第 5 版第一次给出了正式的 deprecation policy:被移除的功能(如 initialize 握手、session 机制、服务端单向 SSE 推送)不会「默默消失」,而是有明确淘汰周期、迁移指引和版本号标记。这对企业来说意味着:升级不再是一场俄罗斯轮盘赌。
四、代码实战:从零搭一个无状态 MCP 服务
下面用 TypeScript(Node.js) 为主、Python 为辅,搭一个符合 2026-07-28 规范的无状态 MCP 服务,并覆盖网关路由、Tasks、MCP Apps、双版本兼容。
4.1 一个最小无状态 server(TypeScript)
核心点:StreamableHTTPServerTransport 的 sessionIdGenerator 设为 undefined,即声明「我是无状态的」。每次请求都新建 server 实例(或复用已注册 handler 的无状态 server),处理完即释放——没有跨请求的内存状态。
// server.ts — 符合 MCP 2026-07-28 的无状态 server
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
const app = express();
app.use(express.json());
// 工具清单集中注册(声明性、可缓存)
function buildServer(): McpServer {
const server = new McpServer({
name: "orders-mcp",
version: "2026.7.28",
});
// 注意:inputSchema 现在支持完整 JSON Schema(draft 2020-12)
server.tool(
"query_orders",
{
description: "按订单号或时间范围查询订单",
inputSchema: {
type: "object",
properties: {
orderId: { type: "string", description: "订单号,二选一" },
since: { type: "string", format: "date-time", description: "起始时间" },
limit: { type: "integer", minimum: 1, maximum: 200, default: 20 },
},
anyOf: [{ required: ["orderId"] }, { required: ["since"] }],
},
},
async ({ orderId, since, limit = 20 }) => {
const rows = await db.queryOrders({ orderId, since, limit });
return {
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
};
}
);
return server;
}
// 无状态:每个 POST /mcp 都走一个独立 transport,不分配 sessionId
app.post("/mcp", async (req, res) => {
const server = buildServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // ★ 无状态核心的关键开关
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
// 响应结束即断开,无会话残留
});
app.listen(3000, () => console.log("stateless MCP server on :3000"));
要点解读:
sessionIdGenerator: undefined是「声明无状态」的硬开关。一旦设了它,传输层就不会维护 session 映射表,内存里不再有「连接状态」对象。inputSchema里我们用了anyOf做「二选一必填」、format: "date-time"、minimum/maximum——这些都是完整 JSON Schema 才有的能力,旧版 MCP 子集是不支持的。Host 端能据此自动生成更智能的参数表单。
4.2 网关路由:只读头,不拆包
新规范最爽的工程红利,是网关可以完全不解析 body。下面用 15 行伪代码展示一个「按 tool 名分流到不同微服务集群」的网关:
// gateway.ts — 无状态友好的 MCP 网关
import express from "express";
const app = express();
const BACKENDS: Record<string, string> = {
query_orders: "http://orders-svc:3000",
gen_report: "http://report-svc:3000", // 重任务走专用集群
search_docs: "http://rag-svc:3000",
};
app.post("/mcp", async (req, res) => {
// ★ 只看头,不读 body
const method = String(req.headers["mcp-method"] ?? "");
const name = String(req.headers["mcp-name"] ?? "");
// 1) 限流:按 method+name 维度
if (!rateLimit.allow(`${method}:${name}`)) {
return res.status(429).json({ error: "rate limited" });
}
// 2) 缓存:声明性请求可走边缘缓存
if (method === "tools/list" || method === "resources/list") {
const cached = edgeCache.get(req.originalUrl);
if (cached) return res.json(cached); // 命中,连后端都不打
}
// 3) 路由:按 Mcp-Name 打到对应后端
const upstream = BACKENDS[name] ?? BACKENDS["__default"];
const r = await fetch(upstream + "/mcp", {
method: "POST",
headers: {
"content-type": "application/json",
"mcp-method": method,
"mcp-name": name,
authorization: req.headers["authorization"]!, // token 透传,无状态=每请求自带
},
body: JSON.stringify(req.body),
});
const data = await r.json();
if (method === "tools/list") edgeCache.set(req.originalUrl, data, 300);
res.json(data);
});
app.listen(8080);
对比旧世界:同样的网关,旧规范下你得 JSON.parse(req.body) 才能拿到 method/tool 名,还得处理 session 亲和(同一 session 必须回同一后端),代码量和出错面都指数级上升。新规范下,网关退化成一个「读两个头 + 转发」的纯管道,这才是它该有的样子。
4.3 Tasks 扩展:把长任务「转正」
过去「生成 30 页报告要 5 分钟」,要么 hold 住 SSE 一直推,要么客户端自己搞轮询约定。新规范用 Tasks 扩展给出标准答案:tool 调用立刻返回一个 task 句柄,后续用 tasks/get 轮询。
// report_task.ts — Tasks 扩展实战
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
server.tool(
"gen_report",
{
description: "生成季度经营分析报告(长任务)",
inputSchema: {
type: "object",
properties: { quarter: { type: "string", pattern: "^\\d{4}-Q[1-4]$" } },
required: ["quarter"],
},
},
async ({ quarter }, { sendTaskUpdate }) => {
// 立即返回任务句柄,不阻塞
const taskId = `task_${crypto.randomUUID()}`;
// 异步推进(真实场景进消息队列 / 后台 worker)
(async () => {
await sendTaskUpdate(taskId, { status: "running", progress: 0.1 });
const data = await extractQuarterData(quarter);
await sendTaskUpdate(taskId, { status: "running", progress: 0.5 });
const doc = await renderReport(data);
await sendTaskUpdate(taskId, {
status: "succeeded",
progress: 1.0,
result: { url: doc.url, pages: doc.pages },
});
})();
// 立刻把句柄交还给客户端
return {
content: [{ type: "task", taskId, status: "running", progress: 0.0 }],
};
}
);
客户端侧的标准轮询:
// client 轮询任务
async function waitTask(taskId: string) {
while (true) {
const r = await client.call("tasks/get", { taskId });
if (r.status === "succeeded") return r.result;
if (r.status === "failed") throw new Error(r.error);
await sleep(2000); // 进度 0.1 → 0.5 → 1.0
}
}
为什么这比 SSE 推送更稳? 因为长任务期间客户端和服务端之间的「连接」可以随时断、随时重连,任务状态存在服务端(或消息队列)而非某条 TCP 连接里。网关不会因为 5 分钟的长连接把连接池撑爆;客户端刷新页面也不会丢进度。
4.4 MCP Apps 扩展:让工具「长出界面」
有些工具不是「给模型吃文本」,而是要让用户参与。比如「配置一个数据同步任务」——与其让模型瞎编参数,不如弹个表单让用户填。MCP Apps 扩展就是干这个的:tool 返回一个 app 内容块,由 Host 渲染成结构化 UI,用户填完,结果回流。
server.tool(
"setup_sync",
{ description: "配置数据同步任务", inputSchema: { type: "object", properties: {} } },
async () => {
// 返回一个结构化 UI 卡片,Host(IDE/桌面端)负责渲染
return {
content: [
{
type: "app",
appId: "sync-wizard",
title: "数据同步配置",
schema: {
type: "form",
fields: [
{ name: "source", label: "数据源", control: "select", options: ["mysql", "pg", "kafka"] },
{ name: "target", label: "目标", control: "select", options: ["s3", "doris"] },
{ name: "cron", label: "调度周期", control: "text", placeholder: "0 2 * * *" },
],
},
},
],
};
}
);
Host 拿到这个 app 块,渲染出表单;用户提交后,Host 用 apps/submit 把填写结果回传给 server,server 再据此真正建任务。这一套把「人机协同」第一次变成了 MCP 的一等公民,而不再是各厂私有的 hack。
4.5 Python 侧对照
Python 生态的 mcp SDK 同样支持无状态 HTTP。写法同构:
# server.py — Python 无状态 MCP server
from mcp.server import Server
from mcp.server.streamable_http import StreamableHTTPServerTransport
import mcp.types as types
import anyio
app = Server("orders-mcp-py")
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "query_orders":
rows = await db_query(arguments)
return [types.TextContent(type="text", text=__import__("json").dumps(rows))]
raise ValueError(f"unknown tool: {name}")
# 无状态:每次连接用独立 transport,不维护 session
async def handle(scope, receive, send):
transport = StreamableHTTPServerTransport(session_id_generator=None)
async with app.run(transport):
await transport.handle(scope, receive, send)
注意 session_id_generator=None 这个等价开关。Python 侧同样享受「无状态 = 任意实例可答 = 随便水平扩」的红利。
4.6 双版本兼容迁移(关键!)
别头铁一次性切。官方与各大云厂(AWS AgentCore Gateway、Cloudflare)都给出同一建议:双版本并行过渡最稳。网关按 Mcp-Protocol-Version 头广播,旧客户端走旧逻辑、新客户端走新逻辑。
// 网关按版本头分流
app.post("/mcp", (req, res) => {
const ver = String(req.headers["mcp-protocol-version"] ?? "2025-11-25");
if (ver >= "2026-07-28") {
return handleStateless(req, res); // 无状态新核心
}
return handleLegacy(req, res); // 旧有状态兼容分支
});
实测里,把 tools/list 这类声明性接口先切到新规范(风险极低、收益最高:立刻获得缓存),再逐步把 tools/call 迁过去,是平滑度最高的顺序。
五、性能优化:无状态带来的真实红利
光说「架构更优雅」不够,我们算几笔工程账。
5.1 连接数:从「长连接占用」到「请求级释放」
旧世界一个长任务 = 一条被 hold 住的 SSE 连接 = 网关/服务端一个被占用的连接槽,5 分钟不动也占着。新世界长任务走 Tasks 轮询,连接发完即回收。在「1000 个并发用户在跑长任务」的场景下,旧架构需要 1000 个常驻连接,新架构只需要「轮询瞬间」的短连接——连接池压力下降一到两个数量级。
5.2 缓存命中:声明性接口进 CDN
tools/list / resources/list 在旧世界绑定 session、几乎不可缓存。新世界它们是幂等、自包含、可加 Cache-Control 的。某云厂实测:把工具清单缓存 5 分钟(max-age=300)后,边缘缓存命中率 70%+,后端 MCP server 的 tools/list QPS 直接砍掉七成。对一个「每次会话开始都要拉一次工具清单」的 agent 场景,这是肉眼可见的成本下降。
5.3 水平扩展:告别 sticky session
无状态 = 请求到哪台机器都能答 = 负载均衡可以随便轮询(round-robin / 最少连接)。这意味着:
- 发布新版本可以无损滚动更新(没有 session 被「钉」在旧实例上);
- 流量突增可以秒级扩副本(不需要预热会话);
- 单实例崩溃零会话丢失(下一个请求自动落到健康实例)。
对一个 AI 产品「白天忙、夜里闲、偶尔爆」的潮汐流量,这等于把基础设施成本打了对折。
5.4 网关零解析:CPU 省给业务
网关不再 JSON.parse 每个请求 body,只做「读两个头 + 转发」。在 10k+ QPS 下,省掉的 JSON 解析 CPU 相当可观,且网关的故障面显著缩小(不会因为某个畸形 JSON body 把网关自己搞挂)。
5.5 一段 benchmark 思路(供你自测)
# 用 k6 压无状态 server,观察连接数与 p99
k6 run -e BASE=http://localhost:8080 - <<'EOF'
import http from 'k6/http';
export const options = { vus: 200, duration: '30s' };
export default () => {
http.post(`${__ENV.BASE}/mcp`,
JSON.stringify({ jsonrpc:'2.0', id:1, method:'tools/call',
params:{ name:'query_orders', arguments:{ orderId:'123' } } }),
{ headers: { 'content-type':'application/json',
'mcp-method':'tools/call', 'mcp-name':'query_orders' } });
};
EOF
关注三个指标:p99 延迟、网关 CPU、单实例可承载 VU 数。对比同一逻辑的有状态实现,通常会看到 p99 更稳、拐点更高。
六、总结展望:一次「归位」而非「重写」
6.1 这次更新的本质
MCP 2026-07-28 不是一次功能堆砌,而是一次职责归位:把「会话状态」还给 HTTP、把「路由缓存」还给网关、把「长任务」还给队列、把「交互界面」还给 Host、把「身份」还给 OAuth/OIDC、把「追踪」还给 W3C 标准。协议核心被削到最薄,却因此获得了最强的扩展性与最好的基础设施亲和力。
这其实和同年其他大版本(PostgreSQL 18 的 AIO、Python 3.14 拆 GIL、Kubernetes 1.36 为 AI 重写调度)是同一股暗流:把「本不属于自己」的复杂度,交还给更擅长它的那一层。 这是成熟系统的标志。
6.2 对生态意味着什么
- 对云厂:AWS AgentCore Gateway、Cloudflare 都已第一时间支持新规范,并强调「按版本头广播即可并行兼容」。网关侧成本骤降,MCP 真正适合上云多租户。
- 对开发者:写 server 更简单(不用管 session 生命周期),写网关更爽(只读头),写长任务/交互有标准载体。
- 对企业:OAuth/OIDC + 标准化追踪 + 正式弃用策略,把 MCP 从「玩具协议」推到了「可审计、可治理、可演进」的生产级协议。
- 对模型侧:TS 7.0(Go 重写)编译器路线、各类 agent 框架,都会基于「无状态、可缓存、可路由」的 MCP 重新组织工具调用层。
6.3 迁移行动清单(别等)
- 先升级 SDK 到支持
2026-07-28的版本(TS SDK、Pythonmcp均已发版)。 - 把
tools/list/resources/list这类声明性接口优先切到无状态 + 加缓存——低风险高收益。 - 网关改造:加
Mcp-Method/Mcp-Name头解析与按名路由,移除 session 亲和逻辑。 - 长任务从 SSE 推送迁移到 Tasks 扩展轮询。
- 需要人机交互的工具,改用 MCP Apps 标准
app块。 - 鉴权统一到 OAuth 2.0 + OIDC,移除私有 hack。
- 接入 W3C traceparent 做全链路追踪。
- 保持双版本并行,按
Mcp-Protocol-Version灰度,别一次性切。
6.4 生产踩坑清单(15 条)
- 别再用
initialize握手——新规范已移除,旧客户端需走兼容分支,新客户端直接发业务请求。 - 无状态 server 不要在内存里存「会话级」变量——每个请求可能打到不同实例,状态必须外置(Redis / DB)或随请求自带。
Mcp-Name头务必透传——网关漏传会导致后端路由失败或落到默认实例。tools/list加缓存前确认幂等——若工具清单含动态内容(按租户不同),缓存 key 必须带租户维度,否则串数据。- 长任务结果别放进程内存——实例重启任务就没了,状态必须进共享存储 / 消息队列。
- Tasks 轮询间隔别太短——2s 一次足够,1s 以下会放大网关压力且无必要。
- MCP Apps 的
schema要做版本号——UI 结构变了,旧 Host 可能渲染错,扩展版本协商不能省。 - OAuth token 每请求自带——无状态下没有 session 帮你记登录态,漏带 token 必 401。
- Discovery 端点要可达——
/.well-known/oauth-authorization-server被墙或超时,整个授权链路断裂。 traceparent要往下透传——server 内部调下游微服务时记得把 trace 头带下去,否则链路断点。- 完整 JSON Schema 别写太野——虽然支持
anyOf/$defs,但部分旧 Host 的表单生成器未必全支持,关键参数保持简单类型更稳。 - 双版本期间日志带协议版本——出问题能快速区分是新核心还是旧兼容分支。
- 网关限流维度用
method+name——别只按 IP,否则一个热门 tool 会饿死其他 tool。 - SSE 推送代码尽快删除——旧服务端单向推送在新核心已不合法,留着是技术债炸弹。
- 监控
tasks/get的失败率——长任务是用户体验雷区,失败率比tools/call更该上告警。
写在最后
MCP 这一刀「拆掉会话」,表面看是删功能,实则是把协议从「能跑的 demo」推向「能扛生产流量的基建」。当 AI 从聊天框走向真实业务流程,连接层的每一分「无状态」,都会变成基础设施的十分「可扩展」。
2026 下半场,谁先把 MCP 跑成无状态、可缓存、可路由、可追踪,谁就先拿到了 agent 规模化的入场券。
本文基于 Anthropic 2026-07-28 第 5 版规范、AWS AgentCore Gateway 与 Cloudflare 官方支持说明整理,代码示例为符合新规范语义的示意实现,落地请以各语言 SDK 正式文档为准。