关于 create-mcp-app
create-mcp-app 是用于构建交互界面的技能,界面可在支持 MCP 的宿主中运行。它指导开发者将 MCP 工具与 HTML 资源配对,并提供框架示例、实现说明和本地测试步骤。
主要功能
- 01将 MCP 工具与 HTML 资源配对
- 02提供 React 等界面框架指南
- 03包含向现有服务添加应用的示例
- 04介绍宿主上下文、流式输入和视图状态
- 05使用参考宿主进行本地测试
适用场景
- 01为 MCP 服务添加交互界面
- 02使用 React 或原生 JavaScript 创建 MCP App
- 03实现界面生命周期和宿主上下文处理
- 04通过 basic-host 本地测试应用
MCP 相关
它指导开发者构建 MCP App:MCP 工具返回数据,HTML 资源则为支持 MCP 的宿主提供界面。内容还涵盖在 MCP 服务中注册工具和资源。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ modelcontextprotocol/ ext-apps --skill create-mcp-app安装后在 Claude Code 中输入/create-mcp-app确认已加载。
详细介绍
创建 MCP App
构建可在 Claude Desktop 等支持 MCP 的宿主中运行的交互式 UI。MCP App 将 MCP 工具与 HTML 资源结合,用于显示丰富的交互式内容。
核心概念:工具 + 资源
每个 MCP App 都需要两个相互关联的部分:
- 工具 — 由大模型/宿主调用,并返回数据
- 资源 — 提供打包后的 HTML UI,用于显示数据
工具的 _meta.ui.resourceUri 引用资源的 URI。
宿主调用工具 → 宿主渲染资源 UI → 服务端返回结果 → UI 接收结果。
快速入门决策树
框架选择
| 框架 | SDK 支持 | 最适合 |
|---|---|---|
| React | 提供 useApp hook | 熟悉 React 的团队 |
| Vanilla JS | 手动管理生命周期 | 简单应用,无构建复杂度 |
| Vue/Svelte/Preact/Solid | 手动管理生命周期 | 偏好使用特定框架 |
项目背景
添加到现有 MCP 服务:
- 从 SDK 导入
registerAppTool、registerAppResource - 添加带有
_meta.ui.resourceUri的工具注册 - 添加用于提供打包后 HTML 的资源注册
创建新的 MCP 服务:
- 配置带有传输方式(stdio 或 HTTP)的服务
- 注册工具和资源
- 使用
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 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 |
每个模板都包含:
- 带有
registerAppTool和registerAppResource的server.ts - 带有 HTTP 和 stdio 传输设置的
main.ts入口文件 - 带有生命周期处理程序的客户端应用(例如
src/mcp-app.ts、src/mcp-app.tsx) - 带有全局样式和宿主样式变量回退值的
src/global.css - 使用
vite-plugin-singlefile的vite.config.ts - 带有
npm run脚本和所需依赖项的package.json - 排除
node_modules/和dist/的.gitignore
API 参考(源文件)
直接阅读 /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 hook |
高级模式
参阅 /tmp/mcp-ext-apps/docs/patterns.md,了解详细用法:
- 仅 App 可见的工具 —
visibility: ["app"],对模型隐藏工具 - 轮询 — 实时仪表板、间隔管理
- 分块响应 — 大文件、分页、Base64 编码
- 错误处理 —
isError,告知模型发生的故障 - 二进制资源 — 通过
resources/read、blob 字段处理音频/视频等 - 网络请求 — 资源、fetch、CSP、
_meta.ui.csp、CORS、_meta.ui.domain - 宿主上下文 — 主题、样式、字体、安全区域内边距
- 全屏模式 —
requestDisplayMode、显示模式更改 - 模型上下文 —
updateModelContext、sendMessage,让模型了解最新信息 - 视图状态 —
viewUUID、localStorage、状态恢复 - 基于可见性的暂停 — IntersectionObserver、暂停动画/WebGL
- 流式输入 —
ontoolinputpartial、渐进式渲染
参考宿主实现
/tmp/mcp-ext-apps/examples/basic-host/ 展示了一种实现支持 MCP Apps 的宿主的方式。Claude Desktop 等真实宿主更加复杂——请将 basic-host 用于本地测试和了解协议,而不要将其视为宿主行为的保证。
关键实现说明
添加依赖项
添加依赖项时,始终使用 npm install,不要手动编写版本号:
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@^2.0.0 @modelcontextprotocol/server@^2.0.0 @modelcontextprotocol/node@^2.0.0 @modelcontextprotocol/express@^2.0.0 zod@^4.2.0 express cors
npm install -D typescript vite vite-plugin-singlefile concurrently cross-env @types/node @types/express @types/cors
ext-apps 2.x 要求使用拆分后的基础 MCP SDK 软件包(@modelcontextprotocol/client、
server、node、express),版本为 ^2.0.0;@modelcontextprotocol/core 会作为
传递依赖引入。不要添加旧版 @modelcontextprotocol/sdk v1 软件包,也不要
替换为尚未发布的本地软件包。
TypeScript 服务执行
除非用户另有指定,否则使用 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'"
说明
SDK 示例使用
bun,但生成的项目应默认使用tsx,以获得更广泛的兼容性。
处理程序注册顺序
在调用 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数组,以支持非 UI 宿主 - 缺少 CSP 配置 - MCP Apps HTML 作为 MCP 资源提供,不存在同源服务;所有网络请求(即使发往
localhost)都需要 CSP 配置 - CSP 或 CORS 配置位于错误的 _meta 对象中 -
_meta.ui.csp和_meta.ui.domain应放在registerAppResource()的读取回调所返回的contents[]对象中,而不是放在registerAppResource()的配置对象中 - 在 app.connect() 之后注册处理程序 - 在调用
app.connect()之前注册所有处理程序 - 大输入未使用流式传输 - 使用
ontoolinputpartial,在生成输入期间显示进度
测试
使用 basic-host
使用 basic-host 示例在本地测试 MCP Apps:
# 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
使用服务器 URL 的 JSON 数组配置 SERVERS(默认值:http://localhost:3001/mcp)。
使用 sendLog 进行调试
将调试日志发送到宿主应用程序(而不只是 iframe 的开发控制台):
await app.sendLog({ level: "info", data: "Debug message" });
await app.sendLog({ level: "error", data: { error: err.message } });