This skill should be used when the user asks to "add an app to my MCP server", "add UI to my MCP server", "add a view to my MCP tool", "enrich MCP tools with UI", "add interactive UI to existing server", "add MCP Apps to my server", or needs to add interactive UI capabilities to an existing MCP server that already has tools. Provides guidance for analyzing existing tools and adding MCP Apps UI resources.
This skill should be used when the user asks to "add an app to my MCP server", "add UI to my MCP server", "add a view to my MCP tool", "enrich MCP tools with UI", "add interactive UI to existing server", "add MCP Apps to my server", or needs to add interactive UI capabilities to an existing MCP server that already has tools. Provides guidance for analyzing existing tools and adding MCP Apps UI resources.
Add UI to MCP Server
Enrich an existing MCP server's tools with interactive UIs using the MCP Apps SDK (@modelcontextprotocol/ext-apps).
How It Works
Existing tools get paired with HTML resources that render inline in the host's conversation. The tool continues to work for text-only clients — UI is an enhancement, not a replacement. Each tool that benefits from UI gets linked to a resource via _meta.ui.resourceUri, and the host renders that resource in a sandboxed iframe when the tool is called.
Getting Reference Code
Clone the SDK repository for working examples and API documentation:
Learn and adapt from /tmp/mcp-ext-apps/examples/basic-server-{framework}/:
Template
Key Files
basic-server-vanillajs/
server.ts, src/mcp-app.ts, mcp-app.html
basic-server-react/
server.ts, src/mcp-app.tsx (uses useApp hook)
basic-server-vue/
server.ts, src/App.vue
basic-server-svelte/
server.ts, src/App.svelte
basic-server-preact/
server.ts, src/mcp-app.tsx
basic-server-solid/
server.ts, src/mcp-app.tsx
Step 1: Analyze Existing Tools
Before writing any code, analyze the server's existing tools and determine which ones benefit from UI.
Read the server source and list all registered tools
For each tool, assess whether it would benefit from UI (returns data that could be visualized, involves user interaction, etc.) vs. is fine as text-only (simple lookups, utility functions)
Identify tools that could become app-only helpers (data the UI needs to poll/fetch but the model doesn't need to call directly)
Present the analysis to the user and confirm which tools to enhance
Decision Framework
Tool output type
UI benefit
Example
Structured data / lists / tables
High — interactive table, search, filtering
List of items, search results
Metrics / numbers over time
High — charts, gauges, dashboards
System stats, analytics
Media / rich content
High — viewer, player, renderer
Maps, PDFs, images, video
Simple text / confirmations
Low — text is fine
"File created", "Setting updated"
Data for other tools
Consider app-only
Polling endpoints, chunk loaders
Step 2: Add Dependencies
npm install @modelcontextprotocol/ext-apps
npm install -D vite vite-plugin-singlefile
Plus framework-specific dependencies if needed (e.g., react, react-dom, @vitejs/plugin-react for React).
Use npm install to add dependencies rather than manually writing version numbers. This lets npm resolve the latest compatible versions. Never specify version numbers from memory.
Step 3: Set Up the Build Pipeline
Vite Configuration
Create vite.config.ts with vite-plugin-singlefile to bundle the UI into a single HTML file:
import { defineConfig } from"vite";
import { viteSingleFile } from"vite-plugin-singlefile";
exportdefaultdefineConfig({
plugins: [viteSingleFile()],
build: {
outDir: "dist",
rollupOptions: {
input: "mcp-app.html", // one per UI, or one shared entry
},
},
});
HTML Entry Point
Create mcp-app.html (or one per distinct UI if tools need different views):
Key variable groups: --color-background-*, --color-text-*, --color-border-*, --font-sans, --font-mono, --font-text-*-size, --font-heading-*-size, --border-radius-*. See src/spec.types.ts for the full list.
For React apps, use the useApp and useHostStyles hooks instead — see basic-server-react/ for the pattern.
Optional Enhancements
App-Only Helper Tools
Tools the UI calls but the model doesn't need to invoke directly (polling, pagination, chunk loading):
registerAppTool(server, "poll-data", {
description: "Polls latest data for the UI",
_meta: { ui: { resourceUri, visibility: ["app"] } },
}, async () => {
const data = awaitgetLatestData();
return { content: [{ type: "text", text: JSON.stringify(data) }] };
});
The UI calls these via app.callServerTool("poll-data", {}).
CSP Configuration
If the UI needs to load external resources (fonts, APIs, CDNs), declare the domains:
Forgetting text content fallback — Always include content array with text for non-UI hosts
Registering handlers after connect() — Register ALL handlers BEFORE calling app.connect()
Missing vite-plugin-singlefile — Without it, assets won't load in the sandboxed iframe
Forgetting resource registration — The tool references a resourceUri that must have a matching resource
Hardcoding styles — Use host CSS variables (var(--color-*)) for theme integration
Not handling safe area insets — Always apply ctx.safeAreaInsets in onhostcontextchanged
Testing
Using basic-host
Test the enhanced server with the basic-host example:
# Terminal 1: Build and run your server
npm run build && npm run serve
# Terminal 2: Run basic-host (from cloned repo)cd /tmp/mcp-ext-apps/examples/basic-host
npm install
SERVERS='["http://localhost:3001/mcp"]' npm run start
# Open http://localhost:8080
Configure SERVERS with a JSON array of your server URLs (default: http://localhost:3001/mcp).