2026年7月28日,MCP(Model Context Protocol)正式发布 2026-07-28 版本规范,官方称之为自发布以来最大规模的协议修订。本文搭建了新旧两套规范的服务端实例,通过真实 HTTP 报文拆解本次改版的核心变化、设计逻辑与生产迁移路径。
1. MCP 是什么 & 这次改版为什么是大地震
简单来说,MCP 是 AI 工具调用的"通用接口协议"------无论你使用的是 Claude、GPT 还是 Gemini 类大模型,都可以通过同一套协议标准调用外部工具、访问数据源与对接服务。
举一个最基础的调用示例:
JavaScript
// AI 客户端通过 MCP 协议调用搜索工具
const result = await client.callTool({
name: "search",
arguments: { q: "otters" }
});
自2025年底发布以来,MCP 迅速成为 AI Agent 生态的主流工具调用协议,GitHub、主流 AI 开发编辑器等均已原生支持,被大量企业用于内部 AI Agent 落地。Anthropic 已将协议捐赠给 Linux 基金会,推动其成为行业通用标准。
而本次 7/28 改版的核心,是彻底移除了协议层的有状态设计:旧版本依赖 initialize 握手 + Mcp-Session-Id 维持会话,所有请求必须绑定到固定服务实例;新版本转为完全无状态架构,每个请求自包含全部必要信息,任意实例均可独立处理。
这不是简单删一个字段的改动,它从底层改变了 MCP 服务的部署架构、网关路由方式、缓存策略与水平扩展模式。
2. 旧规范的设计与生产痛点
2.1 旧规范的完整请求流程
旧规范(2025-11-25)采用经典的"握手-会话"模式,和传统 Web 的 Cookie/Session 机制逻辑一致(注:HTTP 协议本身是无状态的,会话能力是应用层基于协议扩展实现的)。
完整请求时序如下:
Plaintext
客户端 服务端
│ │
│── POST /mcp ──────────────────────────→│
│ method: initialize │
│ params: { protocolVersion, │
│ capabilities, clientInfo } │
│ │
│←── 200 OK ─────────────────────────────│
│ Mcp-Session-Id: <UUID> ← 服务端分配会话
│ result: { 协议版本、服务端能力等 } │
│ │
│ 后续所有请求必须携带 Mcp-Session-Id │
│ │
│── POST /mcp ──────────────────────────→│
│ Mcp-Session-Id: <UUID> │
│ method: tools/list │
│ │
│←── 200 OK ─────────────────────────────│
│ result: { tools: [...] } │
│ │
│── POST /mcp ──────────────────────────→│
│ Mcp-Session-Id: <UUID> │
│ method: tools/call │
│ params: { name, arguments } │
│ │
│←── 200 OK ─────────────────────────────│
│ result: { content: [...] } │
2.2 生产环境三大核心痛点
这套设计在单实例场景下简单易用,但放到分布式生产环境中会带来三个难以回避的问题:
痛点一:负载均衡被会话绑定 若采用内存级 Session 存储,负载均衡必须配置 sticky session(粘性会话),将同一 Session 的请求固定路由到同一实例。一旦对应实例宕机,该实例上的所有会话会全部失效,客户端必须重新握手建立连接。
痛点二:水平扩展依赖共享存储 要解决单点故障问题,必须引入 Redis 等外部存储统一保存 Session 状态。每个请求都需要额外一次 Session 查询开销,同时还要处理 Session 过期、续期、存储宕机降级等一系列运维问题。
痛点三:网关路由必须解析请求体 如果要按方法做路由(比如把 tools/call 路由到执行集群、resources/list 路由到目录服务),网关必须解析 JSON-RPC 请求体才能拿到 method 字段。Nginx 原生不支持该能力,需要引入 Lua 或 NJS 扩展;即使是专业 API 网关,每次请求解析 JSON 也会带来额外性能开销与配置复杂度。
2.3 真实 HTTP 请求:旧规范实测
以下是基于旧规范实现的服务端真实请求与响应报文:
步骤1:initialize 握手请求
HTTP
POST /mcp HTTP/1.1
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1785172985193,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": { "name": "demo-client", "version": "1.0" }
}
}
握手响应(返回会话ID)
HTTP
HTTP/1.1 200 OK
Content-Type: application/json
Mcp-Session-Id: ce843e95-50bc-471f-bd8b-dcbcf75dc998
{
"jsonrpc": "2.0",
"id": 1785172985193,
"result": {
"protocolVersion": "2025-11-25",
"capabilities": { "tools": { "listChanged": true } },
"serverInfo": { "name": "old-mcp-server", "version": "1.0.0" }
}
}
步骤2:携带会话调用 tools/list
HTTP
POST /mcp HTTP/1.1
Mcp-Session-Id: ce843e95-50bc-471f-bd8b-dcbcf75dc998
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1785172985206,
"method": "tools/list",
"params": {}
}
步骤3:不带会话直接请求(被拒绝)
HTTP
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 4,
"error": {
"code": -32600,
"message": "Missing or invalid Mcp-Session-Id"
}
}
可以看到,旧规范下会话是一切请求的前提,没有有效 Session 连工具列表都无法查询。
3. 新规范核心:协议层无状态化
3.1 核心变更总览
新规范从协议层面彻底移除了会话机制,核心变化可以总结为三点:
| 旧规范(2025-11-25) | 新规范(2026-07-28) | 架构影响 |
|---|---|---|
| 必须先 initialize 握手 | 无握手,直接发请求 | 连接成本降低,支持短连接请求 |
| 所有请求携带 Mcp-Session-Id | 无会话ID,请求自包含 | 任意实例可处理任意请求,支持轮询负载均衡 |
| clientInfo 在握手阶段传递 | clientInfo 放在请求 params._meta 中 | 每个请求独立携带上下文,不依赖服务端存储 |
通俗来讲:旧规范是"先登记开户,再凭号办事";新规范是"带齐材料直接办,谁接都能处理"。
3.2 真实 HTTP 请求:新规范实测
以下是基于新规范实现的服务端真实请求与响应报文:
步骤1:发送 initialize 握手(直接被拒绝)
HTTP
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1785172986531,
"method": "initialize",
"params": {}
}
响应(明确告知方法已移除):
HTTP
HTTP/1.1 200 OK
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1785172986531,
"error": {
"code": -32601,
"message": "initialize handshake removed in 2026-07-28"
}
}
注:JSON-RPC 协议标准下,方法不存在属于业务层面错误,通常返回 HTTP 200 状态码 + 错误体;部分实现会使用 400 状态码,二者均被兼容。
步骤2:直接请求 tools/list(无会话)
HTTP
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/list
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1785172986550,
"method": "tools/list",
"params": {}
}
响应(携带缓存控制信息):
HTTP
HTTP/1.1 200 OK
Content-Type: application/json
MCP-Tool-Cache: ttlMs=300000; cacheScope=shared
{
"jsonrpc": "2.0",
"id": 1785172986550,
"result": {
"tools": [
{
"name": "search",
"description": "通用文本搜索工具",
"inputSchema": {
"type": "object",
"properties": {
"q": { "type": "string", "description": "搜索关键词" }
},
"required": ["q"]
}
}
],
"_meta": {
"cacheControl": {
"ttlMs": 300000,
"cacheScope": "shared"
}
}
}
}
步骤3:调用工具(_meta 携带客户端信息)
HTTP
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1785172986551,
"method": "tools/call",
"params": {
"name": "search",
"arguments": { "q": "otters" },
"_meta": {
"io.modelcontextprotocol.clientInfo": {
"name": "demo-client",
"version": "1.0"
}
}
}
}
整个流程没有任何会话依赖,每个请求独立完整,打到任意一个服务实例都能正常处理。
3.3 头体一致性校验机制
新规范新增了 HTTP 头与请求体的一致性校验:Mcp-Method 头的值必须和 JSON-RPC body 中的 method 字段完全一致,Mcp-Name 头的值必须和 params.name 字段一致。
如果二者不匹配,服务端会直接拒绝请求:
HTTP
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1785172986552,
"error": {
"code": -32602,
"message": "Header Mcp-Method (tools/call) disagrees with body method (tools/list)"
}
}
这个设计的核心作用是保障网关路由的正确性:网关按头路由到对应处理集群后,服务端再做一次校验,避免路由错误导致请求被错误处理。
4. Mcp-Method 头:网关层的协议友好设计
4.1 新旧路由方式对比
旧规范下,网关要实现按方法路由,必须深度解析请求体:
Plaintext
收到请求 → 读取完整Body → 解析JSON → 提取method字段 → 路由到对应后端
Nginx 原生不支持该能力,必须引入第三方模块,配置复杂且性能有损耗。
新规范下,方法名直接放在 HTTP 头中,网关只需读取头字段即可路由:
Plaintext
收到请求 → 读取 Mcp-Method 头 → 路由到对应后端
Nginx 原生配置示例:
Nginx
map $http_mcp_method $mcp_backend {
"tools/list" backend_catalog;
"tools/call" backend_executor;
default backend_default;
}
server {
location /mcp {
proxy_pass http://$mcp_backend;
proxy_set_header Host $host;
}
}
4.2 不止是路由:生产级附加价值
把方法名放到头里,带来的收益远不止配置简化:
-
可观测性 :日志、监控系统可以直接记录
Mcp-Method字段,无需解析请求体就能统计每个方法的调用量、耗时与错误率。 -
安全管控:WAF、网关可以直接按方法做限流、权限拦截与风控策略,无需引入 JSON 解析能力。
-
缓存适配 :CDN 与网关缓存可以直接用
Mcp-Method作为缓存 Key 的一部分,适配只读类接口的缓存策略。
5. tools/list 原生缓存:减少重复请求
5.1 旧规范的问题
旧规范中 tools/list 没有任何缓存机制,客户端每次需要确认工具列表时都要发起请求。但实际生产中,大多数 MCP 服务的工具列表更新频率很低(可能几天甚至几周才变更一次),大量重复请求属于无效开销。
在高并发 AI Agent 场景下,tools/list 甚至会成为占比最高的请求,占用不必要的服务资源。
5.2 新规范的缓存机制
新规范在协议层面原生支持缓存控制,tools/list 等只读接口可以同时通过两种方式返回缓存策略:
-
HTTP 响应头 ****
MCP-Tool-Cache:方便网关、CDN 直接读取处理 -
响应体 ****
_meta.cacheControl:方便客户端业务层读取使用
两个核心字段含义:
-
ttlMs:缓存有效期,单位毫秒 -
cacheScope:缓存范围,shared表示可跨客户端共享,private表示仅单客户端可用
5.3 缓存优先级与边界
-
优先级:HTTP 头 > 响应体字段,二者不一致时以响应头为准。
-
适用范围:仅幂等的只读类接口(如
tools/list、resources/list)支持缓存;写入类、执行类接口不允许缓存。 -
失效机制:服务端更新工具列表后,可通过调整
ttlMs控制生效时间;客户端也可主动跳过缓存重新请求。
6. 两大扩展能力适配无状态改造
6.1 MCP Apps:服务端渲染交互式UI
MCP Apps(SEP-1865)是本次新增的能力,允许服务端返回交互式 HTML 界面,由客户端在沙箱 iframe 中渲染,替代旧规范的 Roots 能力。
核心设计:
-
UI 模板提前声明:工具在
tools/list阶段就声明自己的 UI 模板地址,客户端可以预取、缓存与安全审查。 -
沙箱隔离运行:HTML 在受限 iframe 中执行,无法直接访问宿主环境与用户数据。
-
统一审计路径:所有 UI 触发的操作仍然走标准 JSON-RPC 调用流程,经过客户端的权限确认与审计。
适用场景包括数据可视化仪表盘、复杂表单输入、向导式多步操作等。
6.2 Tasks:长任务从长连接改为轮询
旧规范中,长时间运行的任务通过 SSE 长连接流式推送状态,天然和单个服务实例绑定,和有状态会话深度耦合。
新规范将 Tasks 重构为无状态轮询模型:
Plaintext
客户端 服务端
│ │
│── tools/call 触发任务 ────────────────→│
│ │
│←── 返回 taskHandle 任务句柄 ────────────│
│ │
│ 客户端按间隔轮询任务状态 │
│── tasks/get?handle=xxx ───────────────→│ 可打到任意实例
│←── { status: running, progress: 40 } ──│
│ │
│── tasks/get?handle=xxx ───────────────→│ 可打到不同实例
│←── { status: completed, result: ... } ──│
任务状态统一存储在共享存储(Redis/数据库)中,任意实例都可以查询任务状态,完全摆脱了实例绑定。
7. 6 个 SEP 协同实现无状态化
本次无状态化改造不是单个变更,而是由 6 个 SEP(规范增强提案)共同配合完成的完整体系:
| SEP 编号 | 名称 | 核心作用 | 变更类型 | 破坏性 |
|---|---|---|---|---|
| SEP-2575 | 移除 initialize 握手 | 取消握手流程,clientInfo 移入请求 _meta | 核心协议变更 | 是 |
| SEP-2567 | 移除 Mcp-Session-Id | 删除协议层会话机制,请求完全自包含 | 核心协议变更 | 是 |
| SEP-2243 | Mcp-Method / Mcp-Name 头 | 方法与工具名提升到 HTTP 头,支持网关原生路由 | 核心协议变更 | 是 |
| SEP-2549 | 可缓存结果规范 | 定义统一的缓存控制字段与头格式 | 兼容新增 | 否 |
| SEP-2322 | 多轮往返请求(MRTR) | 服务端需要用户输入时返回不透明状态令牌,替代 SSE 长连接交互 | 核心协议变更 | 是 |
| SEP-2663 | Tasks 扩展重构 | 长任务改为句柄轮询模式,适配无状态架构 | 扩展能力变更 | 是(使用Tasks的服务) |
完整逻辑链路:删握手 → 去会话 → 网关可路由 → 接口可缓存 → 交互无长连接 → 长任务无绑定 → 全链路无状态。
除此之外,本次更新还包含授权硬化(OAuth 2.1 + PKCE 强制要求)、废弃 Roots/Sampling/Logging 三项旧能力、JSON Schema 版本升级等配套变更。
8. 迁移指南与生产落地建议
8.1 必须修改的 5 项核心变更
| 序号 | 变更点 | 迁移方式 |
|---|---|---|
| 1 | 移除 initialize 握手 | 删除客户端握手逻辑,直接发起业务请求 |
| 2 | 移除 Mcp-Session-Id | 删除会话管理、续期、失效处理逻辑 |
| 3 | 新增协议版本头 | 每个请求携带 MCP-Protocol-Version: 2026-07-28 |
| 4 | 新增路由头 | 推荐每个请求携带 Mcp-Method、Mcp-Name 头 |
| 5 | clientInfo 位置变更 | 从握手参数移入每个请求的 params._meta 中 |
8.2 废弃功能与迁移窗口
以下功能设置了 12 个月的兼容过渡期,过渡期后将正式移除:
-
Roots → 迁移至 MCP Apps
-
Sampling → 迁移至 MCP Apps + Tasks 组合方案
-
Logging → 迁移至 OpenTelemetry 等标准可观测方案
-
HTTP+SSE 传输 → 迁移至标准 HTTP 请求 + 轮询/流式响应模式
8.3 生产级双版本兼容方案
不建议生产环境一刀切升级,推荐通过版本头做兼容:
-
网关读取
MCP-Protocol-Version头,存在且为新版本则路由到新服务集群。 -
缺失版本头则默认走旧规范逻辑,兼容存量客户端。
-
业务逻辑层抽成公共模块,新旧协议层分别做请求解析与响应封装。
8.4 无状态后的业务会话方案
协议层移除会话,不代表业务不能有会话:
-
客户端生成业务会话 ID,放在请求
_meta中传递。 -
服务端基于业务会话 ID 从共享存储读取上下文,不依赖协议层 Session。
-
鉴权信息通过
Authorization头携带,每个请求独立校验。
8.5 迁移检查清单
-
删除 initialize 握手与会话管理代码
-
所有请求添加协议版本头与方法头
-
clientInfo 移入每个请求的 _meta 字段
-
SSE 长连接交互迁移为 MRTR 或 Tasks 轮询
-
检查并迁移 Roots / Sampling / Logging 废弃能力
-
inputSchema 升级为 JSON Schema 2020-12 兼容
-
错误码对齐 JSON-RPC 标准码
-
远程部署服务接入 OAuth 2.1 + PKCE 授权
9. 实战:新规范服务端完整实现
以下是符合 2026-07-28 规范的最小可用服务端代码,包含所有必要的校验与错误处理:
JavaScript
import http from 'node:http';
const PORT = 3102;
const PROTOCOL_VERSION = '2026-07-28';
const MAX_BODY_SIZE = 1024 * 1024; // 1MB 请求体限制
// 工具定义
const tools = [
{
name: 'search',
description: '通用文本搜索工具',
inputSchema: {
type: 'object',
properties: {
q: { type: 'string', description: '搜索关键词' }
},
required: ['q']
}
}
];
// 统一响应工具
function sendJson(res, statusCode, id, resultOrError) {
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
jsonrpc: '2.0',
id: id ?? null,
...resultOrError
}));
}
function sendError(res, statusCode, id, code, message) {
sendJson(res, statusCode, id, {
error: { code, message }
});
}
const server = http.createServer((req, res) => {
// 仅处理 POST /mcp
if (req.url !== '/mcp') {
return sendError(res, 404, null, -32601, 'Endpoint not found');
}
if (req.method !== 'POST') {
return sendError(res, 405, null, -32601, 'Method not allowed');
}
// 校验 Content-Type
const contentType = req.headers['content-type'];
if (!contentType || !contentType.includes('application/json')) {
return sendError(res, 415, null, -32600, 'Unsupported Media Type');
}
// 校验协议版本
const protocolVersion = req.headers['mcp-protocol-version'];
if (!protocolVersion || protocolVersion !== PROTOCOL_VERSION) {
return sendError(res, 400, null, -32600,
`Unsupported protocol version. Expected ${PROTOCOL_VERSION}`);
}
// 读取请求体,限制大小
let body = '';
let bodySize = 0;
req.on('data', chunk => {
bodySize += chunk.length;
if (bodySize > MAX_BODY_SIZE) {
req.destroy();
return sendError(res, 413, null, -32600, 'Payload too large');
}
body += chunk;
});
req.on('end', () => {
// 解析 JSON
let json;
try {
json = JSON.parse(body);
} catch (e) {
return sendError(res, 400, null, -32700, 'Parse error');
}
const { id, method, params = {} } = json;
// 校验 Mcp-Method 头一致性
const headerMethod = req.headers['mcp-method'];
if (headerMethod && headerMethod !== method) {
return sendError(res, 400, id, -32602,
`Header Mcp-Method (${headerMethod}) disagrees with body method (${method})`);
}
// 路由处理
switch (method) {
case 'initialize':
return sendError(res, 200, id, -32601,
'initialize handshake removed in 2026-07-28');
case 'server/discover':
return sendJson(res, 200, id, {
result: {
serverInfo: { name: 'demo-mcp-server', version: '1.0.0' },
protocolVersion: PROTOCOL_VERSION,
capabilities: { tools: {} }
}
});
case 'tools/list':
res.setHeader('MCP-Tool-Cache', 'ttlMs=300000; cacheScope=shared');
return sendJson(res, 200, id, {
result: {
tools,
_meta: {
cacheControl: { ttlMs: 300000, cacheScope: 'shared' }
}
}
});
case 'tools/call': {
const { name, arguments: args } = params;
// 校验 Mcp-Name 头一致性
const headerName = req.headers['mcp-name'];
if (headerName && headerName !== name) {
return sendError(res, 400, id, -32602,
`Header Mcp-Name (${headerName}) disagrees with body name (${name})`);
}
// 校验工具是否存在
const tool = tools.find(t => t.name === name);
if (!tool) {
return sendError(res, 200, id, -32601, `Tool not found: ${name}`);
}
// 基础参数校验
if (!args || typeof args.q !== 'string' || args.q.trim() === '') {
return sendError(res, 200, id, -32602, 'Invalid params: q is required');
}
// 执行工具逻辑
return sendJson(res, 200, id, {
result: {
content: [
{ type: 'text', text: `搜索结果:找到 3 条关于 "${args.q}" 的内容` }
]
}
});
}
default:
return sendError(res, 200, id, -32601, `Method not found: ${method}`);
}
});
});
server.listen(PORT, () => {
console.log(`MCP Server running at http://localhost:${PORT}/mcp`);
console.log(`Protocol version: ${PROTOCOL_VERSION}`);
});
10. 新旧规范核心差异总览
| 维度 | 旧规范 2025-11-25 | 新规范 2026-07-28 |
|---|---|---|
| 连接模式 | 先握手,后会话绑定 | 无握手,直接请求 |
| 会话机制 | 强制携带 Mcp-Session-Id | 无协议层会话 |
| 网关路由 | 需解析 JSON Body | 读取 Mcp-Method 头即可 |
| 客户端信息 | 握手时一次性传递 | 每个请求 _meta 自包含 |
| 工具列表缓存 | 无原生支持 | 协议级 ttl + 作用域控制 |
| 负载均衡 | 必须粘性会话 | 轮询即可,无实例绑定 |
| 长任务实现 | SSE 长连接绑定实例 | 任务句柄轮询,无状态 |
| 交互UI | Roots 客户端渲染 | MCP Apps 服务端渲染,沙箱隔离 |
| 授权 | 基础 OAuth,可选 PKCE | 强制 OAuth 2.1 + PKCE |
| JSON Schema | draft-07 | 2020-12 |
| 错误码 | 大量自定义错误码 | 回归 JSON-RPC 标准码 |
| 扩展机制 | 内置核心功能 | 独立扩展,反向DNS标识 |
11. 常见面试考点
Q1:MCP 2026-07-28 无状态化的核心设计是什么?
核心是彻底移除协议层会话机制:取消 initialize 握手,删除 Mcp-Session-Id,每个请求自包含全部必要信息。由此服务端任意实例都可以处理任意请求,负载均衡不再需要粘性会话,水平扩展能力大幅提升。
Q2:Mcp-Method 头的设计价值是什么?
将方法名从请求体提升到 HTTP 头,让网关、负载均衡、CDN、WAF 等基础设施无需解析 JSON 就能识别请求类型,原生支持路由、限流、缓存、监控等能力,大幅降低了 MCP 服务的生产部署门槛。
Q3:为什么 Tasks 要从 SSE 改成轮询模式?
旧方案 SSE 长连接天然绑定单个服务实例,和有状态会话深度耦合,无法适配无状态架构。改为任务句柄 + 轮询模式后,任务状态存储在共享存储中,任意实例都可查询,完全支持水平扩展与故障转移。
Q4:无状态化后业务需要会话怎么办?
协议层移除会话,不代表业务不能维护会话。业务可以通过客户端传递业务会话ID、服务端从共享存储读取上下文的方式实现会话能力,只是这部分逻辑下沉到业务层,不再由协议强制约束,架构更灵活。
Q5:本次改版的破坏性变更有哪些?
核心破坏性变更包括:移除 initialize 握手、移除 Mcp-Session-Id、新增强制协议版本头、clientInfo 位置变更、MRTR 替代 SSE 交互、Tasks 重构。废弃功能有 12 个月过渡期,不属于立即破坏性变更。
参考资料
-
MCP 官方规范文档:modelcontextprotocol.io
-
SEP-2575 Remove Initialize Handshake
-
SEP-2567 Remove Session ID
-
SEP-2243 HTTP Method Headers
-
SEP-2549 Cacheable Results
-
SEP-2322 Multi-Round Trip Requests
-
SEP-2663 Tasks Extension
-
SEP-2352 Authorization Hardening