Anthropic、MCPの新仕様を発表――MCPサーバーをクラウドにデプロイ可能に
- これまでMCPサーバーには弱点がありました。クライアントは最初にハンドシェイクをしてセッションIDを受け取り、以降のリクエストは必ず同じマシンに戻らなければなりませんでした。マシンを増やしても意味がなく、サーバーレスでも動かせなかったのです。
- 新仕様はこのルールを廃止しました。ハンドシェイクとセッションIDをまるごと削除し、各リクエストが自分で身元情報を持つようにしたことで、どのマシンでも受け付けられます。ごく普通のロードバランサーを立てるだけで済みます。
- サーバーが処理の途中でユーザーに一言確認したい場合(たとえば「この操作は課金が発生しますが、よろしいですか」など)、今は2往復が必要です。まず「情報が足りません」と返し、クライアントがユーザーに確認してから、元のリクエストを丸ごと再送します。
- 状態管理そのものが消えたわけではなく、表舞台に移っただけです。サーバーがハンドルを発行し、モデルがそれをパラメータとして渡し返します。
- いくつかの機能は削除、あるいは猶予期間付きで廃止予定になりました。ハンドシェイク、セッションID、SSEの再接続はすでに削除。Roots、Sampling、Loggingと旧HTTP+SSEトランスポートは、少なくとも12か月は引き続き使えます。
- 第三者によるセキュリティ分析では、この仕様がセッションハイジャックのような旧来の脆弱性を消し去った一方、開発者に新たなセキュリティ責任を負わせている点が指摘されています。たとえば、1回のリクエストでサーバーに重い処理を起動させ、すぐに離脱するといった攻撃です。
- 最も実用的なポイント: 7月28日は一斉切り替えの日ではありません。今使っているサーバーとクライアントは壊れず、新しいクライアントが古いサーバーに接続した場合は自動的に旧来のハンドシェイクにフォールバックします。
旧MCPのボトルネック――セッションがサーバーを1台のマシンに縛りつけていた
2026年7月28日、MCP公式が第5版仕様2026-07-28を発表し、プロトコルの中核をステートフルな双方向接続からステートレスなリクエスト/レスポンス方式に転換すると同時に、正式な拡張フレームワークと廃止ポリシーを導入しました。Anthropicも同日、Claudeの全製品ラインでこれに追随すると発表しています。
MCPはAIアシスタントを外部のツールやデータに接続するためのプロトコルで、Anthropicが2024年末にオープンソース化して以降、業界全体で広く使われています。主要な4つのSDK(TypeScript、Python、Go、C#)の月間ダウンロード数は合計で約5億回に達し、うちTypeScriptとPythonはそれぞれ累計ダウンロード数が10億回を超えています。
今回の改訂が解決しようとしているのは、非常に具体的なエンジニアリング上の行き詰まりです。MCPサーバーをクラウドにデプロイして大勢に使ってもらうのが難しい、という問題です。
旧MCPは電話をかけるようなものでした。まずダイヤルして(initializeハンドシェイク)、オペレーターから内線番号(Mcp-Session-Id)をもらいます。以降はその内線番号で本人確認をします。オペレーターを変えたいですか?残念ながら、あなたがさっき何を話したか覚えているのは元のオペレーターだけです。
新MCPは宅配便のようなものです。荷物ひとつひとつに送り主・受取人・内容の説明が完全に書かれたラベルが貼られていて、誰が受け取っても処理できます。事前に誰かと打ち合わせる必要はありません。
「電話をかける」というこの設計は、3つの連鎖的な問題を引き起こしていました。
第一に、マシンを増やしても効果がありません。2台目のマシンはあなたの内線番号を知らないので、リクエストが届いてもそのまま固まります。スケールさせるには、ロードバランサーに「誰がどのマシンに行くべきか」を覚えさせる(これをスティッキーセッションと呼びます)か、全マシンでRedisのような共有ストレージからセッションを参照するしかありません。
第二に、サーバーレスやエッジノードでは動きません。こうした環境は「呼び出しのたびに完全に新規で、終わったら忘れる」ことを前提に設計されており、「セッションを覚えておく」という性質と根本的に相性が悪いのです。
第三に、間に立つゲートウェイがあなたの操作内容を理解できません。リクエストの内容はすべてJSONのリクエストボディに入っているため、経路上のロードバランサーや制限機構、ファイアウォールが「今回はどのツールを呼んでいるか」に基づいてルーティングやレート制限をかけようとすると、そのJSONを解析するコストが高く、多くのゲートウェイはそもそもそこまでやっていません。
(誰がどのマシンか覚える必要)
アイドル
ここだけ
アイドル
必ずインスタンス2に戻る必要がある
(ラウンドロビンでOK)
どのリクエストでも受けられる
ステートレスな中核は具体的にどう実現されているか
では、新仕様はどうやってこの行き詰まりを解決したのでしょうか。3つのポイントがあります。
ハンドシェイクが消えた
initializeとnotifications/initializedという一連の開始儀式は完全に削除され、Mcp-Session-IdというHTTPヘッダーも削除されました。以前はハンドシェイク時にやり取りしていた情報――プロトコルバージョン、クライアントは誰か、クライアントがどの能力をサポートしているか――は、今では各リクエストが自分で持ち、_metaフィールドに入れて送ります。サーバー側も、返す結果ごとの_metaに自身の身元を報告することが推奨されています。
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"search","arguments":{"q":"otters"},
"_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}
サーバーの機能を事前に確認する新しい方法
新たにserver/discoverが追加され、サーバーがサポートするプロトコルバージョン、どんな能力を持つか、自分は何者かを確認できるようになりました。ここでの役割分担は明確です。サーバーは必ずこれを実装しなければならず、クライアントは使わなくても構いません。クライアントが望めば、何かを始める前に一度これを問い合わせることができますが、問い合わせずに直接リクエストを送っても完全に合法です。これは必須の開始儀式だった旧来のinitializeとは本質的に異なり、あくまで一回限りの通常のクエリです。
仕様が明言していること――接続とセッションはイコールではない
サーバーは、同一接続上の以前のリクエストからコンテキスト(能力、プロトコルバージョン、クライアントの身元を含む)を推測することに依存してはならない。各リクエストは_metaでこれらの情報を自ら提供する。
開いたままの接続――たとえばずっと動き続けるローカルのstdioプロセスのようなもの――であっても、それは会話ではない。クライアントは同一のパイプの中にまったく無関係なリクエストを詰め込んでも構わず、サーバーは「接続の身元」を「セッションの身元」として扱ってはならない。
ちなみに、subscriptions/listenのような長時間接続もステートレス性を壊しません。それ自体は依然として通常のリクエスト/レスポンスであり、ただその「レスポンス」が開いたままの通知ストリームであるというだけです。その状態はそのリクエストに属するものであり、下層の接続には属しません。
重要な点: 状態は消えたのではなく、暗がりから明るい場所へ移っただけ
ここで最も誤解されやすいポイントがあります。プロトコル層にセッションがなくなったからといって、あなたのアプリケーションがステートレスでなければならないわけではありません。
もしサーバーが複数回の呼び出しをまたいで何かを覚えておく必要があるなら、公式が示している方法はこうです。ツールから明確な「ハンドル」を発行し、モデルがそれを普通のパラメータとして扱い、以降の呼び出しで渡し返す、というものです。
- セッションIDはトランスポート層が管理し、モデルからは見えない
- 状態は接続に付随し、接続が切れると状態も消える
- 別のマシンで処理すると迷子になる
- 問題が起きても調査しづらい。状態がビジネスコードの中にないため
- サーバーがツールからハンドルを返す。これは普通の戻り値にすぎない
- モデルからそれが見え、自分でツール間を連携させられる
- ツールのパラメータとして渡され、接続とは無関係
- 状態はビジネス層が管理し、参照も調整もできる
公式は発表ブログでこの方式を選んだ理由として、モデルがこの状態を見られる点がトランスポート層に隠れたセッション状態より優れていると述べています。
これはトランスポート層にセッション状態を隠すよりも扱いやすいことがわかりました。モデルにはこのハンドルが見えるため、自分でツール間をまたいで連携させられます。
MCP公式発表ブログ、2026-07-28(訳文)
ここには指摘しておくべきトレードオフがあります。状態管理という仕事自体が消えたわけではなく、「トランスポート層がこっそり代行してくれる」から「あなた自身がビジネス層で明示的に管理する」に変わったのです。メリットは透明性・追跡可能性・水平スケーラビリティ。代償は、それが今やあなたのビジネス層の仕事になったことです。
MRTR: サーバーがユーザーに一言確認したいときは、今は2往復必要
サーバーがもう能動的に話しかけられないなら、途中でユーザーに確認したいときはどうするのでしょうか。
まずどんな場面かを説明します。あるツールが処理の途中で、ユーザーに何かの確認を求める必要があるケースです。SupabaseのMCPサーバーが新しいプロジェクトを作成する際、まず「これは課金が発生しますが、よろしいですか」と一言確認したい。あるいはデータを削除するSQLの場合、先にユーザーにOKをもらう必要がある、といったケースです。
以前のやり方は、サーバーが開いたままの双方向ストリームから逆方向にクライアントへリクエストを送り、一言尋ねる(elicitation/create)、クライアント側のモデルを借りて計算する(sampling/createMessage)、あるいはファイルディレクトリの場所を尋ねる(roots/list)というものでした。前提として、そのストリームがずっと開いている必要があり、これはまさにステートレス化が取り払おうとしているものです。
新しいやり方はMRTR(Multi Round-Trip Requests、複数往復リクエスト)と呼ばれます。サーバーはもうリクエストを送らず、代わりに「まだ完了していない結果」を返し、尋ねたい質問をその中に入れます。クライアントがユーザーに確認したあと、答えを添えて元のリクエストを丸ごと再送します。以下の5ステップをクリックしてその流れを確認できます。
(Claudeなど)
"params":{"name":"create_project", …}}
クライアントはいつも通りツール呼び出しを開始し、リクエストIDは1です。この段階は以前と変わりません。
"resultType":"input_required",
"inputRequests":{"confirm":{…}},
"requestState":"署名付きの不透明データ"}}
サーバーは情報が足りないことに気づき、結果を返します(リクエストを送るのではない点に注意)。resultTypeをinput_requiredとし、inputRequestsに尋ねたい質問を入れます。
requestStateはサーバーがクライアントに預ける不透明なデータで、「自分がどこまで処理を進めたか」を記憶するためのものです。これによりサーバー自身は何も保存しなくて済みます。
クライアントはユーザーに質問を提示します。「この新規プロジェクトの作成には課金が発生しますが、よろしいですか?」ユーザーは確認をクリックします。
この段階にサーバーは一切関与しません。この間にサーバーが回収されて別のマシンに切り替わっても構いません。規約により、クライアントはrequestStateの中身を閲覧・解析・改変してはならず、それについていかなる想定もしてはならず、そのまま保管するだけです。
"params":{"name":"create_project", …,
"inputResponses":{"confirm":{…}},
"requestState":"そのまま持ち帰る"}}
クライアントは回答をinputResponsesに入れ、元のリクエスト全体を再送し、requestStateもそのまま持ち帰ります。
リクエストIDは必ず新しいものにしなければなりません(ここでは2)。仕様が明確に要求しており、これはプロトコル上では2つの独立したリクエストとみなされるためです。
サーバーは今度は情報が十分にそろい、処理を実行して最終結果を返します。
この一連のやり取りは、どの段階も「同一マシン」や「同一接続」に依存していません。
Supabaseのプロダクト責任者は、このパターンが長年やりたかったことを実現させたと語っています。
ユーザーへの確認をサポートすることは、以前からロードマップに載っていましたが、Supabase MCPがステートレスで動作していたため、なかなか実現できませんでした。MRTRがそれを変えたのです。おかげで、私たちのツールは実行前にユーザーへ確認を取れるようになりました。たとえば新規プロジェクト作成の課金や、データを削除するクエリについてです。
Inian Parameshwaran、Supabaseプロダクト責任者(訳文)
代償は2か所にある
第一に、元のリクエストを丸ごと再送する必要があります。サーバーは、中間結果をrequestStateにエンコードしておかない限り、前回の処理をやり直す羽目になる可能性があります。
第二に、requestStateは慎重に扱うべき手榴弾のような存在です。これはクライアントの手元を一巡してから戻ってくるため、仕様の要求はかなり厳格です。
サーバーはrequestStateを攻撃者が制御可能な入力として扱わなければならない。これが認可・リソースアクセス・ビジネスロジックに影響する場合、サーバーはHMACやAEADなどの手段で完全性を保護しなければならない(誰にも偽造できない封印を押すようなもの)。検証に失敗したものは直ちに拒否する。この保護を省略できるのは、改ざんされてもせいぜい今回のリクエストが失敗するだけで、それ以上悪い結果にならない場合のみである。
リプレイ防止についてはこの仕様は「すべき」レベルにとどまっており、具体的にはこの保護されたデータの中に3つの要素を入れて逐一チェックすることが挙げられている。誰のものか(別人が流用した場合は拒否)、いつまで有効か(タイムアウトしたら拒否)、元はどのリクエストか(メソッド名と主要パラメータのダイジェストが一致しなければ拒否)。
仕様はさらに率直にこう補足している。これらの対策はリプレイ可能な時間枠を縮小し、ユーザー横断・リクエスト横断の再利用を防ぐだけであり、「一度きりの使用」を保証するものではない。本当に一度きりが必要な場面(たとえばワンタイムの引き換えコードなど)は、サーバー側で別途保証する必要がある。
サーバーが不要になった共有ストレージの代わりに、正しい暗号署名を自分で書く必要が出てきたわけです。
もう一点、MRTRには適用範囲があります。prompts/get、resources/read、tools/callの3つのメソッドでのみ使用可能で、それ以外のリクエストではサーバーはinput_requiredを返してはなりません。
ゲートウェイがようやくMCPを理解できるようになった(発表ブログに載っていないセキュリティ機構つき)
状態の問題は解決しました。残るはもう一つの古い問題です。中間のゲートウェイは、これまで何が呼び出されているのか把握できませんでした。
新仕様は、各POSTリクエストに必須のHTTPヘッダーを追加しました。Mcp-Methodは各リクエストが必ず持ち、今回呼び出しているのがどのメソッドかを示します。Mcp-Nameは具体的な対象を指定する場合、すなわちtools/call、resources/read、prompts/getの3種類のリクエストのときだけ付き、呼び出し対象のツール・リソース・プロンプトを示します。もともと常に付いていたMCP-Protocol-Versionと合わせて、ゲートウェイ・レート制限機構・ファイアウォールはこの数行を読むだけで済むようになりました。
どのツールが呼ばれているかを知るには、JSONリクエストボディ全体を解析するしかありません。高スループットのゲートウェイにとってこのコストは割に合わず、多くのゲートウェイはそもそもこれをやっていません。
メソッドとツール名によるルーティング・レート制限・計測・認可を、すべてヘッダー層で完結できます。リクエストボディに触れる必要はありません。
純粋な利便性機能のように聞こえますが、これには厳格な検証が1つ紐づいています。これは発表ブログには書かれていません。
サーバーはMcp-Method、Mcp-Nameなどのヘッダー値がリクエストボディ内の対応する値と一致するかを検証しなければならず、一致しない場合はHeaderMismatchエラー(エラーコード-32020)を返す。
なぜこれが必須なのか。検証しなければ、攻撃者はヘッダーに無害なツール名を書いてゲートウェイを通過させつつ、リクエストボディでは別の危険なツールを呼び出すことができてしまいます。ゲートウェイはヘッダーを見て通過させ、サーバーはボディを見て実行する――その間に穴が空くのです。
仕様は中間層にも特に念を押しています。プロトコルバージョンが古すぎる場合、あるいはヘッダーがそもそも付いていない場合は、検証されていないヘッダーを信用するより、いっそ拒否すべきだと。
もう一つ、新しくx-mcp-headerという仕組みもあります。サーバーはツールのパラメータ定義で「このパラメータをHTTPヘッダーに複製する」と指定でき、ヘッダー名はMcp-Param-{名前}となり、テナントなどの軸でのルーティングに便利です。この機能はサーバー側では任意ですが、クライアントは対応が必須です。これを覚えておいてください。あとでセキュリティの話でまた登場します。
ツール一覧がついにキャッシュ可能に(上流のプロンプトキャッシュも守られる)
ステートレス化にはもう一つ副産物があります。一覧結果が接続に依存して変化しなくなったため、キャッシュできるようになったのです。
tools/list、prompts/list、resources/list、resources/read、resources/templates/listの5つのメソッドの戻り値には、今後2つのフィールドを必ず含めなければなりません。
publicかprivateかで、経路上の共有プロキシがみんなのために一括キャッシュできるかどうかが決まります。もう一つ、要求レベルはやや低いものの、価値ははるかに大きい項目があります。サーバーはツール一覧を確定した順序で返すべきである。
これがなぜ価値があるのか説明が必要です。ツール一覧はモデルのプロンプトに組み込まれるものであり、プロンプトのキャッシュは先頭から逐字マッチングされます。冒頭が一文字でも変わると、それ以降のキャッシュはすべて無効になり、再計算・再課金されます。ツール一覧の返却順序が毎回バラバラだと、上流のキャッシュは永遠にヒットしません。
「順序を安定させる」という小さな要求が、実際に浮かせる金額は無視できません。
何が削除され、何が猶予期間付きになったか
次は最も入念にチェックすべき部分です。手元のものがどれくらい壊れるか。今回のchangelogは合計で重大な変更9件、軽微な変更12件、廃止4件です。以下の2つのリストは特に注視すべきものです。
initialize/notifications/initializedハンドシェイクMcp-Session-Idヘッダーとプロトコルレベルのセッション- HTTP GETエンドポイント、
resources/subscribeとresources/unsubscribe統一されたsubscriptions/listen長時間ストリームに置き換え。クライアントは通知タイプごとに個別に購読する ping、logging/setLevel、notifications/roots/list_changedログレベルは各リクエストの_metaにlogLevelを含める方式に変更- SSEの再接続(
Last-Event-IDヘッダーとSSEイベントID)ストリームが切れると、進行中のリクエストはそこで失われる。クライアントは新しいリクエストIDに切り替えて丸ごと再送する必要がある tasks/resultとtasks/listtasks/getによるポーリングに変更notifications/elicitation/completeとURLパターンによる確認のelicitationId- サーバーが能動的にリクエストを送るというパターンそのものすべてMRTRに置き換え
- Roots(サーバーにどのディレクトリを見るべきか伝える)ツールのパラメータ、リソースURI、サーバー設定で伝える方式に変更
- Sampling(サーバーがクライアントのモデルを借用する)大手モデルベンダーのAPIに直接接続する方式に変更
- Logging(プロトコル層のログ)
stderrへの出力、あるいはOpenTelemetryの利用に変更 - 旧HTTP+SSEトランスポート2025-03-26の時点ですでにソフトに廃止予定だったが、今回正式に廃止状態に組み入れ、Streamable HTTPへの移行を求める
includeContextのthisServerとallServersの2つの値指定しないか、noneを使う- 動的クライアント登録(DCR)CIMDへの移行を推奨。互換性のためまだ残っているが、将来のバージョンで削除予定
エラーコードも変わった
リソースが見つからない場合のエラーコードは-32002から標準の-32602(無効なパラメータ)に変更され、JSON-RPC仕様と整合させました。さらに3つの新しいエラーコードが割り振られています。-32020はヘッダーとボディの不一致、-32021は必須能力の欠如、-32022はプロトコルバージョン非対応です。仕様は領分もはっきりさせています。-32000から-32019はレガシー領域で、これらの番号はポリシー導入前に各社が独自に占有していたものであり、今後はこの範囲に新しい番号を割り当ててはならず、新規実装もこれらを使うべきではありません。-32020から-32099は仕様専用です。
もしあなたのクライアントコードに-32002という数値がハードコードされていれば、今回修正が必要です。ただし修正しすぎないよう注意してください。仕様は同時に、クライアントが旧バージョンのサーバーから送られてくる-32002を引き続き受け入れることを要求しています。自分から発行しなくなるだけです。
廃止ポリシー自体が今回の新しい取り組み
以前は廃止のタイミングは完全に暗黙の了解に頼っていましたが、今回明文化されました。仕様は機能の3つの状態と最短期間を定義しています。
承認される
12か月
この12か月は「その機能が廃止マークされたバージョンの仕様が発表された時点」から起算され、提案が確定した時点からではありません。これに加えて2つの付随ルールがあります。1つは統一された「削除待ちリスト」ページが用意され、各バージョンのchangelogを自分でつなぎ合わせて確認する必要がなくなったこと。もう1つは、主要SDKには次のバージョンで各言語のネイティブな方式で廃止を明示することが求められている点です(TypeScriptの@deprecated、C#の[Obsolete]、Javaの@Deprecated、GoのDeprecated:コメントなど)。これができない場合は降格プロセスに入ります。
拡張機能が正式な入口になった
中核がスリムになった今、新機能はどこに追加すればいいのでしょうか。
以前は、MCPに機能を追加したければ中核プロトコルに詰め込むしかありませんでした。Tasksもかつては「実験的機能」として中核にぶら下がっていました。今回、正式な拡張フレームワークが確立されました。中核はスリムなまま保ち、新機能は拡張として実装し、クライアントとサーバーはそれぞれ能力の中でどの拡張をサポートしているかを宣言します。
tasks/getで進捗をポーリングする。Tasksは AWS が貢献した最初期の公式拡張のひとつです。AWSによれば、新仕様とそのステートレスな中核は、すでにAmazon Bedrock AgentCoreで利用可能とのことです。Tasksの仕組みは詳しく触れておく価値があります。あるリクエストが長時間かかるとサーバーが判断すると、最終結果ではなくタスク番号を返します。このタスク番号は永続的なハンドルであり、クライアントが接続を切っても、再起動しても、同じ番号で引き続きポーリングできます。タスクには5つの状態があります。
濃い色の3つは終了状態で、そこに到達すると以後変化しません。途中でユーザーへの確認が必要になると、タスクはinput_requiredに移行し、クライアントは新設のtasks/updateで回答します。2本目の接続は不要で、サーバーが能動的にメッセージを送る必要もありません。
認可まわりが4か所引き締められた
削除・追加の話は一通り終わりましたが、公式が単独で取り上げているポイントがまだあります。この1年で実装者が最も時間を費やしたのが認可の領域でした。今回、4か所が変更されました。
localhostコールバックアドレスを拒否してredirect_uriエラーを返していました。コマンドラインクライアントのOAuthフローで
MCP公式発表ブログ、application_typeの変更について(訳文)redirect_uriエラーが出るのはなぜだろうと悩んだことがある人は、たいていこれが原因です。
第三者によるセキュリティ分析: 責任がプロトコルから開発者へ移った
ただし、ここまでは公式見解です。仕様の正式発表前、第三者がこの新仕様を攻撃対象として調査しており、Akamaiが6月に分析結果を発表し、その内容はSecurityWeekで報道されました。
彼らの見解はこうです。新仕様は確かにいくつかの脆弱性を排除した一方で、同時にいくつかの新しい攻撃対象領域を開いており、しかもこれらの新リスクは開発者がどう実装するかに大きく依存する、というものです。
- セッションハイジャック——セッションがなくなったので、ハイジャックする対象もなくなった
- サーバーからの能動的なプロンプト表示——サーバーはもう能動的にリクエストを開始できず、クライアントが尋ねてくるのを受動的に待つのみ
- より強固な認証基準——前節で触れた4か所の引き締め
- 状態オブジェクトや追跡識別子が予測可能な場合: 他人が進行中のワークフローを乗っ取られたり、別のAgentのデータを取得されたり、テナントをまたぐ権限逸脱操作を引き起こされたりする
x-mcp-headerによる情報漏洩: 開発者がうっかりAPIキー、トークン、個人情報をHTTPヘッダーにマッピングしてしまうと、これらの秘密情報はそのままヘッダーに押し込まれ、経路上のあらゆるロードバランサー・プロキシ・ログシステムから丸見えになる- プロトコル混同(Desync): ヘッダーとリクエストボディの不一致により、ヘッダーベースのセキュリティ制御を回避する。これはまさに仕様上の強制検証が防ごうとしているものだが、実際に防げるかどうかはサーバー実装者が真面目に実装しているかどうかにかかっている
- MCP Appsがブラウザの旧来の問題を持ち込んだ: 格納型クロスサイトスクリプティング(XSS)など
- Tasksは「起動して逃げる」型のサービス拒否経路になりうる: タスクの作成はクライアント側にとっては安価だが、サーバー側にとっては高コストである。攻撃者は1回のリクエストだけで、サーバーにCPU・メモリ・データベースストレージを食う重いタスクを起動させ、直後に切断して立ち去ることができる
プロトコルがステートレスモデルに転換し、さらにリッチUIアプリと非同期タスクを導入した以上、重要なセキュリティ境界は今や完全に開発者の実装方法次第になっています。
Maxim Zavodchik氏、Akamai脅威リサーチ シニアディレクター、SecurityWeekの報道より(訳文)
彼らの結論は、これらの変更の重みは漸進的な改善を超えているというものです。それは安全に関する責任の所在を根本的に作り変えました。以前はプロトコルによって強制的に保証されていたセキュリティ上の判断が、今ではますますMCPサーバーの開発者とプラットフォーム運用者に委ねられるようになっています。
報道では、同じくらい重要な一言も補足されています。MCPプロトコル自体が脆弱になったわけではなく、新仕様の上に構築されたMCPサーバー群の攻撃対象領域が拡大したのです。
今すぐ手を動かすべきか
これだけ破壊的変更の話をしてきたので、最後に最も実務的な質問に答えましょう。答えは「最大規模の破壊的アップデート」という印象とは裏腹のものです。
7月28日は一斉切り替えの日ではありません。既存のクライアントとサーバーは今日壊れませんし、7月28日にも壊れません。その日はあくまで仕様の正式文書が発表された日であって、旧プロトコルをオフにする日ではありません。
具体的な互換パスは3つあります。
1つ目、新しいクライアントは古いサーバーに出会うと自動的にフォールバックします。2026-07-28を名乗るクライアントが、2025-11-25以前しか話せないサーバーに出会うと、元のinitializeハンドシェイクにフォールバックし、新旧が問題なく相互接続します。逆に、古いサーバーはプロトコルバージョンヘッダーの付いていないリクエストを受け取った場合、2025-03-26として扱うことができます。
2つ目、SDKのメジャーバージョンアップは別の話です。PythonとTypeScriptはどちらもv2をリリースしており、それぞれ独自の破壊的メジャーバージョンです。いつアップグレードするかは自分で決めればよく、7月28日とは関係ありません。TypeScript SDKのv1.xは、v2正式リリース後も少なくとも6か月はバグ修正とセキュリティ対応を継続します。Pythonのv1.xブランチも重大バグ修正とセキュリティパッチを継続して受け付けます。
3つ目、SDKをアップグレードすること自体は、必ずしも回線上で何を話すかを変えるわけではありません。しかも4つのSDKで挙動が異なる点は要注意です。
2026-07-28を話させるには、配線時に明示的に選択する必要があります。主要な4つのSDK(TypeScript、Python、Go、C#)は発表当日から新仕様に対応し、Rust SDKはbeta対応です。実際に手を動かして修正すべきなのは以下のカテゴリです。一つずつ確認してください。
MCPは開始ハンドシェイクとセッション番号を削除。1つのリクエストがどのマシンでも受け付けられるように
MCP公式が第5版仕様2026-07-28を発表し、プロトコルの中核を「話したら忘れる」方式に変更。何が変わり、何が壊れるかを一枚にまとめました。
↓ 一枚で読み切る · 動く図が1つあります
MCPはAIアシスタントを外部のツールやデータに接続するためのプロトコルで、Anthropicが2024年末にオープンソース化して以降、業界全体で使われています。使う人が増えたことで、あるエンジニアリング上の行き詰まりが無視できなくなりました。MCPサーバーをクラウドにデプロイして大勢に使ってもらうのが難しい、という問題です。
ダウンロード数とディレクトリ数はMCP公式とAnthropicがそれぞれ公表した数値、約20%はhoneycomb.io自身の発表で、いずれも第三者による再検証はされていません。
✔ 接続がずっと開いていれば、サーバーが逆にユーザーに一言尋ねることもできる
✘ サーバーレス(従量課金で終わったら回収されるクラウドサービス)やエッジノードでは動かない: そうした環境は呼び出しのたびに完全に新規で、生まれつき何も覚えていない
✘ 経路上のゲートウェイが理解できない: どのツールが呼ばれているか知るには、リクエスト全体を解析する必要があり、コストが高いので、大半のゲートウェイはそもそもやっていない
新仕様は開始ハンドシェイクとセッション番号を一緒に削除しました。各リクエストが自分でプロトコルバージョン・クライアントの身元・対応する能力を持ち、サーバーは処理が終われば忘れます。ロードバランサー(リクエストを複数マシンに割り振る調整役)はもう誰がどのマシンに属するか覚える必要がなく、順番に振り分けるだけで済みます。
プロトコル層にセッションがなくなっても、アプリケーション側は変わらず何かを覚えておけます。やり方が別の場所に移っただけです。サーバーがツールから表舞台に出したハンドルを発行し、モデルがそれを普通のパラメータとして扱い、次の呼び出しで渡し返します。
セッション番号はトランスポート層が管理し、モデルからは見えません。接続が切れると状態も消え、別マシンに移ると迷子になり、問題が起きても調査しづらい。
ハンドルはツールの戻り値の一つにすぎません。モデルからそれが見え、自分でツール間を連携できます。ビジネス層が管理し、参照も調整もでき、接続とは無関係です。
状態管理という仕事自体は消えたわけではなく、「トランスポート層がこっそり代行する」から「あなた自身がビジネス層で明示的に管理する」に移っただけです。
サーバーはもう能動的に話しかけられません。では途中でユーザーに確認したいときはどうするのか。新仕様が示す答えはMRTR(複数往復リクエスト)です。サーバーはまず「情報が足りません」という半完成の結果を返し、クライアントがユーザーに確認したあと、答えを添えて元のリクエストを丸ごと再送します。
代償は2か所にあります。元のリクエストを丸ごと再送する必要があり、サーバーは前の処理をやり直す羽目になるかもしれません。そのハンドルはクライアントの手元を一巡してから戻ってくるため、仕様はこれを「攻撃者が自由に改ざんできる入力」と定めています。認可や権限に関わるものは、サーバーが偽造不可能な署名を付ける必要があり、検証を通らなければ即座に拒否します。不要になった共有ストレージの代わりに、正しい暗号署名を自分で書く必要が出てきたのです。
今回の変更リストは重大な変更9件、軽微な変更12件、廃止4件です。特にセルフチェックすべきなのはこの2つのリストです。
✘ 再接続機能: ストリームが切れると、進行中のリクエストは失われ、新しい番号に切り替えて丸ごと再送する必要がある
✘ サーバーが能動的にリクエストを送るというパターンそのもの。すべてMRTRに変更
✘ リソースが見つからない場合のエラーコードが-32002から標準の-32602に変更。この数値をハードコードしていたコードは修正が必要
✔ 旧HTTP+SSEトランスポートも同様に12か月の猶予期間があり、その後Streamable HTTPに移行
✔ 動的クライアント登録(DCR)はCIMDへの移行を推奨。互換性のため一時的に残されている
握手して、
セッション番号をもらう。
以降は必ず
同じマシンに戻る。
無駄に空いてる
月間ダウンロード数合計
、削除
セッション番号
、削除
エッジノードも
動かせるように
どうやって確認を取るの?
クライアントの手を
一巡してから戻る。
仕様は攻撃者が
改ざんできる
入力とみなし、署名
検証に失敗したら即拒否。
- × 開始ハンドシェイクとセッション番号
- × SSE再接続、切れたら丸ごと再送
- × サーバーが能動的にリクエストを送るパターンそのもの
- × エラーコード-32002が-32602に変更
Loggingと旧
HTTP+SSEトランスポートは、
この時まで猶予
徹夜で直さないと?
代償は、状態管理もセキュリティもすべて開発者自身の手に落ちてきたことだ。
