ext-apps/examples/basic-host
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Olivier Chafik 70b393a7e6
feat: implement ui/notifications/tool-cancelled + add missing App.onteardown (#120)
* feat: implement ui/notifications/tool-cancelled throughout SDK

Wire the tool-cancelled notification that hosts MUST send when tool
execution is cancelled (user action, timeout, classifier intervention, etc.):

- Add McpUiToolCancelledNotification type in spec.types.ts
- Generate Zod schema via ts-to-zod
- Add AppBridge.sendToolCancelled() method for hosts
- Add App.ontoolcancelled setter for guest UIs
- Export type and schema from types.ts
- Add tests for notification with/without reason

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat: add App.onteardown handler for graceful shutdown

Wire the guest UI side of ui/resource-teardown request:

- Add onteardown setter in App class for guest UIs to handle teardown
- Allow ui/resource-teardown in assertRequestHandlerCapability
- Import McpUiResourceTeardownRequest/Result types
- Add tests for sync and async cleanup handlers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(example): wire tool-cancelled and teardown in basic-host

Update basic-host example to properly use the new notifications:

- Send sendToolCancelled when tool call promise rejects
- Send sendResourceTeardown when React component unmounts
- Store appBridge ref for cleanup access

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(example): implement graceful teardown per spec recommendation

Address review feedback: the host now waits for guest's teardown
response before unmounting, following the spec: "Host SHOULD wait
for a response before tearing down the resource (to prevent data loss)."

Changes:
- Add close button to tool call panels
- Two-phase unmount: isDestroying state dims panel and disables events
- sendResourceTeardown() completes before component removal
- Non-app tool results close immediately

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(examples): add onteardown handlers to demo apps

Demonstrate graceful shutdown with simulated 500ms cleanup work.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-11 20:31:17 +01:00
..
src feat: implement ui/notifications/tool-cancelled + add missing App.onteardown (#120) 2025-12-11 20:31:17 +01:00
index.html Add basic-host and basic-server-react examples 2025-12-04 14:28:27 -06:00
package.json Add basic-host and basic-server-react examples 2025-12-04 14:28:27 -06:00
README.md Smoothen the process of getting started (#103) 2025-12-09 16:15:32 +01:00
sandbox.html Fix mimeType, CSP settings and QR code example (#111) 2025-12-09 20:26:34 +01:00
serve.ts Smoothen the process of getting started (#103) 2025-12-09 16:15:32 +01:00
tsconfig.json Add basic-host and basic-server-react examples 2025-12-04 14:28:27 -06:00
vite.config.ts Add basic-host and basic-server-react examples 2025-12-04 14:28:27 -06:00

Example: Basic Host

A reference implementation showing how to build an MCP host application that connects to MCP servers and renders tool UIs in a secure sandbox.

This basic host can also be used to test MCP Apps during local development.

Key Files

Getting Started

npm install
npm run start
# Open http://localhost:8080

By default, the host application will try to connect to an MCP server at http://localhost:3001/mcp. You can configure this behavior by setting the SERVERS environment variable with a JSON array of server URLs:

SERVERS='["http://localhost:1234/mcp", "http://localhost:5678/mcp"]' npm run start

Architecture

This example uses a double-iframe sandbox pattern for secure UI isolation:

Host (port 8080)
  └── Outer iframe (port 8081) - sandbox proxy
        └── Inner iframe (srcdoc) - untrusted tool UI

Why two iframes?

  • The outer iframe runs on a separate origin (port 8081) preventing direct access to the host
  • The inner iframe receives HTML via srcdoc and is restricted by sandbox attributes
  • Messages flow through the outer iframe which validates and relays them bidirectionally

This architecture ensures that even if tool UI code is malicious, it cannot access the host application's DOM, cookies, or JavaScript context.