MCPの拡張機能ネゴシエーションはどう合意形成されるか
MCPの拡張機能はcapabilities.extensionsフィールドで宣言し合います。識別子の設計・対応していない側の挙動・コア仕様と切り離した理由を仕様から読み解きます。
MCPの拡張機能ネゴシエーションはどう合意形成されるか
MCPの拡張機能とは、コア仕様には含まれない任意の追加機能を指します。クライアントとサーバーは、capabilitiesオブジェクトのextensionsフィールドで「自分がどの拡張機能に対応しているか」を互いに宣言し合い、双方が対応している拡張機能だけを実際に使います。
このフィールドは拡張機能識別子(io.modelcontextprotocol/uiのような文字列)をキーに、拡張機能ごとの設定オブジェクトを値に持つマップです。対応さえ表明すればよく、設定が要らない拡張機能は空オブジェクト{}を渡します。相手が対応していない拡張機能を使おうとした場合の挙動は仕様で明確に決められており、エラーも出さずに黙って終わることはありません。
capabilities.extensionsフィールドはどう書くか
クライアント側の宣言は、2026-07-28リビジョンではリクエストごとの_meta内、io.modelcontextprotocol/clientCapabilitiesというキーの中に載せます。
{
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
}
}
}サーバー側はserver/discoverという、他のリクエストと同じ形の1本のリクエストへの応答の中でcapabilities.extensionsを返します。
{
"result": {
"capabilities": {
"extensions": {
"io.modelcontextprotocol/ui": {}
}
}
}
}2026-07-28より前の版(2025-11-25以前、仕様上は「legacy」と呼ばれます)では、このcapabilities交換はinitializeハンドシェイクの中で1回だけ行われていました。2026-07-28ではハンドシェイクそのものが廃止され、プロトコルバージョンとクライアント情報はリクエストのたびに_metaで運ばれます。拡張機能の宣言方法自体(capabilities.extensionsというフィールド)は両リビジョンで同じで、運び方だけが「接続確立時に1回」から「リクエストごと」に変わったということです。
この変更は単なる書式の移動ではありません。接続確立時の1回きりの宣言だと、そのリクエストを受けたワーカーだけが相手の対応状況を覚えていることになり、後続のリクエストを別のワーカーが処理する構成では状態を共有する仕組みが要ります。拡張機能の宣言をリクエストごとに載せる設計にしたことで、単純なラウンドロビン型のロードバランサーの背後にサーバーインスタンスを何台並べても、どのリクエストも自己完結した情報だけで処理できるようになっています。
拡張機能識別子はなぜベンダープレフィックス必須なのか
拡張機能識別子は{ベンダープレフィックス}/{拡張機能名}という形式で、io.modelcontextprotocol/oauth-client-credentialsのように書きます。プレフィックスは省略できません。この必須化は衝突回避が理由です。
公式拡張機能はio.modelcontextprotocolというプレフィックスを使います。サードパーティが独自の拡張機能を作る場合は、自分が所有するドメインを逆順にしたものをプレフィックスにします。example.comというドメインを持つ会社ならcom.example/my-extensionという形です。Javaのパッケージ命名規則と同じ発想で、複数の組織が同じ名前の拡張機能を別の意味で作ってしまう事故を防いでいます。
破壊的変更を加えるときのルールも識別子に紐づいています。フィールドの削除・リネーム、型の変更、既存の挙動の意味変更、必須フィールドの追加はいずれも破壊的変更にあたり、これらを行うときは同じ識別子のまま更新するのではなくio.modelcontextprotocol/my-extension-v2のように新しい識別子を発行します。設定オブジェクト内でのバージョニングや機能フラグで済ませられるならそちらを優先し、識別子の使い捨ては最後の手段という位置付けです。
対応していない側は何が起きるか — グレースフルデグラデーション
一方だけが拡張機能に対応している状況は普通に起こります。このとき対応している側は、コア仕様の挙動に戻るか、適切なエラーで拒否するかのどちらかを選ばなければなりません。仕様はこの二択をMUSTで要求しています。
具体例で考えると分かりやすくなります。UI要素を返せる拡張機能(MCP Apps)に対応したサーバーは、その拡張機能に対応していないクライアント相手にも意味の通るテキストコンテンツを返し続ける必要があります。逆に特定の認証拡張機能を必須にしたいサーバーは、その拡張機能に対応していないクライアントからの接続そのものを拒否してよい、という非対称な設計です。前者は「無くても困らない機能」、後者は「無いと成立しない機能」という拡張機能の性格の違いがそのまま挙動の違いになっています。
拡張機能を作る側には、この2択のどちらを選ぶかを仕様書に明記することが推奨されています。フォールバック挙動が書かれていない拡張機能は、対応していないクライアントに出会ったときに何が起きるか誰にも分からない、という状態を生みます。
この推奨は拡張機能の作者の善意任せではありません。拡張機能の仕様書はコア仕様と同じRFC 2119の用語(MUST/SHOULD/MAY)を使うこと、コア仕様の一部であるかのような書きぶりにすることがSEP-2133で定められています。フォールバック挙動をSHOULDで書くという推奨自体が、この共通言語のルールの上に成り立っているということです。
拡張機能はなぜコア仕様と切り離して設計されているのか
拡張機能という仕組み自体を定めたSEP-2133は、設計方針として3つの原則を掲げています。まず単純に始めること、次にガバナンスを明確にすること、そして経験を積みながら後で調整することです。この3つは実装の細部を先に固めるより、誰が何を承認できるかという体制を先に固める発想です。
バージョンをコア仕様から意図的に切り離した理由も明記されています。拡張機能は追加的で任意なので、コアのバージョン番号と足並みを揃える必要がありません。別々にバージョニングすることで、拡張機能ごとの反復を速くできるというのが設計判断です。コア仕様の改訂を待たずに、拡張機能だけを頻繁に更新できます。
拡張機能を個別リポジトリ(ext-プレフィックス)単位で管理する理由も、ガバナンスの分散が目的です。同じ領域で互換性のない拡張機能が乱立する事故を、リポジトリという自然なまとまりとメンテナーによる審査で防ぎます。コアメンテナーによる審査を公式拡張機能に必須としていない理由も同根で、コアメンテナーの審査は数か月かかることが珍しくないボトルネックであり、そこを経由しない委任型のレビューによって拡張機能はコア仕様の改訂サイクルに縛られず独自に進化できます。
コア仕様との切り離しは永久固定の扱いではありません。拡張機能が十分に成熟すれば、コアメンテナーによる別のレビューを経てコア仕様そのものに昇格する道も用意されています。ただし業界特化のロジックのように性質上コアに組み込むのが適さない拡張機能は、昇格しないまま拡張機能であり続けることも織り込み済みです。
この仕組みが意図的に決めていないこと
SEP-2133は拡張機能の骨格を決める一方で、いくつかの論点をあえて仕様の対象外としています。拡張機能自身がスキーマをどう変更するかを表明する仕組み、拡張機能同士やコア仕様の特定バージョンへの依存関係、複数の拡張機能をまとめて扱う「プロファイル」という概念は、いずれも未規定です。
これは検討漏れではなく、初期段階の骨組みをまず動かし、経験を積んでから拡張する意図的な先送りです。capabilities.extensionsという宣言の形だけを先に固定し、その中身の設計自由度を拡張機能の書き手に委ねることで、コア仕様の改訂を待たずに拡張機能の実装例を先に積み上げられる、という順序になっています。
正式リリースまでの道筋も決まっています。まずSEPとして提案し、少なくとも1つの公式SDKでリファレンス実装を作り、コアメンテナーの審査を経て、拡張機能リポジトリへ追加されて初めて他のクライアント・サーバー・SDKが実装できる状態になります。実験段階のものはexperimental-ext-というプレフィックスのリポジトリに置かれ、必ずWorking GroupかInterest Groupに紐づけられ、READMEとパッケージ名の両方に実験的であることを明記する決まりです。
セキュリティ面の扱いも仕様書に明記されています。拡張機能は自分が拡張する領域のセキュリティのベストプラクティスを実装しなければならず、クライアントとサーバーの双方は拡張機能によって追加されたフィールドやデータを信頼できないものとして扱い、十分に検証すべきだとされています。拡張機能はコア仕様のメンテナーの審査を経ずに追加できる分、受け取った側が中身を鵜呑みにしない前提が仕組みに織り込まれているということです。
既存の公式拡張機能にはどんなものがあるか
公式拡張機能はmodelcontextprotocol組織のext-で始まるリポジトリに置かれています。認証まわりはext-authが持ち、OAuth 2.0のクライアントクレデンシャルフローや、企業向けの集中管理型アクセス制御を提供します。対話的なUI要素を返すMCP Appsはext-appsが持ち、長時間実行タスクのポーリング・処理途中の入力受付を扱うMCP Tasksも公式拡張機能の1つです。
これらの拡張機能をクライアント側でどこまで実装しているかの対応状況はMCP拡張機能のサポートマトリクスの読み方、MCP AppsとExtensionsの関係はMCP Appsの対応クライアント一覧とExtensionsの仕組み、MCP Tasksがなぜコア仕様から拡張機能へ移ったかはMCP Tasksが拡張機能に移った理由で扱っています。本記事はネゴシエーションという合意形成の仕組みそのものに絞り、個々の拡張機能の対応状況には立ち入りません。
SDK側の実装も義務ではありません。SDKメンテナーは自分たちが対応する拡張機能を自由に選べ、対応する拡張機能があるならドキュメントにその一覧を載せることになっています。拡張機能は既定で無効で、開発者の明示的なオプトインが必要という前提も共通しています。
まとめ
MCPの拡張機能ネゴシエーションは、capabilities.extensionsフィールドで対応を宣言し合うという単純な仕組みの上に成り立っています。ベンダープレフィックス必須の識別子で衝突を避け、対応していない側は「コア仕様に戻る」か「拒否する」かを明示的に選ぶことで、原因の分からない失敗を作らない設計です。
コア仕様から意図的に切り離してバージョニングし、個別リポジトリへガバナンスを委任しているのは、拡張機能をコア仕様の改訂サイクルに縛らず、実験と反復を速く回すためです。この設計判断を理解しておくと、新しい拡張機能に出会ったときに「対応していない相手とどう付き合うSDKなのか」を仕様書から素早く読み取れるようになります。