[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71JXKDGF3BRTVEPBXM7MW5X":3,"skill-same:01M71JXKDGF3BRTVEPBXM7MW5X":117},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":58,"commitSha":59,"skillDirectory":60,"forksCount":61,"repositoryHtmlUrl":62,"repositoryHomepageUrl":63,"ownerHtmlUrl":64,"ownerBlogUrl":65,"ownerDescription":66,"updatedAt":67,"install":68,"resources":80},"01M71JXKDGF3BRTVEPBXM7MW5X","OpenAI Apps MCP",null,"Build ChatGPT apps with MCP servers on Cloudflare Workers. Extend ChatGPT with custom tools and interactive widgets (HTML\u002FJS UI).\n\nUse when: developing ChatGPT extensions, implementing MCP servers, or troubleshooting CORS, widget 404s, MIME types, ASSETS binding errors, Next.js integration issues, or edge platform limitations.","coco-research\u002Fcoco","coco-research","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F68911604?v=4","01M71JRE9C4NM4NSZM4JD0BJTB","Coco Research","01M71JREA0ZMR0PMH3CKA99SN1",631,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fskills\u002Fopenai-apps-mcp\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,5,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"openai","apps","MCP","workers","custom","implementing","extensions","ui","platform","with mcp","build","edge","cloudflare workers","use","interactive","edit","write","read","Next.js","2026-11-10 10:12:30",false,"---\nname: OpenAI Apps MCP\ndescription: |\n  Build ChatGPT apps with MCP servers on Cloudflare Workers. Extend ChatGPT with custom tools and interactive widgets (HTML\u002FJS UI).\n\n  Use when: developing ChatGPT extensions, implementing MCP servers, or troubleshooting CORS, widget 404s, MIME types, ASSETS binding errors, Next.js integration issues, or edge platform limitations.\nuser-invocable: true\nallowed-tools: [Read, Write, Edit, Bash, Glob, Grep]\ndomain: engineering\n---\n\n# Building OpenAI Apps with Stateless MCP Servers\n\n**Status**: Production Ready\n**Last Updated**: 2026-01-21\n**Dependencies**: `cloudflare-worker-base`, `hono-routing` (optional)\n**Latest Versions**: @modelcontextprotocol\u002Fsdk@1.25.3, hono@4.11.3, zod@4.3.5, wrangler@4.58.0\n\n---\n\n## Overview\n\nBuild **ChatGPT Apps** using **MCP (Model Context Protocol)** servers on Cloudflare Workers. Extends ChatGPT with custom tools and interactive widgets (HTML\u002FJS UI rendered in iframe).\n\n**Architecture**: ChatGPT → MCP endpoint (JSON-RPC 2.0) → Tool handlers → Widget resources (HTML)\n\n**Status**: Apps available to Business\u002FEnterprise\u002FEdu (GA Nov 13, 2025). MCP Apps Extension (SEP-1865) formalized Nov 21, 2025.\n\n---\n\n## Quick Start\n\n### 1. Scaffold & Install\n\n```bash\nnpm create cloudflare@latest my-openai-app -- --type hello-world --ts --git --deploy false\ncd my-openai-app\nnpm install @modelcontextprotocol\u002Fsdk@1.25.3 hono@4.11.3 zod@4.3.5\nnpm install -D @cloudflare\u002Fvite-plugin@1.17.1 vite@7.2.4\n```\n\n### 2. Configure wrangler.jsonc\n\n```jsonc\n{\n  \"name\": \"my-openai-app\",\n  \"main\": \"dist\u002Findex.js\",\n  \"compatibility_flags\": [\"nodejs_compat\"],  \u002F\u002F Required for MCP SDK\n  \"assets\": {\n    \"directory\": \"dist\u002Fclient\",\n    \"binding\": \"ASSETS\"  \u002F\u002F Must match TypeScript\n  }\n}\n```\n\n### 3. Create MCP Server (`src\u002Findex.ts`)\n\n```typescript\nimport { Hono } from 'hono';\nimport { cors } from 'hono\u002Fcors';\nimport { Server } from '@modelcontextprotocol\u002Fsdk\u002Fserver\u002Findex.js';\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol\u002Fsdk\u002Ftypes.js';\n\nconst app = new Hono\u003C{ Bindings: { ASSETS: Fetcher } }>();\n\n\u002F\u002F CRITICAL: Must allow chatgpt.com\napp.use('\u002Fmcp\u002F*', cors({ origin: 'https:\u002F\u002Fchatgpt.com' }));\n\nconst mcpServer = new Server(\n  { name: 'my-app', version: '1.0.0' },\n  { capabilities: { tools: {}, resources: {} } }\n);\n\n\u002F\u002F Tool registration\nmcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({\n  tools: [{\n    name: 'hello',\n    description: 'Use this when user wants to see a greeting',\n    inputSchema: {\n      type: 'object',\n      properties: { name: { type: 'string' } },\n      required: ['name']\n    },\n    annotations: {\n      openai: { outputTemplate: 'ui:\u002F\u002Fwidget\u002Fhello.html' }  \u002F\u002F Widget URI\n    }\n  }]\n}));\n\n\u002F\u002F Tool execution\nmcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {\n  if (request.params.name === 'hello') {\n    const { name } = request.params.arguments as { name: string };\n    return {\n      content: [{ type: 'text', text: `Hello, ${name}!` }],\n      _meta: { initialData: { name } }  \u002F\u002F Passed to widget\n    };\n  }\n  throw new Error(`Unknown tool: ${request.params.name}`);\n});\n\napp.post('\u002Fmcp', async (c) => {\n  const body = await c.req.json();\n  const response = await mcpServer.handleRequest(body);\n  return c.json(response);\n});\n\napp.get('\u002Fwidgets\u002F*', async (c) => c.env.ASSETS.fetch(c.req.raw));\n\nexport default app;\n```\n\n### 4. Create Widget (`src\u002Fwidgets\u002Fhello.html`)\n\n```html\n\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\n  \u003Cstyle>\n    body { margin: 0; padding: 20px; font-family: system-ui; }\n  \u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv id=\"greeting\">Loading...\u003C\u002Fdiv>\n  \u003Cscript>\n    if (window.openai && window.openai.getInitialData) {\n      const data = window.openai.getInitialData();\n      document.getElementById('greeting').textContent = `Hello, ${data.name}! 👋`;\n    }\n  \u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n```\n\n### 5. Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\nnpx @modelcontextprotocol\u002Finspector https:\u002F\u002Fmy-app.workers.dev\u002Fmcp\n```\n\n---\n\n## Critical Requirements\n\n**CORS**: Must allow `https:\u002F\u002Fchatgpt.com` on `\u002Fmcp\u002F*` routes\n**Widget URI**: Must use `ui:\u002F\u002Fwidget\u002F` prefix (e.g., `ui:\u002F\u002Fwidget\u002Fmap.html`)\n**MIME Type**: Must be `text\u002Fhtml+skybridge` for HTML resources\n**Widget Data**: Pass via `_meta.initialData` (accessed via `window.openai.getInitialData()`)\n**Tool Descriptions**: Action-oriented (\"Use this when user wants to...\")\n**ASSETS Binding**: Serve widgets from ASSETS, not bundled in worker code\n**SSE**: Send heartbeat every 30s (100s timeout on Workers)\n\n---\n\n## Known Issues Prevention\n\nThis skill prevents **14** documented issues:\n\n### Issue #1: CORS Policy Blocks MCP Endpoint\n**Error**: `Access to fetch blocked by CORS policy`\n**Fix**: `app.use('\u002Fmcp\u002F*', cors({ origin: 'https:\u002F\u002Fchatgpt.com' }))`\n\n### Issue #2: Widget Returns 404 Not Found\n**Error**: `404 (Not Found)` for widget URL\n**Fix**: Use `ui:\u002F\u002Fwidget\u002F` prefix (not `resource:\u002F\u002F` or `\u002Fwidgets\u002F`)\n```typescript\nannotations: { openai: { outputTemplate: 'ui:\u002F\u002Fwidget\u002Fmap.html' } }\n```\n\n### Issue #3: Widget Displays as Plain Text\n**Error**: HTML source code visible instead of rendered widget\n**Fix**: MIME type must be `text\u002Fhtml+skybridge` (not `text\u002Fhtml`)\n```typescript\nserver.setRequestHandler(ListResourcesRequestSchema, async () => ({\n  resources: [{ uri: 'ui:\u002F\u002Fwidget\u002Fmap.html', mimeType: 'text\u002Fhtml+skybridge' }]\n}));\n```\n\n### Issue #4: ASSETS Binding Undefined\n**Error**: `TypeError: Cannot read property 'fetch' of undefined`\n**Fix**: Binding name in wrangler.jsonc must match TypeScript\n```jsonc\n{ \"assets\": { \"binding\": \"ASSETS\" } }  \u002F\u002F wrangler.jsonc\n```\n```typescript\ntype Bindings = { ASSETS: Fetcher };  \u002F\u002F index.ts\n```\n\n### Issue #5: SSE Connection Drops After 100 Seconds\n**Error**: SSE stream closes unexpectedly\n**Fix**: Send heartbeat every 30s (Workers timeout at 100s inactivity)\n```typescript\nconst heartbeat = setInterval(async () => {\n  await stream.writeSSE({ data: JSON.stringify({ type: 'heartbeat' }), event: 'ping' });\n}, 30000);\n```\n\n### Issue #6: ChatGPT Doesn't Suggest Tool\n**Error**: Tool registered but never appears in suggestions\n**Fix**: Use action-oriented descriptions\n```typescript\n\u002F\u002F ✅ Good: 'Use this when user wants to see a location on a map'\n\u002F\u002F ❌ Bad: 'Shows a map'\n```\n\n### Issue #7: Widget Can't Access Initial Data\n**Error**: `window.openai.getInitialData()` returns `undefined`\n**Fix**: Pass data via `_meta.initialData`\n```typescript\nreturn {\n  content: [{ type: 'text', text: 'Here is your map' }],\n  _meta: { initialData: { location: 'SF', zoom: 12 } }\n};\n```\n\n### Issue #8: Widget Scripts Blocked by CSP\n**Error**: `Refused to load script (CSP directive)`\n**Fix**: Use inline scripts or same-origin scripts. Third-party CDNs blocked.\n```html\n\u003C!-- ✅ Works --> \u003Cscript>console.log('ok');\u003C\u002Fscript>\n\u003C!-- ❌ Blocked --> \u003Cscript src=\"https:\u002F\u002Fcdn.example.com\u002Flib.js\">\u003C\u002Fscript>\n```\n\n### Issue #9: Hono Global Response Override Breaks Next.js (v1.25.0-1.25.2)\n**Error**: `No response is returned from route handler` (Next.js App Router)\n**Source**: [GitHub Issue #1369](https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\u002Fissues\u002F1369)\n**Affected Versions**: v1.25.0 to v1.25.2\n**Fixed In**: v1.25.3\n**Why It Happens**: Hono (MCP SDK dependency) overwrites `global.Response`, breaking frameworks that extend it (Next.js, Remix, SvelteKit). NextResponse instanceof check fails.\n**Prevention**:\n- **Upgrade to v1.25.3+** (recommended)\n- **Before fix**: Use `webStandardStreamableHTTPServerTransport` instead\n- **Or**: Run MCP server on separate port from Next.js\u002FRemix\u002FSvelteKit app\n\n```typescript\n\u002F\u002F ✅ v1.25.3+ - Fixed\nconst transport = new StreamableHTTPServerTransport({\n  sessionIdGenerator: undefined,\n});\n\n\u002F\u002F ✅ v1.25.0-1.25.2 - Workaround\nimport { webStandardStreamableHTTPServerTransport } from '@modelcontextprotocol\u002Fsdk\u002Fserver\u002Findex.js';\nconst transport = webStandardStreamableHTTPServerTransport({\n  sessionIdGenerator: undefined,\n});\n```\n\n### Issue #10: Elicitation (User Input) Fails on Cloudflare Workers\n**Error**: `EvalError: Code generation from strings disallowed`\n**Source**: [GitHub Issue #689](https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\u002Fissues\u002F689)\n**Why It Happens**: Internal AJV v6 validator uses prohibited APIs on edge platforms\n**Prevention**: Avoid `elicitInput()` on edge platforms (Cloudflare Workers, Vercel Edge, Deno Deploy)\n\n**Workaround**:\n```typescript\n\u002F\u002F ❌ Don't use on Cloudflare Workers\nconst userInput = await server.elicitInput({\n  prompt: \"What is your name?\",\n  schema: { type: \"string\" }\n});\n\n\u002F\u002F ✅ Use tool parameters instead\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n  const { name } = request.params.arguments as { name: string };\n  \u002F\u002F User provides via tool call, not elicitation\n});\n```\n\n**Status**: Requires MCP SDK v2 to fix properly. Track [PR #844](https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\u002Fpull\u002F844).\n\n### Issue #11: SSE Transport Statefulness Breaks Serverless Deployments\n**Error**: `400: No transport found for sessionId`\n**Source**: [GitHub Issue #273](https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\u002Fissues\u002F273)\n**Why It Happens**: `SSEServerTransport` relies on in-memory session storage. In serverless environments (AWS Lambda, Cloudflare Workers), the initial `GET \u002Fsse` request may be handled by Instance A, but subsequent `POST \u002Fmessages` requests land on Instance B, which lacks the in-memory state.\n**Prevention**: Use **Streamable HTTP transport** (added in v1.24.0) instead of SSE for serverless deployments\n**Solution**: For stateful SSE, deploy to non-serverless environments (VPS, long-running containers)\n\n**Official Status**: Fixed by introducing Streamable HTTP (v1.24+) - now the **recommended standard** for serverless.\n\n### Issue #12: OAuth Configuration Requires TWO Separate Apps\n**Source**: [Cloudflare Remote MCP Server Docs](https:\u002F\u002Fdevelopers.cloudflare.com\u002Fagents\u002Fguides\u002Fremote-mcp-server\u002F)\n**Why It Happens**: OAuth providers validate redirect URLs strictly. Localhost and production have different URLs, so they need separate OAuth client registrations.\n**Prevention**:\n```bash\n# Development OAuth App\nCallback URL: http:\u002F\u002Flocalhost:8788\u002Fcallback\n\n# Production OAuth App\nCallback URL: https:\u002F\u002Fmy-mcp-server.workers.dev\u002Fcallback\n```\n\n**Additional Requirements**:\n- KV namespace for auth state storage (create manually)\n- `COOKIE_ENCRYPTION_KEY` env var: `openssl rand -hex 32`\n- Client restart required after config changes\n\n### Issue #13: Widget State Over 4k Tokens Causes Performance Issues (Community-sourced)\n**Source**: [OpenAI Apps SDK - ChatGPT UI](https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\u002Fbuild\u002Fchatgpt-ui\u002F)\n**Why It Happens**: Widget state persists only to a single widget instance tied to one conversation message. State is reset when users submit via the main chat composer instead of widget controls.\n**Prevention**: Keep state payloads under **4k tokens** for optimal performance\n\n```typescript\n\u002F\u002F ✅ Good - Lightweight state\nwindow.openai.setWidgetState({ selectedId: \"item-123\", view: \"grid\" });\n\n\u002F\u002F ❌ Bad - Will cause performance issues\nwindow.openai.setWidgetState({\n  items: largeArray,           \u002F\u002F Don't store full datasets\n  history: conversationLog,    \u002F\u002F Don't store conversation history\n  cache: expensiveComputation  \u002F\u002F Don't cache large results\n});\n```\n\n**Best Practice**:\n- Store only UI state (selected items, view mode, filters)\n- Fetch data from MCP server on widget mount\n- Use tool calls to persist important data\n\n### Issue #14: Widget-Initiated Tool Calls Fail Without Permission Flag (Community-sourced)\n**Source**: [OpenAI Apps SDK - ChatGPT UI](https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\u002Fbuild\u002Fchatgpt-ui\u002F)\n**Why It Happens**: Components initiating tool calls via `window.openai.callTool()` require the tool marked as \"able to be initiated by the component\" on the MCP server. Without this flag, calls fail silently.\n**Prevention**: Mark tools as `widgetCallable: true` in annotations\n\n```typescript\n\u002F\u002F MCP Server - Mark tool as widget-callable\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n  tools: [{\n    name: 'update_item',\n    description: 'Update an item',\n    inputSchema: { \u002F* ... *\u002F },\n    annotations: {\n      openai: {\n        outputTemplate: 'ui:\u002F\u002Fwidget\u002Fitem.html',\n        \u002F\u002F ✅ Required for widget-initiated calls\n        widgetCallable: true\n      }\n    }\n  }]\n}));\n\n\u002F\u002F Widget - Now allowed to call tool\nwindow.openai.callTool({\n  name: 'update_item',\n  arguments: { id: itemId, status: 'completed' }\n});\n```\n\n---\n\n## Widget Development Best Practices\n\n### File Upload Limitations (Community-sourced)\n**Source**: [OpenAI Apps SDK - ChatGPT UI](https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\u002Fbuild\u002Fchatgpt-ui\u002F)\n\n`window.openai.uploadFile()` only supports 3 image formats: `image\u002Fpng`, `image\u002Fjpeg`, and `image\u002Fwebp`. Other formats fail silently.\n\n```typescript\n\u002F\u002F ✅ Supported\nwindow.openai.uploadFile({ accept: 'image\u002Fpng,image\u002Fjpeg,image\u002Fwebp' });\n\n\u002F\u002F ❌ Not supported (fails silently)\nwindow.openai.uploadFile({ accept: 'application\u002Fpdf' });\nwindow.openai.uploadFile({ accept: 'text\u002Fcsv' });\n```\n\n**Alternative for Other File Types**:\n1. Use base64 encoding in tool arguments\n2. Request user paste text content\n3. Use external upload service (S3, R2) and pass URL\n\n### Tool Performance Targets (Community-sourced)\n**Source**: [OpenAI Apps SDK - Troubleshooting](https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\u002Fdeploy\u002Ftroubleshooting)\n\nTool calls exceeding \"a few hundred milliseconds\" cause UI sluggishness in ChatGPT. Official docs recommend profiling backends and implementing caching for slow operations.\n\n**Performance Targets**:\n- **\u003C 200ms**: Ideal response time\n- **200-500ms**: Acceptable but noticeable\n- **> 500ms**: Sluggish, needs optimization\n\n**Optimization Strategies**:\n```typescript\n\u002F\u002F 1. Cache expensive computations\nconst cache = new Map();\nif (cache.has(key)) return cache.get(key);\nconst result = await expensiveOperation();\ncache.set(key, result);\n\n\u002F\u002F 2. Use KV\u002FD1 for pre-computed data\nconst cached = await env.KV.get(`result:${id}`);\nif (cached) return JSON.parse(cached);\n\n\u002F\u002F 3. Paginate large datasets\nreturn {\n  content: [{ type: 'text', text: 'First 20 results...' }],\n  _meta: { hasMore: true, nextPage: 2 }\n};\n\n\u002F\u002F 4. Move slow work to async tasks\n\u002F\u002F Return immediately, update via follow-up\n```\n\n---\n\n## MCP SDK 1.25.x Updates (December 2025)\n\n**Breaking Changes** from @modelcontextprotocol\u002Fsdk@1.24.x → 1.25.x:\n- Removed loose type exports (Prompts, Resources, Roots, Sampling, Tools) - use specific schemas\n- ES2020 target required (previous: ES2018)\n- `setRequestHandler` is now typesafe - incorrect schemas throw type errors\n\n**New Features**:\n- **Tasks** (v1.24.0+): Long-running operations with progress tracking\n- **Sampling with Tools** (v1.24.0+): Tools can request model sampling\n- **OAuth Client Credentials** (M2M): Machine-to-machine authentication\n\n**Migration**: If using loose type imports, update to specific schema imports:\n```typescript\n\u002F\u002F ❌ Old (removed in 1.25.0)\nimport { Tools } from '@modelcontextprotocol\u002Fsdk\u002Ftypes.js';\n\n\u002F\u002F ✅ New (1.25.1+)\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol\u002Fsdk\u002Ftypes.js';\n```\n\n---\n\n## Zod 4.0 Migration Notes (MAJOR UPDATE - July 2025)\n\n**Breaking Changes** from zod@3.x → 4.x:\n- `.default()` now expects input type (not output type). Use `.prefault()` for old behavior.\n- ZodError: `error.issues` (not `error.errors`)\n- `.merge()` and `.superRefine()` deprecated\n- Optional properties with defaults now always apply\n\n**Performance**: 14x faster string parsing, 7x faster arrays, 6.5x faster objects\n\n**Migration**: Update validation code:\n```typescript\n\u002F\u002F Zod 4.x\ntry {\n  const validated = schema.parse(data);\n} catch (error) {\n  if (error instanceof z.ZodError) {\n    return { content: [{ type: 'text', text: error.issues.map(e => e.message).join(', ') }] };\n  }\n}\n```\n\n---\n\n## Dependencies\n\n```json\n{\n  \"dependencies\": {\n    \"@modelcontextprotocol\u002Fsdk\": \"^1.25.3\",\n    \"hono\": \"^4.11.3\",\n    \"zod\": \"^4.3.5\"\n  },\n  \"devDependencies\": {\n    \"@cloudflare\u002Fvite-plugin\": \"^1.17.1\",\n    \"@cloudflare\u002Fworkers-types\": \"^4.20260103.0\",\n    \"vite\": \"^7.2.4\",\n    \"wrangler\": \"^4.54.0\"\n  }\n}\n```\n\n## Official Documentation\n\n- **MCP Specification**: https:\u002F\u002Fmodelcontextprotocol.io\u002F (Latest: 2025-11-25)\n- **MCP SDK**: https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\n- **OpenAI Apps SDK**: https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\n- **MCP Apps Extension (SEP-1865)**: http:\u002F\u002Fblog.modelcontextprotocol.io\u002Fposts\u002F2025-11-21-mcp-apps\u002F\n- **Context7 Library ID**: \u002Fmodelcontextprotocol\u002Ftypescript-sdk\n\n## Production Reference\n\n**Open Source Example**: https:\u002F\u002Fgithub.com\u002Fjezweb\u002Fchatgpt-app-sdk (portfolio carousel widget)\n- **Live in Production**: Rendering in ChatGPT Business\n- **MCP Server**: Full JSON-RPC 2.0 implementation with tools + resources (~310 lines)\n- **Widget Integration**: WordPress API → `window.openai.toolOutput` → React carousel\n- **Database**: D1 (SQLite) for contact form submissions\n- **Stack**: Hono 4 + React 19 + Tailwind v4 + Drizzle ORM\n- **Key Files**:\n  - `\u002Fsrc\u002Flib\u002Fmcp\u002Fserver.ts` - Complete MCP handler\n  - `\u002Fsrc\u002Fserver\u002Ftools\u002Fportfolio.ts` - Tool with widget annotations\n  - `\u002Fsrc\u002Fwidgets\u002FPortfolioWidget.tsx` - Data access pattern\n- **Verified**: All 14 known issues prevented, zero errors in production\n\n---\n\n## Community Resources\n\n### Deployment Tools\n\n**Cloudflare One-Click Deploy**: Deploy MCP servers to Cloudflare Workers with pre-built templates and auto-configured CI\u002FCD. Includes OAuth wrapper and Python support.\n- Docs: https:\u002F\u002Fdevelopers.cloudflare.com\u002Fagents\u002Fguides\u002Fremote-mcp-server\u002F\n- Blog: https:\u002F\u002Fblog.cloudflare.com\u002Fmodel-context-protocol\u002F\n\n### Frameworks\n\n**Skybridge** (Community): React-focused framework with HMR support for widgets and enhanced MCP server helpers. Unofficial but actively maintained.\n- GitHub: https:\u002F\u002Fgithub.com\u002Falpic-ai\u002Fskybridge\n- Docs: https:\u002F\u002Fwww.skybridge.tech\u002F\n\n> **Note**: Community frameworks are not officially supported. Use at your own discretion\n","# Building OpenAI Apps with Stateless MCP Servers\n\n**Status**: Production Ready\n**Last Updated**: 2026-01-21\n**Dependencies**: `cloudflare-worker-base`, `hono-routing` (optional)\n**Latest Versions**: @modelcontextprotocol\u002Fsdk@1.25.3, hono@4.11.3, zod@4.3.5, wrangler@4.58.0\n\n---\n\n## Overview\n\nBuild **ChatGPT Apps** using **MCP (Model Context Protocol)** servers on Cloudflare Workers. Extends ChatGPT with custom tools and interactive widgets (HTML\u002FJS UI rendered in iframe).\n\n**Architecture**: ChatGPT → MCP endpoint (JSON-RPC 2.0) → Tool handlers → Widget resources (HTML)\n\n**Status**: Apps available to Business\u002FEnterprise\u002FEdu (GA Nov 13, 2025). MCP Apps Extension (SEP-1865) formalized Nov 21, 2025.\n\n---\n\n## Quick Start\n\n### 1. Scaffold & Install\n\n```bash\nnpm create cloudflare@latest my-openai-app -- --type hello-world --ts --git --deploy false\ncd my-openai-app\nnpm install @modelcontextprotocol\u002Fsdk@1.25.3 hono@4.11.3 zod@4.3.5\nnpm install -D @cloudflare\u002Fvite-plugin@1.17.1 vite@7.2.4\n```\n\n### 2. Configure wrangler.jsonc\n\n```jsonc\n{\n  \"name\": \"my-openai-app\",\n  \"main\": \"dist\u002Findex.js\",\n  \"compatibility_flags\": [\"nodejs_compat\"],  \u002F\u002F Required for MCP SDK\n  \"assets\": {\n    \"directory\": \"dist\u002Fclient\",\n    \"binding\": \"ASSETS\"  \u002F\u002F Must match TypeScript\n  }\n}\n```\n\n### 3. Create MCP Server (`src\u002Findex.ts`)\n\n```typescript\nimport { Hono } from 'hono';\nimport { cors } from 'hono\u002Fcors';\nimport { Server } from '@modelcontextprotocol\u002Fsdk\u002Fserver\u002Findex.js';\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol\u002Fsdk\u002Ftypes.js';\n\nconst app = new Hono\u003C{ Bindings: { ASSETS: Fetcher } }>();\n\n\u002F\u002F CRITICAL: Must allow chatgpt.com\napp.use('\u002Fmcp\u002F*', cors({ origin: 'https:\u002F\u002Fchatgpt.com' }));\n\nconst mcpServer = new Server(\n  { name: 'my-app', version: '1.0.0' },\n  { capabilities: { tools: {}, resources: {} } }\n);\n\n\u002F\u002F Tool registration\nmcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({\n  tools: [{\n    name: 'hello',\n    description: 'Use this when user wants to see a greeting',\n    inputSchema: {\n      type: 'object',\n      properties: { name: { type: 'string' } },\n      required: ['name']\n    },\n    annotations: {\n      openai: { outputTemplate: 'ui:\u002F\u002Fwidget\u002Fhello.html' }  \u002F\u002F Widget URI\n    }\n  }]\n}));\n\n\u002F\u002F Tool execution\nmcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {\n  if (request.params.name === 'hello') {\n    const { name } = request.params.arguments as { name: string };\n    return {\n      content: [{ type: 'text', text: `Hello, ${name}!` }],\n      _meta: { initialData: { name } }  \u002F\u002F Passed to widget\n    };\n  }\n  throw new Error(`Unknown tool: ${request.params.name}`);\n});\n\napp.post('\u002Fmcp', async (c) => {\n  const body = await c.req.json();\n  const response = await mcpServer.handleRequest(body);\n  return c.json(response);\n});\n\napp.get('\u002Fwidgets\u002F*', async (c) => c.env.ASSETS.fetch(c.req.raw));\n\nexport default app;\n```\n\n### 4. Create Widget (`src\u002Fwidgets\u002Fhello.html`)\n\n```html\n\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\n  \u003Cstyle>\n    body { margin: 0; padding: 20px; font-family: system-ui; }\n  \u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv id=\"greeting\">Loading...\u003C\u002Fdiv>\n  \u003Cscript>\n    if (window.openai && window.openai.getInitialData) {\n      const data = window.openai.getInitialData();\n      document.getElementById('greeting').textContent = `Hello, ${data.name}! 👋`;\n    }\n  \u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n```\n\n### 5. Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\nnpx @modelcontextprotocol\u002Finspector https:\u002F\u002Fmy-app.workers.dev\u002Fmcp\n```\n\n---\n\n## Critical Requirements\n\n**CORS**: Must allow `https:\u002F\u002Fchatgpt.com` on `\u002Fmcp\u002F*` routes\n**Widget URI**: Must use `ui:\u002F\u002Fwidget\u002F` prefix (e.g., `ui:\u002F\u002Fwidget\u002Fmap.html`)\n**MIME Type**: Must be `text\u002Fhtml+skybridge` for HTML resources\n**Widget Data**: Pass via `_meta.initialData` (accessed via `window.openai.getInitialData()`)\n**Tool Descriptions**: Action-oriented (\"Use this when user wants to...\")\n**ASSETS Binding**: Serve widgets from ASSETS, not bundled in worker code\n**SSE**: Send heartbeat every 30s (100s timeout on Workers)\n\n---\n\n## Known Issues Prevention\n\nThis skill prevents **14** documented issues:\n\n### Issue #1: CORS Policy Blocks MCP Endpoint\n**Error**: `Access to fetch blocked by CORS policy`\n**Fix**: `app.use('\u002Fmcp\u002F*', cors({ origin: 'https:\u002F\u002Fchatgpt.com' }))`\n\n### Issue #2: Widget Returns 404 Not Found\n**Error**: `404 (Not Found)` for widget URL\n**Fix**: Use `ui:\u002F\u002Fwidget\u002F` prefix (not `resource:\u002F\u002F` or `\u002Fwidgets\u002F`)\n```typescript\nannotations: { openai: { outputTemplate: 'ui:\u002F\u002Fwidget\u002Fmap.html' } }\n```\n\n### Issue #3: Widget Displays as Plain Text\n**Error**: HTML source code visible instead of rendered widget\n**Fix**: MIME type must be `text\u002Fhtml+skybridge` (not `text\u002Fhtml`)\n```typescript\nserver.setRequestHandler(ListResourcesRequestSchema, async () => ({\n  resources: [{ uri: 'ui:\u002F\u002Fwidget\u002Fmap.html', mimeType: 'text\u002Fhtml+skybridge' }]\n}));\n```\n\n### Issue #4: ASSETS Binding Undefined\n**Error**: `TypeError: Cannot read property 'fetch' of undefined`\n**Fix**: Binding name in wrangler.jsonc must match TypeScript\n```jsonc\n{ \"assets\": { \"binding\": \"ASSETS\" } }  \u002F\u002F wrangler.jsonc\n```\n```typescript\ntype Bindings = { ASSETS: Fetcher };  \u002F\u002F index.ts\n```\n\n### Issue #5: SSE Connection Drops After 100 Seconds\n**Error**: SSE stream closes unexpectedly\n**Fix**: Send heartbeat every 30s (Workers timeout at 100s inactivity)\n```typescript\nconst heartbeat = setInterval(async () => {\n  await stream.writeSSE({ data: JSON.stringify({ type: 'heartbeat' }), event: 'ping' });\n}, 30000);\n```\n\n### Issue #6: ChatGPT Doesn't Suggest Tool\n**Error**: Tool registered but never appears in suggestions\n**Fix**: Use action-oriented descriptions\n```typescript\n\u002F\u002F ✅ Good: 'Use this when user wants to see a location on a map'\n\u002F\u002F ❌ Bad: 'Shows a map'\n```\n\n### Issue #7: Widget Can't Access Initial Data\n**Error**: `window.openai.getInitialData()` returns `undefined`\n**Fix**: Pass data via `_meta.initialData`\n```typescript\nreturn {\n  content: [{ type: 'text', text: 'Here is your map' }],\n  _meta: { initialData: { location: 'SF', zoom: 12 } }\n};\n```\n\n### Issue #8: Widget Scripts Blocked by CSP\n**Error**: `Refused to load script (CSP directive)`\n**Fix**: Use inline scripts or same-origin scripts. Third-party CDNs blocked.\n```html\n\u003C!-- ✅ Works --> \u003Cscript>console.log('ok');\u003C\u002Fscript>\n\u003C!-- ❌ Blocked --> \u003Cscript src=\"https:\u002F\u002Fcdn.example.com\u002Flib.js\">\u003C\u002Fscript>\n```\n\n### Issue #9: Hono Global Response Override Breaks Next.js (v1.25.0-1.25.2)\n**Error**: `No response is returned from route handler` (Next.js App Router)\n**Source**: [GitHub Issue #1369](https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\u002Fissues\u002F1369)\n**Affected Versions**: v1.25.0 to v1.25.2\n**Fixed In**: v1.25.3\n**Why It Happens**: Hono (MCP SDK dependency) overwrites `global.Response`, breaking frameworks that extend it (Next.js, Remix, SvelteKit). NextResponse instanceof check fails.\n**Prevention**:\n- **Upgrade to v1.25.3+** (recommended)\n- **Before fix**: Use `webStandardStreamableHTTPServerTransport` instead\n- **Or**: Run MCP server on separate port from Next.js\u002FRemix\u002FSvelteKit app\n\n```typescript\n\u002F\u002F ✅ v1.25.3+ - Fixed\nconst transport = new StreamableHTTPServerTransport({\n  sessionIdGenerator: undefined,\n});\n\n\u002F\u002F ✅ v1.25.0-1.25.2 - Workaround\nimport { webStandardStreamableHTTPServerTransport } from '@modelcontextprotocol\u002Fsdk\u002Fserver\u002Findex.js';\nconst transport = webStandardStreamableHTTPServerTransport({\n  sessionIdGenerator: undefined,\n});\n```\n\n### Issue #10: Elicitation (User Input) Fails on Cloudflare Workers\n**Error**: `EvalError: Code generation from strings disallowed`\n**Source**: [GitHub Issue #689](https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\u002Fissues\u002F689)\n**Why It Happens**: Internal AJV v6 validator uses prohibited APIs on edge platforms\n**Prevention**: Avoid `elicitInput()` on edge platforms (Cloudflare Workers, Vercel Edge, Deno Deploy)\n\n**Workaround**:\n```typescript\n\u002F\u002F ❌ Don't use on Cloudflare Workers\nconst userInput = await server.elicitInput({\n  prompt: \"What is your name?\",\n  schema: { type: \"string\" }\n});\n\n\u002F\u002F ✅ Use tool parameters instead\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n  const { name } = request.params.arguments as { name: string };\n  \u002F\u002F User provides via tool call, not elicitation\n});\n```\n\n**Status**: Requires MCP SDK v2 to fix properly. Track [PR #844](https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\u002Fpull\u002F844).\n\n### Issue #11: SSE Transport Statefulness Breaks Serverless Deployments\n**Error**: `400: No transport found for sessionId`\n**Source**: [GitHub Issue #273](https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\u002Fissues\u002F273)\n**Why It Happens**: `SSEServerTransport` relies on in-memory session storage. In serverless environments (AWS Lambda, Cloudflare Workers), the initial `GET \u002Fsse` request may be handled by Instance A, but subsequent `POST \u002Fmessages` requests land on Instance B, which lacks the in-memory state.\n**Prevention**: Use **Streamable HTTP transport** (added in v1.24.0) instead of SSE for serverless deployments\n**Solution**: For stateful SSE, deploy to non-serverless environments (VPS, long-running containers)\n\n**Official Status**: Fixed by introducing Streamable HTTP (v1.24+) - now the **recommended standard** for serverless.\n\n### Issue #12: OAuth Configuration Requires TWO Separate Apps\n**Source**: [Cloudflare Remote MCP Server Docs](https:\u002F\u002Fdevelopers.cloudflare.com\u002Fagents\u002Fguides\u002Fremote-mcp-server\u002F)\n**Why It Happens**: OAuth providers validate redirect URLs strictly. Localhost and production have different URLs, so they need separate OAuth client registrations.\n**Prevention**:\n```bash\n# Development OAuth App\nCallback URL: http:\u002F\u002Flocalhost:8788\u002Fcallback\n\n# Production OAuth App\nCallback URL: https:\u002F\u002Fmy-mcp-server.workers.dev\u002Fcallback\n```\n\n**Additional Requirements**:\n- KV namespace for auth state storage (create manually)\n- `COOKIE_ENCRYPTION_KEY` env var: `openssl rand -hex 32`\n- Client restart required after config changes\n\n### Issue #13: Widget State Over 4k Tokens Causes Performance Issues (Community-sourced)\n**Source**: [OpenAI Apps SDK - ChatGPT UI](https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\u002Fbuild\u002Fchatgpt-ui\u002F)\n**Why It Happens**: Widget state persists only to a single widget instance tied to one conversation message. State is reset when users submit via the main chat composer instead of widget controls.\n**Prevention**: Keep state payloads under **4k tokens** for optimal performance\n\n```typescript\n\u002F\u002F ✅ Good - Lightweight state\nwindow.openai.setWidgetState({ selectedId: \"item-123\", view: \"grid\" });\n\n\u002F\u002F ❌ Bad - Will cause performance issues\nwindow.openai.setWidgetState({\n  items: largeArray,           \u002F\u002F Don't store full datasets\n  history: conversationLog,    \u002F\u002F Don't store conversation history\n  cache: expensiveComputation  \u002F\u002F Don't cache large results\n});\n```\n\n**Best Practice**:\n- Store only UI state (selected items, view mode, filters)\n- Fetch data from MCP server on widget mount\n- Use tool calls to persist important data\n\n### Issue #14: Widget-Initiated Tool Calls Fail Without Permission Flag (Community-sourced)\n**Source**: [OpenAI Apps SDK - ChatGPT UI](https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\u002Fbuild\u002Fchatgpt-ui\u002F)\n**Why It Happens**: Components initiating tool calls via `window.openai.callTool()` require the tool marked as \"able to be initiated by the component\" on the MCP server. Without this flag, calls fail silently.\n**Prevention**: Mark tools as `widgetCallable: true` in annotations\n\n```typescript\n\u002F\u002F MCP Server - Mark tool as widget-callable\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n  tools: [{\n    name: 'update_item',\n    description: 'Update an item',\n    inputSchema: { \u002F* ... *\u002F },\n    annotations: {\n      openai: {\n        outputTemplate: 'ui:\u002F\u002Fwidget\u002Fitem.html',\n        \u002F\u002F ✅ Required for widget-initiated calls\n        widgetCallable: true\n      }\n    }\n  }]\n}));\n\n\u002F\u002F Widget - Now allowed to call tool\nwindow.openai.callTool({\n  name: 'update_item',\n  arguments: { id: itemId, status: 'completed' }\n});\n```\n\n---\n\n## Widget Development Best Practices\n\n### File Upload Limitations (Community-sourced)\n**Source**: [OpenAI Apps SDK - ChatGPT UI](https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\u002Fbuild\u002Fchatgpt-ui\u002F)\n\n`window.openai.uploadFile()` only supports 3 image formats: `image\u002Fpng`, `image\u002Fjpeg`, and `image\u002Fwebp`. Other formats fail silently.\n\n```typescript\n\u002F\u002F ✅ Supported\nwindow.openai.uploadFile({ accept: 'image\u002Fpng,image\u002Fjpeg,image\u002Fwebp' });\n\n\u002F\u002F ❌ Not supported (fails silently)\nwindow.openai.uploadFile({ accept: 'application\u002Fpdf' });\nwindow.openai.uploadFile({ accept: 'text\u002Fcsv' });\n```\n\n**Alternative for Other File Types**:\n1. Use base64 encoding in tool arguments\n2. Request user paste text content\n3. Use external upload service (S3, R2) and pass URL\n\n### Tool Performance Targets (Community-sourced)\n**Source**: [OpenAI Apps SDK - Troubleshooting](https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\u002Fdeploy\u002Ftroubleshooting)\n\nTool calls exceeding \"a few hundred milliseconds\" cause UI sluggishness in ChatGPT. Official docs recommend profiling backends and implementing caching for slow operations.\n\n**Performance Targets**:\n- **\u003C 200ms**: Ideal response time\n- **200-500ms**: Acceptable but noticeable\n- **> 500ms**: Sluggish, needs optimization\n\n**Optimization Strategies**:\n```typescript\n\u002F\u002F 1. Cache expensive computations\nconst cache = new Map();\nif (cache.has(key)) return cache.get(key);\nconst result = await expensiveOperation();\ncache.set(key, result);\n\n\u002F\u002F 2. Use KV\u002FD1 for pre-computed data\nconst cached = await env.KV.get(`result:${id}`);\nif (cached) return JSON.parse(cached);\n\n\u002F\u002F 3. Paginate large datasets\nreturn {\n  content: [{ type: 'text', text: 'First 20 results...' }],\n  _meta: { hasMore: true, nextPage: 2 }\n};\n\n\u002F\u002F 4. Move slow work to async tasks\n\u002F\u002F Return immediately, update via follow-up\n```\n\n---\n\n## MCP SDK 1.25.x Updates (December 2025)\n\n**Breaking Changes** from @modelcontextprotocol\u002Fsdk@1.24.x → 1.25.x:\n- Removed loose type exports (Prompts, Resources, Roots, Sampling, Tools) - use specific schemas\n- ES2020 target required (previous: ES2018)\n- `setRequestHandler` is now typesafe - incorrect schemas throw type errors\n\n**New Features**:\n- **Tasks** (v1.24.0+): Long-running operations with progress tracking\n- **Sampling with Tools** (v1.24.0+): Tools can request model sampling\n- **OAuth Client Credentials** (M2M): Machine-to-machine authentication\n\n**Migration**: If using loose type imports, update to specific schema imports:\n```typescript\n\u002F\u002F ❌ Old (removed in 1.25.0)\nimport { Tools } from '@modelcontextprotocol\u002Fsdk\u002Ftypes.js';\n\n\u002F\u002F ✅ New (1.25.1+)\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol\u002Fsdk\u002Ftypes.js';\n```\n\n---\n\n## Zod 4.0 Migration Notes (MAJOR UPDATE - July 2025)\n\n**Breaking Changes** from zod@3.x → 4.x:\n- `.default()` now expects input type (not output type). Use `.prefault()` for old behavior.\n- ZodError: `error.issues` (not `error.errors`)\n- `.merge()` and `.superRefine()` deprecated\n- Optional properties with defaults now always apply\n\n**Performance**: 14x faster string parsing, 7x faster arrays, 6.5x faster objects\n\n**Migration**: Update validation code:\n```typescript\n\u002F\u002F Zod 4.x\ntry {\n  const validated = schema.parse(data);\n} catch (error) {\n  if (error instanceof z.ZodError) {\n    return { content: [{ type: 'text', text: error.issues.map(e => e.message).join(', ') }] };\n  }\n}\n```\n\n---\n\n## Dependencies\n\n```json\n{\n  \"dependencies\": {\n    \"@modelcontextprotocol\u002Fsdk\": \"^1.25.3\",\n    \"hono\": \"^4.11.3\",\n    \"zod\": \"^4.3.5\"\n  },\n  \"devDependencies\": {\n    \"@cloudflare\u002Fvite-plugin\": \"^1.17.1\",\n    \"@cloudflare\u002Fworkers-types\": \"^4.20260103.0\",\n    \"vite\": \"^7.2.4\",\n    \"wrangler\": \"^4.54.0\"\n  }\n}\n```\n\n## Official Documentation\n\n- **MCP Specification**: https:\u002F\u002Fmodelcontextprotocol.io\u002F (Latest: 2025-11-25)\n- **MCP SDK**: https:\u002F\u002Fgithub.com\u002Fmodelcontextprotocol\u002Ftypescript-sdk\n- **OpenAI Apps SDK**: https:\u002F\u002Fdevelopers.openai.com\u002Fapps-sdk\n- **MCP Apps Extension (SEP-1865)**: http:\u002F\u002Fblog.modelcontextprotocol.io\u002Fposts\u002F2025-11-21-mcp-apps\u002F\n- **Context7 Library ID**: \u002Fmodelcontextprotocol\u002Ftypescript-sdk\n\n## Production Reference\n\n**Open Source Example**: https:\u002F\u002Fgithub.com\u002Fjezweb\u002Fchatgpt-app-sdk (portfolio carousel widget)\n- **Live in Production**: Rendering in ChatGPT Business\n- **MCP Server**: Full JSON-RPC 2.0 implementation with tools + resources (~310 lines)\n- **Widget Integration**: WordPress API → `window.openai.toolOutput` → React carousel\n- **Database**: D1 (SQLite) for contact form submissions\n- **Stack**: Hono 4 + React 19 + Tailwind v4 + Drizzle ORM\n- **Key Files**:\n  - `\u002Fsrc\u002Flib\u002Fmcp\u002Fserver.ts` - Complete MCP handler\n  - `\u002Fsrc\u002Fserver\u002Ftools\u002Fportfolio.ts` - Tool with widget annotations\n  - `\u002Fsrc\u002Fwidgets\u002FPortfolioWidget.tsx` - Data access pattern\n- **Verified**: All 14 known issues prevented, zero errors in production\n\n---\n\n## Community Resources\n\n### Deployment Tools\n\n**Cloudflare One-Click Deploy**: Deploy MCP servers to Cloudflare Workers with pre-built templates and auto-configured CI\u002FCD. Includes OAuth wrapper and Python support.\n- Docs: https:\u002F\u002Fdevelopers.cloudflare.com\u002Fagents\u002Fguides\u002Fremote-mcp-server\u002F\n- Blog: https:\u002F\u002Fblog.cloudflare.com\u002Fmodel-context-protocol\u002F\n\n### Frameworks\n\n**Skybridge** (Community): React-focused framework with HMR support for widgets and enhanced MCP server helpers. Unofficial but actively maintained.\n- GitHub: https:\u002F\u002Fgithub.com\u002Falpic-ai\u002Fskybridge\n- Docs: https:\u002F\u002Fwww.skybridge.tech\u002F\n\n> **Note**: Community frameworks are not officially supported. Use at your own discretion\n",{"name":5,"domain":48,"description":49,"allowed-tools":50,"user-invocable":57},"engineering","Build ChatGPT apps with MCP servers on Cloudflare Workers. Extend ChatGPT with custom tools and interactive widgets (HTML\u002FJS UI).\n\nUse when: developing ChatGPT extensions, implementing MCP servers, or troubleshooting CORS, widget 404s, MIME types, ASSETS binding errors, Next.js integration issues, or edge platform limitations.\n",[51,52,53,54,55,56],"Read","Write","Edit","Bash","Glob","Grep",true,"main","cd89f013b617f7eea982a9bbc60e992974fda2aa","skills\u002Fopenai-apps-mcp",15,"https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco","https:\u002F\u002Fcocoresearch.org\u002Fcoco\u002F","https:\u002F\u002Fgithub.com\u002Fcoco-research","https:\u002F\u002Fcocoresearch.org","Local-first, open-source tools for people who work with AI agents: governed memory, agents that check their own work, and data that stays on your machine.","2026-11-11 12:18:13",{"source":8,"skillName":5,"commands":69,"prompt":79,"zipAvailable":57},[70,73,76],{"tool":71,"command":72},"npx","npx skills add coco-research\u002Fcoco --skill \"OpenAI Apps MCP\"",{"tool":74,"command":75},"bunx","bunx skills add coco-research\u002Fcoco --skill \"OpenAI Apps MCP\"",{"tool":77,"command":78},"pnpm","pnpm dlx skills add coco-research\u002Fcoco --skill \"OpenAI Apps MCP\"","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fskills\u002Fopenai-apps-mcp\u002FSKILL.md",[81,87,93,97,103,107,111],{"path":82,"resourceType":83,"resourceTypeDesc":84,"sizeBytes":85,"sourceHtmlUrl":86},"assets\u002Fexample-template.txt","ASSET","资产文件",416,"https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fskills\u002Fopenai-apps-mcp\u002Fassets\u002Fexample-template.txt",{"path":88,"resourceType":89,"resourceTypeDesc":90,"sizeBytes":91,"sourceHtmlUrl":92},"references\u002Fexample-reference.md","REFERENCE","参考文档",760,"https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fskills\u002Fopenai-apps-mcp\u002Freferences\u002Fexample-reference.md",{"path":94,"resourceType":89,"resourceTypeDesc":90,"sizeBytes":95,"sourceHtmlUrl":96},"references\u002Fopenai-metadata-format.md",6139,"https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fskills\u002Fopenai-apps-mcp\u002Freferences\u002Fopenai-metadata-format.md",{"path":98,"resourceType":99,"resourceTypeDesc":100,"sizeBytes":101,"sourceHtmlUrl":102},"scripts\u002Fexample-script.sh","SCRIPT","脚本",302,"https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fskills\u002Fopenai-apps-mcp\u002Fscripts\u002Fexample-script.sh",{"path":104,"resourceType":89,"resourceTypeDesc":90,"sizeBytes":105,"sourceHtmlUrl":106},"templates\u002Fbasic\u002Fpackage.json",507,"https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fskills\u002Fopenai-apps-mcp\u002Ftemplates\u002Fbasic\u002Fpackage.json",{"path":108,"resourceType":99,"resourceTypeDesc":100,"sizeBytes":109,"sourceHtmlUrl":110},"templates\u002Fbasic\u002Fvite.config.ts",636,"https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fskills\u002Fopenai-apps-mcp\u002Ftemplates\u002Fbasic\u002Fvite.config.ts",{"path":112,"resourceType":113,"resourceTypeDesc":114,"sizeBytes":115,"sourceHtmlUrl":116},"templates\u002Fbasic\u002Fwrangler.jsonc","OTHER","其他",261,"https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fskills\u002Fopenai-apps-mcp\u002Ftemplates\u002Fbasic\u002Fwrangler.jsonc",[118,143,153,171,206,225],{"skillNo":119,"name":120,"nameZh":6,"description":121,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":125,"publishedAt":43},"01M71KEJXF9KMT0BWCMRSRFA4M","trading-super-intelligence","Trading Super Intelligence Team. Named real-world personas across 8 cells. Built local-first (LM Studio) and validator-gated. Illustrative composites; see DISCLAIMER.md.","https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fsystems\u002Fsuperintelligence\u002Ftrading\u002FSKILL.md",[124],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142],"super","trading super intelligence","intelligence","trading","validator","personas","local","composites","studio","team","across","world","first","gated","2026","lm studio","local-first",{"skillNo":144,"name":145,"nameZh":6,"description":146,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":147,"categories":148,"keywords":150,"publishedAt":43},"01M71KEFWS3CMAP7DF1P0FWTDE","strategy-super-intelligence","Strategy Super Intelligence Team. Named real-world personas across 7 cells. Built local-first (LM Studio) and validator-gated. Illustrative composites; see DISCLAIMER.md.","https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fsystems\u002Fsuperintelligence\u002Fstrategy\u002FSKILL.md",[149],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[151,126,152,128,130,131,132,133,134,135,136,137,138,139,140,141,142],"strategy","strategy super intelligence",{"skillNo":154,"name":155,"nameZh":6,"description":156,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":157,"categories":158,"keywords":165,"publishedAt":43},"01M71KED56ZEPK2QE84QSQPMT0","risk-compliance-super-intelligence","Risk & Compliance (GRC) Super Intelligence Team. Named real-world personas across 7 cells. Built local-first (LM Studio) and validator-gated. Illustrative composites; see DISCLAIMER.md.","https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fsystems\u002Fsuperintelligence\u002Frisk-compliance\u002FSKILL.md",[159,160],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":161,"categoryName":162,"taxonomyType":163,"taxonomyTypeDesc":164},"01JBSEEDCAT000000000000037","律师与法务","PROFESSION","适用职业",[166,126,167,168,169,128,130,131,132,133,170,134,135,136,137,138,139,140,141,142],"risk","risk compliance super intelligence","compliance","risk compliance","grc",{"skillNo":172,"name":173,"nameZh":6,"description":174,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":175,"categories":176,"keywords":190,"publishedAt":43},"01M71KE9A2FWSC74VA96W1EZSN","product-design-super-intelligence","Your product + design brain trust and decision partner. Named personas across 8 cells spanning product strategy, discovery\u002Fresearch, growth\u002Fmetrics, design foundations\u002Fusability, design leadership\u002Fcraft, UX\u002Fcontent, design systems, and a…","https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fsystems\u002Fsuperintelligence\u002Fproduct-design\u002FSKILL.md",[177,180,183,184,187],{"categoryNo":178,"categoryName":179,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":181,"categoryName":182,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000021","资料检索与研究",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":185,"categoryName":186,"taxonomyType":163,"taxonomyTypeDesc":164},"01JBSEEDCAT000000000000031","产品经理",{"categoryNo":188,"categoryName":189,"taxonomyType":163,"taxonomyTypeDesc":164},"01JBSEEDCAT000000000000032","设计师",[191,192,128,193,126,194,195,196,197,198,199,135,136,200,201,140,202,203,204,205],"design","product","product design","product design super intelligence","foundations","product-design","decision","design systems","usability","leadership","ux","brain","craft","metrics","trust",{"skillNo":207,"name":208,"nameZh":6,"description":209,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":210,"categories":211,"keywords":214,"publishedAt":43},"01M71KE6H9FPSD9TECYFBPXZ8Y","legal-ip-super-intelligence","Your legal, IP, and regulatory brain trust. 31 named personas across 7 cells covering patent strategy, tech licensing, regulatory litigation, AI governance, open-source compliance, privacy law, and antitrust.","https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fsystems\u002Fsuperintelligence\u002Flegal-ip\u002FSKILL.md",[212,213],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":161,"categoryName":162,"taxonomyType":163,"taxonomyTypeDesc":164},[215,126,216,128,217,218,205,219,131,220,221,222,151,135,136,223,168,224,140,202],"ip","legal ip super intelligence","legal","sep","tech","litigation","ai governance","open","open-source","governance",{"skillNo":226,"name":227,"nameZh":6,"description":228,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":229,"categories":230,"keywords":232,"publishedAt":43},"01M71KE36QRY610V6YVB9JWRND","healthcare-life-sciences-super-intelligence","Your healthcare and life sciences brain trust. 25 named personas across 7 cells covering drug discovery, medtech, biotech strategy, health policy, genomics, clinical trials, and healthcare economics.","https:\u002F\u002Fgithub.com\u002Fcoco-research\u002Fcoco\u002Fblob\u002Fmain\u002Fsystems\u002Fsuperintelligence\u002Fhealthcare-life-sciences\u002FSKILL.md",[231],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[233,126,234,235,128,218,205,236,237,131,238,239,151,135,136,240,241,242,243,140],"healthcare","healthcare life sciences super intelligence","life","economics","discovery","policy","clinical","clinical trials","health","trials","genomics"]