云计算百科
云计算领域专业知识百科平台

MCP 7/28 最大改版实测:扒开真实 HTTP 请求,无状态化到底改了什么

2026年7月28日,MCP(Model Context Protocol)正式发布 2026-07-28 版本规范,官方称之为自发布以来最大规模的协议修订。本文搭建了新旧两套规范的服务端实例,通过真实 HTTP 报文拆解本次改版的核心变化、设计逻辑与生产迁移路径。


1. MCP 是什么 & 这次改版为什么是大地震

简单来说,MCP 是 AI 工具调用的“通用接口协议”——无论你使用的是 Claude、GPT 还是 Gemini 类大模型,都可以通过同一套协议标准调用外部工具、访问数据源与对接服务。

举一个最基础的调用示例:

// 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 协议本身是无状态的,会话能力是应用层基于协议扩展实现的)。

完整请求时序如下:

客户端 服务端
│ │
│── 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 握手请求

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/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

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/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 握手(直接被拒绝)

POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Content-Type: application/json

{
"jsonrpc": "2.0",
"id": 1785172986531,
"method": "initialize",
"params": {}
}

响应(明确告知方法已移除):

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(无会话)

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/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 携带客户端信息)

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/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 新旧路由方式对比

旧规范下,网关要实现按方法路由,必须深度解析请求体:

收到请求 → 读取完整Body → 解析JSON → 提取method字段 → 路由到对应后端

Nginx 原生不支持该能力,必须引入第三方模块,配置复杂且性能有损耗。

新规范下,方法名直接放在 HTTP 头中,网关只需读取头字段即可路由:

收到请求 → 读取 Mcp-Method 头 → 路由到对应后端

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 重构为无状态轮询模型:

    客户端 服务端
    │ │
    │── 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 规范的最小可用服务端代码,包含所有必要的校验与错误处理:

    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

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » MCP 7/28 最大改版实测:扒开真实 HTTP 请求,无状态化到底改了什么
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!