メインコンテンツまでスキップ

MCP Apps: ツール呼び出しの中のインタラクティブUI

上級

MCPは最初の1年間ずっとテキストプロトコルだった。ツール呼び出しはJSONまたはMarkdownを返し、クライアントは好きなようにレンダリングした。MCP Apps2026年1月26日にファイナルとなり、2026-07-28コア仕様に組み込まれた最初の公式拡張 — がUIチャンネルを追加する。サーバは今やHTMLの塊を配信でき、クライアントはそれをサンドボックス化されたiframeでレンダリングし、iframeは**postMessage上のJSON-RPC 2.0**で応答する。すべての書き込みは、他のツール呼び出しと同じ監査パスを通る。「Claudeの中で完全なwebappを動かす」ではなく、インタラクティブなレポート + 実行前確認と考えるとよい。

What you'll learn
  • MCP Appsが実際に何であるか — そして意図的にそうではない4つのこと
  • ワイヤ形状:ケイパビリティ・ネゴシエーション、ui://リソース、ツールの _meta.ui リンク、そしてpostMessageブリッジ
  • セキュリティモデル — サンドボックス、CSP、Permission Policy、ホスト承認 — そしてそれでも残る露出面
  • プレーンなツール結果ではなくAppsに手を伸ばすべきとき(稀ではあるが価値のあるパターン)
  • これがあなたが既に話している2026-07-28ステートレス仕様の他の部分とどのように組み合わさるか

一段落バージョン

サーバは ui://<server>/<name> URIで1つ以上のUIリソースをMIMEタイプ text/html;profile=mcp-app で宣言する。ツールはスキーマに _meta.ui.resourceUri を付けることで「UIがある」と広告する。tools/call の際、サーバは通常のテキスト/データに加えてUIリソースへの参照を返せる。クライアントは resources/read でHTMLを取得し、サンドボックス化された <iframe> に入れ、iframeとホストはJSON-RPC 2.0を使って postMessage で会話する。UI起点のツール呼び出しも、他のツール呼び出しと同じユーザ承認を必要とする — Appはレンダリングでき、提案でき、しかしサイレントに実行することはできない。

MCP Appsがそうではないもの

Watch out
  • 完全なwebappランタイムではない。iframeはデフォルトでネットワークなし(`connect-src 'none'`)、トップレベルナビゲーションなし、サードパーティスクリプトなし。もしアイデアがCDNからReactをロードして自分のAPIを呼ぶ必要があるなら、これはそれではない。
  • ツール呼び出しの承認をバイパスする方法ではない。何かを変更するUIアクションは、依然として通常のJSON-RPCツール呼び出しとして流れ、ホストがログ、スロットル、明示的なユーザ同意を要求できる。
  • サニタイゼーションではない。サンドボックスはUIが何をできるかを制限するのであって、ユーザに何をタイプさせるように仕向けるかを制限しない。すべてのAppをサードパーティコードのように扱い、そもそもどのサーバがUIをレンダリングできるかを許可リスト化すること。
  • 永続的ではない。セッションはなく、ツール呼び出しが終わればiframeは消える。呼び出し間で必要な状態は、サーバ側のハンドルとして返すことで保持する — ステートレス仕様の他の部分と全く同じ。

4つの可動部品

Guided walkthrough1 of 4
  1. クライアントはリクエストごとの _meta.capabilities の中で、リバースDNS名前空間 io.modelcontextprotocol/ui の下に拡張を広告し、レンダリング可能なMIMEタイプを列挙する。そのケイパビリティを見ないサーバは単にUIチャンネルをスキップしてプレーンな結果を返す — 拡張全体は両側でオプトインだ。

実際にタイプするワイヤ形状

クライアントリクエストでのケイパビリティ広告:

{
"jsonrpc": "2.0",
"method": "tools/call",
"params": { "name": "get_weather", "arguments": { "location": "Milan" } },
"_meta": {
"capabilities": {
"extensions": {
"io.modelcontextprotocol/ui": {
"mimeTypes": ["text/html;profile=mcp-app"]
}
}
}
}
}

サーバ側のツールスキーマ:

{
"name": "get_weather",
"description": "Get current weather and a 7-day forecast for a location.",
"inputSchema": {
"type": "object",
"properties": { "location": { "type": "string" } },
"required": ["location"]
},
"_meta": {
"ui": {
"resourceUri": "ui://weather-server/dashboard-template",
"visibility": ["model", "app"]
}
}
}

その隣に宣言されるUIリソース:

{
"uri": "ui://weather-server/dashboard-template",
"mimeType": "text/html;profile=mcp-app",
"_meta": {
"ui": {
"connectDomains": [],
"permissions": []
}
}
}

そしてiframeが返す最初のメッセージ:

UI → ホスト:postMessage上のJSON-RPC 2.0

window.parent.postMessage(
{
  jsonrpc: "2.0",
  id: 1,
  method: "ui/initialize",
  params: {
    toolName: "get_weather",
    toolArguments: { location: "Milan" },
    toolResult: /* whatever the server returned alongside the UI */
  }
},
"*"
);

その後のすべて — ホストに別のツール呼び出しをさせるよう頼む、リソースを購読する、UI側のイベントを送信する — は、同じチャンネル上のさらなるJSON-RPC 2.0であり、サーバ側で既に話しているワイヤプロトコルと同一の構造だ。これが要点だ:UI開発者はカスタムシムを学ぶのではなく、標準の @modelcontextprotocol/sdk を使える

1画面で見るセキュリティモデル

What you'll learn
  • サンドボックス:コンテンツはサンドボックス化された <iframe> でレンダリングされる。デフォルト:トップレベルナビゲーションなし、任意オリジンへのフォームなし、ポップアップなし、プラグインなし。
  • デフォルトCSP:connect-src 'none'。UIはどこにも fetch() できない。特定のオリジンを許可するには、サーバがリソースの _meta.ui.connectDomains でそれらを宣言し、ホストがそのリストからCSPヘッダを構築する。
  • Permission Policy:リソースの _meta.ui.permissions がiframeの allow 属性にマップされる — camera、microphone、geolocation、clipboard-write。何も暗黙的に付与されない。
  • すべてのUI起点の書き込みは通常のツール呼び出しだ。ホストはそれを検証し、ユーザ承認を要求することがあり、モデル起点の呼び出しと同じ監査ストリームにログする。
  • テンプレートはプリフェッチ可能でハッシュ可能。特定のハッシュにピン留めするホストは、下でテンプレートが差し替えられる攻撃を検出できる。しないホストは、その日サーバが配信するものを何でも受け取る。

実際にAppに手を伸ばすべきとき

意図的にハードルは高い — すべてのAppはあなたにとってクライアントの表面積であり、ユーザにとって攻撃面だ。UIがチャットのターンを明確に上回るときにだけ手を伸ばすこと:

  • データグリッドでの確認。「これから更新する47行だ — 不要なものはチェックを外して。」チャット表示だと巨大か不誠実になる。チェックボックス付きグリッドは誠実で速い。
  • チャート駆動の承認。「クエリプラン / コスト予測 / トレース・ウォーターフォールだ。承認または拒否。」チャートはHTMLでは安く、ASCIIでは酷い。
  • 構造化ピッカー — 入力の形がテキストではないもの。ミニカレンダー付き日付範囲、ファイルシステムや組織図上のツリーセレクト、境界ボックス付きマップピッカー。
  • インプレースエディタ — 全体の答えを再生成するよりdiff表示 + 承認/拒否が優れる小さなステップのため。

もしアイデアが「ダッシュボード全体を埋め込みたい」ならここには属さない — リンクアウトすること。もしアイデアが「サンドボックス内で任意のユーザコードを実行したい」なら、絶対にここには属さない。

2025-11-25互換性コントラクト

MCP Appsは2026-07-28コア仕様で導入された拡張フレームワークに乗るが、拡張自体は2026-01-26以来ファイナルだ。実務的な含意:古い2025-11-25リビジョン + この拡張だけを話すクライアントも既にAppsをレンダリングできる。ステートレスな2026-07-28クライアントは、リクエストごとのケイパビリティマップの1エントリとしてそれを拾う。拡張が両側でオプトインなので、それを追加するサーバは古いクライアントを決して壊さない — 単にUI分岐をスキップするだけだ。これはすべての将来のMCP拡張が取る形なので、このハンドシェイクを理解することは、来たるものへの負債を返済する。

MCP AppsがAILmanacマップのどこに位置するか

  • MCP 2026-07-28: ステートレス仕様 — Appsが乗るコアプロトコル。特に拡張フレームワークのセクション(SEP-2133)。
  • MCPとツール接続 — API側のコネクタ。iframeをレンダリングするのはクライアントであり、今日のコネクタはトランスポートを抽象化しているので、Appsは変更なしで流れる。
  • Claude CodeにおけるMCP — Claude CodeのMCPサポートが向かう先。UIレンダリングはクライアントのケイパビリティであり、サーバのケイパビリティではないので、これはどのAppsが実際にレンダリングされるかを決める面だ。
  • MCPサーバのセキュア化 — iframeの向こう側にあるサーバのハードニングパターン。

クイックチェック

Check yourself

0/4
  1. MCP Appsのiframeはホストにどのように応答するか?
  2. デフォルトで、MCP Appsのiframeはどのようなネットワークアクセスを持つか?
  3. ツールがUIをレンダリングしたい。プレーンなツールをUI付きに変える唯一のメタデータは何か?
  4. iframe内のUIがサーバ上で何かを変更したい。何が起こるか?

用語集

MCP Apps 用語集
Enter キーまたはスペースキーでカードを裏返します。左右の矢印キーでカードを移動できます。用語を表示しました。
1 / 9

ソースとさらなる読み物

次に