Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 124 additions & 27 deletions docs/guides/mcp-apps-in-a2ui-surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,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)).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The linked Lit example (mcp-apps-component.ts) contains two discrepancies with the security model described in this guide:

  1. Outer Iframe Sandboxing: The guide states that the host template should not sandbox the proxy iframe (line 24). However, the Lit component includes a sandbox attribute with allow-same-origin (line 79 of that file).
  2. Inner Iframe Isolation: The guide emphasizes that the inner iframe MUST NOT include allow-same-origin (lines 19 and 27). However, the Lit component explicitly includes it when calling sendSandboxResourceReady (line 179 of that file).

These should be corrected in the sample code to ensure it serves as a secure and consistent reference implementation. The Angular example (mcp-app.ts) correctly follows these principles.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — verified this is a real issue.

Angular sample correctly passes sandbox: 'allow-scripts' to sendSandboxResourceReady() (line ~89 of mcp-app.ts), so the inner iframe ends up with only allow-scripts.

Lit sample passes sandbox: 'allow-scripts allow-forms allow-popups allow-modals allow-same-origin' (line ~179 of mcp-apps-component.ts), which violates the guide's security model.

Additionally:

  • The outer iframe in Lit has sandbox="allow-scripts allow-forms allow-popups allow-modals allow-same-origin" (line 79) — the guide says the proxy iframe should not be sandboxed.
  • The shared sandbox.ts (line 66) sets an initial default of allow-scripts allow-same-origin allow-forms on the inner iframe before sendSandboxResourceReady overrides it — this means there's a brief window where the inner iframe has allow-same-origin.

This is a docs PR so fixing the Lit sample code is out of scope here, but it should be filed as a separate issue. The guide's security model description is correct — the Lit sample doesn't follow it.

- 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`).
Expand Down Expand Up @@ -113,51 +113,148 @@ 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:8080` 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:8080` by default.

#### Step 3: Build and Start the Angular Client

In a **new terminal**:

```bash
cd samples/client/angular/
npm install
npm run build:sandbox
npm start -- mcp_calculator
```

> ⚠️ **`build:sandbox` is required**: This step bundles the sandbox proxy (`sandbox.html` and `sandbox.js`) into the Angular project's public assets. Without it, the MCP app iframe won't load.

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 8080) are running |
| Security self-test fails in dev | Add `?disable_security_self_test=true` to the URL |
Loading