xiaohongshu
xiaohongshu
mcp-apps
Build MCP Apps - interactive UI components that render inside AI hosts like Claude, ChatGPT, and VSCode. This skill should be used when creating tools with rich UI, building dashboards or forms for AI interactions, or adding visual interfaces to MCP servers.
Full skill instructions
Build interactive UI applications that render directly inside MCP hosts like Claude Desktop, ChatGPT, and VSCode. MCP Apps extend the Model Context Protocol to return rich interfaces instead of plain text.
MCP Apps let tools return rich, interactive interfaces instead of plain text. When a tool declares a UI resource, the host renders it in a sandboxed iframe, and users interact with it directly in the conversation.
Key benefits over regular web apps:
┌─────────────────────────────────────────────────────────────┐
│ MCP Host (Claude, VSCode) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Sandboxed Iframe │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ Your MCP App UI │ │ │
│ │ │ (React, Vue, Svelte, or vanilla) │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ postMessage │ │
│ │ │ │ │
│ └─────────────────────────┼────────────────────────────┘ │
│ │ │
│ JSON-RPC │
│ │ │
│ ┌─────────────────────────┼────────────────────────────┐ │
│ │ MCP Server │ │
│ │ • Tools with _meta.ui.resourceUri │ │
│ │ • UI Resources (bundled HTML) │ │
│ └───────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘
_meta.ui.resourceUri pointing to a ui:// resource# Scaffold a new MCP App project
python scripts/init_mcp_app.py my-mcp-app --path ./projects
This creates a complete project structure with server, UI, and configuration.
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk
npm install -D typescript vite vite-plugin-singlefile express cors @types/express @types/cors tsx
my-mcp-app/
├── package.json
├── tsconfig.json
├── vite.config.ts
├── server.ts # MCP server
├── mcp-app.html # UI entry point
└── src/
└── mcp-app.ts # UI logic
See references/boilerplate.md for complete file contents.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
const server = new McpServer({
name: "My MCP App Server",
version: "1.0.0",
});
// The ui:// scheme tells hosts this is an MCP App resource
const resourceUri = "ui://my-tool/app.html";
// Register the tool with UI metadata
registerAppTool(
server,
"my-tool",
{
title: "My Tool",
description: "A tool with interactive UI",
inputSchema: {
type: "object",
properties: {
query: { type: "string" }
}
},
_meta: {
ui: { resourceUri }
}
},
async (args) => {
// Tool logic here
return {
content: [{ type: "text", text: JSON.stringify(args) }]
};
}
);
// Register the UI resource
registerAppResource(
server,
resourceUri,
resourceUri,
{ mimeType: RESOURCE_MIME_TYPE },
async () => {
const html = await fs.readFile("dist/mcp-app.html", "utf-8");
return {
contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }]
};
}
);
import express from "express";
import cors from "cors";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
const app = express();
app.use(cors());
app.use(express.json());
app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true,
});
res.on("close", () => transport.close());
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(3001, () => {
console.log("Server listening on http://localhost:3001/mcp");
});
import { App } from "@modelcontextprotocol/ext-apps";
const app = new App({ name: "My App", version: "1.0.0" });
// Connect to host
await app.connect();
// Receive initial tool result
app.ontoolresult = (result) => {
const data = result.content?.find((c) => c.type === "text")?.text;
console.log("Received:", data);
renderUI(data);
};
// Call server tools from UI
async function fetchData() {
const result = await app.callServerTool({
name: "fetch-data",
arguments: { query: "example" },
});
return result.content?.find((c) => c.type === "text")?.text;
}
// Update model context
async function notifyModel(info: string) {
await app.updateModelContext({
content: [{ type: "text", text: info }],
});
}
// Log for debugging
app.log("info", "App initialized");
// Open links in user's browser
app.openLink("https://example.com");
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My MCP App</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1rem; }
/* Add your styles */
</style>
</head>
<body>
<div id="app">Loading...</div>
<script type="module" src="/src/mcp-app.ts"></script>
</body>
</html>
import { defineConfig } from "vite";
import { viteSingleFile } from "vite-plugin-singlefile";
export default defineConfig({
plugins: [viteSingleFile()],
build: {
outDir: "dist",
rollupOptions: {
input: process.env.INPUT,
},
},
});
{
"type": "module",
"scripts": {
"build": "INPUT=mcp-app.html vite build",
"serve": "npx tsx server.ts",
"dev": "npm run build && npm run serve"
}
}
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist"
},
"include": ["*.ts", "src/**/*.ts"]
}
Build and start your server:
npm run build && npm run serve
Expose locally with cloudflared:
npx cloudflared tunnel --url http://localhost:3001
Add as custom connector in Claude:
Chat with Claude and trigger your tool.
The ext-apps repo includes a test host:
git clone https://github.com/modelcontextprotocol/ext-apps
cd ext-apps/examples/basic-host
npm install
SERVERS='["http://localhost:3001/mcp"]' npm start
Navigate to http://localhost:8080 to test.
MCP Apps run in sandboxed iframes with:
registerAppTool(server, "camera-tool", {
// ...
_meta: {
ui: {
resourceUri: "ui://camera/app.html",
permissions: ["camera", "microphone"], // Request additional permissions
csp: ["https://cdn.example.com"] // Allow external resources
}
}
});
MCP Apps work with any web framework. The ext-apps repo has starters for:
See references/patterns.md for framework-specific patterns.
app.ontoolresult = (result) => {
renderForm(result.content);
};
async function handleSubmit(formData: FormData) {
const result = await app.callServerTool({
name: "submit-form",
arguments: Object.fromEntries(formData),
});
if (result.isError) {
showError(result.content);
} else {
showSuccess(result.content);
await app.updateModelContext({
content: [{ type: "text", text: "User submitted form successfully" }],
});
}
}
let intervalId: number;
app.ontoolresult = async () => {
// Start polling for updates
intervalId = setInterval(async () => {
const result = await app.callServerTool({
name: "get-metrics",
arguments: {},
});
updateDashboard(result.content);
}, 5000);
};
// Clean up on disconnect
window.addEventListener("beforeunload", () => {
clearInterval(intervalId);
});
let currentStep = 0;
const steps = ["configure", "review", "confirm"];
async function nextStep(data: unknown) {
const result = await app.callServerTool({
name: `workflow-${steps[currentStep]}`,
arguments: data,
});
currentStep++;
if (currentStep < steps.length) {
renderStep(currentStep, result.content);
} else {
await app.updateModelContext({
content: [{ type: "text", text: "Workflow completed" }],
});
renderComplete(result.content);
}
}
_meta.ui.resourceUri in descriptionapp.connect() is called before other operationsreferences/boilerplate.md - Complete project setup codereferences/patterns.md - Common UI patterns and examplesxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience