When MCP Apps make sense
How it works
- Server declares a UI resource using the
ui://URI scheme (MIME typetext/html;profile=mcp-app). - A regular tool returns metadata pointing at that UI resource.
- Host preloads the UI before/while the tool runs — streaming inputs into the app.
- Host fetches the HTML and renders it in a sandboxed iframe.
- App talks to host via
postMessageusing standard MCP JSON-RPC.
Tool metadata example
{
"name": "show_color_picker",
"description": "Open an interactive color picker.",
"inputSchema": { "type": "object" },
"_meta": {
"io.modelcontextprotocol/ui": {
"resourceUri": "ui://color-picker/index.html"
}
}
}
Sandbox & security model
Every MCP App runs inside a sandboxed <iframe>. It cannot:
- Access the parent document or DOM
- Read host cookies or localStorage
- Navigate the parent page
- Execute scripts in the parent context
Communication goes through postMessage. The host filters which MCP capabilities the app can use (e.g., it can disable sendOpenLink).
Building one
Anthropic ships a create-mcp-app skill for Claude Code:
cp -r ext-apps/plugins/mcp-apps/skills/create-mcp-app \
~/.claude/skills/create-mcp-app
# Ask Claude Code:
# "Create an MCP App that displays a color picker"
cd app/
npm install && npm run build && npm run serve
Reference examples in ext-apps include map-server (CesiumJS), threejs-server, scenario-modeler, pdf-server, qr-server, and starter templates for React, Vue, Svelte, Preact, Solid, and vanilla JS.
Client support
MCP Apps is an optional extension — clients must negotiate it explicitly. Today it is supported in Claude, Claude Desktop, VS Code GitHub Copilot, and Goose.
Quick summary
- MCP Apps embed sandboxed HTML UIs inside conversations
- Tools reference a
ui://resource via_meta.io.modelcontextprotocol/ui - Iframe + postMessage gives strong isolation by default
- Negotiated as an extension capability — not part of core MCP