diff --git a/docs/assets/mcp-apps-calculator-demo.gif b/docs/assets/mcp-apps-calculator-demo.gif new file mode 100644 index 000000000..83a18e8dd Binary files /dev/null and b/docs/assets/mcp-apps-calculator-demo.gif differ diff --git a/docs/guides/mcp-apps-in-a2ui-surface.md b/docs/guides/mcp-apps-in-a2ui-surface.md index 2d86e22f1..c061f60fa 100644 --- a/docs/guides/mcp-apps-in-a2ui-surface.md +++ b/docs/guides/mcp-apps-in-a2ui-surface.md @@ -8,6 +8,8 @@ This guide explains how **Model Context Protocol (MCP) Applications** are integr The Model Context Protocol (MCP) allows MCP servers to deliver rich, interactive HTML-based user interfaces to hosts. A2UI provides a secure environment to run these third-party applications. +MCP Calculator demo — loading the app, opening the calculator, and chatting with the agent + ## Double-Iframe Isolation Pattern To run untrusted third-party code securely, A2UI utilizes a **double-iframe** isolation pattern. This approach isolates raw DOM injection from the main application while maintaining a structured JSON-RPC channel. @@ -21,7 +23,7 @@ To prevent this, A2UI strictly excludes `allow-same-origin` for the inner iframe ### The Architecture 1. **[Sandbox Proxy (`sandbox.html`)](https://github.com/google/A2UI/blob/main/samples/client/shared/mcp_apps_inner_iframe/sandbox.html)**: An intermediate `iframe` served from the same origin. It isolates raw DOM injection from the main app while maintaining a structured JSON-RPC channel. - - Permissions: **Do not sandbox** in the host template (e.g., [`mcp-app.ts`](https://github.com/google/A2UI/blob/main/samples/client/angular/projects/mcp_calculator/src/a2ui-catalog/mcp-app.ts) or [`mcp-apps-component.ts`](https://github.com/google/A2UI/blob/main/samples/client/lit/contact/ui/custom-components/mcp-apps-component.ts)). + - Permissions: **Do not sandbox** in the host template (e.g., [`mcp-app.ts`](https://github.com/google/A2UI/blob/main/samples/client/angular/projects/mcp_calculator/src/a2ui-catalog/mcp-app.ts) or [`mcp-apps-component.ts`](https://github.com/google/A2UI/blob/main/samples/client/lit/custom-components-example/ui/custom-components/mcp-apps-component.ts)). - Host origin validation: Validates that messages come from the expected host origin. 2. **Embedded App (Inner Iframe)**: The innermost `iframe`. Injected dynamically via `srcdoc` with restricted permissions. - Permissions: `sandbox="allow-scripts allow-forms allow-popups allow-modals"` (**MUST NOT** include `allow-same-origin`). @@ -113,51 +115,156 @@ Because `allow-same-origin` is strictly omitted for the innermost iframe, the fo ## Prerequisites To run the samples, ensure you have the following installed: -- **Python (uv)** (version 3.12 or higher suggested) -- **Node.js (npm)** (version 18 or higher recommended) + +- **Python 3.10+** — Required for the agent and MCP server backends +- **[uv](https://docs.astral.sh/uv/)** — Fast Python package manager (used to run all Python samples) +- **Node.js 18+** and **npm** — Required for building and running the client apps +- **A `GEMINI_API_KEY`** — Required by all ADK-based agents. Get one from [Google AI Studio](https://aistudio.google.com/apikey) + +> ⚠️ **Environment variable setup**: You can either export `GEMINI_API_KEY` in your shell or create a `.env` file in each agent directory. The agents use `dotenv` to load `.env` files automatically. +> +> ```bash +> # 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 +> ``` ## Samples -There are two primary samples demonstrating MCP Apps integration: +There are two primary samples demonstrating MCP Apps integration. Each sample requires running **multiple terminals** — one for each backend service and one for the client. + +--- -### 1. Contact Multi-Surface Sample (Lit & ADK Agent) +### Sample 1: Contact Multi-Surface (Lit Client + ADK Agent) This sample verifies the sandbox with a Lit-based client and an ADK-based A2A agent. -- **A2A Agent Server**: - - Path: [`samples/agent/adk/contact_multiple_surfaces/`](https://github.com/google/A2UI/tree/main/samples/agent/adk/contact_multiple_surfaces/) - - Command: `uv run .` (requires `GEMINI_API_KEY` in `.env`) -- **Lit Client App**: - - Path: [`samples/client/lit/contact/`](https://github.com/google/A2UI/tree/main/samples/client/lit/contact/) - - Command: `npm run dev` (requires building the Lit renderer first) - - URL: `http://localhost:5173/` +#### Step 1: Start the A2A Agent Server + +```bash +cd samples/agent/adk/contact_lookup/ +export GEMINI_API_KEY="your-key" # or use a .env file +uv run . +``` + +> ⚠️ **Python version**: This agent requires Python ≥ 3.13 (see its `pyproject.toml`). If `uv run .` fails with a Python version error, ensure you have Python 3.13+ available. + +The agent starts on `http://localhost:10003` by default. + +#### Step 2: Build the Lit Renderer + +In a **new terminal**, build the renderers (required before the client can run): + +```bash +cd samples/client/lit/ +npm install +npm run build:renderer +``` + +> ⚠️ **First-time build**: The `build:renderer` script builds three packages (`web_core`, `markdown-it`, and `lit` renderer) in sequence. This may take a minute on the first run. + +#### Step 3: Start the Lit Client + +```bash +cd samples/client/lit/ +npm run serve:shell +``` + +The client starts at `http://localhost:5173/`. + +> 💡 **Shortcut**: You can run agent + client together with: +> ```bash +> cd samples/client/lit/ +> npm run demo:contact +> ``` +> This builds the renderer and starts both the shell and the contact_lookup agent concurrently. **What to expect**: A contact page where actions prompt an app interface on specific interactions. -### 2. MCP Apps (Calculator) (Angular) +--- + +### Sample 2: MCP Calculator (Angular Client + MCP Server + Proxy Agent) + +This sample verifies the sandbox with an Angular-based client, an MCP Proxy Agent, and a remote MCP Server. It requires **three** backend processes. + +#### Step 1: Start the MCP Server (Calculator) + +```bash +cd samples/agent/mcp/mcp-apps-calculator/ +uv run . +``` -This sample verifies the sandbox with an Angular-based client, an MCP Proxy Agent, and a remote MCP Server. +The MCP server starts on `http://localhost:8000` using SSE transport. -- **MCP Server (Calculator)**: - - Path: [`samples/agent/mcp/mcp-apps-calculator/`](https://github.com/google/A2UI/tree/main/samples/agent/mcp/mcp-apps-calculator/) - - Command: `uv run .` (runs on port 8000) -- **MCP Apps Proxy Agent**: - - Path: [`samples/agent/adk/mcp_app_proxy/`](https://github.com/google/A2UI/tree/main/samples/agent/adk/mcp_app_proxy/) - - Command: `uv run .` (requires `GEMINI_API_KEY` in `.env`) -- **Angular Client App**: - - Path: [`samples/client/angular/`](https://github.com/google/A2UI/tree/main/samples/client/angular/) - - Command: `npm start -- mcp_calculator` (requires `npm run build:sandbox` and `npm install`) - - URL: `http://localhost:4200/?disable_security_self_test=true` +#### Step 2: Start the MCP Apps Proxy Agent + +In a **new terminal**: + +```bash +cd samples/agent/adk/mcp_app_proxy/ +export GEMINI_API_KEY="your-key" # or use a .env file +uv run . +``` + +The proxy agent starts on `http://localhost:10006` by default. + +#### Step 3: Build and Start the Angular Client + +In a **new terminal**: + +```bash +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` is required**: The Angular CLI (`@angular/cli`) is a dev dependency. Without `--include=dev`, `ng serve` won't be available. +> +> ⚠️ **`build:renderer` and `build:sandbox` are both required**: `build:renderer` compiles the A2UI renderer packages that the Angular app depends on. `build:sandbox` bundles the sandbox proxy into the Angular project's public assets. Without either, the app won't work. + +The client starts at `http://localhost:4200/`. + +#### Step 4: Open in Browser + +Navigate to: + +``` +http://localhost:4200/?disable_security_self_test=true +``` **What to expect**: A basic calculator will be rendered. You can execute arithmetic calculations cleanly through the sandbox. +--- + ## URL Options for Testing For testing purposes, you can opt-out of the security self-test by using specific URL query parameters. -`disable_security_self_test=true` +### `disable_security_self_test=true` -This query parameter allows you to bypass the security self-test that verifies iframe isolation. This is useful for debugging and testing environments. +This query parameter allows you to bypass the security self-test that verifies iframe isolation. This is useful for debugging and testing environments where the double-iframe setup may not pass strict origin checks (e.g., `localhost` development). Example usage: -`http://localhost:4200/?disable_security_self_test=true` +``` +http://localhost:4200/?disable_security_self_test=true +``` + +## Troubleshooting + +| Problem | Solution | +|---------|----------| +| `GEMINI_API_KEY environment variable not set` | Export the key or add a `.env` file in the agent directory | +| Python version error on `contact_lookup` agent | Install Python 3.13+ (required by that sample's `pyproject.toml`) | +| `npm run build:renderer` fails | Make sure you ran `npm install` first in `samples/client/lit/` | +| Angular client shows blank page | Ensure you ran `npm run build:sandbox` before `npm start` | +| MCP app iframe doesn't load | Check that both the MCP server (port 8000) and proxy agent (port 10006) are running | +| `ng serve` not found | Run `npm install --include=dev` to install dev dependencies including `@angular/cli` | +| "URL with hostname not allowed" | Angular 21 restricts allowed hosts. Use `localhost` (the default) — do not pass `--host 0.0.0.0` | +| Security self-test fails in dev | Add `?disable_security_self_test=true` to the URL |