MCP 2026-07-28 仕様: セッションが消えた — ステートレス移行ガイド

2026年7月28日、Model Context Protocol 仕様の新しいリビジョンがリリースされ、現行 (Current) の
プロトコルバージョンになりました。直前のリビジョン 2025-11-25 から8か月ぶりです。MCP 共同創案者の
David Soria Parra は公式発表で、今回のリリースを「remote MCP が登場してから1年あまりの中で、MCP に
とって最も重要なもの」と表現しました。
一言でまとめるとこうです。MCP がステートレスになりました。 initialize ハンドシェイクが消え、
Mcp-Session-Id ヘッダーが消えました。いま MCP サーバーやクライアントを運用しているなら、これは
「いつか読めばいい」リリースノートではなく、コードを直す必要のあるブレイキングチェンジです。
影響はすでに表面化しています。8月6日、Cloudflare は Workers で MCP サーバーを作る際に使っていた
McpAgent を deprecated・feature-frozen に切り替え、ステートレスハンドラーへ移行するよう案内
しました。セッションがなくなれば、セッションを保持するための Durable Object も不要になるからです。
この話は第12節で改めて扱います。
この記事は仕様変更の一覧をそのまま書き写すのではなく、実際にどのコードを直すべきかを中心に まとめます。各項目に SEP 番号を付けてあるので、根拠が必要なら該当の PR をそのまま開いてください。
1. セッションが消えた (SEP-2567)
最大の変更です。Streamable HTTP トランスポートから プロトコルレベルのセッションと
Mcp-Session-Id ヘッダーが削除されました。
その帰結としてもう一つ条件が付きます。tools/list、resources/list、prompts/list が
もはやコネクションごとに異なる値を返せなくなりました。 以前は「このセッションは認証済みユーザー
だから管理者向けツールまで見せる」といった実装が可能でしたが、これからは一覧のレスポンスがコネク
ションに依存しません。
では、呼び出しをまたいで状態を保持する必要のあるサーバーはどうするのか。仕様が示す答えは明確です — サーバーが発行した明示的なハンドルを、通常のツール引数としてやり取りせよということです。
// Before — セッションヘッダーに依存した構造
app.post('/mcp', async (c) => {
const sessionId = c.req.header('Mcp-Session-Id');
const state = await sessions.get(sessionId); // このヘッダーはもう届かない
// ...
});
// After — 状態はサーバーが発行したハンドルとして、ツール引数に載せて明示的に受け渡す
server.tool('open_workspace', {}, async () => ({
resultType: 'complete',
content: [{ type: 'text', text: 'ワークスペースを開きました' }],
structuredContent: { workspaceHandle: 'ws_01J8ZQ...' }, // サーバーが発行
}));
server.tool('write_file', {
workspaceHandle: z.string(), // クライアントが次の呼び出しにそのまま載せて送る
path: z.string(),
body: z.string(),
}, async ({ workspaceHandle, path, body }) => { /* ... */ });
暗黙的なコネクション状態が、明示的なデータに置き換わったわけです。ロードバランサーの背後にインス タンスを複数並べたり、サーバーレスで動かしたりするときに格段に扱いやすくなります。どのインスタンス がリクエストを受けても関係なくなるからです。
2. initialize ハンドシェイクが消えた (SEP-2575)
initialize と notifications/initialized がまるごと削除されました。代わりに すべてのリクエストが、
自分自身に必要な情報を _meta に載せて持ち歩きます。
_meta キー |
方向 | 必須 | 内容 |
|---|---|---|---|
io.modelcontextprotocol/protocolVersion |
クライアント → サーバー | はい | プロトコルバージョン ("2026-07-28") |
io.modelcontextprotocol/clientCapabilities |
クライアント → サーバー | はい | このリクエストに関係するクライアントの能力 |
io.modelcontextprotocol/clientInfo |
クライアント → サーバー | いいえ (SHOULD) | クライアント名・バージョン |
io.modelcontextprotocol/logLevel |
クライアント → サーバー | いいえ | このリクエストに対する最小ログレベル |
io.modelcontextprotocol/serverInfo |
サーバー → クライアント (結果の _meta) |
いいえ (SHOULD) | サーバー名・バージョン |
// すべてのリクエストがこの形になる
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "write_file",
"arguments": { "path": "a.txt", "body": "hi" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": { },
"io.modelcontextprotocol/clientInfo": { "name": "my-client", "version": "1.0.0" }
}
}
}
必須フィールドが欠けたリクエストは malformed です。サーバーは必ず (MUST) JSON-RPC の
-32602 (Invalid params) で拒否しなければならず、HTTP ならレスポンスステータスは
400 Bad Request でなければなりません。バージョンが合わない場合は
UnsupportedProtocolVersionError (-32022) を返します。
能力の宣言にもルールが一つ加わりました。サーバーは、クライアントが宣言していない能力に依存しては
なりません (MUST NOT)。 リクエストを処理するのに宣言されていない能力が必要なら
MissingRequiredClientCapabilityError (-32021) を返さなければならず、data.requiredCapabilities
に不足している能力を列挙しなければなりません。
仕様はステートレスの意味をかなり強く釘刺しています。サーバーは、同じコネクション上の以前の リクエストに依拠して文脈を組み立ててはなりません (MUST NOT)。 stdio プロセスですら会話やセッション ではないと明記されています — クライアントは同じトランスポートに互いに無関係なリクエストを混在させて 送ることができ、サーバーはコネクションやプロセスの同一性をセッション継続性の代用として扱ってはなり ません。
ハンドシェイクの往復が丸ごとなくなったので、コールドスタートが頻発する環境 — エッジランタイムや サーバーレス — では体感できる利点があります。逆に「初期化時に一度受け取って使い回していた」情報に 依存していたコードは、すべて手を入れる必要があります。
3. server/discover: これからは必須実装です (SEP-2575)
ハンドシェイクが消えた穴を埋める RPC です。サーバーは server/discover を必ず (MUST) 実装しなければ
なりません。 サポートするプロトコルバージョン、能力、アイデンティティを知らせるためのものです。
クライアント側からは二通りの使い道があります。
- 他のリクエストを送る前に呼び出して バージョンを選び取る (MAY)
- STDIO では 後方互換の探針 として使う — レスポンスが返れば新型、返らなければ旧型
DiscoverResult の形はこうです。フィールド名に注意してください — サポートバージョンの一覧は
supportedVersions であり、サーバーのアイデンティティは結果のトップレベルではなく _meta の中に
入ります。
{
"resultType": "complete",
"supportedVersions": ["2026-07-28", "2025-11-25"],
"capabilities": { "tools": {}, "resources": {} },
"_meta": {
"io.modelcontextprotocol/serverInfo": { "name": "my-mcp-server", "version": "2.0.0" }
}
}
バージョンが合わないときにサーバーが返すエラーも決まっています。サポート一覧を一緒に載せることで、 クライアントが選び直せるようになります。
{
"jsonrpc": "2.0", "id": 1,
"error": {
"code": -32022,
"message": "Unsupported protocol version",
"data": { "supported": ["2026-07-28", "2025-11-25"], "requested": "1900-01-01" }
}
}
サーバーを作っているなら、今回の移行で 真っ先に追加すべきコード がこれです。なければ仕様違反です。
旧型・新型のクライアントを両方受けるには
仕様は実装を三つに分類して呼び分けます。
| 用語 | 意味 |
|---|---|
| Modern | バージョン・アイデンティティ・能力をリクエストごとに _meta で載せて送るバージョン (2026-07-28 以降) |
| Legacy | initialize ハンドシェイクでセッションを確立するバージョン (2025-11-25 以前) |
| Dual-era | 両方をサポートする実装 |
Dual-era サーバーは クライアントがどう話しかけてくるかで判断します。 リクエストがモダンな _meta
を伴って来ればステートレスとして処理し、initialize が来ればレガシー方式で処理します。一つのエンド
ポイントで両方を同時に提供しても構いません (MAY)。
クライアント側の検出はトランスポートごとに異なります。
- stdio —
server/discoverで突いてみて、認識可能なモダンのエラーでなければレガシーと判断 - Streamable HTTP — モダンなリクエストを送ってみて、
400 Bad Requestの 本文を確認したうえで フォールバック
判定結果は個々のリクエストではなく サーバーの属性 です。stdio ならプロセスの生存期間中、HTTP なら オリジン単位でキャッシュしておくのが適切です (SHOULD)。
注意すべき組み合わせがあります。レガシークライアント → モダンサーバーは失敗します。 レガシー
クライアントには上位バージョンへ上がる手段がないからです。そのため、モダン専用サーバーなら
initialize リクエストに対するエラーメッセージに サポートバージョンを書いておくのが望ましい
(SHOULD) です — レガシークライアントの利用者が目にできる唯一の手がかりが、そのメッセージかもしれ
ないからです。
4. 購読の方式が変わった (SEP-2575)
HTTP GET エンドポイントと resources/subscribe・resources/unsubscribe が消え、
subscriptions/listen 一つに統合されました。単一の long-lived な POST レスポンスストリームです。
クライアントは受け取りたい通知の種類を 明示的にオプトイン します。
toolsListChangedpromptsListChangedresourcesListChangedresourceSubscriptions
サーバーはそれを確認し、送る通知に io.modelcontextprotocol/subscriptionId タグを付けます。
ここで混同しやすい点が一つあります。notifications/progress や notifications/message のような
リクエストに付随する通知は、subscriptions/listen ストリームには流れません。 これらの通知は、
元のリクエストのレスポンスストリームを引き続き流れます。進捗表示が急に出なくなったら、ここを疑うと
いいでしょう。
5. MRTR — サーバーがクライアントに問い返す方式 (SEP-2322)
従来は、サーバーからクライアントへリクエストを投げる方向が別に用意されていました。roots/list、
sampling/createMessage、elicitation/create などです。仕様の表現をそのまま移すと、サーバーは
これからこの種のリクエストを 必ず MRTR パターンで送らなければならず (MUST)、従来のサーバー主導の
リクエストパターンはもうサポートされません。ブレイキングチェンジです。
新しい流れはこうです。
- クライアントがリクエストを送る (
id: 1) - サーバーは情報がさらに必要なら、
resultType: "input_required"のInputRequiredResultを返す - クライアントはユーザーに尋ねたうえで、元のリクエストを新しい
idで送り直しながら 回答を載せる
ここで最も間違えやすい点があります。inputRequests と inputResponses は配列ではなくマップです。
キーはサーバーが付けた識別子で、値は ElicitRequest・CreateMessageRequest・ListRootsRequest の
いずれかである 完全なリクエストオブジェクト (method + params) です。
// ステップ2 — サーバーのレスポンス
{
"jsonrpc": "2.0", "id": 1,
"result": {
"resultType": "input_required",
"inputRequests": {
"github_login": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "GitHub のユーザー名を入力してください",
"requestedSchema": {
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"]
}
}
}
},
"requestState": "AEAD-protected blob"
}
}
レスポンスも同じキーを使うマップで、値は各リクエストの 結果オブジェクト です。
// ステップ3 — 新しい id で再試行し、回答と requestState をそのまま載せて送る
{
"jsonrpc": "2.0", "id": 2,
"method": "tools/call",
"params": {
"name": "delete_file",
"arguments": { "path": "a.txt" },
"inputResponses": {
"github_login": { "action": "accept", "content": { "name": "octocat" } }
},
"requestState": "AEAD-protected blob"
}
}
requestState — これが MRTR をステートレスにする仕掛けです
requestState は サーバーにとってのみ意味を持つ不透明な文字列 です。サーバーが必要な文脈をここに
エンコードして送り返すと、クライアントがそのまま返してくれます。サーバーが状態ストアを持たなくて
済む理由が、まさにこれです。
ルールはかなり厳格です。
- サーバーはすべての
InputRequiredResultに、inputRequestsとrequestStateのうち 少なくとも一方を必ず含めなければなりません (MUST) - クライアントは受け取った値を 正確にそのままエコーバックしなければならず (MUST)、中身を覗いたり パースしたり改変したりしてはなりません (MUST NOT)。受け取っていないなら、再試行に入れてもなりません
- 再試行の JSON-RPC
idは元のリクエストと 異ならなければなりません (MUST)。独立したリクエスト だからです
セキュリティ要件が特に重要です。この値は クライアントを経由して届くため、攻撃者が改ざんできる入力 です (MUST としてそう扱えと明記されています)。 認可・リソースアクセス・ビジネスロジックに影響を 与えるなら、サーバーは 完全性を必ず保護しなければならず (HMAC や AEAD など)、検証に失敗した状態は 拒否しなければなりません。 改ざんされてもリクエストが失敗する以外に何も起こらない場合に限り、 省略できます。
リプレイ防止のために、完全性保護されたペイロードの中に次を入れ、受信のたびに検証することが推奨され ます (SHOULD)。
- 認証された主体 — 別の主体が提示したら拒否
- 短い有効期限 (TTL) — 期限後に提示されたら拒否
- 元のリクエスト識別子 (メソッド名 + 主要パラメータのダイジェスト) — 一致しないリクエストで提示され たら拒否
ただしこれらの対策は リプレイの窓を狭めるだけで、単一使用を保証するものではありません。 一回限り の使用がどうしても必要なら、サーバー側で別途強制しなければなりません (MUST)。
MRTR を使えるリクエストは三つだけです
| クライアントのリクエスト | InputRequiredResult の可否 |
|---|---|
prompts/get |
可 |
resources/read |
可 |
tools/call |
可 |
それ以外のリクエストに送ってはなりません (MUST NOT)。 また、クライアントが能力として宣言していない
種類のリクエストを inputRequests に入れてもなりません — クライアントが elicitation を宣言して
いなければ、elicitation/create を入れることはできません。
双方向リクエストが、一方向の再試行に置き換わりました。トランスポートの観点ではずっと単純になりまし たが、クライアントは 再試行ループを自前で実装しなければなりません。 これまでサーバー主導の リクエストを処理していたコールバックは、すべてこの構造へ移す必要があります。
6. resultType がすべての結果に必須になった (SEP-2322)
すべての結果に resultType フィールドが 必須 で付きます。
"complete"— 通常の結果"input_required"— MRTR の中間結果
後方互換に関するルールが明記されています。以前のプロトコルのサーバーがこのフィールドを欠いている
場合、クライアントは必ず (MUST) "complete" として扱わなければなりません。 クライアントを作るなら、
この分岐を必ず入れておきましょう。
7. なくなったもの
ping
logging/setLevel
notifications/roots/list_changed
tasks/result (→ tasks/get のポーリングで代替)
tasks/list
notifications/elicitation/complete
ログレベルはこれから、リクエストごとに _meta の io.modelcontextprotocol/logLevel で指定します。
そして サーバーは、このフィールドがないリクエストに対して notifications/message を送ってはなり
ません (MUST NOT)。 無条件にログを流していたサーバーなら、条件を追加する必要があります。
SSE ストリームの再開もなくなりました。Last-Event-ID ヘッダーと SSE のイベント ID が Streamable HTTP
から削除されたため、レスポンスストリームが切れると、進行中だったリクエストはそのまま失われます。
クライアントは新しいリクエスト ID で送り直さなければなりません (MUST)。再接続すれば続きから受け取れる
ことを前提に書かれたコードがあるなら、ここで静かに壊れます。
8. Tasks がコアから拡張に外れた (SEP-2663)
実験的機能だった tasks がコアプロトコルから外れ、公式拡張 io.modelcontextprotocol/tasks へ移りま
した。単なる引っ越しではなく、設計が組み直されています。
| 変更 | 内容 |
|---|---|
tasks/result の削除 |
ブロッキング方式でした。tasks/get の ポーリング で代替 |
tasks/update の新設 |
クライアント → サーバーの入力受け渡し用 |
tasks/list の削除 |
— |
| ハンドルの返却 | サーバーが リクエストごとのオプトインなしに タスクハンドルを自発的に返せる |
拡張は能力 (capabilities) の extensions マップで知らせます。識別子は _meta キーの命名規則に従い、
接頭辞が必須です。
{
"capabilities": {
"tools": {},
"extensions": { "io.modelcontextprotocol/tasks": {} }
}
}
片方だけが拡張をサポートする場合のルールも定められています。サポートしている側が コアの動作へ フォールバックするか、適切なエラーで拒否しなければなりません (MUST)。
9. Deprecated — Roots、Sampling、Logging (SEP-2577)
三つの機能が非推奨に指定されました。非推奨期間中は 完全に動作しますが、新規に作る実装では使うな、 という意味です。仕様が勧める代替手段はこうです。
| 非推奨の対象 | 代替 |
|---|---|
| Roots | ディレクトリ・ファイルをツールパラメータ、リソース URI、サーバー設定で渡す |
| Sampling | LLM プロバイダーの API と直接連携 |
| Logging | stdio は stderr へ、それ以外は OpenTelemetry |
併せて非推奨に再分類されたものもあります。
- HTTP+SSE トランスポート —
2025-03-26から既に deprecated でしたが、今回正式に Deprecated 状態になりました。Streamable HTTP へ移行する必要があります (SEP-2596) includeContextの"thisServer"・"allServers"— フィールドを省略するか"none"を使います- OAuth 2.0 Dynamic Client Registration (RFC 7591) — Client ID Metadata Documents で代替。これをサポートしない認可サーバーのために、後方互換用としては残ります
ここで重要なのは、今回同時に導入された 機能ライフサイクルポリシー です。Active / Deprecated / Removed の三状態を定義し、非推奨になった機能は削除対象になるまで 最低12か月 仕様に残ります。 ただし無条件ではありません — ポリシーの 迅速削除 (expedited-removal) 例外 が適用されると 最低90日 に短縮されます。活発なセキュリティリスクがある場合がそれにあたります。つまり上記の機能が すぐ次のリビジョンで消えるわけではありませんが、「1年は無条件に安全だ」と読んではいけません。
10. キャッシュとルーティング — 地味ですが実務に大きく効くもの
メジャーな変更一覧には載っていませんが、実装にはすぐ影響する項目です。
リスト結果がキャッシュ可能になりました (SEP-2549)。 tools/list、prompts/list、
resources/list、resources/read、resources/templates/list の結果に ttlMs と cacheScope が
必須 で付きます。ttlMs は鮮度のヒント (ミリ秒) で、cacheScope は "public" / "private" で
中間キャッシュが共有してよいかを決めます。ポーリングを減らすためのものです。
リクエストヘッダーが必須になりました (SEP-2243)。 Streamable HTTP の POST には Mcp-Method と
Mcp-Name を必ず載せなければなりません。本文をパースせずにルーティングできるようにするためです。
リバースプロキシや WAF の手前で MCP トラフィックを判別する必要があったなら、これからはヘッダーを
見るだけで済みます。
tools/list は決定的な順序で返すべきです (SHOULD)。 クライアントのキャッシュと LLM のプロンプト キャッシュのヒット率のためです。ツール一覧をマップから取り出してその都度違う順序で返していたなら、 ソートを入れましょう。プロンプトキャッシュが毎回壊れていたはずです。
OpenTelemetry のトレースコンテキストが規約化されました (SEP-414)。 _meta に traceparent、
tracestate、baggage を載せて伝播します。
11. エラーコードが振り直された
エラーコードの割り当てポリシーができました。JSON-RPC のサーバーエラー範囲をこう分けます。
-32000〜-32019— 実装定義の領域 (既存 SDK の使用分はそのまま認める)-32020〜-32099— MCP 仕様の予約
これに合わせて番号が変わったものがあります。
| エラー | 変更前 | 変更後 |
|---|---|---|
HeaderMismatch |
-32001 |
-32020 |
MissingRequiredClientCapability |
-32003 |
-32021 |
UnsupportedProtocolVersion |
-32004 |
-32022 |
| リソースが存在しない | -32002 |
-32602 (Invalid Params) |
ここで誤解しやすい点があります。上の三つは今回のリビジョンのドラフト段階で初めて生まれたコードなので、 正式リリースされた以前のバージョンを実装したコードにはそもそも存在しません。 実際に既存コードへ 埋め込まれている可能性があるのは、最後の行の一つだけです。
リソースが存在しない場合が -32002 から -32602 (Invalid Params) に変わったのは、JSON-RPC 仕様に
合わせるための変更です。このバージョンを実装した側は -32002 を 送出してはなりません (MUST NOT)
が、クライアントは旧バージョンのサーバーが送る -32002 を 引き続き受け入れるのが望ましい (SHOULD)
です。
12. Cloudflare Workers を使っているなら — McpAgent が非推奨になった
今回の仕様変更が実装にどんな波紋を起こすかを最もよく示す事例が Cloudflare です。2026年8月6日、
Cloudflare は「The next generation of MCP」を発表し、McpAgent を取り除きました。
なぜこれが自然な帰結なのかは、前の第1・2節を読んでいれば察しがつくはずです。これまで Workers で MCP
サーバーを動かすには、Durable Objects が事実上必須でした。Mcp-Session-Id で識別されるセッションと、
そのセッションに紐づく SSE ストリームを維持しなければならず、リクエストを同じインスタンスへ固定する
何かが必要だったからです。セッションが消えたことで、その前提そのものがなくなりました。
Cloudflare の表現はこうです。
This release of the MCP 2026-07-28 specification … removes the need for
McpAgent. While Durable Objects remain the right primitive when an application itself needs state, MCP itself no longer requires a Durable Object to speak the protocol.
ドキュメント上の McpAgent の現状は 「deprecated and feature-frozen」 です。いますぐ使えなくなった
わけではありませんが、新機能はもう追加されません。代替は createMcpHandler で、公式の説明は
「MCP SDK v2 のサーバーファクトリから、呼び出し可能な ステートレス な MCP リクエストハンドラーを
作る」というものです。
export default {
fetch(request, env, ctx) {
return createMcpHandler(createServer)(request, env, ctx);
},
} satisfies ExportedHandler;
Durable Object クラスも、セッションルーティングもありません。リクエストごとにサーバーを新しく作って 処理し、それで終わりです。
ここで誤解しやすい点が一つあります。Durable Objects が不要になったわけではありません。 なくなった のは「MCP プロトコルを話すための」DO です。アプリ自体に状態が必要なら、DO は今でも正しい道具です。 公式ドキュメントの案内が、まさにその区別を突いています。
Store cross-request data behind an authenticated handle in a Durable Object, D1, KV, or R2 rather than an MCP session ID.
第1節で見た「サーバーが発行した明示的なハンドル」がまさにこの話です。状態を置く場所は従来どおり DO・D1・KV・R2 であり、その状態を指す鍵を MCP のセッション ID ではなく、認証されたハンドルに 置き換えよということです。
移行の負担も一度に集中しません。Cloudflare は /mcp エンドポイントが「新プロトコルと、2025年の
Streamable HTTP クライアントによるステートレスなリクエストの両方を受け付ける」と述べています。旧型
クライアントを使う利用者が残っていても、サーバーを先に移せます。
コストについては注意が必要です。Cloudflare は「動く部品が減った分のコスト削減」という定性的な表現に とどめており、具体的な削減数値は示していません。 DO インスタンスが外れる分、料金が下がる方向で あることは確かですが、何パーセントという数字を期待して臨むべき話ではありません。
13. 移行チェックリスト
サーバーを運用中なら、順に確認しましょう。
-
server/discoverの実装 — 必須、なければ仕様違反 -
Mcp-Session-Idに依存するコードの除去 → サーバー発行のハンドルをツール引数へ -
initialize/notifications/initializedハンドラーの削除 - リクエストごとに
_metaからプロトコルバージョン・クライアント能力を読むよう変更 - すべての結果に
resultType: "complete"を追加 -
resources/subscribe・unsubscribe→subscriptions/listen - サーバー主導のリクエスト (
roots/list、sampling/createMessage、elicitation/create) → MRTR -
ping、logging/setLevelハンドラーの削除 -
logLevelのないリクエストにnotifications/messageを送らないようガードを追加 - リスト結果に
ttlMs、cacheScopeを追加 -
tools/listのソート (プロンプトキャッシュのヒット率) - エラーコード定数の再確認
- HTTP+SSE トランスポートを使っているなら Streamable HTTP へ
- tasks を使っているなら公式拡張へ移行 —
tasks/result→tasks/getのポーリング、tasks/updateの新設、tasks/listの削除 - 認可: 認可レスポンスの
issを検証 (MUST)、資格情報を issuer 単位でキーイングし、他の認可サーバーへ再利用しない (MUST NOT) - DCR を使っているなら
application_typeを指定し、さらに Client ID Metadata Documents への移行を検討 - Cloudflare Workers なら
McpAgent→createMcpHandler、状態は認証されたハンドルで
クライアントなら、ここにさらに二つ加わります。
-
resultTypeのないレスポンスを"complete"として扱う (旧型サーバー互換、MUST) - MRTR の再試行ループの実装 —
requestStateを そのまま エコーバック、再試行のidは元のリクエストと別に - SSE 再開への期待を排除 — ストリームが切れたら新しいリクエスト ID で再発行
おわりに
Tier 1 の SDK 四つ — TypeScript、Python、Go、C# — はリリース時点ですでに 2026-07-28 を話します。
Rust SDK はベータでサポートしています。公式発表も「セッション識別子に依存していた開発者には、ある程度の
移行コストがある」と認めています。
とはいえ方向性そのものは明確です。暗黙的なコネクション状態を取り払い、すべてをリクエストの中に明示的に 収めていく方向です。エッジやサーバーレスのようにインスタンスが立ち上がっては消える環境で MCP サーバーを 動かしているなら、今回の変更は損ではなく得です。セッションストアを抱え込む理由がなくなったからです。
非推奨期間は最低12か月なので、今日すぐにすべてを作り直す必要はありません。ただし server/discover は
例外です。あれは非推奨ではなく 新規の必須要件 なので、新しい仕様に対応していると言うためには、
いま入れる必要があります。