create-mcp-app by modelcontextprotocol/ext-apps
npx skills add https://github.com/modelcontextprotocol/ext-apps --skill create-mcp-app构建可在支持 MCP 的主机(如 Claude Desktop)内部运行的交互式 UI。MCP 应用将 MCP 工具与 HTML 资源相结合,以显示丰富的交互式内容。
每个 MCP 应用都需要两个相互关联的部分:
工具的 _meta.ui.resourceUri 引用了资源的 URI。
主机调用工具 → 主机渲染资源 UI → 服务器返回结果 → UI 接收结果。
| 框架 | SDK 支持 | 最适合 |
|---|---|---|
| React | 提供 useApp 钩子 | 熟悉 React 的团队 |
| Vanilla JS | 手动生命周期管理 | 简单应用,无需构建复杂性 |
| Vue/Svelte/Preact/Solid | 手动生命周期管理 | 有特定框架偏好 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
添加到现有的 MCP 服务器:
registerAppTool、registerAppResource_meta.ui.resourceUri 的工具注册创建新的 MCP 服务器:
vite-plugin-singlefile 配置构建系统克隆 SDK 仓库以获取工作示例和 API 文档:
git clone --branch "v$(npm view @modelcontextprotocol/ext-apps version)" --depth 1 https://github.com/modelcontextprotocol/ext-apps.git /tmp/mcp-ext-apps
从 /tmp/mcp-ext-apps/examples/basic-server-{framework}/ 学习和调整:
| 模板 | 关键文件 |
|---|---|
basic-server-vanillajs/ | server.ts, src/mcp-app.ts, mcp-app.html |
basic-server-react/ | server.ts, src/mcp-app.tsx (使用 useApp 钩子) |
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 |
每个模板都包含:
registerAppTool 和 registerAppResource 的 server.tsmain.tssrc/mcp-app.ts, src/mcp-app.tsx)src/global.cssvite-plugin-singlefile 的 vite.config.tsnpm run 脚本和所需依赖项的 package.jsonnode_modules/ 和 dist/ 的 .gitignore直接从 /tmp/mcp-ext-apps/src/ 阅读 JSDoc 文档:
| 文件 | 内容 |
|---|---|
src/app.ts | App 类,处理程序(ontoolinput, ontoolresult, onhostcontextchanged, onteardown 等),生命周期 |
src/server/index.ts | registerAppTool, registerAppResource, 辅助函数 |
src/spec.types.ts | 所有类型定义:McpUiHostContext, McpUiStyleVariableKey(CSS 变量名), McpUiResourceCsp(CSP 配置)等 |
src/styles.ts | applyDocumentTheme, applyHostStyleVariables, applyHostFonts |
src/react/useApp.tsx | 用于 React 应用的 useApp 钩子 |
查看 /tmp/mcp-ext-apps/docs/patterns.md 获取详细方案:
visibility: ["app"],对模型隐藏工具isError,通知模型失败情况resources/read 和 blob 字段处理音频/视频等_meta.ui.csp,CORS,_meta.ui.domainrequestDisplayMode,显示模式更改updateModelContext, sendMessage,保持模型信息同步viewUUID, localStorage,状态恢复ontoolinputpartial,渐进式渲染/tmp/mcp-ext-apps/examples/basic-host/ 展示了一种实现支持 MCP 应用的主机的方式。像 Claude Desktop 这样的真实世界主机更为复杂——请使用 basic-host 进行本地测试和协议理解,而不是将其作为主机行为的保证。
始终 使用 npm install 添加依赖项,而不是手动编写版本号:
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk zod express cors
npm install -D typescript vite vite-plugin-singlefile concurrently cross-env @types/node @types/express @types/cors
这可以让 npm 解析最新的兼容版本。切勿 凭记忆指定版本号。
除非用户另有指定,否则使用 tsx 运行 TypeScript 服务器文件。例如:
npm install -D tsx
npm pkg set scripts.dev="cross-env NODE_ENV=development concurrently 'cross-env INPUT=mcp-app.html vite build --watch' 'tsx --watch main.ts'"
[!NOTE] SDK 示例使用
bun,但生成的项目应默认使用tsx以获得更广泛的兼容性。
在调用 app.connect() 之前 注册所有处理程序:
const app = new App({ name: "My App", version: "1.0.0" });
// 先注册处理程序
app.ontoolinput = (params) => { /* 处理输入 */ };
app.ontoolresult = (result) => { /* 处理结果 */ };
app.onhostcontextchanged = (ctx) => { /* 处理上下文 */ };
app.onteardown = async () => { return {}; };
// 等等
// 然后连接
await app.connect();
content 数组localhost——都需要 CSP 配置_meta.ui.csp 和 _meta.ui.domain 应放在 registerAppResource() 的 read 回调返回的 contents[] 对象中,而不是在 registerAppResource() 的配置对象中app.connect() 之前 注册所有处理程序ontoolinputpartial 在输入生成过程中显示进度使用 basic-host 示例在本地测试 MCP 应用:
# 终端 1:构建并运行你的服务器
npm run build && npm run serve
# 终端 2:运行 basic-host(从克隆的仓库)
cd /tmp/mcp-ext-apps/examples/basic-host
npm install
SERVERS='["http://localhost:3001/mcp"]' npm run start
# 打开 http://localhost:8080
使用服务器 URL 的 JSON 数组配置 SERVERS(默认:http://localhost:3001/mcp)。
将调试日志发送到主机应用程序(而不仅仅是 iframe 的开发控制台):
await app.sendLog({ level: "info", data: "调试消息" });
await app.sendLog({ level: "error", data: { error: err.message } });
每周安装量
643
仓库
GitHub 星标数
1.9K
首次出现
2026年2月10日
安全审计
安装于
opencode613
codex612
github-copilot607
gemini-cli605
amp576
kimi-cli575
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.
Every MCP App requires two parts linked together:
The tool's _meta.ui.resourceUri references the resource's URI.
Host calls tool → Host renders resource UI → Server returns result → UI receives result.
| Framework | SDK Support | Best For |
|---|---|---|
| React | useApp hook provided | Teams familiar with React |
| Vanilla JS | Manual lifecycle | Simple apps, no build complexity |
| Vue/Svelte/Preact/Solid | Manual lifecycle | Framework preference |
Adding to existing MCP server:
registerAppTool, registerAppResource from SDK_meta.ui.resourceUriCreating new MCP server:
vite-plugin-singlefileClone the SDK repository for working examples and API documentation:
git clone --branch "v$(npm view @modelcontextprotocol/ext-apps version)" --depth 1 https://github.com/modelcontextprotocol/ext-apps.git /tmp/mcp-ext-apps
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/ |
Each template includes:
server.ts with registerAppTool and registerAppResourcemain.ts entry point with HTTP and stdio transport setupsrc/mcp-app.ts, src/mcp-app.tsx) with lifecycle handlerssrc/global.css with global styles and host style variable fallbacksvite.config.ts using vite-plugin-singlefilepackage.json with scripts and required dependenciesRead JSDoc documentation directly from /tmp/mcp-ext-apps/src/:
| File | Contents |
|---|---|
src/app.ts | App class, handlers (ontoolinput, ontoolresult, onhostcontextchanged, onteardown, etc.), lifecycle |
src/server/index.ts | registerAppTool, registerAppResource, helper functions |
See /tmp/mcp-ext-apps/docs/patterns.md for detailed recipes:
visibility: ["app"], hiding tools from modelisError, informing model of failuresresources/read, blob field_meta.ui.csp, CORS, _meta.ui.domainrequestDisplayMode, display mode changesupdateModelContext, , keeping model informed/tmp/mcp-ext-apps/examples/basic-host/ shows one way an MCP Apps-capable host could be implemented. Real-world hosts like Claude Desktop are more sophisticated—use basic-host for local testing and protocol understanding, not as a guarantee of host behavior.
Always use npm install to add dependencies rather than manually writing version numbers:
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk zod express cors
npm install -D typescript vite vite-plugin-singlefile concurrently cross-env @types/node @types/express @types/cors
This lets npm resolve the latest compatible versions. Never specify version numbers from memory.
Unless the user has specified otherwise, use tsx for running TypeScript server files. For example:
npm install -D tsx
npm pkg set scripts.dev="cross-env NODE_ENV=development concurrently 'cross-env INPUT=mcp-app.html vite build --watch' 'tsx --watch main.ts'"
[!NOTE] The SDK examples use
bunbut generated projects should default totsxfor broader compatibility.
Register ALL handlers BEFORE calling app.connect():
const app = new App({ name: "My App", version: "1.0.0" });
// Register handlers first
app.ontoolinput = (params) => { /* handle input */ };
app.ontoolresult = (result) => { /* handle result */ };
app.onhostcontextchanged = (ctx) => { /* handle context */ };
app.onteardown = async () => { return {}; };
// etc.
// Then connect
await app.connect();
content array for non-UI hostslocalhost—require a CSP configuration_meta.ui.csp and _meta.ui.domain go in the contents[] objects returned by registerAppResource()'s read callback, not in registerAppResource()'s config objectapp.connect()Test MCP Apps locally 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).
Send debug logs to the host application (rather than just the iframe's dev console):
await app.sendLog({ level: "info", data: "Debug message" });
await app.sendLog({ level: "error", data: { error: err.message } });
Weekly Installs
643
Repository
GitHub Stars
1.9K
First Seen
Feb 10, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode613
codex612
github-copilot607
gemini-cli605
amp576
kimi-cli575
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
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 |
npm run.gitignore excluding node_modules/ and dist/src/spec.types.ts | All type definitions: McpUiHostContext, McpUiStyleVariableKey (CSS variable names), McpUiResourceCsp (CSP configuration), etc. |
src/styles.ts | applyDocumentTheme, applyHostStyleVariables, applyHostFonts |
src/react/useApp.tsx | useApp hook for React apps |
sendMessageviewUUID, localStorage, state recoveryontoolinputpartial, progressive renderingontoolinputpartial