migrate-oai-app by modelcontextprotocol/ext-apps
npx skills add https://github.com/modelcontextprotocol/ext-apps --skill migrate-oai-app将现有的 OpenAI Apps SDK 应用程序迁移到 MCP Apps SDK (@modelcontextprotocol/ext-apps)。MCP Apps SDK 为对话式客户端中的交互式 UI 提供了一个标准化的开放协议。
npm install、pnpm add、yarn add),而不是手动编写版本号。这可以让包管理器解析最新的兼容版本。切勿凭记忆指定版本号。克隆 SDK 仓库以获取完整的迁移文档和工作示例:
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/docs/migrate_from_openai_apps.md
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
直接从 /tmp/mcp-ext-apps/src/* 阅读 JSDoc 文档:
| 文件 | 内容 |
|---|---|
src/app.ts | App 类、处理程序、生命周期 |
src/server/index.ts | registerAppTool、registerAppResource |
src/spec.types.ts | 类型定义 |
src/react/useApp.tsx | 用于 React 应用的 useApp 钩子 |
src/react/use*.ts* | 用于 React 应用的其他 use* 钩子 |
查看 /tmp/mcp-ext-apps/examples/basic-server-{framework}/ 以获取按前端框架组织的基本 SDK 使用示例:
| 模板 | 关键文件 |
|---|---|
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 |
MCP Apps HTML 作为 MCP 资源提供,而不是作为网页,并在没有同源服务器的沙盒 iframe 中运行。每个来源都必须在 CSP 中声明——包括提供你的 JS/CSS 包的来源(开发环境中的 localhost,生产环境中的 CDN)。缺少的来源会静默失败。
在编写任何迁移代码之前,构建应用并调查它引用的所有来源:
记录你的发现为三个列表,并注明每个来源是通用的、仅开发环境还是仅生产环境:
如果未找到任何来源,应用可能不需要自定义 CSP 域。
MCP 客户端会发出跨域请求。如果使用 Express,app.use(cors()) 可以处理此问题。
对于原始 HTTP 服务器,配置标准 CORS 并额外:
mcp-session-id、mcp-protocol-version、last-event-idmcp-session-id使用 registerAppTool() 和 registerAppResource() 辅助函数,而不是原始的 server.registerTool() / server.registerResource()。这些辅助函数会自动处理 MCP Apps 元数据格式。
有关服务器端映射表,请参阅 /tmp/mcp-ext-apps/docs/migrate_from_openai_apps.md。
根本性的范式转变:OpenAI 使用同步全局对象(window.openai.toolInput、window.openai.theme),该对象在你的代码运行之前就已预填充。MCP Apps 使用带有异步事件处理程序的 App 实例。
主要区别:
App 实例并注册处理程序(ontoolinput、ontoolresult、onhostcontextchanged)在调用 connect() 之前。(事件可能在连接后立即触发,因此必须先注册处理程序。)app.ontoolinput 对应 window.openai.toolInput,app.ontoolresult 对应 window.openai.toolOutput。app.getHostContext() 访问主机环境(主题、区域设置等)。对于 React 应用,useApp 钩子会自动管理此生命周期——请参阅 basic-server-react/ 了解其模式。
有关客户端映射表,请参阅 /tmp/mcp-ext-apps/docs/migrate_from_openai_apps.md。
这些 OpenAI 功能目前还没有 MCP 对应项:
服务器端:
| OpenAI 功能 | 状态/解决方法 |
|---|---|
_meta["openai/toolInvocation/invoking"] / _meta["openai/toolInvocation/invoked"] | 进度指示器尚不可用 |
_meta["openai/widgetDescription"] | 使用 app.updateModelContext() 获取动态上下文 |
客户端:
| OpenAI 功能 | 状态/解决方法 |
|---|---|
window.openai.widgetState / setWidgetState() | 使用 localStorage 或服务器端状态 |
window.openai.uploadFile() / getFileDownloadUrl() | 文件操作尚不可用 |
window.openai.requestModal() / requestClose() | 模态框管理尚不可用 |
window.openai.view | 尚不可用 |
放慢速度,仔细遵循此检查清单中的每一项:
| 模式 | 指示 |
|---|---|
"openai/ | 旧的元数据键 → _meta.ui.* |
text/html+skybridge | 旧的 MIME 类型 → RESOURCE_MIME_TYPE 常量 |
text/html;profile=mcp-app | 新的 MIME 类型,但更推荐 RESOURCE_MIME_TYPE 常量 |
_domains" 或 _domains: | snake_case CSP → camelCase(connect_domains → connectDomains) |
| 模式 | 指示 |
|---|---|
window.openai.toolInput | 旧的全局变量 → ontoolinput 处理程序中的 params.arguments |
window.openai.toolOutput | 旧的全局变量 → ontoolresult 处理程序中的 params.structuredContent |
window.openai | 旧的全局 API → App 实例方法 |
对于 CSP 调查中的每个来源,展示它在 registerAppResource() CSP 配置中出现的位置。CSP 调查中的每个来源(通用的、仅开发环境的、仅生产环境的)都必须包含在 CSP 配置中——MCP Apps HTML 在没有同源服务器的沙盒 iframe 中运行。如果某个来源未包含在 CSP 配置中,请立即添加。
对于 CSP 调查中的每个条件性(仅开发环境、仅生产环境)来源,展示控制运行时 URL 和 CSP 条目的相同配置设置(环境变量、配置文件等)所在的代码。如果 CSP 中有一个硬编码的来源应该是条件性的,请立即修复——应用必须为生产环境做好准备。
使用 basic-host 示例测试迁移后的应用:
# 终端 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
一旦应用在 basic-host 中加载,请确认:
ontoolinput 处理程序随工具参数触发ontoolresult 处理程序随工具结果触发每周安装量
203
仓库
GitHub 星标数
1.9K
首次出现
2026年2月10日
安全审计
安装于
codex194
opencode193
github-copilot191
gemini-cli190
amp184
kimi-cli184
Migrate existing OpenAI Apps SDK applications to the MCP Apps SDK (@modelcontextprotocol/ext-apps). The MCP Apps SDK provides a standardized, open protocol for interactive UIs in conversational clients.
npm install, pnpm add, yarn add) instead of manually writing version numbers. This lets the package manager resolve the latest compatible versions. Never specify version numbers from memory.Clone the SDK repository for complete migration documentation and working examples:
git clone --branch "v$(npm view @modelcontextprotocol/ext-apps version)" --depth 1 https://github.com/modelcontextprotocol/ext-apps.git /tmp/mcp-ext-apps
Read the migration reference guide with "before/after" mapping tables: /tmp/mcp-ext-apps/docs/migrate_from_openai_apps.md
Read JSDoc documentation directly from /tmp/mcp-ext-apps/src/*:
| File | Contents |
|---|---|
src/app.ts | App class, handlers, lifecycle |
src/server/index.ts | registerAppTool, registerAppResource |
src/spec.types.ts | Type definitions |
src/react/useApp.tsx | useApp hook for React apps |
See /tmp/mcp-ext-apps/examples/basic-server-{framework}/ for basic SDK usage examples organized by front-end 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/ |
MCP Apps HTML is served as an MCP resource, not as a web page, and runs in a sandboxed iframe with no same-origin server. Every origin must be declared in CSP—including the origin serving your JS/CSS bundles (localhost in dev, your CDN in production). Missing origins fail silently.
Before writing any migration code , build the app and investigate all origins it references:
Document your findings as three lists, and note for each origin whether it's universal, dev-only, or prod-only:
If no origins are found, the app may not need custom CSP domains.
MCP clients make cross-origin requests. If using Express, app.use(cors()) handles this.
For raw HTTP servers, configure standard CORS and additionally:
mcp-session-id, mcp-protocol-version, last-event-idmcp-session-idUse registerAppTool() and registerAppResource() helpers instead of raw server.registerTool() / server.registerResource(). These helpers handle the MCP Apps metadata format automatically.
See /tmp/mcp-ext-apps/docs/migrate_from_openai_apps.md for server-side mapping tables.
The fundamental paradigm shift: OpenAI uses a synchronous global object (window.openai.toolInput, window.openai.theme) that's pre-populated before your code runs. MCP Apps uses an App instance with async event handlers.
Key differences:
App instance and register handlers (ontoolinput, ontoolresult, onhostcontextchanged) before calling connect(). (Events may fire immediately after connection, so handlers must be registered first.)app.ontoolinput for window.openai.toolInput, app.ontoolresult for window.openai.toolOutput.app.getHostContext().For React apps, the useApp hook manages this lifecycle automatically—see basic-server-react/ for the pattern.
See /tmp/mcp-ext-apps/docs/migrate_from_openai_apps.md for client-side mapping tables.
These OpenAI features don't have MCP equivalents yet:
Server-side:
| OpenAI Feature | Status/Workaround |
|---|---|
_meta["openai/toolInvocation/invoking"] / _meta["openai/toolInvocation/invoked"] | Progress indicators not yet available |
_meta["openai/widgetDescription"] | Use app.updateModelContext() for dynamic context |
Client-side:
| OpenAI Feature | Status/Workaround |
|---|---|
window.openai.widgetState / setWidgetState() | Use localStorage or server-side state |
window.openai.uploadFile() / getFileDownloadUrl() | File operations not yet available |
window.openai.requestModal() / requestClose() | Modal management not yet available |
window.openai.view |
Slow down and carefully follow each item in this checklist:
| Pattern | Indicates |
|---|---|
"openai/ | Old metadata keys → _meta.ui.* |
text/html+skybridge | Old MIME type → RESOURCE_MIME_TYPE constant |
text/html;profile=mcp-app | New MIME type, but prefer RESOURCE_MIME_TYPE constant |
_domains" or _domains: |
| Pattern | Indicates |
|---|---|
window.openai.toolInput | Old global → params.arguments in ontoolinput handler |
window.openai.toolOutput | Old global → params.structuredContent in ontoolresult |
window.openai | Old global API → App instance methods |
For each origin from your CSP investigation, show where it appears in the registerAppResource() CSP config. Every origin from the CSP investigation (universal, dev-only, prod-only) must be included in the CSP config—MCP Apps HTML runs in a sandboxed iframe with no same-origin server. If an origin was not included in the CSP config, add it now.
For each conditional (dev-only, prod-only) origin from your CSP investigation, show the code where the same configuration setting (env var, config file, etc.) controls both the runtime URL and the CSP entry. If the CSP has a hardcoded origin that should be conditional, fix it now—the app must be production-ready.
Test the migrated app 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
Once the app loads in basic-host, confirm:
ontoolinput handler fires with tool argumentsontoolresult handler fires with tool resultWeekly Installs
203
Repository
GitHub Stars
1.9K
First Seen
Feb 10, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
codex194
opencode193
github-copilot191
gemini-cli190
amp184
kimi-cli184
agent-browser 浏览器自动化工具 - Vercel Labs 命令行网页操作与测试
150,000 周安装
src/react/use*.ts* | Other use* hooks for React apps |
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 |
| Not yet available |
snake_case CSP → camelCase (connect_domains → connectDomains) |