MCP 2.0 移除握手與工作階段,改採逐請求協定
MCP 2.0: the release that deleted the handshake
MCP 2.0 於 2026-07-28 移除 initialize 握手及 Mcp-Session-Id 工作階段,改由每個請求攜帶協定版本和用戶端資訊。新增的 Mcp-Method 和 Mcp-Name 標頭讓閘道可在不解析本文的情況下路由及計量;需要跨呼叫保留狀態時,伺服器可透過工具建立明確識別值,供模型在後續請求傳回。
文章對照舊有握手流程與逐請求設計,並以 Appwrite MCP 伺服器遷移拆解 CORS 設定、工具錯誤處理及指標調整,呈現協定改版的運維差異。
MCP 版本都是日期。2024-11-05、2025-03-26、2025-06-18、2025-11-25,而現在是2026-07-28。從外面看,它們全都像是同一類事件。
但最新一次不是。工作組將候選版本稱為「自推出以來對協定作出的最大修訂」,通常這代表有一份很長的更新記錄。但這次的意思是,連線模型已不復存在。沒有initialize。沒有工作階段 ID。沒有保持開啟的串流。如果一年前你要我用一句話描述 MCP,現在那句話就不再正確。
你今天部署的任何內容都不會因此停止運作,正因如此,這項改變很容易被略過。
握手時代
直到2025-11-25(包括該版本)為止,每次 MCP 修訂版都以同樣方式開始。用戶端傳送initialize,當中包括協定版本、用戶端功能及clientInfo。伺服器回覆所選版本、自身功能及serverInfo。用戶端傳送notifications/initialized。只有這時,連線才可以傳送任何有用內容。
這次交換會建立工作階段,其後所有內容都以該工作階段為基準。在 Streamable HTTP 上,伺服器可以產生Mcp-Session-Id,用戶端之後會在每個請求中重複傳送該 ID;DELETE會結束工作階段,Last-Event-ID則會恢復中斷的 SSE 串流。
只要記得 MCP 的起源,這個設計就完全説得通。2024 年 11 月,它是透過 stdio 作為編輯器子程序執行的程式。一個用戶端、一個伺服器、一條管道,而管道的生命週期就是工作階段。當每樣東西都只有一個時,在開始時協商一次功能,毫無成本。
Streamable HTTP 在2025-03-26推出,將這個模型帶到網絡上;到了網絡,「連線會記住狀態」就不再是免費的。
原文文章提供互動版本。
因此你只能二選一:黏性路由,或共用工作階段儲存庫。問題相同,只是付出的代價不同。每次部署都會中斷進行中的工作階段。複本當機,就會連同對話一起消失。由於工作階段 ID 無法告訴閘道請求要做甚麼,閘道必須開啟 JSON 主體,才能判斷它要計量的是tools/list還是資料庫刪除操作。
2026-07-28 實際帶來的改變
握手已不復存在。沒有initialize、沒有initialized、也沒有Mcp-Session-Id。以往只需協商一次的所有內容,現在都會隨每個請求一併傳送,放在_meta中。同一個工具呼叫,改動前後如下:
改動前:2 次往返
POST /mcp HTTP/1.1
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2025-11-25","capabilities":{},
"clientInfo":{"name":"my-app","version":"1.0"}}}
POST /mcp HTTP/1.1
Mcp-Session-Id: 1868a90c-3a3f-4f5b
Content-Type: application/json
{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"search","arguments":{"q":"otters"}}}
改動後:1 個請求
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":1,"method":"tools/call",
"params":{"name":"search","arguments":{"q":"otters"},
"_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}
第二個是冷啟動。該用戶端從未與這部伺服器通訊,事前沒有任何設定,事後也不會保留任何狀態。新增的三個標頭讓閘道無須開啟主體,就能路由及計量請求。
版本協商不再是一個階段,而是變成一個欄位。伺服器會逐一接受或拒絕每個請求;如果無法使用該版本,就會回覆UnsupportedProtocolVersionError,列出可用版本,讓用戶端重試。另有一個server/discover方法,可透過一次呼叫傳回支援的版本及功能;伺服器必須實作該方法,但用戶端不必呼叫。現在,探索功能只是方便之舉,不再是前置條件。
伺服器呼叫用戶端不再需要保持管道開啟。這是工作階段不可或缺的依賴。一個需要向用戶提問的工具,以往會透過一條在整個呼叫期間保持開啟的串流來提問,令呼叫必須固定在同一個程序上。

原文文章提供互動版本。
根據 SEP-2322,伺服器會傳回 InputRequiredResult,然後中斷連線。用戶端稍後會以 inputResponses 及伺服器交給它的 requestState 作出回應,之後任何實例都可以接手處理。SEP-2260 進一步收緊規定:伺服器只有在正處理用戶端發出的請求時,才可開始一項請求。兩者取代了 elicitation/create 和 sampling/createMessage。
狀態已公開。規格直言它去了哪裏:
需要在多次呼叫之間保留狀態的伺服器,可以像 HTTP API 一向所做的那樣:由工具產生明確的控制代碼(
basket_id、browser_id),並讓模型在之後的呼叫中把它當作一般參數傳回。
規格的原文是,這「讓模型看見狀態,而不是把它隱藏起來」。工作階段包含模型從未看見的狀態,一旦消失,模型便無法取回。
基礎設施毋須解析流量,也能讀取流量。
| SEP | 新增內容 | 帶來的好處 |
|---|---|---|
| 2243 |
Mcp-Method 和 Mcp-Name 請求標頭 |
毋須檢查本文即可進行路由、用量計量及 WAF 規則處理 |
| 2549 |
在列出和讀取操作中使用 ttlMs 和 cacheScope |
用戶端知道工具清單的有效期,以及哪些人可以共用清單 |
| 414 |
在 _meta 中使用 traceparent / tracestate / baggage
|
用戶端、閘道和伺服器之間的一條追蹤記錄,並可延伸至任何 OTel 後端 |
伺服器會拒絕標頭與本文內容不一致的請求。這令標頭值得信任,供任何不檢視本文的系統使用。
為甚麼這是 2.0,而不是 1.4
協定變更是最容易察覺的部分。治理方面的變更則決定這一切能否延續至下一次修訂,而且共有三項。
擴充功能提升為一級公民。它們會取得反向 DNS 識別碼,透過 extensions 對照表進行協商,版本獨立於規格,並在各自的 ext-* 存放庫中維護,由各自的維護者負責。Tasks 則從核心移至 io.modelcontextprotocol/tasks。MCP Apps 也以擴充功能形式推出:它是在沙盒 iframe 中由伺服器轉譯的 HTML,並透過相同的 JSON-RPC 回傳資料。核心維持精簡,有趣的部分則按各自步伐發展。
功能現在設有生命週期:啟用、已棄用、已移除;並提供有文件記錄的遷移路徑,而且功能棄用後至少十二個月才符合移除資格。三項功能即時進入這個流程。
| 已棄用 | 改用 |
|---|---|
| Roots | 工具參數、資源 URI 或伺服器設定 |
| Sampling | 直接使用你的 LLM 供應商 API |
| Logging |
stdio 上的 stderr;任何結構化資料則使用 OpenTelemetry |
三項功能仍然可用。這正是把政策寫下來的用意。
符合性成為一道關卡。屬於 Standards Track 的 SEP,必須等相應情境加入符合性測試套件後,才能進入 Final 階段;官方 SDK 則會按分級制度,根據該套件評分。四款 Tier 1 SDK(TypeScript、Python、Go、C#)在規格發布當日推出時,已提供 2026-07-28 支援;Rust 則仍處於 beta 階段。
尚餘的破壞性變更不多,但值得留意。工具結構描述改用完整的 JSON Schema 2020-12,因此現在允許使用 oneOf、$ref 和條件式,而且規格明確指出,不得自動解參照外部 $ref URI。structuredContent 現在可接受任何 JSON 值,而不限於物件。找不到資源錯誤由 MCP 自訂的 -32002 改為標準的 -32602,因此會影響所有按該字面值進行比對的實作。每個 2025-11-25 Tasks 實作都需要遷移。
我們早已押注於此
我花了十六個月建置 Appwrite 的 MCP 伺服器,而大部分時間都花在授權上。處理傳輸層時,託管伺服器推出時只設定了一個旗標:
StreamableHTTPSessionManager(app=server, json_response=False, stateless=True)
不設工作階段。每個請求都會攜帶持有人權杖;驗證權杖,據此建立用戶端,處理呼叫,然後忘掉一切。我真想把這説成是有先見之明。實情比較平淡:OAuth 早已令工作階段狀態變得毫無意義,因為權杖會在每個請求中識別用戶,而我不想運行 Redis 去保存一些根本用不上的東西。
規格於7月28日推出。我們在7月29日透過 PR #89 合併了對2026-07-28的支援,做法是從 MCP Python SDK v1.28 升級至 v2.0.0。HTTP 和 OAuth 介面完全沒有改動。之所以只花一天而不是一季,是因為 v2 的工作階段管理器會根據協定版本標頭進行路由,因此一次部署便能同時服務兩個時代:
def _is_modern_request(scope: Scope) -> bool:
"""Match the SDK's era-routing predicate: a present ``MCP-Protocol-Version``
that is not a handshake-era revision goes to the modern path."""
version = _header(scope, b"mcp-protocol-version")
return version is not None and version not in HANDSHAKE_PROTOCOL_VERSIONS
握手版本由2024-11-05至2025-11-25會走舊版路徑。其他版本則會走逐請求路徑。沒有任何用戶端因此故障。
三件事確實花了不少工夫:
-
工具錯誤。 v1 會把拋出的例外包裝成
CallToolResult(isError=true)。v2 則會把未捕獲的例外轉成經淨化的-32603,因此模型無法得知呼叫失敗的原因。我們的confirm_write拒絕回應,只有模型看得到才有用,所以現在每個處理程序都會明確回傳is_error=True。 -
CORS。 每個新式請求都會附帶
Mcp-Method和Mcp-Name。如果允許清單沒有列出它們,瀏覽器用戶端在到達驗證程式碼前就會失敗。 -
指標。 新版用戶端從不傳送
initialize,因此我們的握手計數器會在用戶端遷移期間悄然歸零;偏偏那正是最不應失去圖表的時候。現在計數器會根據活動時段計算,因此過渡期間的時間序列仍可比較。
這些都不屬於協定工作,只是依賴項改變形式時必須付出的常規代價。這次只花了一天,是因為十八個月前基於完全無關的理由作出的一個決定,剛好配合得上。
令人安心的平淡
一個2026-07-28 MCP 伺服器會接收 HTTP 請求、讀取一些標頭,然後回傳回應。整個運作方式就是如此。你可以把它放在 CDN 後方、快取它的清單端點,甚至在別人對話進行期間重新啟動它,而無須通知任何人。
這個協定沒有2024年推出的那個版本那麼有趣。我想這正是重點。雙向、有狀態的版本讀起來更有趣,維運起來卻麻煩得多;而維運正是我們大多數人實際上在做的事。
參考資料
| 詞語 | 在本文中的意思 |
|---|---|
| 握手時代 | 每個 MCP 修訂版本,直至2025-11-25為止;當時連線以initialize開始,之後一直沿用同一工作階段。 |
Mcp-Session-Id |
用來識別該工作階段的標頭。在2026-07-28中移除。 |
_meta |
每個請求中的中繼資料物件。現在會攜帶協定版本、用戶端資訊及功能,每個請求各有一份。 |
server/discover |
一次呼叫,回傳支援的版本、功能及身份。伺服器必須執行,用戶端則可選擇使用。 |
| 黏性路由 | 讓負載平衡器將同一用戶端的每個請求都傳送至同一後端。這是伺服器端工作階段狀態所付出的代價。 |
requestState |
伺服器隨InputRequiredResult一併傳回的不透明資料塊,令後續請求可由任何執行個體處理。 |
| SEP | 規格增強提案,是 MCP 的變更流程。標準軌道 SEP 現在必須在進入 Final 階段前提供符合性情境。 |
| Streamable HTTP | 於2025-03-26引入的 HTTP 傳輸方式,取代原有的 HTTP+SSE 傳輸方式。 |
| CIMD | 用戶端 ID 中繼資料文件,是 OAuth 流程中動態用戶端註冊的替代方案。 |
規格
- 候選版本公告及正式版本
-
2026-07-28、版本管理與協商,以及server/discover - 功能生命週期政策及已棄用功能登記表
2025-11-25變更記錄,用來瞭解最後一個握手時代修訂版仍在新增哪些內容
程式碼
- appwrite/mcp,以及 PR #89 — 雙時代遷移
- 託管伺服器位於
https://mcp.appwrite.io/,或claude mcp add --transport http appwrite https://mcp.appwrite.io/
最初刊載於 chiragaggarwal.tech。
來源:DEV Community · dev.to


