Skip to content

Commit 6166242

Browse files
Add Claude Code plugin with "Create MCP App" skill (#256)
Add a Claude Code plugin (`plugins/mcp-apps/`) for distribution via the plugin marketplace. The plugin provides the "Create MCP App" skill that guides the agent through building MCP Apps with interactive UIs. The skill includes: - Instructions to clone SDK repo for reference templates - Framework selection guidance (React, Vanilla JS, Vue, Svelte, etc.) - Tool visibility configuration (`_meta.ui.visibility`) - Host styling integration patterns - Testing workflow using `basic-host` - Common mistakes to avoid Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 969b905 commit 6166242

File tree

5 files changed

+264
-0
lines changed

5 files changed

+264
-0
lines changed

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ examples/basic-host/**/*.tsx
33
examples/basic-server-*/**/*.ts
44
examples/basic-server-*/**/*.tsx
55
**/vendor/**
6+
SKILL.md

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,7 @@ Uses npm workspaces. Examples in `examples/` are separate packages:
8585
- `basic-server-*` - Starter templates (vanillajs, react, vue, svelte, preact, solid). Use these as the basis for new examples.
8686
- `basic-host` - Reference host implementation
8787
- Other examples showcase specific features (charts, 3D, video, etc.)
88+
89+
## Claude Code Plugin
90+
91+
The `plugins/mcp-apps/` directory contains a Claude Code plugin distributed via the plugin marketplace. It provides the "Create MCP App" skill (`plugins/mcp-apps/skills/create-mcp-app/SKILL.md`) that guides users through building MCP Apps with interactive UIs.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "mcp-apps",
3+
"version": "0.1.0",
4+
"description": "Claude Code skill for building MCP Apps with interactive UIs",
5+
"author": {
6+
"name": "MCP Apps"
7+
},
8+
"repository": "https://github.com/anthropics/mcp-ext-apps",
9+
"license": "MIT",
10+
"keywords": ["mcp", "mcp-apps", "model-context-protocol", "ui", "interactive"]
11+
}

plugins/mcp-apps/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# MCP Apps Plugin for Claude Code
2+
3+
A Claude Code plugin that provides the "Create MCP App" skill for building MCP Apps with interactive UIs.
4+
5+
## Installation
6+
7+
Install via Claude Code:
8+
9+
1. Run `/plugin` in Claude Code
10+
2. Navigate to the Discover tab
11+
3. Install "mcp-apps"
12+
13+
## Usage
14+
15+
Invoke the skill by asking Claude Code to:
16+
17+
- "Create an MCP App"
18+
- "Add a UI to an MCP tool"
19+
- "Build an interactive MCP widget"
20+
- "Scaffold an MCP App"
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
---
2+
name: Create MCP App
3+
description: This skill should be used when the user asks to "create an MCP App", "add a UI to an MCP tool", "build an interactive MCP widget", "scaffold an MCP App", or needs guidance on MCP Apps SDK patterns, UI-resource registration, MCP App lifecycle, or host integration. Provides comprehensive guidance for building MCP Apps with interactive UIs.
4+
---
5+
6+
# Create MCP App
7+
8+
Build interactive UIs that run inside MCP-enabled hosts like Claude Desktop. An MCP App combines an MCP tool with an HTML resource to display rich, interactive content.
9+
10+
## Core Concept: Tool + Resource
11+
12+
Every MCP App requires two parts linked together:
13+
14+
1. **Tool** - Called by the LLM/host, returns data
15+
2. **Resource** - Serves the bundled HTML UI that displays the data
16+
3. **Link** - The tool's `_meta.ui.resourceUri` references the resource
17+
18+
```
19+
Host calls tool → Server returns result → Host renders resource UI → UI receives result
20+
```
21+
22+
## Quick Start Decision Tree
23+
24+
### Framework Selection
25+
26+
| Framework | SDK Support | Best For |
27+
|-----------|-------------|----------|
28+
| React | `useApp` hook provided | Teams familiar with React |
29+
| Vanilla JS | Manual lifecycle | Simple apps, no build complexity |
30+
| Vue/Svelte/Preact/Solid | Manual lifecycle | Framework preference |
31+
32+
### Project Context
33+
34+
**Adding to existing MCP server:**
35+
- Import `registerAppTool`, `registerAppResource` from SDK
36+
- Add tool registration with `_meta.ui.resourceUri`
37+
- Add resource registration serving bundled HTML
38+
39+
**Creating new MCP server:**
40+
- Set up server with transport (stdio or HTTP)
41+
- Register tools and resources
42+
- Configure build system with `vite-plugin-singlefile`
43+
44+
## Getting Reference Code
45+
46+
Clone the SDK repository for working examples and API documentation:
47+
48+
```bash
49+
git clone --branch "v$(npm view @modelcontextprotocol/ext-apps version)" --depth 1 https://github.com/modelcontextprotocol/ext-apps.git /tmp/mcp-ext-apps
50+
```
51+
52+
### Framework Templates
53+
54+
Learn and adapt from `/tmp/mcp-ext-apps/examples/basic-server-{framework}/`:
55+
56+
| Template | Key Files |
57+
|----------|-----------|
58+
| `basic-server-vanillajs/` | `server.ts`, `src/mcp-app.ts`, `mcp-app.html` |
59+
| `basic-server-react/` | `server.ts`, `src/mcp-app.tsx` (uses `useApp` hook) |
60+
| `basic-server-vue/` | `server.ts`, `src/App.vue` |
61+
| `basic-server-svelte/` | `server.ts`, `src/App.svelte` |
62+
| `basic-server-preact/` | `server.ts`, `src/mcp-app.tsx` |
63+
| `basic-server-solid/` | `server.ts`, `src/mcp-app.tsx` |
64+
65+
Each template includes:
66+
- Complete `server.ts` with `registerAppTool` and `registerAppResource`
67+
- Client-side app with all lifecycle handlers
68+
- `vite.config.ts` with `vite-plugin-singlefile`
69+
- `package.json` with all required dependencies
70+
- `.gitignore` excluding `node_modules/` and `dist/`
71+
72+
### API Reference (Source Files)
73+
74+
Read JSDoc documentation directly from source:
75+
76+
| File | Contents |
77+
|------|----------|
78+
| `src/app.ts` | `App` class, handlers (`ontoolinput`, `ontoolresult`, `onhostcontextchanged`, `onteardown`), lifecycle |
79+
| `src/server/index.ts` | `registerAppTool`, `registerAppResource`, tool visibility options |
80+
| `src/spec.types.ts` | All type definitions: `McpUiHostContext`, CSS variable keys, display modes |
81+
| `src/styles.ts` | `applyDocumentTheme`, `applyHostStyleVariables`, `applyHostFonts` |
82+
| `src/react/useApp.tsx` | `useApp` hook for React apps |
83+
| `src/react/useHostStyles.ts` | `useHostStyles`, `useHostStyleVariables`, `useHostFonts` hooks |
84+
85+
### Advanced Examples
86+
87+
| Example | Pattern Demonstrated |
88+
|---------|---------------------|
89+
| `examples/wiki-explorer-server/` | `callServerTool` for interactive data fetching |
90+
| `examples/system-monitor-server/` | Polling pattern with interval management |
91+
| `examples/video-resource-server/` | Binary/blob resources |
92+
| `examples/sheet-music-server/` | `ontoolinput` - processing tool args before execution completes |
93+
| `examples/threejs-server/` | `ontoolinputpartial` - streaming/progressive rendering |
94+
| `examples/map-server/` | `updateModelContext` - keeping model informed of UI state |
95+
| `examples/transcript-server/` | `updateModelContext` + `sendMessage` - background context updates + user-initiated messages |
96+
| `examples/basic-host/` | Reference host implementation using `AppBridge` |
97+
98+
## Critical Implementation Notes
99+
100+
### Adding Dependencies
101+
102+
Use `npm install` to add dependencies rather than manually writing version numbers:
103+
104+
```bash
105+
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk zod
106+
```
107+
108+
This lets npm resolve the latest compatible versions. Never specify version numbers from memory.
109+
110+
### TypeScript Server Execution
111+
112+
Use `tsx` as a devDependency for running TypeScript server files:
113+
114+
```bash
115+
npm install -D tsx
116+
```
117+
118+
```json
119+
"scripts": {
120+
"serve": "tsx server.ts"
121+
}
122+
```
123+
124+
Note: The SDK examples use `bun` but generated projects should use `tsx` for broader compatibility.
125+
126+
### Handler Registration Order
127+
128+
Register ALL handlers BEFORE calling `app.connect()`:
129+
130+
```typescript
131+
const app = new App({ name: "My App", version: "1.0.0" });
132+
133+
// Register handlers first
134+
app.ontoolinput = (params) => { /* handle input */ };
135+
app.ontoolresult = (result) => { /* handle result */ };
136+
app.onhostcontextchanged = (ctx) => { /* handle context */ };
137+
app.onteardown = async () => { return {}; };
138+
139+
// Then connect
140+
await app.connect();
141+
```
142+
143+
### Tool Visibility
144+
145+
Control who can access tools via `_meta.ui.visibility`:
146+
147+
```typescript
148+
// Default: visible to both model and app
149+
_meta: { ui: { resourceUri, visibility: ["model", "app"] } }
150+
151+
// UI-only (hidden from model) - for refresh buttons, form submissions
152+
_meta: { ui: { resourceUri, visibility: ["app"] } }
153+
154+
// Model-only (app cannot call)
155+
_meta: { ui: { resourceUri, visibility: ["model"] } }
156+
```
157+
158+
### Host Styling Integration
159+
160+
**Vanilla JS** - Use helper functions:
161+
```typescript
162+
import { applyDocumentTheme, applyHostStyleVariables, applyHostFonts } from "@modelcontextprotocol/ext-apps";
163+
164+
app.onhostcontextchanged = (ctx) => {
165+
if (ctx.theme) applyDocumentTheme(ctx.theme);
166+
if (ctx.styles?.variables) applyHostStyleVariables(ctx.styles.variables);
167+
if (ctx.styles?.css?.fonts) applyHostFonts(ctx.styles.css.fonts);
168+
};
169+
```
170+
171+
**React** - Use hooks:
172+
```typescript
173+
import { useApp, useHostStyles } from "@modelcontextprotocol/ext-apps/react";
174+
175+
const { app } = useApp({ appInfo, capabilities, onAppCreated });
176+
useHostStyles(app); // Handles theme, styles, fonts automatically
177+
```
178+
179+
### Safe Area Handling
180+
181+
Always respect `safeAreaInsets`:
182+
183+
```typescript
184+
app.onhostcontextchanged = (ctx) => {
185+
if (ctx.safeAreaInsets) {
186+
const { top, right, bottom, left } = ctx.safeAreaInsets;
187+
document.body.style.padding = `${top}px ${right}px ${bottom}px ${left}px`;
188+
}
189+
};
190+
```
191+
192+
## Common Mistakes to Avoid
193+
194+
1. **Handlers after connect()** - Register ALL handlers BEFORE calling `app.connect()`
195+
2. **Missing single-file bundling** - Must use `vite-plugin-singlefile`
196+
3. **Forgetting resource registration** - Both tool AND resource must be registered
197+
4. **Missing resourceUri link** - Tool must have `_meta.ui.resourceUri`
198+
5. **Ignoring safe area insets** - Always handle `ctx.safeAreaInsets`
199+
6. **No text fallback** - Always provide `content` array for non-UI hosts
200+
7. **Hardcoded styles** - Use host CSS variables for theme integration
201+
202+
## Testing
203+
204+
### Using basic-host
205+
206+
Test MCP Apps locally with the basic-host example:
207+
208+
```bash
209+
# Terminal 1: Build and run your server
210+
npm run build && npm run serve
211+
212+
# Terminal 2: Run basic-host (from cloned repo)
213+
cd /tmp/mcp-ext-apps/examples/basic-host
214+
npm install
215+
SERVERS='["http://localhost:3001/mcp"]' npm run start
216+
# Open http://localhost:8080
217+
```
218+
219+
Configure `SERVERS` with a JSON array of your server URLs (default: `http://localhost:3001/mcp`).
220+
221+
### Debug with sendLog
222+
223+
Send debug logs to the host application (rather than just the iframe's dev console):
224+
225+
```typescript
226+
await app.sendLog({ level: "info", data: "Debug message" });
227+
await app.sendLog({ level: "error", data: { error: err.message } });
228+
```

0 commit comments

Comments
 (0)