Skip to content

A2UI Surface における MCP Apps 統合

このガイドでは、Model Context Protocol (MCP) ApplicationsA2UI surface 内にどのように統合され、表示されるかを、セキュリティモデルとテストガイドラインとともに説明します。

NOTE: 中核となる A2UI-over-MCP プロトコルを探している場合は、MCP tool call から A2UI JSON payload を返す方法を説明した A2UI over MCP を参照してください。

概要

Model Context Protocol(MCP) は、MCP サーバーがリッチでインタラクティブな HTML ベースのユーザーインターフェースを host に届けられるようにします。A2UI は、こうしたサードパーティアプリケーションを実行するための安全な環境を提供します。

MCP Calculator デモ — アプリを読み込み、計算機を開き、エージェントとチャットする様子

Double-Iframe 分離パターン

信頼できないサードパーティコードを安全に実行するために、A2UI は double-iframe 分離パターンを利用します。この方式は、構造化された JSON-RPC チャネルを維持しながら、raw DOM injection をメインアプリケーションから隔離します。

セキュリティ上の理由

allow-scripts を使った標準的な single-iframe sandboxing は、allow-same-origin と組み合わせるとしばしば回避され、コンテナ化が無効化されます。allow-scriptsallow-same-origin の両方を持つ iframe は、親 DOM とプログラム的にやり取りしたり、自身の sandbox 属性を削除したりすることで sandbox を脱出できます。

これを防ぐため、A2UI はサードパーティアプリケーションが実行される inner iframe で allow-same-origin を厳格に除外します。

アーキテクチャ

  1. Sandbox Proxy (sandbox.html): 同一 origin から提供される中間 iframe です。構造化された JSON-RPC チャネルを維持しながら、raw DOM injection をメインアプリから分離します。
    • 権限: host template では sandbox しないでください。例: mcp-app.ts または mcp-apps-component.ts
    • Host origin 検証: メッセージが期待される host origin から来ていることを検証します。
  2. Embedded App(Inner Iframe): 最も内側の iframe です。制限された権限で srcdoc により動的に注入されます。
    • 権限: sandbox="allow-scripts allow-forms allow-popups allow-modals"(allow-same-origin絶対に含めないでください)。
    • 分離: unique origin により、localStoragesessionStorageIndexedDB、cookies へのアクセスがなくなります。

物理的な Iframe のネスト

flowchart TD
    subgraph "Host Application"
        A[A2UI Page] --> B["Host Component e.g., McpApp"]
    end
    subgraph "Sandbox Proxy"
        B -->|Message Relay| C[iframe sandbox.html]
    end
    subgraph "Embedded App"
        C -->|Dynamic Injection| D[inner iframe untrusted content]
    end

End-to-End アーキテクチャと Lifecycle Flow

完全なサイクルには、layout tree 階層、完全に分離された backend actor(Proxy Agent と MCP Server)、そして分離されたサードパーティウィジェットが native sibling(例: Pong ゲームの scoreboard)と反応的に相互作用する方法が含まれます。

graph TD
    %% 1. Top Tier (Strict vertical hierarchy)
    MCPServer["MCP App Server<br/>(Hosts widget resources & core tools)"]

    %% 2. Middle-Top Tier
    Agent["AI Agent<br/>(A2UI Backend Coordinator)"]

    %% 3. Subgraph for Host layout tree (Bottom Tier)
    subgraph HostApp ["Host Application"]
        direction TB
        Shell["A2UI Rendering Engine & State Manager<br/>(Orchestrates native layout & state bindings)"]

        subgraph LayoutTree ["A2UI Component Tree"]
            McpComponent["McpApp Component<br/>(Sandboxed HTML/JS Widget)"]
            SiblingComponent["Other A2UI Components<br/>(e.g., PongScoreBoard)"]
        end

        Shell <-->|"1. Initialize postMessage Event Bridge"| McpComponent
        Shell -.->|"5. Reactive State Update<br/>(e.g., Render updated score)"| SiblingComponent
    end

    %% Vertical Channel connecting Top to Middle-Top
    MCPServer ==>|"MCP Protocol (SSE / Stdio)"| Agent

    %% Unidirectional Data Cycle (Flowing vertically through the center)
    McpComponent ==>|"2. Tool Action Request<br/>(e.g., score_update)"| Shell
    Shell ==>|"3. Action Delegation (A2UI Protocol)"| Agent
    Agent ==>|"4. State Mutation & Sync (dataModelUpdate)"| Shell

    %% Style Sibling Relationship
    McpComponent -.->|"No Direct Access (Strictly Isolated)"| SiblingComponent

Sibling Update Loop の仕組み

  1. postMessage イベントブリッジの初期化(1): host shell が double-iframe sandbox をインスタンス化し、McpApp コンポーネントとの安全な message relay bridge を確立します。
  2. Tool Action Request(2): ユーザーが sandboxed app と操作すると(例: Pong ゲームで得点する)、app は postMessage bridge 経由でメッセージを post し、tool action をトリガーします。
  3. Action Delegation(3): host layout engine が action をインターセプトし、A2UI/A2A プロトコルを介して AI Proxy Agent に実行を委任します。外部計算やリソースが必要な場合、agent は標準 MCP Protocol(SSE / Stdio)を使って MCP App Server と任意に連携します。
  4. 状態変更と同期(4): agent が action を処理し、master session state を変更し、dataModelUpdate を host state manager へ push します。
  5. Reactive State Update(5): host がローカル store を更新し、その state path にバインドされた sibling A2UI コンポーネント(例: native scoreboard や display)の反応的更新をトリガーします。sandboxed コンポーネントと native sibling 要素の直接通信は、コンテナ化セキュリティを維持するため厳格にブロックされます。

使用方法 / コード例

MCP Apps コンポーネントは通常、A2UI カタログの custom ノードとして解決されます。開発者がコードで使う例を示します。

1. カタログ内に登録する

コンポーネントをアプリケーションカタログに登録する必要があります。たとえば Angular では次のようになります。

import {Catalog} from '@a2ui/angular';
import {inputBinding} from '@angular/core';

export const DEMO_CATALOG = {
  McpApp: {
    type: () => import('./mcp-app').then(r => r.McpApp),
    bindings: ({properties}) => [
      inputBinding(
        'content',
        () => ('content' in properties && properties['content']) || undefined,
      ),
      inputBinding('title', () => ('title' in properties && properties['title']) || undefined),
    ],
  },
} as Catalog;

2. A2UI メッセージ内で使用する

Host または Agent の context で、この custom node に変換される A2UI メッセージを送信します。

{
  "type": "custom",
  "name": "McpApp",
  "properties": {
    "content": "<h1>Hello, World!</h1>",
    "title": "My MCP App"
  }
}

content が複雑、またはエンコードが必要な場合は、URL-encoded 文字列を渡せます。

{
  "type": "custom",
  "name": "McpApp",
  "properties": {
    "content": "url_encoded:%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E",
    "title": "My MCP App"
  }
}

通信プロトコル

Host と embedded inner iframe の間の通信は、postMessage 上の構造化された JSON-RPC チャネルで行われます。

  • Events: Host Component は proxy からの SANDBOX_PROXY_READY_METHOD メッセージを待ち受けます。
  • Bridging: AppBridge が message relay を処理します。開発者、特に信頼されない iframe 内の MCP App Developer は、bridge.callTool() を使って MCP server の tool を呼び出せます。
  • The Host: コールバックを解決します(例: 特定の resizing、Tool results)。

制限事項

最も内側の iframe では allow-same-origin が厳格に省略されるため、次の条件が適用されます。

  • MCP app は localStoragesessionStorageIndexedDB、cookies を使用できません。各アプリケーションは unique origin で実行されます。
  • parent による直接 DOM 操作はブロックされます。すべての相互作用は message passing を通じて行う必要があります。

前提条件

サンプルを実行するには、次がインストールされていることを確認してください。

  • Python 3.10+ — agent と MCP server backend に必要
  • uv — 高速な Python パッケージマネージャー(すべての Python サンプルの実行に使用)
  • Node.js 18+npm — client app のビルドと実行に必要
  • GEMINI_API_KEY — すべての ADK ベース agent に必要。Google AI Studio で取得できます。

⚠️ 環境変数の設定: shell で GEMINI_API_KEY を export するか、各 agent ディレクトリに .env ファイルを作成できます。agent は dotenv を使って .env ファイルを自動読み込みします。

# Option 1: Export in shell
export GEMINI_API_KEY="your-api-key-here"

# Option 2: Create .env file in the agent directory
echo 'GEMINI_API_KEY=your-api-key-here' > .env

サンプル

MCP Apps 統合を示す主要なサンプルは 2 つあります。各サンプルでは 複数のターミナル が必要です。backend service ごとに 1 つ、client 用に 1 つ使用します。


サンプル 1: MCP App Standalone Sample(Lit Client & ADK Agent)

このサンプルは、Lit ベース client と ADK ベース A2A agent で sandbox を検証します。

Step 1: Agent を起動する

別のターミナルで agent ディレクトリに移動し、agent を起動します。

cd samples/agent/adk/mcp-apps-in-a2ui-sample
uv run agent.py

agent は http://localhost:8000 で実行されます。

Step 2: Client を起動する

新しいターミナルで client ディレクトリに移動し、dev server を起動します(Lit renderer を先にビルドする必要があります)。

cd samples/client/lit/mcp-apps-in-a2ui-sample
npm install
npm run dev

client は http://localhost:5173/ で起動します。

Step 3: ブラウザで開く

ブラウザを開き、http://localhost:5173/ に移動します。MCP App を読み込む A2UI インターフェースが表示されるはずです。

期待される結果: sandboxed iframe 内で MCP App を読み込むページです。iframe 内の "Call Agent Tool" ボタンをクリックすると、agent が処理する action がトリガーされます。


サンプル 2: MCP Apps(電卓 + Pong)(Angular クライアント + MCP Server + Proxy Agent)

このサンプルは、Angular ベース client、MCP Proxy Agent、リモート MCP Server で sandbox を検証します。3 つの backend process が必要です。

Step 1: MCP Server(Calculator) を起動する

cd samples/mcp/mcp-apps-calculator/
uv run .

MCP server は SSE transport を使って http://localhost:8000 で起動します。

Step 2: MCP Apps Proxy Agent を起動する

新しいターミナルで次を実行します。

cd samples/agent/adk/mcp_app_proxy/
export GEMINI_API_KEY="your-key"  # or use a .env file
uv run .

proxy agent はデフォルトで http://localhost:10006 で起動します。

Step 3: Angular Client をビルドして起動する

新しいターミナルで次を実行します。

cd samples/client/angular/

# Build the renderers (required — Angular depends on local renderer packages)
npm run build:renderer

npm install --include=dev
npm run build:sandbox
npm start -- mcp_calculator

⚠️ --include=dev が必要です: Angular CLI(@angular/cli) は dev dependency です。--include=dev がないと ng serve を利用できません。

⚠️ build:rendererbuild:sandbox はどちらも必要です: build:renderer は Angular app が依存する A2UI renderer package をコンパイルします。build:sandbox は sandbox proxy を Angular プロジェクトの public assets にバンドルします。どちらかが欠けると app は動作しません。

client は http://localhost:4200/ で起動します。

Step 4: ブラウザで開く

次へ移動します。

http://localhost:4200/?disable_security_self_test=true

期待される結果: calculator app または pong app を読み込む smart chip のセットがレンダリングされます。どちらの app も、それぞれの sandboxed iframe 内で実行されます。

Calculator App Pong App
計算機アプリで乗算を行うアニメーション GIF Pong アプリをプレイするアニメーション GIF

テスト用 URL オプション

テスト目的で、特定の URL query parameter を使って security self-test を opt-out できます。

disable_security_self_test=true

この query parameter により、iframe 分離を検証する security self-test をバイパスできます。double-iframe 設定が厳格な origin check を通過しない可能性があるデバッグやテスト環境(例: localhost 開発)で便利です。

使用例:

http://localhost:4200/?disable_security_self_test=true

トラブルシューティング

問題 解決方法
GEMINI_API_KEY environment variable not set キーを export するか、agent ディレクトリに .env ファイルを追加します。
contact_lookup agent の Python version error Python 3.13+ をインストールします(そのサンプルの pyproject.toml で必要)。
npm run build:renderer fails 先に samples/client/lit/npm install を実行したことを確認します。
Angular client shows blank page npm start の前に npm run build:sandbox を実行したことを確認します。
MCP app iframe doesn't load MCP server(port 8000) と proxy agent(port 10006) の両方が実行中であることを確認します。
ng serve not found npm install --include=dev を実行して、@angular/cli を含む dev dependency をインストールします。
"URL with hostname not allowed" Angular 21 は許可 host を制限します。デフォルトの localhost を使い、--host 0.0.0.0 は渡さないでください。
Security self-test fails in dev URL に ?disable_security_self_test=true を追加します。