教程
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。站內Gemini API JSON 輸出教程講模型側;Gemini Structured Output 教程有云端示例。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。