Skip to content
Merged
Show file tree
Hide file tree
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
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,5 @@ coverage/
a2a_agents/python/a2ui_agent/src/a2ui/assets/**/*.json
## new agent SDK path
agent_sdks/python/src/a2ui/assets/**/*.json
## Generated JS file from the strictly-typed `sandbox.ts`.
samples/client/angular/projects/mcp_calculator/public/sandbox_iframe/sandbox.js
*.tsbuildinfo
## Generated files for sandbox frame.
samples/client/angular/projects/mcp_calculator/public/sandbox_iframe/
2 changes: 1 addition & 1 deletion samples/client/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"build:renderer": "cd ../../../renderers && for dir in 'web_core' 'markdown/markdown-it'; do (cd \"$dir\" && npm install && npm run build); done",
"serve:agent:restaurant": "cd ../../agent/adk/restaurant_finder && uv run .",
"demo:restaurant": "npm run build:renderer && concurrently -k -n \"AGENT,WEB\" -c \"magenta,blue\" \"npm run serve:agent:restaurant\" \"npm start -- restaurant\"",
"build:sandbox": "esbuild projects/mcp_calculator/public/sandbox_iframe/sandbox.ts --bundle --outfile=projects/mcp_calculator/public/sandbox_iframe/sandbox.js --format=esm --platform=browser"
"build:sandbox": "esbuild ../shared/sandbox_iframe/sandbox.ts --bundle --outfile=projects/mcp_calculator/public/sandbox_iframe/sandbox.js --format=esm --platform=browser --alias:@modelcontextprotocol/ext-apps/app-bridge=./node_modules/@modelcontextprotocol/ext-apps/dist/src/app-bridge.js && cp ../shared/sandbox_iframe/sandbox.html projects/mcp_calculator/public/sandbox_iframe/sandbox.html"
},
"prettier": {
"printWidth": 100,
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,17 @@ export class McpApp

window.addEventListener('message', this.messageHandler);

// Set src to trigger load AFTER listener is ready
// TODO: Make the sandbox URL configurable. To ensure CORS encapsulation, the sandbox
// should be served from a different origin than the host app.
const sandboxUrl = 'sandbox_iframe/sandbox.html';

// Check for query param to opt-out of origin toggle (for testing)
const urlParams = new URLSearchParams(window.location.search);
const disableSecuritySelfTest = urlParams.get('disable_security_self_test') === 'true';

const currentOrigin = window.location.origin;
let sandboxOrigin = currentOrigin;
let sandboxUrl = `${sandboxOrigin}/sandbox_iframe/sandbox.html`;
if (disableSecuritySelfTest) {
sandboxUrl += '?disable_security_self_test=true';
}
this.iframeSrc.set(
this.sanitizer.bypassSecurityTrustResourceUrl(sandboxUrl),
);
Expand Down
17 changes: 16 additions & 1 deletion samples/client/shared/sandbox_iframe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ A2UI utilizes a **double-iframe isolation pattern** to run untrusted third-party

Testing for any changes in this directory requires bringing up the relevant clients and servers.

### Contact Multi-Surface Sample (Lit & ADK Agent)
### 1. Contact Multi-Surface Sample (Lit & ADK Agent)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

mcp_apps_iframe


This test verifies the sandbox with a Lit-based client and an ADK-based A2A agent.

Expand All @@ -26,3 +26,18 @@ This test verifies the sandbox with a Lit-based client and an ADK-based A2A agen
- Path: `../../lit/contact/`
- Command: `npm run dev` (requires building the Lit renderer first)
- URL: `http://localhost:5173/`

### 2. MCP Apps (Calculator) (Angular)

This test verifies the sandbox with an Angular-based client, an MCP Proxy Agent, and a remote MCP Server.

- **MCP Server (Calculator)**:
- Path: `../../../agent/mcp/mcp-apps-calculcator/`
- Command: `uv run .` (runs on port 8000)
- **MCP Apps Proxy Agent**:
- Path: `../../../agent/adk/mcp_app_proxy/`
- Command: `uv run .` (requires `GEMINI_API_KEY` in `.env`)
- **Angular Client App**:
- Path: `../../angular/`
- Command: `npm start -- mcp_calculator` (requires `npm run build:sandbox` and `npm install`)
- URL: [`http://localhost:4200/?disable_security_self_test=true`](http://localhost:4200/?disable_security_self_test=true)
20 changes: 13 additions & 7 deletions samples/client/shared/sandbox_iframe/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,19 @@ if (!document.referrer.match(ALLOWED_REFERRER_PATTERN)) {
const EXPECTED_HOST_ORIGIN = new URL(document.referrer).origin;
const OWN_ORIGIN = new URL(window.location.href).origin;

// Security self-test: verify iframe isolation is working correctly.
try {
window.top!.alert("If you see this, the sandbox is not setup securely.");
throw "FAIL";
} catch (e) {
if (e === "FAIL") {
throw new Error("The sandbox is not setup securely.");
// Check for query param to opt-out of security self-test (for testing)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

mcp_apps_iframe.ts

const urlParams = new URLSearchParams(window.location.search);
const disableSelfTest = urlParams.get('disable_security_self_test') === 'true';

if (!disableSelfTest) {
// Security self-test: verify iframe isolation is working correctly.
try {
window.top!.alert("If you see this, the sandbox is not setup securely.");
throw "FAIL";
} catch (e) {
if (e === "FAIL") {
throw new Error("The sandbox is not setup securely.");
}
}
}

Expand Down
Loading