</> 代码前沿Code Frontier

MCP 无状态化:Agent 集成的分水岭

2026-10-05 协议 约 12 分钟 规范版本 2026-07-28

2026 年 7 月 28 日的 MCP 规范更新,改动看起来非常技术化: 删掉了协议级会话,删掉了初始化握手。但它的后果远超一个协议细节 —— 远程 MCP 服务器从此和任何普通 HTTP 服务没有区别, 可以放在标准负载均衡器后面,任意实例都能处理任意请求。

这让 MCP 真正进入了"可以按普通后端服务来运维"的阶段。 同时也推翻了此前大量实现里关于状态、鉴权和生命周期的一整套假设。

先理解它原来是什么形状

MCP 用 JSON-RPC 2.0 通信,定义了三个角色:Host(承载模型的宿主应用)、 Client(宿主为每个服务器创建的连接器)、Server(暴露能力的一方)。

服务器对外提供三类能力,按"谁来决定使用"划分:

原语由谁控制说明
Tools模型模型可主动调用的函数,输入由 JSON Schema 描述
Resources应用用 URI 标识的只读数据,由宿主决定是否附加为上下文
Prompts用户可复用的模板,通常呈现为斜杠命令

旧规范里,客户端与服务器之间必须先握手建立会话,之后所有请求都挂在那个会话上。 好处是状态明确;坏处是服务器必须记住这个会话 —— 一旦部署多个实例,就要么做会话粘滞,要么把会话状态外置到 Redis。

改了什么

新规范的核心变化可以归纳为四件事:

1. 会话与初始化握手消失

每个请求自带协议版本和客户端能力声明,服务器不需要记住任何跨请求的状态。 这是"可水平扩容"的直接前提。与之配套的是新增的 server/discover: 客户端在做任何实质操作之前,先查询服务器支持的版本与能力。

2. 列表结果可缓存

工具列表、资源列表这类元数据变化不频繁,允许缓存。 对客户端来说这减少了启动时的往返;对服务器来说减少了无意义的重复计算。

3. 服务器主动请求被 Multi Round-Trip Requests 取代

这是最容易被低估的一条。原先服务器可以主动向客户端发请求 (典型场景是 elicitation:任务中途需要用户补充参数或确认)。 但"服务器主动发请求"天然依赖长连接和会话 —— 无状态服务器做不到。

新方案把这类交互改成多轮往返:服务器返回一个"需要补充信息"的结果, 客户端完成收集后再发起下一次请求,带上完整上下文。 语义上等价,但控制流反过来由客户端驱动,因而兼容无状态部署。

4. Tasks 移入扩展

长任务(Tasks)从核心规范移到扩展中。原因是早期采用者的反馈表明这套抽象还需要迭代, 放在核心里会限制演进速度。用的话要显式启用。

判断要点

如果你的 MCP 服务器实现里维护了"连接 → 会话 → 上下文"的映射表, 这个表在新规范下就是多余的,而且会成为扩容的瓶颈。 改造的方向是:把每个请求需要的一切信息都放进请求本身。

鉴权:从浏览器授权到工作负载身份

无状态化之后,鉴权成为最棘手的问题。旧模型的默认假设是"有个人坐在浏览器前批准访问", 这对交互式客户端成立,但越来越多的调用方是以云工作负载身份运行、代表一个不在场的用户的代理。

这次规范在鉴权方面的改动:

  • issuer 校验(RFC 9207),防止签发方混淆
  • 签发方绑定的客户端凭据
  • 用 Client ID Metadata Documents(CIMD)取代动态客户端注册,成为首选的注册路径
  • Enterprise-Managed Authorization 作为扩展提供,并已稳定
  • 弃用 Roots、Sampling、Logging,以及旧的 HTTP+SSE 传输(给出至少 12 个月的下线窗口)

路线图里还明确了下一步方向:DPoP(持有证明)的定稿与推广, 以及通过 Workload Identity Federation、企业托管授权背后的 ID-JAG 授权、 标准令牌交换来定义代理身份与委托的路径。

迁移提醒

如果你在 2026 年上半年实现了 MCP 服务器,很可能用了动态客户端注册和旧 HTTP+SSE 传输。 这两条都进了弃用流程。虽然给了至少 12 个月窗口,但新接入的客户端可能已经不再支持旧路径 —— 兼容层需要同时维护两套,或者干脆只留新的。

代码层面的变化

对绝大多数用官方 SDK 的开发者,改造集中在两处:不再维护会话、把能力声明放进每个请求。 下面是新规范下一个工具服务器的骨架(TypeScript SDK):

server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import {
  ListToolsRequestSchema,
  CallToolRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

// 注意:没有会话对象,没有 initialize 处理
const server = new Server(
  { name: "notes", version: "1.2.0" },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "search_notes",
      description: "在笔记库中按关键词检索",
      inputSchema: {
        type: "object",
        properties: {
          query: { type: "string", description: "检索关键词" },
          limit: { type: "number", description: "返回条数,默认 10" },
        },
        required: ["query"],
      },
    },
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (req) => {
  const { name, arguments: args } = req.params;

  if (name !== "search_notes") {
    // 未知工具按协议返回错误,不要抛异常
    return { isError: true, content: [{ type: "text", text: `unknown tool: ${name}` }] };
  }

  const query = String(args?.query ?? "");
  const limit = Number(args?.limit ?? 10);
  const rows = await db.search(query, Math.min(limit, 50));

  return {
    content: [{ type: "text", text: JSON.stringify(rows) }],
  };
});

如果你用 Streamable HTTP 部署远程服务器,无状态化之后请求处理就变成一个普通 handler, 不需要再为每个连接分配实例:

http.ts
import express from "express";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";

const app = express();
app.use(express.json());

app.post("/mcp", async (req, res) => {
  // 每个请求独立处理:不读会话、不写会话
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: undefined,   // 关键:无状态模式
  });
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

// 因为无状态,直接放在负载均衡后面即可,任意实例都能处理
app.listen(8080);

对现有集成的影响

按影响程度,可以把改动分成三档:

影响涉及内容需要做什么
必须改 会话管理、初始化握手、旧 HTTP+SSE 传输 移除会话状态,改 Streamable HTTP
可能需要改 服务器主动请求(elicitation 等) 改为多轮往返模式
建议关注 动态客户端注册、Roots / Sampling / Logging 迁到 CIMD;弃用项规划下线

为什么说这是分水岭

MCP 最初的价值主张是"把 M 个应用与 N 个工具的集成数量从 M×N 降到 M+N"。 这个主张在开发阶段成立得很早,在生产阶段却一直有个软肋: 它看起来像一套 API,运维起来却不像。

无状态化改变的就是这一点。当 MCP 服务器不再需要粘滞会话、 不再需要为长连接单独设计扩缩容策略时,它就可以放进任何已有的 API 网关、限流层、可观测体系里 —— 不需要为它单独建一套基础设施。

一个协议能不能被广泛采用,往往不取决于它设计得多优雅, 而取决于它能不能被塞进现有的运维体系里。这次的改动正是朝这个方向。
参考来源
  1. MCP 官方文档(角色划分、三类原语、传输方式、2026-07-28 规范变更)
  2. MCP 官方博客《The New MCP Roadmap》(会话与握手移除、server/discover、列表缓存、Tasks 扩展化、鉴权改进)
  3. MCP 规范迁移说明(CIMD 取代动态客户端注册、RFC 9207 issuer 校验、弃用项与 12 个月下线窗口)
  4. 生态采用情况综述(企业采用比例、SDK 下载量、各厂商支持情况)