
mcp-apps-ts
by olaservo
Agent skills for building, testing, and learning about MCP
SKILL.md
name: mcp-apps-ts description: Build interactive HTML UIs for MCP servers using the MCP Apps extension (SEP-1865). Covers server-side tool registration with UI resources, client-side App lifecycle, and host integration via AppBridge. Use when creating visual interfaces for MCP tools.
TypeScript MCP Apps Builder
Build interactive HTML UIs for MCP tools using the MCP Apps extension (SEP-1865).
This is an experimental extension. MCP Apps enables servers to deliver interactive HTML UIs that run in sandboxed iframes, allowing rich visual interfaces while maintaining security.
Tip: Stay up to date! MCP Apps is under active development. Before starting, check the ext-apps repository for:
- Open Pull Requests - upcoming changes
- Issues - known bugs and feature requests
- Recent Commits - latest changes
- Releases - version history
How It Works
- Browse the snippet catalog below or in
snippets/ - Identify your role: server developer, app developer, or host integrator
- Copy the snippets you need into your project
- Customize the copied code for your use case
Quick Start Decision Trees
What Role Are You Building?
Want a complete, runnable starter project?
-> Copy a SCAFFOLD (new!)
- scaffold-vanilla-server: Full server + vanilla JS UI
Copy the directory, run npm install && npm run dev
See snippets/scaffold/vanilla-server/README.md
Building an MCP server that provides tools with UIs?
-> Start with SERVER snippets
- tool-with-ui: Register tool with associated HTML UI
- tool-with-structured: Return structured content
- resource-with-csp: Add Content Security Policy
- server-with-private-tools: Hide tools from model (NEW!)
Building the HTML UI that displays to users?
-> Start with APP snippets
- app-vanilla-basic: Simple App class setup (vanilla JS)
- app-vanilla-full: Full lifecycle handlers (vanilla JS)
- app-react-basic: React hooks integration
- app-react-with-styles: React with host styling (NEW!)
- tool-calling: Call back to server tools
- app-with-display-mode: Request fullscreen/pip (NEW!)
- app-with-model-context: Update model context (NEW!)
Building a host/client that embeds MCP app UIs?
-> Start with HOST snippets
- host-full-integration: Complete end-to-end flow (single server)
- host-multi-server: Connect to multiple servers with UI routing
- sandbox-proxy: Required for security
- app-bridge-basic: Just the AppBridge setup
- app-bridge-handlers: Full handlers setup
Which App Framework Should I Use?
Minimal dependencies, simple UI?
-> Use Vanilla JS snippets
- app-vanilla-basic for getting started
- app-vanilla-full for full control
React-based UI with state management?
-> Use React snippets
- app-react-basic for hooks integration
- app-react-with-styles for host styling
Phase 1: Research
1.1 Understand MCP Apps Architecture
MCP Apps uses a two-part registration pattern: Tool + UI Resource.
| Component | Package | Role |
|---|---|---|
| Server | @modelcontextprotocol/sdk + ext-apps/server | Register tools with ui:// resources |
| App | @modelcontextprotocol/ext-apps | HTML UI running in sandbox iframe |
| Host | @modelcontextprotocol/ext-apps/app-bridge | Embeds and manages app iframes |
1.2 Key Concepts
ui://URI Scheme: Tools reference UI resources viaui://tool-name/app.htmlURIsRESOURCE_URI_META_KEY: Constant for linking tools to UIs in_metaRESOURCE_MIME_TYPE:text/html;profile=mcp-appidentifies MCP App resources- PostMessageTransport: Communication between app iframe and host
- Double-iframe Sandboxing: Outer iframe isolates, inner iframe runs app with
allow-scripts - Host Context: Theme, locale, styles, and safe area information from the host
- Display Modes: Inline, fullscreen, or picture-in-picture display options
1.3 Browse Available Snippets
| Snippet | Description | Best For |
|---|---|---|
scaffold-vanilla-server | Complete starter project | Quickest start - copy & run |
tool-with-ui | Tool with UI resource registration | Basic server setup |
tool-with-structured | Tool returning structuredContent | Rich data responses |
resource-with-csp | UI resource with CSP metadata | Security-conscious apps |
server-with-private-tools | Tools hidden from model | UI-only actions (NEW!) |
app-vanilla-basic | Basic App class (vanilla JS) | Quick prototypes |
app-vanilla-full | Full lifecycle handlers | Production apps |
app-react-basic | React hooks integration | React projects |
app-react-with-styles | React with host styling | Themed React apps (NEW!) |
tool-calling | Call MCP tools from app | Interactive UIs |
app-with-display-mode | Request fullscreen/pip | Immersive UIs (NEW!) |
app-with-model-context | Update model context | State persistence (NEW!) |
app-bridge-basic | Basic host embedding | Simple integration |
app-bridge-handlers | Full AppBridge handlers | Custom hosts |
host-full-integration | Complete host flow | End-to-end hosting |
host-multi-server | Multi-server host | Multiple server routing |
sandbox-proxy | Sandbox proxy HTML | Host security |
Phase 2: Implement
2.1 Server-Side: Register Tool with UI
npm install @modelcontextprotocol/sdk @modelcontextprotocol/ext-apps zod
Copy the tool-with-ui snippet and customize:
- Define your tool's input schema
- Create the UI resource HTML content
- Link tool to UI via
ui://URI and_meta.ui.resourceUri
For tools that should only be callable by the UI (not the model), use server-with-private-tools:
_meta: { ui: { resourceUri: "ui://...", visibility: ["app"] } }
See also: For MCP server basics (transports, tool registration patterns), refer to the mcp-server-ts skill.
2.2 App-Side: Build the UI
Vanilla JS:
npm install @modelcontextprotocol/ext-apps
React:
npm install @modelcontextprotocol/ext-apps react react-dom
Copy the appropriate app snippet and implement:
- Initialize App with name and version
- Register handlers BEFORE calling
connect() - Handle
ontoolresultfor tool execution results - Handle
ontoolcancelledfor cancellation - Handle
onhostcontextchangedfor theme/style changes - Call tools via
app.callServerTool()
2.3 Host Context & Styling
Apps can access host context for theme, styles, and safe areas:
const context = app.getHostContext();
// {
// theme: "light" | "dark",
// locale: "en-US",
// styles: { variables: { ... }, css: { fonts: "..." } },
// safeAreaInsets: { top, right, bottom, left },
// availableDisplayModes: ["inline", "fullscreen", "pip"]
// }
React: Use useHostStyleVariables() and useHostFonts() hooks to automatically apply host styles.
2.4 Display Modes
Apps can request different display modes:
// Check if fullscreen is available
if (context?.availableDisplayModes?.includes("fullscreen")) {
await app.requestDisplayMode({ mode: "fullscreen" });
}
2.5 Model Context Updates
Apps can update the model's context with state information:
await app.updateModelContext({
content: [{ type: "text", text: "User selected 3 items" }],
structuredContent: { items: 3, total: 150.00 }
});
2.6 Host-Side: Embed Apps (Optional)
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk
Copy the host-full-integration snippet for the complete flow, or start with app-bridge-basic for just the AppBridge setup:
- Connect MCP client to server (see mcp-client-ts skill)
- Create AppBridge with the connected client
- Set up sandbox proxy iframe (use
sandbox-proxysnippet) - Register handlers before connecting
- Load UI resource and initialize app
Critical: The App initiates
ui/initialize, the Host responds! If building without AppBridge SDK, you must handle the request/response correctly. See "Common Pitfalls" in the Architecture reference.
See also: For MCP client basics (connecting to servers, calling tools), refer to the mcp-client-ts skill.
Phase 3: Test
3.1 Build All Components
# Build UI (using Vite with vite-plugin-singlefile)
npm run build
# Start MCP server
npm run serve
3.2 Test with Reference Host
# Clone ext-apps repo for test host
git clone https://github.com/modelcontextprotocol/ext-apps.git
cd ext-apps/examples/basic-host
npm install && npm start
# Open http://localhost:8080
3.3 Quality Checklist
- Tool correctly registers with
ui://resource -
RESOURCE_MIME_TYPEistext/html;profile=mcp-app - App initializes without errors
- Handlers registered BEFORE
connect() -
ontoolresultreceives tool execution results -
ontoolcancelledhandles cancellation gracefully -
onhostcontextchangedresponds to theme changes -
callServerTool()successfully calls server - CSP is properly declared (if using)
3.4 Common Gotchas
1. Always describe your UI in content:
The model doesn't see the visual UI - only the tool result content. Always include a text description of what was rendered:
return {
content: [{ type: "text", text: "Displayed weather widget showing 72°F, sunny conditions" }],
structuredContent: { temp: 72, condition: "sunny" }
};
2. Data flow to model context:
contentandstructuredContent→ sent to model context_meta→ NOT sent to model (only for host/UI metadata)- Don't put large base64 data in
structuredContent- usecontentwith resource references
3. Check for UI support before registering (coming soon):
Once PR #313 merges, use hasUiSupport() to conditionally register UI tools:
server.oninitialized = ({ clientCapabilities }) => {
if (hasUiSupport(clientCapabilities)) {
registerAppTool(server, "weather", { /* ... */ }, handler);
}
};
Available Snippets Catalog
Scaffold (Complete Starter Projects)
| Name | Description |
|---|---|
scaffold-vanilla-server | Complete MCP App server with vanilla JS UI - copy entire directory and run |
Quick Start with Scaffold:
# Copy the scaffold to your project
cp -r snippets/scaffold/vanilla-server ./my-mcp-app
# Install and run
cd my-mcp-app
npm install
npm run dev
Server runs at http://localhost:3102/mcp. Test with the basic-host example.
Server (MCP Server with UI)
| Name | Description |
|---|---|
tool-with-ui | Register MCP tool with UI resource using registerAppTool and registerAppResource |
tool-with-structured | Tool returning structuredContent for typed responses |
resource-with-csp | UI resource with Content Security Policy metadata |
server-with-private-tools | Tools with visibility: ["app"] hidden from model |
App (HTML UI in iframe)
| Name | Description |
|---|---|
app-vanilla-basic | Basic App class with ontoolresult handler (vanilla JS) |
app-vanilla-full | Full lifecycle: all handlers including ontoolcancelled, onhostcontextchanged |
app-react-basic | React component with useApp hook |
app-react-with-styles | React with useHostStyleVariables and useHostFonts |
tool-calling | Examples of calling MCP tools from app UI |
app-with-display-mode | Request fullscreen/pip display modes |
app-with-model-context | Update model context with app state |
Host (Embedding Apps)
| Name | Description |
|---|---|
app-bridge-basic | Basic AppBridge setup with PostMessageTransport |
app-bridge-handlers | Full handlers: onmessage, onopenlink, onloggingmessage, onsizechange |
host-full-integration | Complete flow: MCP client + tool call + UI detection + AppBridge |
host-multi-server | Multi-server host with tool aggregation and UI routing |
sandbox-proxy | Sandbox proxy HTML for double-iframe security |
Reference Files
For deeper guidance, load these reference documents:
- MCP Apps Architecture - Component overview, data flow, security, common pitfalls, open PRs/issues
- MCP Apps API Reference - Full API documentation
Important: Read the "Common Pitfalls" section in the Architecture doc before implementing a host. Key issues include srcdoc iframe origins, protocol direction, message sequencing, and handler overwrite bugs.
SDK Packages
| Package | Import Path | Purpose |
|---|---|---|
| Main SDK | @modelcontextprotocol/ext-apps | App class, types, style utilities |
| Server helpers | @modelcontextprotocol/ext-apps/server | registerAppTool, registerAppResource |
| React | @modelcontextprotocol/ext-apps/react | useApp, useHostStyleVariables, useHostFonts, useHostStyles |
| App Bridge | @modelcontextprotocol/ext-apps/app-bridge | AppBridge for hosts |
External Resources
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です