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」)では、同じ宣言をinitializeハンドシェイクの中で1回だけ交わしていました。拡張機能の宣言を定めたSEP-2133の例も、クライアントはinitializeリクエストのcapabilities.extensionsに、サーバーはinitializeの応答に載せる形で書かれています。capabilities.extensionsというフィールドは両方で共通で、運び方だけが変わりました。
宣言の運び方の違い
legacy(2025-11-25以前)
クライアントはinitializeリクエストのcapabilitiesに、サーバーはその応答にextensionsを載せます。相手の対応状況は、ハンドシェイクで確立したセッションの状態として持ちます。
modern(2026-07-28以降)
クライアントは毎回_metaのio.modelcontextprotocol/clientCapabilitiesに載せます。サーバーはserver/discoverの応答で返し、この応答にはキャッシュの有効期間(ttlMs)が付きます。
modernのclientCapabilitiesは必須フィールドです。欠けたリクエストは不正として扱われ、サーバーはJSON-RPCのエラーコード-32602(HTTPでは400 Bad Request)で拒否しなければなりません。サーバーが宣言されていない機能に頼ることも禁じられています。処理に必要な機能が宣言に無ければ、不足をdata.requiredCapabilitiesに列挙したMissingRequiredClientCapabilityError(-32021)を返します。
宣言を毎回載せる設計の狙いは、サーバーが「接続の事前状態に頼らず」バージョンと能力を読み取れるようにすることです。仕様は、リクエストごとのフィールドがその目的だと述べています。接続の事前状態に頼らないので、リクエストを受けたインスタンスは届いた内容だけで判断できます。
legacyとmodernの混在には注意が要ります。legacyのクライアントがmodernだけのサーバーに接続すると失敗し(HTTPでは必須ヘッダー不足で400)、fall-forwardの仕組みはありません。逆に両対応(dual-era)のサーバーは、initializeで始まる接続にはlegacy、_meta付きのリクエストにはmodernで応答し、同じエンドポイントで両方を受けられます。
拡張機能識別子はなぜベンダープレフィックス必須なのか
拡張機能識別子は{ベンダープレフィックス}/{拡張機能名}という形式で、io.modelcontextprotocol/oauth-client-credentialsのように書きます。プレフィックスは省略できず(_metaのキー名と同じ規則で、違いは「プレフィックスが任意か必須か」だけです)、この必須化は衝突回避が理由です。
公式拡張機能はio.modelcontextprotocolというプレフィックスを使います。サードパーティが独自の拡張機能を作る場合は、自分が所有するドメインを逆順にしたものをプレフィックスにするよう、仕様が求めています(SHOULD)。example.comというドメインを持つ会社ならcom.example/my-extensionという形です。Javaのパッケージ命名規則と同じ発想で、複数の組織が同じ名前の拡張機能を別の意味で作ってしまう事故を防いでいます。
プレフィックスの予約範囲は思ったより広く、2番目のラベルがmodelcontextprotocolかmcpのものはMCP専用です。dev.mcp/やcom.mcp.tools/は予約済みで、com.example.mcp/は2番目がexampleなので予約されません。名前の部分は英数字で始まり英数字で終わる必要があり、途中にはハイフン・アンダースコア・ドットを使えます。
破壊的変更を加えるときのルールも識別子に紐づいています。フィールドの削除・リネーム、型の変更、既存の挙動の意味変更、必須フィールドの追加はいずれも破壊的変更にあたり、これらを行うときは同じ識別子のまま更新するのではなくio.modelcontextprotocol/my-extension-v2のように新しい識別子を発行します。拡張機能の概要ページは、設定オブジェクト内の機能フラグやバージョニングで済むならそちらを優先し、新しい識別子は避けられない破壊的変更のときだけと書いています。
対応していない側は何が起きるか — グレースフルデグラデーション
一方だけが拡張機能に対応している状況は普通に起こります。このとき対応している側は、コア仕様の挙動に戻るか、適切なエラーで拒否するかのどちらかを選ばなければなりません(MUST)。
サーバーが拡張機能の有無で分岐する流れ
- 1
宣言を読む
リクエストの
_metaにあるclientCapabilities.extensionsに、その識別子のキーがあるかを見ます。 - 2
あれば拡張機能の挙動
キーの値(設定オブジェクト)を読み、拡張機能が定めた形で応答します。
- 3
無ければコアへ戻るか拒否する
その拡張機能が無くても成立するなら、コア仕様だけで意味の通る応答を返します。必須の拡張機能なら、適切なエラーで拒否します。
SEP-2133には、この分岐のコード例が載っています。UI拡張機能の対応を調べ、あればUI付きのツールを、無ければテキストだけのツールを登録する形です。
const hasUISupport = clientCapabilities?.extensions?.[
"io.modelcontextprotocol/ui"
]?.mimeTypes?.includes("text/html;profile=mcp-app");
if (hasUISupport) {
// Register tools with UI features
} else {
// Register text-only fallback
}宣言の値まで見ている点が見どころです。extensionsにキーがあるだけでなく、mimeTypesにtext/html;profile=mcp-appが含まれるかまで調べています。設定オブジェクトの中身は拡張機能ごとに決まるので、「対応している」の判定は識別子の有無だけで終わらないことがあります。
拡張機能の性格で決まる分岐
無くても困らない
UI要素を返せる拡張機能(MCP Apps)に対応したサーバーは、未対応のクライアントにも意味の通るテキストコンテンツを返し続けます。
無いと成立しない
特定の認証拡張機能を必須にしたいサーバーは、その拡張機能に未対応のクライアントからの接続を拒否できます(MAY)。
拡張機能を作る側には、この2択のどちらを選ぶかを仕様書に明記することが推奨されています。フォールバック挙動が書かれていない拡張機能は、対応していないクライアントに出会ったときに何が起きるか誰にも分からない、という状態を生みます。
この推奨は善意任せではありません。拡張機能の仕様書にはコア仕様と同じRFC 2119の用語(MUST/SHOULD/MAY)を使うことが求められ(MUST)、コア仕様の一部であるかのような書きぶりにすること(SHOULD)も定められています。
拡張機能はなぜコア仕様と切り離して設計されているのか
拡張機能という仕組み自体を定めたSEP-2133は、設計方針として3つの原則を掲げています。まず単純に始めること、次にガバナンスを明確にすること、そして経験を積みながら後で調整することです。この3つは実装の細部を先に固めるより、誰が何を承認できるかという体制を先に固める発想です。
バージョンをコア仕様から意図的に切り離した理由も明記されています。拡張機能は追加的で任意なので、コアのバージョン番号と足並みを揃える必要がありません。別々にバージョニングすることで、拡張機能ごとの反復を速くできるというのが設計判断です。コア仕様の改訂を待たずに、拡張機能だけを頻繁に更新できます。
拡張機能を個別リポジトリ(ext-プレフィックス)単位で管理する理由も、ガバナンスの分散が目的です。同じ領域で互換性のない拡張機能が乱立する事故を、リポジトリという自然なまとまりとメンテナーによる審査で防ぎます。承認後の更新にコアメンテナーの審査を挟まない理由も同根です。SEP-2133は、コアメンテナーの審査が長いときには数か月かかるボトルネックだと書いています。新しい拡張機能の採用にはこの審査が要りますが、承認後の更新や修正は拡張機能リポジトリのメンテナーが扱うため、コア仕様の改訂サイクルを待たずに進められます。なお更新では後方互換性を考慮することが求められます。
コア仕様との切り離しは永久固定の扱いではありません。拡張機能が十分に成熟すれば、コアメンテナーによる別のレビューを経てコア仕様そのものに昇格する道も用意されています。ただし業界特化のロジックのように性質上コアに組み込むのが適さない拡張機能は、昇格しないまま拡張機能であり続けることも織り込み済みです。
この仕組みが意図的に決めていないこと
SEP-2133は拡張機能の骨格を決める一方で、いくつかの論点をあえて仕様の対象外としています。拡張機能自身がスキーマをどう変更するかを表明する仕組み、拡張機能同士やコア仕様の特定バージョンへの依存関係、複数の拡張機能をまとめて扱う「プロファイル」という概念は、いずれも未規定です。
これは検討漏れではなく、初期段階の骨組みをまず動かし、経験を積んでから拡張する意図的な先送りです。capabilities.extensionsという宣言の形だけを先に固定し、その中身の設計自由度を拡張機能の書き手に委ねることで、コア仕様の改訂を待たずに拡張機能の実装例を先に積み上げられる、という順序になっています。
正式リリースまでの道筋も決まっています。まずSEPとして提案し、少なくとも1つの公式SDKでリファレンス実装を作り、コアメンテナーの審査を経て、拡張機能リポジトリへ追加されて初めて他のクライアント・サーバー・SDKが実装できる状態になります。実験段階のものはexperimental-ext-というプレフィックスのリポジトリに置かれ、必ずWorking GroupかInterest Groupに紐づけられ、READMEとパッケージ名の両方に実験的であることを明記する決まりです。
セキュリティ面の扱いも仕様書に明記されています。拡張機能は自分が拡張する領域のセキュリティのベストプラクティスを実装しなければならず、クライアントとサーバーの双方は拡張機能によって追加されたフィールドやデータを信頼できないものとして扱い、十分に検証すべきだとされています。拡張機能は追加後の更新にコアメンテナーの審査が入りません。受け取った側が拡張機能由来のフィールドを鵜呑みにしない前提は、仕組みに織り込まれています。
既存の公式拡張機能にはどんなものがあるか
公式拡張機能はmodelcontextprotocol組織のext-auth・ext-apps・ext-skillsなどのリポジトリに置かれています。拡張機能の概要ページには、次の4系統が並んでいます。
公式拡張機能の4系統
認証(ext-auth)
OAuth 2.0のクライアントクレデンシャルフロー(マシン間認証)と、集中的なアクセス制御が要る企業環境向けの枠組み(Enterprise-Managed Authorization)の2つです。
MCP Apps(ext-apps)
会話の中にチャート・フォーム・動画プレーヤーなどの対話的なUI要素を表示します。
MCP Tasks
長時間実行の処理を、ポーリング・処理途中の入力・耐久的なハンドルで扱います。
Skills over MCP(ext-skills)
MCPのリソースを通じて、Agent Skillsの手順書や補助ファイルを見つけて読み取ります。
拡張機能には3つの区分があります。io.modelcontextprotocolは正式採択されたもの、experimental-ext-リポジトリのものは採択前の試作段階で、どちらもMCPの組織が管理します。それ以外の組織が出すものはMCPのガバナンスの対象外です。
これらの拡張機能をクライアント側でどこまで実装しているかの対応状況はMCP拡張機能のサポートマトリクスの読み方、MCP AppsとExtensionsの関係はMCP Appsの対応クライアント一覧とExtensionsの仕組み、MCP Tasksがなぜコア仕様から拡張機能へ移ったかはMCP Tasksが拡張機能に移った理由で扱っています。本記事はネゴシエーションという合意形成の仕組みそのものに絞り、個々の拡張機能の対応状況には立ち入りません。
SDK側の実装も義務ではありません。SDKメンテナーは自分たちが対応する拡張機能を自由に選べ、対応する拡張機能があるならドキュメントにその一覧を載せることになっています。SDKが拡張機能に対応する場合でも、既定は無効で、開発者の明示的なオプトインが必要です(MUST)。拡張機能への対応は、SDKの適合性評価にも要りません。
まとめ
拡張機能の対応は、識別子の有無だけでなく設定オブジェクトの中身まで見て判断します。未対応の相手には、コアへ戻るか拒否するかを拡張機能の性格に応じて選びます。
新しい拡張機能に出会ったら、仕様書にフォールバック挙動が書かれているかを最初に見ると、対応していない相手との付き合い方が分かります。