教程
Stateless MCP 是什么?2026 MCP 无状态架构、JSON-RPC 与 Remote Server 完整解析
2026-07-28 版 MCP 把协议层改成无状态:每个 JSON-RPC 请求自带版本与能力,Remote Server 可以跑在普通 HTTP 负载均衡后面。工具参数仍是 JSON,落地时仍要本地校验。
Model Context Protocol(MCP)让 AI 客户端能发现工具、资源和提示词,并把它们接到模型上下文里。2026 年最大的架构变化,是把 MCP 从「先握手、再带 Session ID 的双向有状态协议」,改成「每个请求自描述、可独立路由的无状态 JSON-RPC」。如果你已经在用 Claude Desktop、Cursor 或自建 Agent 调 Remote MCP Server,这次变更直接影响你怎么部署、怎么扩缩容、怎么在网关层限流。读完你可以分清协议无状态和应用有状态,并知道工具参数里的 JSON 仍然要本地校验。
MCP 与 Stateless 在解决什么
MCP 解决的是「模型怎么安全、可发现地调用外部能力」。客户端(Claude、ChatGPT、IDE Agent)需要一份标准方式列出你的工具、读资源、拉提示词模板;服务端(GitHub、数据库、内部 API 的 MCP 适配层)需要一份标准方式暴露这些能力,而不必为每个客户端写定制插件。
早期 MCP 在传输层保留了会话:客户端先initialize,服务端回能力清单,后续请求还要带Mcp-Session-Id,把流量钉在同一台实例或共享 Session Store 上。这对本地 stdio 进程没问题;一旦 Remote Server 要水平扩展、跑在 Cloud Run / Lambda、或经 API 网关做按工具限流,会话粘性就成了瓶颈。
2026-07-28 规范(Release Candidate)把协议层改成无状态:处理任意请求所需的元数据都在请求本身里,任意实例 behind 普通 round-robin 负载均衡都能接。官方说明见MCP 2026-07-28 规范公告与Statelessness 章节。
有状态时代留下了什么
旧流程里,Streamable HTTP 客户端通常要先走一遍握手:
- 发送
initialize,交换协议版本与 client/server capabilities。 - 收到
initialized通知,服务端下发Mcp-Session-Id响应头。 - 之后的
tools/call、resources/read都要带上同一个 Session ID,否则网关或实例内存里找不到上下文。
生产上的典型代价:负载均衡要开 sticky session;多副本之间要 Redis 存 Session;Serverless 冷启动后旧 Session 失效;GitHub MCP 等热门 Server 一度不得不维护 Redis 层。Google 在Scaling AI Agent Infrastructure里把这次变更称为「自 MCP 发布以来最大的规范改动」——核心就是去掉传输层会话管理。
| 维度 | 有状态时代(2025 及更早) | 无状态核心(2026-07-28) |
|---|---|---|
| 握手 | initialize / initialized必填 |
已退役;可选server/discover |
| 会话标识 | Mcp-Session-Id响应头 |
已移除(SEP-2567) |
| 能力协商 | 连接建立时交换一次 | 每个请求的_meta携带 |
| 水平扩展 | 粘性路由 + 共享 Session Store | 普通 round-robin 即可 |
2026-07-28 无状态核心
规范对「无状态」的定义很硬:服务端不得依赖同一连接上的先前请求来推断协议版本、客户端身份或 capabilities;每个请求必须在_meta里自带这些信息。多个任务、线程或对话的请求可以交错在同一传输上;连接或 stdio 进程本身不是会话边界。
客户端在每个请求的params._meta(或等价位置)里携带:
io.modelcontextprotocol/protocolVersion— 必填,例如2026-07-28。io.modelcontextprotocol/clientCapabilities— 必填;空对象表示不支持可选能力。io.modelcontextprotocol/clientInfo— 建议填写,用于日志与调试(服务端不应据此做安全决策)。
若客户端想先了解服务端能力,可以调用新的server/discoverRPC,但不是必须——任何请求都可以作为第一个请求打到任意实例。服务端还可以给tools/list等响应加ttlMs,让客户端在 TTL 内缓存工具列表,减少重复发现调用。
需要跨多次工具调用保留的业务状态(购物车、浏览器会话、工单草稿)不应藏在传输 Session 里,而应像普通 HTTP API 一样:工具返回显式 handle(basket_id、draft_id),模型在后续tools/call的参数 JSON 里把它传回来。模型能看见 handle,比黑盒 Session 更容易调试。
JSON-RPC 在 MCP 里怎么跑
MCP 消息层始终是 JSON-RPC 2.0:每条请求有jsonrpc、id、method、params;响应带result或error;通知没有id。这和你在 Apple Agent 文章里看到的「工具名 + 参数对象」是同一套形状——MCP 只是把方法名标准化成tools/call,参数里再嵌name与arguments。
一次典型的无状态tools/call长这样(HTTP 头在下一节展开):
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: searchInvoices
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "searchInvoices",
"arguments": {
"status": "unpaid",
"limit": 10
},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
"io.modelcontextprotocol/clientInfo": {
"name": "jsonvue-demo",
"version": "1.0.0"
}
}
}
}
成功时result里通常是带content数组的工具输出(多为type: text的 JSON 字符串或结构化块)。失败时 JSON-RPCerror带code与message;Streamable HTTP 下若 HTTP 头与 body 里的 method/name 不一致,规范要求返回-32020类 header mismatch 错误。
对 JSONVue 读者来说,最值得盯的是arguments:外部 Agent 更容易把数字发成字符串、漏掉必填键。MCP 无状态化不会替你校验业务 JSON——这和 Structured Output 管模型输出、MCP 管工具调用的分工一致。站内Apple AI Agent 与 JSON一文已说明:同一领域函数可以服务 App Intent、Foundation Models Tool 和 MCP,差别只在适配层。
Remote Server 与 Streamable HTTP
Remote MCP Server 指客户端经 HTTPS 访问的 MCP 端点,而不是本地 stdio 子进程。Streamable HTTP 是当前 Remote 部署的主传输:单 POST 可以完成一次 RPC;长任务可以返回开放的通知流,但状态仍 scoped 到该请求,而不是连接级 Session。
2026-07-28 起,Streamable HTTP 请求必须携带与 body 一致的三个头(SEP-2243),方便网关、WAF、限流器在不解析 JSON body 的情况下路由:
MCP-Protocol-Version— 须与_meta里的 protocolVersion 一致,否则 400。Mcp-Method— 对应 JSON-RPCmethod,如tools/call。Mcp-Name— 工具、提示或资源名,如searchInvoices。
部署形态因此变简单:同一 Docker 镜像多副本 + 普通 ALB/nginx round-robin;Cloud Run / Cloud Functions 按需伸缩,不必为 MCP 单独挂 Redis Session;按Mcp-Name做 QPS 配额也比深包检测 body 便宜。GitHub MCP Server 等生产服务已在向无状态规范升级。
stdio 本地 Server 仍然可用,但规范明确:同一 stdio 进程上可以交错无关请求,Server 不得把进程身份当成会话 ID。本地开发与云端 Remote 应共用同一套工具实现,只是传输适配不同。
应用仍可带状态
「协议无状态」不等于「你的业务无状态」。购物车、多步审批、浏览器自动化里未完成的表单,仍然可以、也应该有状态——只是状态要显式,而不是绑在Mcp-Session-Id上。
推荐模式:
- 第一个工具调用创建资源,返回
{ "draftId": "dr_8k2", ... }。 - 工具描述里写清楚:后续步骤必须传入
draftId。 - 服务端用
draftId查 DB 或缓存;丢件时返回 JSON-RPC 业务错误,而不是 mysteriously 404 Session。
长任务方面,规范通过 Tasks 等扩展支持 MRTR(Multi-Request Task Routing):工具可以先返回status: input_required,客户端把用户补答附在后续请求的_meta里继续。这仍是无状态协议上的请求/响应模式,只是响应可能跨多轮交互。
和 Structured Output 的关系
MCP 与 Structured Output 解决不同层的问题,但 JSON 形状经常在同一条 Agent 流水线里碰面:
| 层 | 机制 | 约束什么 |
|---|---|---|
| 模型输出 | Structured Output + JSON Schema | 模型最终答复或抽取结果的字段与类型 |
| 工具调用 | MCP tools/call+ 工具 inputSchema |
传给 Server 的arguments对象 |
| 业务 API | REST / GraphQL JSON body | Server 内部或下游 HTTP 的真实载荷 |
最佳实践是维护一份字段表,生成 MCP 工具的 inputSchema、REST OpenAPI 与给模型的 Structured Output Schema。站内AI Structured Output 教程讲模型侧;Gemini API JSON 输出有云端示例。MCP 无状态化后,工具列表可能被客户端缓存——Schema 版本变了要 bump 工具名或协议版本,避免旧缓存把错误形状发进arguments。
落地时怎么看 JSON
联调 Remote MCP Server 时,把三份 JSON 并排看最省时间:客户端发出的tools/call参数、你的领域服务收到的 HTTP body、工具返回给模型的result.content。形状不一致时,问题几乎总在适配层,而不是「模型不够聪明」。
建议在浏览器里走一遍:JSON 格式化确认能 parse;JSON 校验抓尾逗号与类型错误;用JSON Schema校验工具 inputSchema 与 API body 共用字段;JSON Diff对比「模型 arguments」与「实际 HTTP 请求体」。固定三份夹具:mcp-args.valid.json、http-body.valid.json、mcp-tool-error.json,CI 里跑同一套 Schema。
常见问题 FAQ
无状态 MCP 还需要 WebSocket 长连接吗?
Remote 部署以 Streamable HTTP 为主:单次 POST 完成 RPC,长通知流也是请求级响应流,不是旧式「先握手再绑 Session」的连接状态。本地 stdio 仍是长寿命进程,但协议语义上每个请求独立。
旧客户端带 Mcp-Session-Id 还能连新 Server 吗?
2026-07-28 Server 不再识别协议级 Session ID。客户端需升级到在每条请求的 _meta 里带 protocolVersion 与 clientCapabilities,并发送必需的 HTTP 头。混跑版本时应在网关按 MCP-Protocol-Version 分流。
tools/list 每次都要调吗?
不是。Server 可在响应里给 ttlMs,客户端在 TTL 内缓存。工具或 Schema 变更时应缩短 TTL 或变更工具名/版本,避免 stale 列表。
MCP 会替 Server 校验 arguments 吗?
工具可声明 inputSchema,但 Server 仍必须做服务端校验。外部 Agent 常发错类型;无状态协议不会减少这类错误。返回结构化 JSON-RPC error 比静默 500 更利于模型重试。
总结与下一步
Stateless MCP 把 2026 年的 Remote Server 拉回了普通 HTTP 运维模型:JSON-RPC 2.0 承载方法,_meta承载协议上下文,Mcp-Method / Mcp-Name头让网关能看懂流量。initialize 与 Mcp-Session-Id 退场,换来的是任意实例可接任意请求、Serverless 友好、按工具限流更简单。
业务状态用显式 ID 在 arguments 里传递;JSON 契约仍要本地校验。下一步:对照 2026-07-28 规范检查你的 Remote 端点是否发送完整 _meta 与 HTTP 头;把 MCP arguments 与 REST body 用同一份 Schema 钉死;用 JSONVue 工具在浏览器里验一遍往返 JSON。