Claude Media
MCP Appsとは — インタラクティブUIをチャットに埋め込む拡張機能

MCP Appsとは — インタラクティブUIをチャットに埋め込む拡張機能

MCP AppsはMCPサーバーがサンドボックスiframeでHTML UIを返し、チャット内で双方向にツールを呼べるようにする拡張仕様です。仕組みと使いどころをまとめます。

MCP Appsは、MCPサーバーがテキストだけでなくインタラクティブなHTML UIをチャット内に返せるようにする拡張仕様です。サンドボックス化されたiframeの中でフォームやダッシュボード、3Dビューアーなどが動き、ユーザーはタブを切り替えずにその場で操作できます。ClaudeとClaude Desktopはすでにこの拡張に対応しています。

MCP Appsとは何か

MCP Appsは、MCPのツールとリソースという2つの既存プリミティブを組み合わせた拡張です。ツールの説明に _meta.ui.resourceUri フィールドでUIリソースへの参照を持たせ、ホスト側がそのリソースを取得してサンドボックスiframe内にレンダリングします。テキスト応答では表現しきれない、データの掘り下げや多数の設定項目を持つフォームに向いています。

普通のWebアプリとの違いは、会話から離れないことです。ユーザーはリンクを開いてタブを切り替える必要がなく、UIは会話の文脈の隣に留まり続けます。

既存のMCP応答形式との違い

従来のMCPツールは、テキスト・画像・リソース・構造化データのいずれかをホストに返し、ホストがそれを会話の一部として表示する仕組みでした。MCP Appsはこのパターンを拡張し、ツールの説明にインタラクティブなUIへの参照を持たせられるようにしたものです。既存の応答形式を置き換えるのではなく、選択肢を1つ増やす位置づけです。

テキストや画像は読んで終わりですが、MCP Appsはレンダリング後もユーザーの操作を受け取り、サーバーへ新しいツール呼び出しを投げ返せます。この往復が、ダッシュボードやフォームのように継続的なやり取りを必要とする用途を支えます。

もう1つの違いは、ホストがUIリソースをツール呼び出しの前に先読みできる点です。_meta.ui.resourceUri がツール説明に含まれていれば、ツールが呼ばれる前にUIの取得を始められます。ツールの入力をアプリへストリーミングする、といった使い方もこの先読みが前提です。

なぜ普通のWebアプリではダメなのか

利点

MCP Appsが別ページのWebアプリに勝つ4点

  • 文脈の保持

    UIは会話の中に留まります。どのスレッドで何を開いたか迷いません。

  • 双方向のデータ連携

    アプリはサーバーの任意のツールを呼べます。ホストは新しい結果をアプリにpushできます。

  • ホスト機能への委譲

    「この結果でミーティングを予約して」のような成果の依頼を、ユーザーが接続済みの機能へホスト経由で回せます(ユーザーの同意が前提)。

  • セキュリティの担保

    サンドボックスiframeで動くので、親ページから隔離されます。

単独のWebアプリでこれを実現しようとすると、独自のAPI・認証・状態管理を一式作ることになります。MCP Appsは既存のMCPパターンに乗るぶん、その負担がありません。

内部の仕組み — UIプリロードからJSON-RPC通信まで

LLMがMCP Appsに対応したツールを呼ぶと、次の4段階が進みます。

手順

ツール呼び出しからUI表示までの流れ

  1. 1

    UIプリロード

    ツール説明の _meta.ui.resourceUri を見て、ホストが ui:// リソースをツール呼び出し前に読み込みます。

  2. 2

    リソース取得

    JavaScriptとCSSを束ねたHTMLページをサーバーから取得します。外部スクリプトを読み込む場合は _meta.ui.csp で許可オリジンを指定します。

  3. 3

    サンドボックスレンダリング

    Webホストは通常、HTMLを会話内のサンドボックスiframeに描画します。マイクやカメラの権限は、リソースの _meta.ui.permissions で要求します。

  4. 4

    双方向通信

    アプリとホストはJSON-RPCベースの独自ダイアレクトで会話します。tools/call のようにコアMCPと共通のメッセージもあれば、ui/initialize のように似たものや、ui/ プレフィックスを持つ新規メッセージもあります。

ユーザーがアプリを操作すると、アプリはホストへ tools/call を要求し、ホストがそれをMCPサーバーへ転送します。返ってきた新しいデータはホスト経由でアプリに届き、画面が更新されます。この往復はすべてpostMessageのチャネルの上を通り、アプリはホストから隔離されたままです。

最小構成のサーバーとUIはどう書くか

公式のbuild guideにある時刻表示アプリが最小の例です。サーバー側は、ツールとUIリソースを同じ ui:// のURIで結びます。

const resourceUri = "ui://get-time/mcp-app.html";
 
registerAppTool(server, "get-time", {
  title: "Get Time",
  description: "Returns the current server time.",
  inputSchema: {},
  _meta: { ui: { resourceUri } },
}, async () => ({
  content: [{ type: "text", text: new Date().toISOString() }],
}));
 
registerAppResource(server, resourceUri, resourceUri,
  { mimeType: RESOURCE_MIME_TYPE }, async () => ({
    contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }],
  }));

html はビルド済みのHTMLを読み込んだ文字列です。registerAppTool が _meta.ui.resourceUri を持つツールを登録し、registerAppResource が同じURIでHTMLを返します。この2つのURIが食い違うと、ホストはUIを解決できません。

UI側は App クラスでホストにつなぎます。

const app = new App({ name: "Get Time App", version: "1.0.0" });
app.connect();
 
app.ontoolresult = (result) => {
  // ホストが最初のツール結果を押し込んでくる
  serverTimeEl.textContent = result.content?.find((c) => c.type === "text")?.text ?? "[ERROR]";
};
 
getTimeBtn.addEventListener("click", async () => {
  const result = await app.callServerTool({ name: "get-time", arguments: {} });
  // ボタン操作のたびにサーバーへ往復する
});

app.callServerTool() は毎回サーバーへの往復を伴います。build guideは、遅延を前提にUIを設計するよう注意しています。

App クラスには、ログの送信、URLを開く依頼、アプリ側のデータでモデルの文脈を更新するメソッドもあります。アプリが使える機能はホストが決めます(後述)。

_meta.ui に書くフィールド

設定は _meta.ui に集約されています。実装時に迷いやすいのは次の3つです。

フィールド役割例
resourceUri役割ツールが参照するUIリソースの場所。ui:// スキームで、パス構造は自由例ui://get-time/mcp-app.html
csp役割外部スクリプト・アセットを読み込む場合の許可オリジン例外部CDNのドメイン
permissions役割マイクやカメラなど、追加の権限の要求例マイク、カメラ

iframeのCSPは既定で何も許可しません。build guideでは、CSSやJSを外部に置く代わりに vite-plugin-singlefile でHTML 1枚に同梱する手を採っており、この方法ならCSPの設定は要りません。

対応していないホストではどうなるか

MCP Appsは拡張なので、既定では無効です。クライアントとサーバーの両方が、capabilityの extensions に io.modelcontextprotocol/ui を宣言して初めて使われます。クライアントは各リクエストの _meta["io.modelcontextprotocol/clientCapabilities"] に対応を載せます。受け取れるMIMEタイプとして text/html;profile=mcp-app を指定します。サーバー側の宣言は server/discover のレスポンスに入ります。

片側だけが対応している場合の扱いは、拡張の概要ページに2通り書かれています。

くらべる

片側が拡張に対応していないとき

フォールバック

コアの動作へ戻る

UI付きのツールを提供するサーバーは、UI拡張に対応しないクライアント向けにも、意味の通じるテキストの結果を返しておきます。

拡張が必須の場合

リクエストを拒否する

拡張が必須のサーバーは、対応しないクライアントからの接続をエラーで断れます。認証拡張がその例です。

拡張の仕様を変えるときは、能力フラグやバージョンで吸収するのが原則です。互換性を壊す場合だけ、-v2 のような新しい識別子を使います。

get-time の例も、content に時刻の文字列を入れています。

どんな場面で使うべきか

公式ドキュメントは5つの適用場面を挙げています。いずれも、テキスト応答だけでは往復のやり取りが増えてしまう場面です。

場面テキスト応答との違い
複雑なデータの探索テキスト応答との違い地域別の売上をインタラクティブな地図でドリルダウンできる
多数の選択肢を持つ設定テキスト応答との違いデプロイ設定のような相互依存する項目を、フォーム1画面でまとめて見せる
リッチメディアの閲覧テキスト応答との違いPDFや3Dモデルをパン・ズーム・回転しながら会話内で確認できる
リアルタイム監視テキスト応答との違いダッシュボードが常時接続を維持し、都度「今の状態は」と聞かずに更新される
多段階のワークフローテキスト応答との違い経費申請の承認やコードレビューを、ナビゲーションと状態保持付きで1件ずつ処理できる

公式の概要ページには、ExcalidrawのMCP AppがClaudeの会話内でシーケンス図を描くアニメーションが載っています。図を描く手を会話の中で見せる使い方です。

逆に、これらの利点が活きない用途では、普通のWebアプリの方がシンプルです。

対応クライアントとセキュリティモデル

概要ページの「Client support」が挙げるのは8つです。Claude、Claude Desktop、VS Code GitHub Copilot、Microsoft 365 Copilotが前半です。残りはGoose、Postman、MCPJam、Archestra.AIです。一方、Extension Support MatrixのMCP Apps列にはChatGPT・Cursor・PostHog Codeも並び、合計11クライアントです。

ページ同士で数が違うので、最新の状況はマトリクス側で見るのが確実です。内訳はMCP Appsのクライアント対応表にまとめています。

同じマトリクスには、fast-agent、MCP Inspector、mcpc MCP CLIの行もありますが、MCP Apps列は空欄です。Claude Codeの行はマトリクスにありません。

セキュリティモデルの核心はサンドボックスiframeです。アプリは親ウィンドウのDOMを読めず、ホストのCookieやローカルストレージにも触れません。親ページへのナビゲーションや親コンテキストでのスクリプト実行もできません。

通信はすべてpostMessage API経由で、アプリが使える機能はホストが決めます。たとえば呼べるツールを絞ったり、sendOpenLink を無効にしたりできます。この設計により、開発元を完全には信頼していないサードパーティ製アプリでも、ホストは安全にレンダリングできます。

自前のMCPクライアントでMCP Appsに対応するなら、選択肢は2つあります。ひとつは @mcp-ui/client パッケージのReactコンポーネント、もうひとつはSDKのApp Bridgeモジュールです。App Bridgeは、サンドボックスiframeでのレンダリング、メッセージパッシング、ツール呼び出しのプロキシ、セキュリティポリシーの強制を引き受けます。統合の進め方は、基本ホストの実装例(basic-host)が示しています。

対応フレームワークと実装の自由度

MCP Appsは特定のフレームワークを前提にしていません。転送方式はstdioやHTTPではなくpostMessageです。標準的なWeb技術の組み合わせなので、React・Vue・Svelte・Preact・Solid・素のJavaScriptのどれでも実装できます。

@modelcontextprotocol/ext-apps パッケージの App クラスは通信を扱いやすくするラッパーで、必須ではありません。依存を増やしたくない場合や細かい制御が必要な場合は、postMessageプロトコルを直接実装できます。プロトコルの仕様は、ext-appsリポジトリに2026-01-26の版として置かれています。

公式のexamplesディレクトリには、カテゴリー別に実装例が揃っています。

カテゴリー実装例
3D・可視化実装例CesiumJSの地球儀、Three.jsのシーン、シェーダーエフェクト
データ探索実装例コホートヒートマップ、顧客セグメンテーション、Wikipedia探索
ビジネス用途実装例シナリオモデラー、予算配分ツール
メディア実装例PDFビューアー、動画リソース、楽譜表示、音声合成
ユーティリティ実装例QRコード生成、システム監視、音声認識(文字起こし)
開発用スターター実装例React / Vue / Svelte / Preact / Solid / 素のJavaScript

近い用途のサンプルを出発点にできます。既存のUIコンポーネントをプレビューに再利用する発想は、Storybook MCPサーバーが実例です。

まとめ

MCP Appsは、会話を離れずに操作面を差し込める代わりに、ホストごとの対応差を前提に作る拡張です。対象にしたいホストが概要ページの8クライアントの外にあるなら、UIよりテキストの結果を主役に作る、という判断軸になります。

実際に手を動かして作る手順はMCP Appを実際に作ってみるで扱います。スキル・MCP・サブエージェントの使い分けはClaude Code SkillsとMCPの違いが参考になります。

この記事を共有:XはてブLinkedIn
MCP をもっと見る →