【入門】MCP Appsとは?公式サンプルを試して概要と内部的な仕組みを理解する

【入門】MCP Appsとは?公式サンプルを試して概要と内部的な仕組みを理解する

2026.03.10

リテールアプリ共創部のるおんです。

みなさんはMCP Apps をご存知でしょうか?AIチャットの会話内に、操作可能な地図やグラフ、フォームなどの インタラクティブなUIを直接埋め込む ことができる仕組みです。

例:ピザレシピアプリ
crkbcmqzkom8loqpk9ka

以前、OpenAIの「Apps in ChatGPT(Apps SDK)」を使って、ChatGPTの会話内にインタラクティブなピザレシピアプリを組み込むという記事を書きました。

https://dev.classmethod.jp/articles/apps-in-chatgpt-apps-sdk-chatgpt/

当時はOpenAI独自のSDKとして提供されていましたが、この「AIチャットの中にインタラクティブなUIを埋め込む」という仕組みが、MCP Apps としてMCP(Model Context Protocol)の公式拡張仕様に標準化されました。これにより、ChatGPTだけでなく Claude、VS Code、Gooseなど、MCPに対応したあらゆるクライアント で同じ仕組みが使えるようになっています。

今回は、MCPの基本から始めて、MCP Apps の概要と仕組みを解説し、公式サンプルの map-server を実際にClaude Desktopで動かして内部構造を見ていきます。

先に結論

MCP Apps は、通常のMCPツール定義に _meta.ui フィールドを追加するだけで、テキストを返す関数をインタラクティブなWebアプリに変える仕組みです。

通常の MCP ツール MCP Apps ツール
結果の表示 テキストのみ テキスト + iframe 内の UI
ユーザー操作 不可(結果を読むだけ) ボタン、フォーム等で操作可能
通信 ツール呼び出し → 結果で終了 表示後も双方向通信が継続

MCPとは

MCP Apps を理解するために、まず MCP(Model Context Protocol)の基本を押さえます。

MCPの概要

MCPは、AIアプリケーションと外部システムを接続するためのオープンソースの標準プロトコルです。Anthropicが策定し公開されました。

https://modelcontextprotocol.io/

これまで、各AIアプリケーションが独自にAPIやプラグインを実装していたため、接続先が変わるたびに実装を書き直す必要がありました。MCPはこの問題を解決し、1つのプロトコルで様々なAIアプリケーションと外部システムを接続できるようにします。

MCPのアーキテクチャ

MCPはクライアント-サーバーアーキテクチャに従います。登場人物は3つです。

参加者 役割
MCPホスト AIアプリケーション本体。複数のクライアントを管理する Claude Desktop, VS Code, Cursor
MCPクライアント ホスト内のコンポーネント。サーバーごとに1つ自動生成され、1対1の接続を管理する ホストが内部で生成
MCPサーバー 外部システムへのアクセスを提供するプログラム ファイルシステム、DB、API など
┌─ MCPホスト(Claude Desktop)─────────┐
│                                       │
│  MCPクライアント1 ←→ MCPサーバーA(ファイルシステム)
│  MCPクライアント2 ←→ MCPサーバーB(データベース)
│  MCPクライアント3 ←→ MCPサーバーC(外部API)
│                                       │
└───────────────────────────────────────┘

MCPのプリミティブ

MCPサーバーは3種類の プリミティブ(基本構成要素) を公開できます。

プリミティブ 説明
ツール AIが呼び出せる実行可能な関数 searchFlights, queryDatabase
リソース 読み取り専用のデータソース file:///config.json, db://schema
プロンプト 再利用可能なインタラクションテンプレート /plan-vacation, /code-review

通常のMCPツールはテキストを返して終わりです。例えば「東京の天気を教えて」と聞くと、MCPサーバーが天気APIを叩いて「東京:晴れ、25°C」というテキストを返します。

しかし、これだけでは不十分なケースがあります。

  • 地図をドラッグしてエリアを選びたい
  • グラフをインタラクティブに操作したい
  • フォームに入力して送信したい

ここで MCP Apps の出番です。

MCP Appsとは

概要

MCP Apps は、MCPの拡張仕様です。MCPサーバーがHTMLベースのインタラクティブなUIをAIチャットの会話内に埋め込めるようにします。

https://modelcontextprotocol.io/extensions/apps

通常のMCPツールがテキストを返すのに対し、MCP Apps ツールはWebアプリケーションを返します。このWebアプリは、ホスト(Claude Desktopなど)の会話内にサンドボックス化されたiframeとして表示されます。

MCP Appsが適しているユースケース

  • 複雑なデータの探索:インタラクティブマップ、ドリルダウン可能なグラフ
  • 多くのオプションでの設定:検証付きフォーム、デプロイメント設定画面
  • リッチメディアの表示:PDFビューア、3Dモデル、動画プレイヤー
  • リアルタイム監視:ライブメトリクス、システムステータスダッシュボード
  • マルチステップワークフロー:経費承認、コードレビュー

MCP Appsの仕組み

MCP Apps のライフサイクルは4つのステップで構成されます。

1. UIプリロード
通常のMCPツールとMCP Appsツールの違いは、ツール定義に _meta.ui フィールドがあるかどうかです。

typescript
// 通常のMCPツール定義
{
  name: "geocode",
  description: "地名から緯度経度を取得する",
  inputSchema: { query: { type: "string" } }
}

// MCP Appsツール定義(_meta.ui が追加されている)
{
  name: "show-map",
  description: "インタラクティブな世界地図を表示する",
  inputSchema: { /* ... */ },
+  _meta: {
+    ui: {
+      resourceUri: "ui://cesium-map/mcp-app.html"
+    }
+  }
}

この _meta.ui.resourceUri が、表示するUIのHTMLリソースを指しています。ホストはこのフィールドを見つけると、ツールが呼ばれる前にHTMLリソースを先読みできます。

2. リソース取得
ホストがサーバーから ui:// のHTMLリソースを取得します。HTML + JavaScript + CSS がバンドルされた形で返されます。

3. サンドボックスレンダリング
ホストは取得したHTMLをサンドボックス化された iframe 内にレンダリングします。iframeにすることで、アプリがホストの親ページにアクセスできないようセキュリティを確保しています。

4. 双方向通信
アプリとホストは postMessage を使ったJSON-RPCプロトコルで双方向に通信します。アプリはサーバーのツールを呼び出したり、AIのコンテキストを更新したりできます。

_meta.ui というたった1つのフィールドの追加が、MCPツールを「テキストを返す関数」から「対話的なWebアプリケーション」に変えます。

重要な設計:AppとServerは直接通信しない

MCP Apps で押さえるべき重要なポイントがあります。MCP App(iframe内)とMCPサーバーは直接通信しません

MCP App ←→ Agent(ホスト) ←→ MCP Server
    (postMessage)    (stdio / HTTP)

App が知っているのは「postMessage でホストと話す方法」だけです。App がサーバーのツールを呼びたい場合は、ホスト(Agent)にお願いし、ホストがサーバーに転送します。

これはセキュリティのための設計です。iframe内のアプリにサーバーへの直接接続を渡すと、悪意のあるアプリがサーバーを自由に操作できてしまいます。ホストを中継させることで、すべてのリクエストをホストが検閲・制御できます。

公式サンプル(map-server)を試してみた

セットアップ

MCP Appsの公式リポジトリ ext-apps には、様々なサンプルが含まれています。

https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/map-server

今回はClaude Desktopで map-server(CesiumJS 3Dグローブ)を試してみます。

Claude Desktopの設定ファイル(~/Library/Application Support/Claude/claude_desktop_config.json)に以下を追加します。

{
  "mcpServers": {
    "map": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-map", "--stdio"]
    }
  }
}

設定後、Claude Desktopを再起動します。

動かしてみる

Claude Desktopで「東京の地図を見せて」と入力すると、会話の中にインタラクティブな3D地図が表示されました。

スクリーンショット 2026-03-10 20.43.09

ズームやスクロールで地図を自由に操作できます。テキストで「東京は北緯35度...」と返されるのではなく、操作可能な地図そのものが会話に埋め込まれます。

map-serverの内部構造

map-serverのコードを読んで、MCP Apps がどのように動いているかを見ていきます。

ファイル構成

map-server/
├── server.ts        ← MCPサーバー(バックエンド)
├── src/mcp-app.ts   ← MCP App(フロントエンド・iframe内)
├── mcp-app.html     ← Appの HTML シェル
├── main.ts          ← エントリーポイント(stdio / HTTP 切り替え)
└── package.json

MCP Apps の開発は、通常のWebアプリ開発と同じ構造です。バックエンド(server.ts)とフロントエンド(mcp-app.ts)に分かれています。

サーバー側:server.ts

サーバー側では、2つのツールを登録しています。

1. show-map(MCP Apps ツール)

typescript
registerAppTool(
  server,
  "show-map",
  {
    title: "Show Map",
    description: "Display an interactive world map...",
    inputSchema: {
      west: z.number().optional().default(-0.5),
      south: z.number().optional().default(51.3),
      east: z.number().optional().default(0.3),
      north: z.number().optional().default(51.7),
      label: z.string().optional(),
    },
+   _meta: { ui: { resourceUri } },
  },
  async ({ west, south, east, north, label }) => ({
    content: [
      {
        type: "text",
        text: `Displaying globe at: W:${west}, S:${south}, E:${east}, N:${north}`,
      },
    ],
  }),
);

ポイントは _meta: { ui: { resourceUri } } です。registerAppTool は通常の registerTool_meta.ui を自動追加するヘルパーで、resourceUri には "ui://cesium-map/mcp-app.html" が指定されています。

2. geocode(通常のMCPツール)

server.registerTool(
  "geocode",
  {
    title: "Geocode",
    description: "Search for places using OpenStreetMap...",
    inputSchema: {
      query: z.string().describe("Place name or address to search for"),
    },
  },
  async ({ query }) => {
    const results = await geocodeWithNominatim(query);
    return { content: [{ type: "text", text: textContent }] };
  },
);

こちらはUIなしの通常のツールです。OpenStreetMap の Nominatim API で住所検索してテキストで返します。

Claudeが「東京の地図を見せて」と言われたとき、まず geocode で座標を取得し、次に show-map で地図を表示するという2段階で動きます

App側:src/mcp-app.ts

App側(iframe内で動くフロントエンド)の核心部分です。

import { App } from "@modelcontextprotocol/ext-apps";

const app = new App(
  { name: "CesiumJS Globe", version: "1.0.0" },
  { tools: { listChanged: true } },
);

// ホストからツール入力(座標)を受け取る
app.ontoolinput = async (params) => {
  const args = params.arguments;
  if (args && viewer) {
    setViewToBoundingBox(viewer, bbox);
  }
};

// ホストに接続
await app.connect();
  • new App() で SDK(@modelcontextprotocol/ext-apps)のインスタンスを作成
  • app.connect() で postMessage 経由でホストに接続
  • app.ontoolinput でホストから転送されたツール入力(座標データ)を受け取る

さらに、ユーザーが地図を操作すると app.updateModelContext() でAIのコンテキストを更新します。

app.updateModelContext({
  content: [
    {
      type: "text",
      text: `地図が lat ${center.lat}, lon ${center.lon} を表示中`,
    },
  ],
});

これにより、ユーザーが地図をドラッグして別のエリアを表示した後に「このエリアのおすすめスポットは?」と聞くと、AIは現在表示中のエリアを踏まえて回答できます。App内でのユーザー操作が、AIの理解を更新するというのがMCP Appsの面白いところです。

エントリーポイント:main.ts

MCPには複数のトランスポート(通信方式)があり、ローカル環境では stdio(標準入出力)、リモート環境では Streamable HTTP が使われます。main.ts では --stdio フラグでこれを切り替えています。

if (process.argv.includes("--stdio")) {
  await startStdioServer(createServer);   // ローカル用(Claude Desktopなど)
} else {
  await startStreamableHTTPServer(createServer);  // リモート用(HTTP経由)
}

先ほど Claude Desktop の設定で "args": ["--stdio"] を指定したのは、ここに繋がっています。stdio の場合、Claude Desktop が npx コマンドを自動実行してサーバープロセスを起動し、stdin/stdout で通信するため、手動でサーバーを立ち上げる必要はありません。パッケージは npm レジストリから取得・キャッシュされます。

全体の流れ

1. Claude Desktop が server.ts を --stdio で起動
2. 「東京の地図を見せて」→ Claude が geocode ツールで座標取得
3. Claude が show-map ツールを呼ぶ → サーバーがテキスト結果 + _meta.ui を返す
4. ホストが _meta.ui.resourceUri の HTML を取得
5. iframe 内で mcp-app.ts が動き、CesiumJS を初期化
6. app.ontoolinput で座標を受け取り、カメラを東京に移動
7. ユーザーが地図を操作 → app.updateModelContext() で AI に位置を通知

server.ts がデータを取得し、mcp-app.ts がそれを視覚化する。通常のWebアプリの「バックエンド / フロントエンド」と本質的に同じ構造です。

MCP Apps でできそうなこと

MCP Apps の仕組みを理解した上で、実際にどんなユースケースが考えられるか列挙してみます。

リテール・EC

  • 商品カタログ:「おすすめのスニーカーを見せて」→ 商品カードをカルーセルで表示し、そのままカートに追加
  • 店舗検索マップ:「近くの店舗を教えて」→ 地図上にピンを表示し、営業時間・在庫状況を確認
  • 注文トラッキング:配送状況をリアルタイムで地図上に表示し、AIに「いつ届く?」と聞ける
  • サイズ・フィッティングガイド:体型情報を入力するフォームを表示し、AIがサイズを提案

飲食・レストラン

  • メニュー表示・注文:料理の画像付きメニューを表示し、カスタマイズしながらそのまま注文
  • レシピアプリ:材料リストと手順をインタラクティブに表示(まさに以前のApps in ChatGPTで作ったピザレシピのようなもの)
  • 予約管理:空席カレンダーを表示して、会話の流れで予約を完結
  • アレルギー・栄養情報ビューア:食事制限に合わせたメニューをフィルタリング表示

業務・開発者向け

  • 営業ダッシュボード:売上データをグラフで可視化し、AIに「先月比で落ちた地域を教えて」と聞ける
  • 在庫管理画面:在庫一覧をテーブルで表示し、発注ボタンで直接操作
  • データベースエクスプローラー:テーブル構造やデータをインタラクティブに探索
  • インフラモニタリング:システムメトリクスのダッシュボードをチャット内に

MCP Apps の SDK / フレームワーク

MCP Apps を開発するための選択肢も整理しておきます。

パッケージ 用途
@modelcontextprotocol/ext-apps iframe内のApp(フロントエンド)を作る。ホストとの通信を抽象化
@modelcontextprotocol/sdk MCPサーバー(バックエンド)を作る。ツール登録やリソース配信を担当
@mcp-ui/client MCP Apps対応の独自ホストアプリケーションを構築する

sdk(Server側)に関しては、従来のMCP開発で使われていたものになりますが、MCP Apps を作る場合、UIを作成するために ext-apps(App側) も必要になります。
@mcp-ui/client は、Claude Desktop のようなホスト自体を自作したい場合にのみ使用します。

App開発では、React / Vue / Svelte / Preact / Solid / Vanilla JS のスターターテンプレートが公式に用意されています。

https://github.com/modelcontextprotocol/ext-apps/tree/main/examples

おわりに

今回は、MCPの基本からMCP Appsの概要と仕組み、公式サンプル map-server の内部構造を解説しました。

MCP Apps は、AIチャットの「テキストだけ」という制約を取り払い、インタラクティブなUIを会話内に埋め込む仕組みです。既存のMCPツール定義に _meta.ui を追加するだけで、Webアプリケーションとしてレンダリングされる。しかも、バックエンド / フロントエンドの分離は通常のWeb開発と同じ構造なので、Web開発の経験がそのまま活かせます。

MCPが「AIに手足を与える」仕組みだとすれば、MCP Appsは「AIに顔を与える」仕組みです。チャットだけでは伝えきれない情報を、GUIで直感的に表現できるようになります。

以上、どなたかの参考になれば幸いです。

参考

https://modelcontextprotocol.io/

https://modelcontextprotocol.io/extensions/apps

https://github.com/modelcontextprotocol/ext-apps

https://modelcontextprotocol.io/docs/learn/architecture

https://apps.extensions.modelcontextprotocol.io/api/

この記事をシェアする

FacebookHatena blogX

関連記事