你把 MCP SDK 升到最新版,TypeScript 也重新 compile 過,測試全綠。

Production 上線後,Client 還是在做 initialize。Server 還在讀 Mcp-Session-Id。Load Balancer 還是 sticky。某些 Tool 依賴 server-to-client request,舊的 Tasks wire 也還在跑。

這種 migration failure 很常見,因為 SDK 版本和 wire 上實際跑的 protocol era 可以不同。

MCP 2026-07-28 這次升級同時碰到 protocol session、per-request metadata、server-to-client interaction、Tasks、HTTP transport、authorization 和 deprecated features。[1]

所以真正值得量的是:Production traffic 還有多少依賴 2025-era 的 wire contract、session assumptions 與 feature semantics?

Part 01 處理 State 去哪裡。Part 02 處理沒有 Session 後,多輪互動、長任務、Routing、Caching 與 Tracing 怎麼運作。

Part 03 收最後一條線:一個已經在 Production 跑的 MCP Client / Server,怎麼跨過 protocol era,而不是一次把所有舊 Client 切斷。

閱讀前提:如果你還不熟悉「protocol session 拿掉後,state 去哪裡」,先讀 Part 01;如果 MRTR、Tasks、Headers 還很陌生,先讀 Part 02。這一篇預設你已理解前兩篇,主要寫給正在維護 MCP client/server、gateway 或 production integration 的讀者。

版本範圍:本文以 MCP 2026-07-28 與 2026-08-18 可取得的 current specification、Deprecated Features registry 與 Tier-1 SDK documentation 為準。MCP 仍快速演進,正式發布前應再做一次 drift check。SEP-2596 目前已是 Final;HTTP+SSE 的 current registry 仍以「SEP-2596 reaches Final 後三個月」表達 earliest-removal 條件。本文沿用 registry 的 current wording,不自行推算成固定日曆日期。[5][7]

先確認:wire 上實際跑的是哪個 protocol era?

官方 TypeScript SDK v2 的 migration guide 寫得很直接:即使程式已經在 v2 packages 上,預設也不代表它會自動在 wire 上講 2026-07-28。Client 端要透過 versionNegotiation 選擇 modern era;Server 端則要走新的 serving entry。[3]

這個細節非常適合拿來當 migration 的第一個檢查點。

假設你只做:

npm update
npm test

然後看到 CI 綠燈,不代表下面這些事情已經發生:

  • initialize / initialized 已經離開 Production path
  • Mcp-Session-Id 已經沒有業務依賴
  • Session-local State 已經搬成 explicit application state
  • server-initiated requests 已經遷移成 MRTR 或其他新 lifecycle
  • 舊 change notifications 已經換成 subscriptions/listen
  • experimental Tasks 已經換成 current Tasks Extension
  • HTTP+SSE 或 2025-era Streamable HTTP compatibility 已經有退場計畫
  • OAuth flow 已經符合 2026-07-28 的 issuer / resource binding 規則
  • DCR 使用量已經下降到可以安全轉向 CIMD
  • Roots / Sampling / Logging 的依賴已經被盤點

所以我不會從「升 SDK」開始。

我會先做 Protocol Migration Inventory

第一階段先盤點 legacy dependency

一個實際的 inventory 可以先分成七類:

類別你要找什麼2026-07-28 要往哪裡走
Protocol lifecycleinitializeinitializedMcp-Session-Idper-request metadata、server/discover
Statesession-local memory、sticky affinity、shared protocol session storeexplicit application state / durable backend
Server-to-client interactionelicitation、sampling、roots request flowMRTR / new input lifecycle;deprecated features另行遷移
Notificationslong-lived unsolicited list/resource notificationssubscriptions/listen
Long-running work2025 experimental Tasks、同步長 requestcurrent Tasks Extension
HTTP transportGET/DELETE session semantics、Last-Event-ID、legacy HTTP+SSE2026-07-28 Streamable HTTP
Authorization / governanceold issuer assumptions、DCR、Roots/Sampling/Loggingcurrent Authorization、CIMD、documented migration paths

這個 Inventory 最有價值的地方,不是做一張漂亮 Checklist。

而是逼團隊回答:

哪些東西只是 SDK API?哪些已經進到 Load Balancer、Redis、OAuth Server、Browser Host、Observability 和業務資料裡?

如果 Session 已經滲進 architecture,升級就不是 Search & Replace。

Modern-first,不代表遇到任何 400 就回 Legacy

2026-07-28 的 Streamable HTTP spec 提供了一個很重要的 backward compatibility 原則。

支援 modern 與 legacy 的 Client,可以先送 modern request。如果收到 400 Bad Request不能直接判定「這台 Server 是舊版」。[2]

因為 modern Server 本身也會用 400 回:

  • Unsupported Protocol Version
  • Missing Required Client Capability
  • header validation failure

正確邏輯比較像:

Send modern request

Success

Stay modern

400 Bad Request

Inspect response body
      ├─ Recognised modern JSON-RPC error
      │      ↓
      │   Fix / renegotiate modern request

      └─ Empty or not a recognised modern error

          Fall back to initialize-era flow

這個差別看起來很小,實際上會決定你的 compatibility layer 是「version negotiation」,還是「看到錯誤就亂降級」。

如果你把所有 400 都當 legacy signal,一個 modern request 只是 Header 寫錯,也可能被 Client 悄悄切回舊 protocol era。

Production 會變成最難查的那種狀態:

沒有真的 fail,但一直沒有完成 migration。

官方 TypeScript SDK 把這個問題包成 versionNegotiationmode: "auto" 會先用 server/discover probe modern era,再對 2025-only Server fallback;pin: "2026-07-28" 則不 fallback。[3]

但這是 SDK implementation pattern,不是 MCP 規範要求所有語言都必須用同一套 API。

不同 Tier-1 SDK 甚至可以有不同 default。這也是為什麼 runbook 應該記錄「實際 negotiated protocol era」,而不是只記 package version。

MCP 2026 migration 應以 Protocol Era 為單位:Modern-first detection、dual-era serving、telemetry 與 legacy drain

Figure 3-1|Migration 的核心不是一次切換,而是辨識 protocol era、把 modern / legacy traffic 放進可觀測的相容路徑,再根據 telemetry 逐步 drain legacy。Dual-era serving 是 migration strategy,不是 MCP 規定的部署拓撲。

Dual-era 可以暫時共存,但一定要有退場條件

Server 端也一樣。

官方 TypeScript SDK v2 的 createMcpHandler() 可以用同一個 endpoint 服務 2026-07-28,並預設以 stateless legacy mode 兼容部分 2025-era traffic。[3]

如果你原本是 sessionful v1 Streamable HTTP,migration guide 甚至示範另一種做法:

Incoming request

Legacy classifier
      ├─ legacy → existing legacy handler
      └─ modern → strict modern handler

這很好用,因為你可以:

  1. 先加 modern lane
  2. 不立刻砍掉 legacy lane
  3. 看真實 traffic 走哪一條
  4. 修掉剩下的依賴
  5. 最後才移除 legacy handler

但這裡要守一條界線:

Dual-era support 是 transition architecture,不是 protocol requirement。

如果兩年後你的架構還保留:

if legacy:
    old_stack()
else:
    new_stack()

而且沒有 telemetry、沒有 drain target、沒有 removal criteria,那不是「向後兼容做得很好」。

那叫 migration 永遠沒結束。

Routing 只是第一步,真正的工作在舊 semantics

即使 modern 和 legacy request 已經能被正確分類,migration 還只完成一半。

因為舊 protocol era 的一些責任,不只是 wire format 不同。

1. Session State 不能只是搬到另一個隱藏角落

舊 Server 如果用:

Mcp-Session-Id

in-memory session object

browser context / basket / workspace / workflow progress

新的做法不是把它改成:

someSingletonMap[clientId]

然後宣布「我們沒有 Mcp-Session-Id 了」。

這只是在 API 表面 Stateless,Application 仍偷偷綁 Instance。

Part 01 的原則要在 migration 時真的落地:

  • Protocol context 隨 Request
  • Application state 用 explicit handle 定位
  • Durable state 放 application backend
  • Authorization 每次 Call 仍獨立判斷

2. Server-to-client request 不能只換 Method Name

2026-07-28 移除了原本的 server-to-client JSON-RPC request channel。[3]

如果 Tool 執行途中需要 Client Input,modern path 應該回 input_required,讓 Client retry 原 request,也就是 Part 02 的 MRTR。

如果是長時間工作,中途又需要 Input,則走 Tasks 自己的 tasks/get → tasks/update lifecycle。

不要把兩者混成:

「反正都是 input_required。」

它們的 State ownership 完全不同。

3. Change notification 的生命週期也變了

2025-era 可以依賴長駐 connection 送 list_changedresources/updated

Modern era 把 change notifications 移到 subscriptions/listen stream。[3]

如果你原本的 Client UI 假設:

連線建立後,Server 有任何變化都會自己推過來

這個假設也要進 migration inventory。

4. 2025-era Streamable HTTP 和 2026 Streamable HTTP 不是同一個 Wire Contract

2025-03-262025-11-25 也叫 Streamable HTTP,但那一代仍可以:

  • mint Mcp-Session-Id
  • 用 HTTP GET 打開 standalone SSE stream
  • 用 HTTP DELETE 結束 Session
  • 透過 Last-Event-ID 做 stream resume
  • 在 SSE stream 上送 server-initiated JSON-RPC requests

這些都不是 2026-07-28 的 current behaviour。[2]

所以 migration 文件不要只寫:

Transport = Streamable HTTP,已完成。

要寫清楚:

哪個 protocol revision 的 Streamable HTTP?

授權(authorization)也在 migration 範圍內

如果 Remote MCP 有 OAuth,2026-07-28 的 migration 不應只看 transport。

Current Authorization spec 明確把 Authorization 定義成 OPTIONAL,但 HTTP-based implementation 一旦支援 Authorization,SHOULD conform to the current spec。[4]

其中幾個最值得 migration team 逐項檢查的地方:

Authorization response issuer validation

Client 在 redirect user-agent 前,要從 validated Authorization Server metadata 記錄預期 issuer。

收到 Authorization response 後:

  • 如果有 iss,要和記錄的 issuer 比對
  • 如果 metadata 宣告 authorization_response_iss_parameter_supported=true,但 response 沒有 iss,要拒絕
  • 比對不能自己做 host case folding、移除 default port 或其他 URI normalization[4]

這是防 OAuth mix-up 類問題的 trust boundary。

Token 要綁定到真正的 MCP Resource

Client 必須依 RFC 8707 把 resource 放進 Authorization Request 與 Token Request,指向要使用的 MCP Server。[4]

Server 收到 Access Token 時,也必須驗證 Token 是為自己這個 resource / audience 發行。

所以 migration 時不要只問:

Token 還能不能 decode?

要問:

這顆 Token 本來就是發給這台 MCP Server 的嗎?

CIMD 是主路徑,DCR 還不是「立刻刪掉」

Current Authorization spec 對 Client ID Metadata Documents(CIMD)的方向很明確:

  • Authorization Server 和 MCP Client SHOULD support CIMD
  • DCR MAY support,主要保留 backward compatibility
  • Current Deprecated Features registry 已把 Dynamic Client Registration 列為 Deprecated,migration path 是 CIMD[4][5]

但這不等於:

今天把 DCR endpoint 關掉。

如果你的 Authorization Server、Client fleet 或企業 IdP 還沒有完整支援 CIMD,直接關掉 DCR 只會製造 auth outage。

比較合理的 rollout 是:

Add CIMD support

Measure registration path

Prefer CIMD where available

Keep DCR fallback for legacy counterparts

Drain DCR usage

Remove only when compatibility allows

Migration 的關鍵字又回到同一個:Telemetry

Deprecated 不是 Removed,更不是每個 Feature 同一個 Clock

Part 03 最容易被寫錯的地方,是把所有 deprecated feature 都丟進一句:

「MCP 給你一年,之後就會刪掉。」

Current Deprecated Features registry 不是這樣寫的。[5]

目前可以分成兩組。

2026-07-28 進入 Deprecated 的 Features

  • Roots
  • Sampling
  • Logging
  • Dynamic Client Registration

Registry 給它們的 earliest removal 都是:

First revision released on or after 2027-07-28

但這個日期只是 eligible for removal

不是 2027-07-28 00:00 自動消失。

Current registry 明確寫:actual removal 是 Core Maintainer 在 release preparation 的決定,可能更晚。[5]

Grandfathered deprecations

Legacy HTTP+SSE 不一樣。

它從 2025-03-26 就被描述為 deprecated,早於 feature lifecycle policy。Current registry 把它列成 grandfathered case,migration target 是 Streamable HTTP,earliest removal 寫成:

Three months after SEP-2596 reaches Final[5]

SEP-2596 目前已進入 Final。[7] Current Deprecated Features registry 仍把 HTTP+SSE 的 earliest removal 寫成「Three months after SEP-2596 reaches Final」。[5]

在 registry 沒有給出更具體日期前,production runbook 直接引用這個 current condition 即可,不自行製造 calendar date。

也不要只拿 release blog 裡比較概括的「year-long offramp」來寫 production removal plan。[1]

對 rollout runbook,我會以 current Deprecated Features registry 為 operational reference。

MCP Deprecated 不等於 Removed:不同 feature 有不同 migration path 與 earliest-removal clock

Figure 3-2|Roots、Sampling、Logging、DCR 在 2026-07-28 進入 Deprecated;HTTP+SSE 則是 grandfathered case。Earliest removal 只代表具備移除資格,不是自動 removal date。圖中狀態以 2026-08-18 為準。

每個 Deprecated Feature 應該搬去哪?

這些 feature 的 migration target 分散在不同 layer,MRTR 只處理其中一類互動。

Current registry / SEP-2577 給的 migration path 是:[5][6]

Deprecated featureMigration direction
RootsTool parameters、resource URIs、server configuration
SamplingServer 直接整合 LLM provider APIs
Loggingstdio 用 stderr;structured observability 用 OpenTelemetry
DCRClient ID Metadata Documents(CIMD)
HTTP+SSEStreamable HTTP

這個表其實透露一個很重要的設計方向:

MCP 不再試圖把所有 Application / Infrastructure responsibility 都塞進 Core Protocol。

有些 responsibility 回到 Tool Contract。

有些回到標準 OAuth。

有些回到 OpenTelemetry。

有些變成 Extension。

所以這一段不能只做 old/new method 對照;很多 replacement 根本落在不同 abstraction layer。

Production migration 我會這樣推進

如果是已經有真實使用者的 Remote MCP,我不會一次切。

Gate 1:建立 Baseline

先量舊世界到底還剩多少。

至少記:

% requests using initialize-era flow
% requests carrying Mcp-Session-Id
GET / DELETE / Last-Event-ID traffic on MCP endpoint
legacy HTTP+SSE connections
server-initiated elicitation / sampling / roots calls
experimental 2025 Tasks usage
DCR vs CIMD registration path
Roots / Sampling / Logging negotiated or invoked
modern protocol errors and fallback reasons

沒有這些數字,後面所有「應該可以砍了」都只是感覺。

Gate 2:把 modern 路徑上線,但先不拔 legacy

Client 可以 modern-first probe。

Server 可以加 modern handler。

Gateway 可以開始辨識 MCP-Protocol-Version 與 modern request metadata。

這一階段的目標不是立刻把所有 traffic 切過去。

是先確認:

Modern path 在真實 workload 下能完整跑完。

Gate 3:搬 semantics,也一起把授權邊界收斂

這裡才是 migration 最花時間的地方:

  • session-local state → explicit handle + durable backend
  • server-to-client interactive flow → MRTR
  • long-running work → current Tasks Extension
  • change notifications → subscriptions/listen
  • 2025 transport assumptions → 2026 per-request model
  • hard-coded wire/error assumptions → current protocol semantics

這些如果沒做,只把 Request route 到 modern handler,通常很快就會撞牆。

授權也在這一階段一起驗證

如果有 OAuth:

  • issuer validation
  • resource parameter
  • token audience validation
  • CIMD support
  • DCR fallback telemetry
  • scope / step-up behaviour

要和 protocol migration 一起測,不要等最後才發現 modern Client 連得到 Server,但 auth flow 全壞。

Gate 4:Drain Legacy

開始設定退場判斷。

例如:

legacy traffic < internal threshold
no critical customer depends on legacy-only path
no unexplained modern→legacy fallback
deprecated feature invocation is understood
DCR fallback is within accepted compatibility budget
rollback path has been tested

這裡我故意不給固定 1% 或 0.1%。

因為 B2B MCP Gateway、公開 MCP Server、內部 Agent Platform 的 compatibility cost 完全不同。

Threshold 是產品與風險決策,不是 protocol spec。

Gate 5:Remove

最後才是:

  • 刪 legacy handler
  • 移除 sticky-routing rules
  • 下掉 protocol-session store
  • 關閉不再需要的 old transport
  • 移除 deprecated feature implementation
  • 清掉 fallback-only tests / dashboards
  • 更新 runbook 與 incident playbook

Removal 是 migration 的最後一個 action,不是第一個。

Migration 最容易卡在這些地方

SDK 升了,就以為 Protocol 升了

官方 TypeScript SDK 本身就證明這個假設不可靠。[3]

Protocol era 要從實際 negotiated behaviour 觀測。

遇到任何 400 就 fallback

Modern Server 也會回 400。

不看 error body 就 fallback,會把真正的 modern misconfiguration 藏起來。[2]

Dual-era 跑得很穩,所以永久保留

Compatibility path 沒有 drain plan,就會變新的 legacy。

Mcp-Session-Id 拿掉了,但 State 還在 Instance RAM

這不是 Stateless migration,只是 Session ID 改名。

看到 DCR Deprecated,就直接關 DCR

Current spec 仍允許 DCR 做 backward compatibility。[4]

先有 CIMD coverage,再 drain DCR。

看到 Deprecated,就當 Removed

Current registry 目前甚至明確寫著:在這套 policy 下 尚沒有 Feature 被移除。[5]

用 Launch Blog 的一句話當 Removal Calendar

Release blog 很適合理解方向。

Production runbook 的 exact status / earliest removal 應該回到 current registry 與 current spec。[1][5]

特別是 HTTP+SSE,現在就不適合硬寫某個 calendar removal date。

最後,用一張 checklist 檢查退場條件

如果我要在 PRD / Architecture Review 裡用一張表收掉,會是這樣:

AreaReady to migrate?
Protocol era能辨識 modern / legacy,不會 blind fallback
Clientmodern negotiation 已啟用並有 telemetry
Servermodern handler 可獨立完成真實 workload
State不再依賴 protocol session / instance-local hidden state
InteractionMRTR / Tasks / subscriptions lifecycle 分工清楚
Transport2025 Streamable HTTP 與 HTTP+SSE dependency 已盤點
Authorizationissuer、resource/audience、CIMD/DCR path 已測
DeprecationsRoots / Sampling / Logging / DCR / HTTP+SSE 有 migration owner
Observability能看到 era、fallback reason、deprecated usage
Rollbackmodern rollout 失敗可以退,不需要資料回滾賭運氣
Removallegacy traffic 達到內部退場門檻後才執行

這張表裡最重要的其實不是「全部打勾」。

而是每一格都有 owner、measurement、exit criterion

回頭看這三篇,其實只在回答三個問題

Part 01 的問題是:

Session 拿掉後,State 去哪裡?

答案是 protocol session state 消失,但 application state、domain data 和 authorization responsibility 沒有消失。它們只是被迫變得更 explicit。

Part 02 的問題是:

沒有 Session,互動和長時間工作怎麼繼續?

答案是 MRTR、Tasks、Headers、Caching、Trace Context 各自接手一段明確 lifecycle,而不是再造一個萬用 Session。

Part 03 最後的問題是:

已經在跑的系統怎麼安全跨過去?

重點是把 protocol era 變成可辨識、可路由、可觀測的 production state;把舊 semantics 一個個搬走;等 legacy traffic 真正失去存在理由,再移除 compatibility path。

Migration 真正完成的那一刻,不是 package.json 裡版本號變了。

是 Production 裡那些只有舊 protocol era 才需要的架構假設,終於不再收到流量。

參考資料

  1. Model Context Protocol, The 2026-07-28 Specification, 28 July 2026. https://blog.modelcontextprotocol.io/posts/2026-07-28/
  2. Model Context Protocol, Streamable HTTP: 2026-07-28 Specification. https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http
  3. MCP TypeScript SDK, Supporting protocol revision 2026-07-28. https://ts.sdk.modelcontextprotocol.io/v2/migration/support-2026-07-28
  4. Model Context Protocol, Authorization: 2026-07-28 Specification. https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization
  5. Model Context Protocol, Deprecated Features: 2026-07-28 Specification. https://modelcontextprotocol.io/specification/2026-07-28/deprecated
  6. Model Context Protocol, SEP-2577: Deprecate Roots, Sampling, and Logging. https://modelcontextprotocol.io/seps/2577-deprecate-roots-sampling-and-logging
  7. Model Context Protocol, SEP-2596: Specification Feature Lifecycle and Deprecation Policy. https://modelcontextprotocol.io/seps/2596-spec-feature-lifecycle-and-deprecation