MCP 无状态化:Google 重塑 Agent 基础设施

Google 主导了 MCP 协议自发布以来最大的规范变更——彻底移除有状态会话。2026-07-28 规范如何让 MCP 真正适配云原生架构。

上周二凌晨两点,我的告警响了。一个 MCP 网关节点崩溃,带走了 3000 个活跃的 Agent 会话。Redis 里确实存着会话数据,但粘性路由层来不及重新分配连接。等我们恢复时,几十个长时间运行的 Agent 工作流已经静默失败了。

盯着事故复盘报告,我意识到:会话本身就是问题所在。

Google 比我们更早得出了同样的结论。8 月 5 日,Google Cloud 发布了他们在 2026-07-28 MCP 规范更新上的工作成果。核心变化只有一句话:MCP 现在是无状态的。

没有 Mcp-Session-Id,没有握手流程,没有粘性路由。每个请求自包含所有信息。

原来的问题:有状态 MCP 是云原生的反模式

如果你在生产环境部署过 MCP 服务器,你一定体会过那种痛苦。原始协议要求一个会话生命周期:

  1. 客户端发送 initialize 请求
  2. 服务端返回能力集和 Mcp-Session-Id
  3. 客户端在后续每个请求中携带该会话 ID
  4. 服务端维护每个会话的状态(协商的能力、上下文等)

这种设计对本地 stdio 连接是合理的——IDE 和语言服务器之间的通信。但一旦规模化部署,它就变成了噩梦。

基础设施的”协议税”

2026-07-28 规范之前,一个生产级 MCP 部署长这样:

┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  MCP 客户端  │────▶│   负载均衡器      │────▶│  MCP 服务端 Pod  │
│  (Agent)    │     │  (粘性路由)       │     │  (有状态)        │
└─────────────┘     └──────────────────┘     └─────────────────┘
                            │                         │
                            │                         ▼
                            │                 ┌───────────────┐
                            │                 │  Redis 集群    │
                            │                 │  (会话存储)     │
                            └────────────────▶└───────────────┘

这张图里每个组件的存在都是因为一样东西:Mcp-Session-Id 头。需要粘性路由让请求命中正确的服务器;需要 Redis 让会话在 Pod 重启后存活;需要理解会话亲和性的健康检查逻辑;需要能重放初始化的自定义故障转移代码。

实际代价:

  • 无法水平扩展:粘性会话把客户端绑定在特定服务器上,产生热点
  • 无容错能力:服务器崩溃 = 会话丢失 = Agent 工作流失败
  • 无法 Serverless 化:Cloud Run 和 Lambda 的冷启动会打断会话连续性
  • 运维复杂度高:Redis 集群、会话复制、亲和性规则

GitHub 的 MCP Server 团队报告说,他们维护了一个 6 节点的 Redis 集群,唯一目的就是存 MCP 会话状态。这些基础设施不服务任何业务逻辑——纯粹的协议税。

解决方案:自描述请求

2026-07-28 规范采取了激进的方案:彻底删除会话的概念。每个 MCP 请求现在携带服务端处理它所需的全部信息。

新的 _meta 字段

不再通过握手协商能力,每个请求内联嵌入自身上下文:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "_meta": {
      "protocolVersion": "2026-07-28",
      "clientInfo": {
        "name": "my-agent",
        "version": "2.1.0"
      },
      "capabilities": {
        "elicitation": true,
        "streaming": true
      }
    },
    "name": "get_weather",
    "arguments": {
      "location": "上海"
    }
  }
}

不需要先调 initialize。没有会话 ID。服务端读取 _meta,理解客户端支持什么,处理请求,返回响应。结束。

新 HTTP 头:无需解析 Body 即可路由

规范引入了三个 HTTP 头,使基础设施层可以在不解析 JSON Body 的情况下做路由决策:

POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weather
  • Mcp-Protocol-Version:负载均衡器可以路由到对应版本的服务器池
  • Mcp-Method:按方法路由(tools/call 到计算密集型 Pod,resources/list 到缓存友好型 Pod)
  • Mcp-Name:按具体工具或资源名称路由,无需检查请求体

对基础设施团队来说这是巨大的提升。你的 nginx 或 Envoy 配置现在可以纯靠 Header 做路由决策——不需要在热路径上用 Lua 脚本解析 JSON。

改造后的架构:云原生 MCP

升级后的部署架构:

┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  MCP 客户端  │────▶│   负载均衡器      │────▶│  MCP 服务端      │
│  (Agent)    │     │  (轮询)          │     │  (无状态)        │
└─────────────┘     └──────────────────┘     └─────────────────┘

                                              (不需要 Redis)

整个会话层消失了。你获得的是:

  • 轮询负载均衡:任何服务器处理任何请求
  • Serverless 部署:Cloud Run、Cloud Functions、Lambda——冷启动不再是问题,因为没有会话需要恢复
  • 透明故障转移:服务器宕机?下一个请求自动路由到其他地方
  • 零外部状态:不需要 Redis、DynamoDB 或任何会话存储

真实案例:GitHub 的迁移

GitHub 的 MCP Server 团队是早期采用者,迁移成果:

  • 移除了 6 节点 Redis 集群(会话存储)
  • 取消了负载均衡器的粘性会话配置
  • P99 延迟降低 40ms(每次请求不再有 Redis 往返)
  • 支持从 4 个 Pod 到 40 个 Pod 的弹性伸缩,无需会话排空

他们 PR 的描述是:“删掉的基础设施比新增的代码还多。“

HTTP 缓存:协议级内置

无状态模型解锁了另一个云原生原语:HTTP 缓存。规范为可缓存的响应添加了两个字段:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [...],
    "_meta": {
      "ttlMs": 300000,
      "cacheScope": "public"
    }
  }
}
  • ttlMs:响应保持新鲜的时间(毫秒)。tools/list 响应附带 ttlMs: 300000 意味着”缓存 5 分钟”
  • cacheScope"public"(CDN 共享缓存)或 "private"(仅客户端本地缓存)

对于很少变化的工具和资源列表,CDN 可以直接返回缓存响应,完全不需要命中源站。想象一个列出 50 个工具的 MCP 服务器——以前每次 Agent 调用都要通过会话重新获取列表,现在它等效于一个被缓存的 GET 请求。

多轮请求(MRTR):无状态的服务端主动交互

无状态化最棘手的部分:服务端到客户端的通信怎么办?原始规范通过会话实现 elicitation(引出)功能——服务端在请求处理过程中向客户端索要额外输入。

2026-07-28 规范用多轮请求(Multi Round-Trip Requests, MRTR)解决了这个问题:

// 1. 客户端发送初始请求
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "tools/call",
  "params": {
    "_meta": {
      "protocolVersion": "2026-07-28",
      "capabilities": { "elicitation": true }
    },
    "name": "deploy_service",
    "arguments": { "env": "production" }
  }
}

// 2. 服务端响应引出请求(需要确认)
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "result": {
    "_meta": {
      "status": "elicitation_required",
      "elicitation": {
        "requestId": "elic-abc",
        "message": "即将部署到生产环境,确认?(yes/no)",
        "schema": { "type": "string", "enum": ["yes", "no"] }
      }
    }
  }
}

// 3. 客户端发送引出响应
{
  "jsonrpc": "2.0",
  "id": "req-002",
  "method": "elicitation/respond",
  "params": {
    "_meta": {
      "protocolVersion": "2026-07-28",
      "capabilities": { "elicitation": true }
    },
    "requestId": "elic-abc",
    "response": "yes"
  }
}

不需要会话。requestId 将对话串联起来,而无需服务端侧状态。服务端可以把待处理的引出请求存在业务数据库中(本来就有的),而不是单独的会话存储。

迁移指南:代码对比

服务端实现(Python)

之前(有状态):

from mcp.server import MCPServer

server = MCPServer()

@server.on_initialize
async def handle_init(params):
    # 存储会话能力
    session = create_session(params.client_info)
    return {"capabilities": {...}, "sessionId": session.id}

@server.on_tool_call
async def handle_tool(session_id, params):
    session = redis.get(f"session:{session_id}")
    if not session:
        raise SessionExpiredError()
    # 使用会话上下文处理...

之后(无状态):

from mcp.server import MCPServer

server = MCPServer()

@server.on_tool_call
async def handle_tool(request):
    # 所需一切都在请求中
    version = request.meta.protocol_version
    capabilities = request.meta.capabilities
    # 直接处理——无需会话查询
    result = await execute_tool(request.params)
    return result

Cloud Run 部署配置

# service.yaml - 就这些。不需要 Redis,不需要会话亲和性。
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: mcp-server
spec:
  template:
    spec:
      containers:
        - image: gcr.io/my-project/mcp-server:latest
          resources:
            limits:
              memory: 512Mi
              cpu: "1"
      containerConcurrency: 80

没有 sessionAffinity: true。没有 Redis sidecar 容器。没有会话排空的 preStop 钩子。

MCP Transports 工作组

Google 没有独自完成这项工作。他们与 Hugging Face 联合创立了 MCP Transports 工作组,推动这一变更通过规范流程。工作组的目标:确保 MCP 传输层在各云厂商、边缘部署和 Serverless 平台上都能工作,不产生供应商锁定。

这种协作很重要。MCP 的价值在于通用性——一个协议覆盖所有 Agent 到工具的通信。如果只是 Google 的私有方案,生态就会碎片化。通过官方规范流程与多元利益方合作,无状态模型成为共享标准,而非专有扩展。

常见问题

这会破坏现有的 MCP 服务器吗?

2026-07-28 是一个新的协议版本。客户端通过 Mcp-Protocol-Version 声明使用的版本。服务端可以在迁移期间同时支持旧版(基于会话)和新版(无状态)模式。规范建议 6 个月的过渡窗口。

长时间运行的工具调用怎么办?

长时间操作的机制不变——服务端返回一个进度令牌,客户端轮询或通过 SSE 获取更新。区别在于进度令牌是自包含的,不绑定在会话上。

还能用 SSE 做流式响应吗?

可以。Server-Sent Events 仍然支持流式响应。连接是按请求而非按会话的。每个 SSE 流是独立的——如果断开,客户端在下次请求中携带完整上下文重试。

认证怎么处理?

认证与会话状态正交。OAuth 令牌、API Key、mTLS 都和之前一样工作——它们在标准 HTTP 头中传递,与 MCP 协议状态无关。

这真的是 MCP 自发布以来最大的规范变更吗?

是的。移除会话模型影响了协议的每一层:初始化、能力协商、错误处理和传输语义。这是一次哲学层面的转变——从”MCP 是一个连接协议”到”MCP 是一个请求协议”。

对生态的意义

无状态化不仅是性能优化,它改变了架构上的可能性:

  • 边缘 MCP:将工具服务器部署到 CDN 边缘节点(Cloudflare Workers、Lambda@Edge),获得亚 10ms 延迟
  • CI/CD 中的 MCP:临时容器可以直接提供 MCP 服务,无需预热
  • MCP 网格:多个工具服务器在单一入口后面,通过 Mcp-Name 头路由
  • MCP 可观测性:标准 HTTP 语义意味着标准 HTTP 工具(访问日志、链路追踪、指标)无需 MCP 特定的插桩即可工作

Google 的博客将此称为”Agent 协议的 HTTP 时刻”——MCP 不再是专用 RPC 机制,而成为标准 Web 服务模式。我认为这个比喻恰如其分。

开头提到的凌晨两点告警?在新规范下,这种事根本不可能发生。没有会话就没有会话丢失。服务器崩溃,负载均衡器路由到别处,Agent 甚至不知道发生了什么。

这就是”无状态”在大规模场景下的意义。


完整规范解读见 2026-07-28 规范更新分析。初识 MCP?从什么是 Model Context Protocol?开始。