{"id":"75b00874-001b-4e61-a892-1e0a910e9c35","shortId":"yLE8L3","kind":"skill","title":"OpenAI Apps MCP","tagline":"Build ChatGPT apps with MCP servers on Cloudflare Workers. Extend ChatGPT with custom tools and interactive widgets (HTML/JS UI).\n\nUse when: developing ChatGPT extensions, implementing MCP servers, or troubleshooting CORS, widget 404s, MIME types, ASSETS binding errors, Next.js i","description":"# 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/sdk@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/JS 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/Enterprise/Edu (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/sdk@1.25.3 hono@4.11.3 zod@4.3.5\nnpm install -D @cloudflare/vite-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/index.js\",\n  \"compatibility_flags\": [\"nodejs_compat\"],  // Required for MCP SDK\n  \"assets\": {\n    \"directory\": \"dist/client\",\n    \"binding\": \"ASSETS\"  // Must match TypeScript\n  }\n}\n```\n\n### 3. Create MCP Server (`src/index.ts`)\n\n```typescript\nimport { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';\n\nconst app = new Hono<{ Bindings: { ASSETS: Fetcher } }>();\n\n// CRITICAL: Must allow chatgpt.com\napp.use('/mcp/*', cors({ origin: 'https://chatgpt.com' }));\n\nconst mcpServer = new Server(\n  { name: 'my-app', version: '1.0.0' },\n  { capabilities: { tools: {}, resources: {} } }\n);\n\n// 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://widget/hello.html' }  // Widget URI\n    }\n  }]\n}));\n\n// 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 } }  // Passed to widget\n    };\n  }\n  throw new Error(`Unknown tool: ${request.params.name}`);\n});\n\napp.post('/mcp', 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('/widgets/*', async (c) => c.env.ASSETS.fetch(c.req.raw));\n\nexport default app;\n```\n\n### 4. Create Widget (`src/widgets/hello.html`)\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    body { margin: 0; padding: 20px; font-family: system-ui; }\n  </style>\n</head>\n<body>\n  <div id=\"greeting\">Loading...</div>\n  <script>\n    if (window.openai && window.openai.getInitialData) {\n      const data = window.openai.getInitialData();\n      document.getElementById('greeting').textContent = `Hello, ${data.name}! 👋`;\n    }\n  </script>\n</body>\n</html>\n```\n\n### 5. Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\nnpx @modelcontextprotocol/inspector https://my-app.workers.dev/mcp\n```\n\n---\n\n## Critical Requirements\n\n**CORS**: Must allow `https://chatgpt.com` on `/mcp/*` routes\n**Widget URI**: Must use `ui://widget/` prefix (e.g., `ui://widget/map.html`)\n**MIME Type**: Must be `text/html+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('/mcp/*', cors({ origin: 'https://chatgpt.com' }))`\n\n### Issue #2: Widget Returns 404 Not Found\n**Error**: `404 (Not Found)` for widget URL\n**Fix**: Use `ui://widget/` prefix (not `resource://` or `/widgets/`)\n```typescript\nannotations: { openai: { outputTemplate: 'ui://widget/map.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/html+skybridge` (not `text/html`)\n```typescript\nserver.setRequestHandler(ListResourcesRequestSchema, async () => ({\n  resources: [{ uri: 'ui://widget/map.html', mimeType: 'text/html+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\" } }  // wrangler.jsonc\n```\n```typescript\ntype Bindings = { ASSETS: Fetcher };  // 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// ✅ Good: 'Use this when user wants to see a location on a map'\n// ❌ 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<!-- ✅ Works --> <script>console.log('ok');</script>\n<!-- ❌ Blocked --> <script src=\"https://cdn.example.com/lib.js\"></script>\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://github.com/modelcontextprotocol/typescript-sdk/issues/1369)\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/Remix/SvelteKit app\n\n```typescript\n// ✅ v1.25.3+ - Fixed\nconst transport = new StreamableHTTPServerTransport({\n  sessionIdGenerator: undefined,\n});\n\n// ✅ v1.25.0-1.25.2 - Workaround\nimport { webStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/index.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://github.com/modelcontextprotocol/typescript-sdk/issues/689)\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// ❌ Don't use on Cloudflare Workers\nconst userInput = await server.elicitInput({\n  prompt: \"What is your name?\",\n  schema: { type: \"string\" }\n});\n\n// ✅ Use tool parameters instead\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n  const { name } = request.params.arguments as { name: string };\n  // User provides via tool call, not elicitation\n});\n```\n\n**Status**: Requires MCP SDK v2 to fix properly. Track [PR #844](https://github.com/modelcontextprotocol/typescript-sdk/pull/844).\n\n### Issue #11: SSE Transport Statefulness Breaks Serverless Deployments\n**Error**: `400: No transport found for sessionId`\n**Source**: [GitHub Issue #273](https://github.com/modelcontextprotocol/typescript-sdk/issues/273)\n**Why It Happens**: `SSEServerTransport` relies on in-memory session storage. In serverless environments (AWS Lambda, Cloudflare Workers), the initial `GET /sse` request may be handled by Instance A, but subsequent `POST /messages` 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://developers.cloudflare.com/agents/guides/remote-mcp-server/)\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://localhost:8788/callback\n\n# Production OAuth App\nCallback URL: https://my-mcp-server.workers.dev/callback\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://developers.openai.com/apps-sdk/build/chatgpt-ui/)\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// ✅ Good - Lightweight state\nwindow.openai.setWidgetState({ selectedId: \"item-123\", view: \"grid\" });\n\n// ❌ Bad - Will cause performance issues\nwindow.openai.setWidgetState({\n  items: largeArray,           // Don't store full datasets\n  history: conversationLog,    // Don't store conversation history\n  cache: expensiveComputation  // 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://developers.openai.com/apps-sdk/build/chatgpt-ui/)\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// MCP Server - Mark tool as widget-callable\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n  tools: [{\n    name: 'update_item',\n    description: 'Update an item',\n    inputSchema: { /* ... */ },\n    annotations: {\n      openai: {\n        outputTemplate: 'ui://widget/item.html',\n        // ✅ Required for widget-initiated calls\n        widgetCallable: true\n      }\n    }\n  }]\n}));\n\n// 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://developers.openai.com/apps-sdk/build/chatgpt-ui/)\n\n`window.openai.uploadFile()` only supports 3 image formats: `image/png`, `image/jpeg`, and `image/webp`. Other formats fail silently.\n\n```typescript\n// ✅ Supported\nwindow.openai.uploadFile({ accept: 'image/png,image/jpeg,image/webp' });\n\n// ❌ Not supported (fails silently)\nwindow.openai.uploadFile({ accept: 'application/pdf' });\nwindow.openai.uploadFile({ accept: 'text/csv' });\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://developers.openai.com/apps-sdk/deploy/troubleshooting)\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- **< 200ms**: Ideal response time\n- **200-500ms**: Acceptable but noticeable\n- **> 500ms**: Sluggish, needs optimization\n\n**Optimization Strategies**:\n```typescript\n// 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// 2. Use KV/D1 for pre-computed data\nconst cached = await env.KV.get(`result:${id}`);\nif (cached) return JSON.parse(cached);\n\n// 3. Paginate large datasets\nreturn {\n  content: [{ type: 'text', text: 'First 20 results...' }],\n  _meta: { hasMore: true, nextPage: 2 }\n};\n\n// 4. Move slow work to async tasks\n// 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/sdk@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// ❌ Old (removed in 1.25.0)\nimport { Tools } from '@modelcontextprotocol/sdk/types.js';\n\n// ✅ New (1.25.1+)\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.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// 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/sdk\": \"^1.25.3\",\n    \"hono\": \"^4.11.3\",\n    \"zod\": \"^4.3.5\"\n  },\n  \"devDependencies\": {\n    \"@cloudflare/vite-plugin\": \"^1.17.1\",\n    \"@cloudflare/workers-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://modelcontextprotocol.io/ (Latest: 2025-11-25)\n- **MCP SDK**: https://github.com/modelcontextprotocol/typescript-sdk\n- **OpenAI Apps SDK**: https://developers.openai.com/apps-sdk\n- **MCP Apps Extension (SEP-1865)**: http://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/\n- **Context7 Library ID**: /modelcontextprotocol/typescript-sdk\n\n## Production Reference\n\n**Open Source Example**: https://github.com/jezweb/chatgpt-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  - `/src/lib/mcp/server.ts` - Complete MCP handler\n  - `/src/server/tools/portfolio.ts` - Tool with widget annotations\n  - `/src/widgets/PortfolioWidget.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/CD. Includes OAuth wrapper and Python support.\n- Docs: https://developers.cloudflare.com/agents/guides/remote-mcp-server/\n- Blog: https://blog.cloudflare.com/model-context-protocol/\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://github.com/alpic-ai/skybridge\n- Docs: https://www.skybridge.tech/\n\n> **Note**: Community frameworks are not officially supported. Use at your own discretion","tags":["openai","apps","mcp","coco","rkz91","agent-skills","agents-md","ai-agents","claude-code","codex","cursor","developer-tools"],"capabilities":["skill","source-rkz91","skill-openai-apps-mcp","topic-agent-skills","topic-agents-md","topic-ai-agents","topic-claude-code","topic-codex","topic-cursor","topic-developer-tools","topic-llm-tools","topic-mcp","topic-pm-tools","topic-product-management","topic-productivity"],"categories":["coco"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/rkz91/coco/openai-apps-mcp","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add rkz91/coco","source_repo":"https://github.com/rkz91/coco","install_from":"skills.sh"}},"qualityScore":"0.453","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 7 github stars · SKILL.md body (17,533 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:14:08.036Z","embedding":null,"createdAt":"2026-05-18T13:21:40.750Z","updatedAt":"2026-05-18T19:14:08.036Z","lastSeenAt":"2026-05-18T19:14:08.036Z","tsv":"'-01':56 '-11':1733 '-123':1156 '-1865':129,1750 '-21':57 '-25':1734 '-500':1444 '/agents/guides/remote-mcp-server/':1871 '/agents/guides/remote-mcp-server/)':1020 '/alpic-ai/skybridge':1900 '/apps-sdk':1745 '/apps-sdk/build/chatgpt-ui/)':1106,1234,1340 '/apps-sdk/deploy/troubleshooting)':1413 '/callback':1059 '/jezweb/chatgpt-app-sdk':1765 '/mcp':238,323,366,374,458 '/messages':948 '/model-context-protocol/':1875 '/modelcontextprotocol/typescript-sdk':1739,1757 '/modelcontextprotocol/typescript-sdk/issues/1369)':719 '/modelcontextprotocol/typescript-sdk/issues/273)':915 '/modelcontextprotocol/typescript-sdk/issues/689)':813 '/modelcontextprotocol/typescript-sdk/pull/844).':893 '/posts/2025-11-21-mcp-apps/':1753 '/remix/sveltekit':769 '/src/lib/mcp/server.ts':1814 '/src/server/tools/portfolio.ts':1818 '/src/widgets/portfoliowidget.tsx':1823 '/sse':937 '/widgets':339,482 '1':136,442,1377,1456 '1.0.0':251 '1.17.1':173,1719 '1.24':1538 '1.25':1529,1540 '1.25.0':1612 '1.25.1':1618 '1.25.3':70,164,1712 '10':792 '100':561 '100s':428,576 '11':895 '12':663,1005 '13':123,1086 '1369':716 '14':438,1213,1829 '14x':1669 '19':1807 '2':176,463,1384,1477,1512 '2.0':110,1782 '20':1506 '200':1443 '200ms':1439 '2025':124,133,1533,1631,1732 '2026':55 '21':132 '273':912 '3':203,489,1344,1390,1496,1636 '30000':591 '30s':427,572 '310':1787 '32':1078 '4':347,524,1513,1638,1686,1805 '4.0':1625 '4.11.3':72,166,1714 '4.20260103.0':1721 '4.3.5':74,168,1716 '4.54.0':1725 '4.58.0':76 '400':903 '404':466,470 '404s':35 '4k':1090,1144 '5':353,556 '500ms':1449 '6':593 '6.5':1676 '689':810 '7':632 '7.2.4':175,1723 '7x':1673 '8':665 '844':890 '8788/callback':1051 '9':694 'abl':1249 'accept':1358,1367,1370,1446 'access':398,449,636,1825 'action':404,610 'action-ori':403,609 'activ':1895 'ad':966 'addit':1060 'affect':720 'ajv':818 'allow':235,371,1309 'altern':1372 'alway':1666 'annot':282,484,1273,1295,1822 'api':823,1792 'app':2,6,45,80,117,126,147,160,184,227,249,346,711,770,1011,1047,1054,1100,1228,1334,1408,1741,1747 'app.get':338 'app.post':322 'app.use':237,457 'appear':604 'appli':1667 'application/pdf':1368 'architectur':103 'argument':1317,1383 'array':1675 'asset':38,195,199,231,412,417,525,545,547,552 'async':259,292,324,340,516,582,865,1285,1518 'auth':1065 'authent':1596 'auto':1859 'auto-configur':1858 'avail':118 'avoid':828 'aw':930 'await':328,332,583,849,1472,1487 'b':953 'backend':1430 'bad':627,1159 'base':62 'base64':1379 'bash':139,355,1044 'behavior':1652 'best':1186,1324 'bind':39,198,230,413,526,537,546,551 'block':445,452,668,691 'blog':1872 'blog.cloudflare.com':1874 'blog.cloudflare.com/model-context-protocol/':1873 'blog.modelcontextprotocol.io':1752 'blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/':1751 'bodi':327,334 'break':699,737,899,1534,1632 'build':4,43,78,358 'built':1855 'bundl':419 'busi':1775 'business/enterprise/edu':120 'c':325,341 'c.env.assets.fetch':342 'c.json':336 'c.req.json':329 'c.req.raw':343 'cach':1179,1183,1433,1457,1461,1486,1492,1495 'cache.get':1468 'cache.has':1465 'cache.set':1474 'call':877,1207,1218,1241,1263,1304,1311,1415 'callabl':1282 'callback':1048,1055 'calltoolrequestschema':223,291,864,1621 'cannot':530 'capabl':252 'carousel':1767,1795 'catch':1693 'caus':1092,1161,1421 'cd':156 'cdns':690 'chang':1084,1535,1633 'chat':1133 'chatgpt':5,14,26,79,91,104,594,1102,1230,1336,1425,1774 'chatgpt.com':236,241,372,461 'check':747 'ci/cd':1861 'click':1844 'client':1041,1079,1589 'close':566 'cloudflar':11,60,88,142,798,833,845,932,1013,1841,1850 'cloudflare-worker-bas':59 'cloudflare/vite-plugin':172,1718 'cloudflare/workers-types':1720 'code':422,498,802,1683 'communiti':1096,1224,1330,1404,1837,1878,1904 'community-sourc':1095,1223,1329,1403 'compat':187,190 'complet':1321,1815 'compon':1238,1255 'compos':1134 'comput':1459,1483 'config':1083 'configur':177,1007,1860 'connect':558 'const':226,242,297,326,330,579,774,786,847,867,1460,1470,1485,1689 'contact':1800 'contain':989 'content':304,650,1389,1501,1700 'context':84 'context7':1754 'control':1138 'convers':1122,1177 'conversationlog':1173 'cooki':1070 'cor':33,214,239,369,443,454,459 'creat':141,204,348,1068 'credenti':1590 'critic':233,367 'csp':670,676 'custom':16,93 'd':171 'd1':1797 'data':394,585,638,645,1198,1211,1484,1692,1824 'databas':1796 'dataset':1171,1499 'decemb':1532 'default':345,1640,1664 'deno':837 'depend':58,734,1708,1710 'deploy':154,354,361,838,901,974,979,1839,1845,1846 'deprec':1660 'descript':263,402,612,1290 'devdepend':1717 'develop':25,1045,1323 'developers.cloudflare.com':1019,1870 'developers.cloudflare.com/agents/guides/remote-mcp-server/':1869 'developers.cloudflare.com/agents/guides/remote-mcp-server/)':1018 'developers.openai.com':1105,1233,1339,1412,1744 'developers.openai.com/apps-sdk':1743 'developers.openai.com/apps-sdk/build/chatgpt-ui/)':1104,1232,1338 'developers.openai.com/apps-sdk/deploy/troubleshooting)':1411 'differ':1034 'direct':677 'directori':196 'disallow':806 'discret':1914 'display':491 'dist/client':197 'dist/index.js':186 'doc':1017,1427,1868,1901 'document':439,1727 'doesn':595 'drizzl':1810 'drop':559 'e':1705 'e.g':382 'e.message':1706 'edg':825,831,836 'elicit':793,879 'elicitinput':829 'encod':1380 'encrypt':1071 'endpoint':106,447 'enhanc':1889 'env':1073 'env.kv.get':1488 'environ':929,984 'error':40,318,448,469,495,528,563,599,639,671,702,800,902,1567,1694,1696,1834 'error.errors':1656 'error.issues':1654 'error.issues.map':1704 'es2018':1558 'es2020':1554 'evalerror':801 'event':589 'everi':426,571 'exampl':1762 'exceed':1416 'execut':289 'expect':1642 'expens':1458 'expensivecomput':1180 'expensiveoper':1473 'export':344,1545 'extend':13,90,740 'extens':27,127,1748 'extern':1392 'fail':748,796,1219,1264,1353,1364 'fals':155 'faster':1670,1674,1678 'featur':1569 'fetch':451,533,1197 'fetcher':232,553 'file':1326,1375,1813 'filter':1196 'first':1505 'fix':456,476,504,536,568,607,643,678,725,755,773,886,992 'flag':188,1222,1262 'focus':1881 'follow':1525 'follow-up':1524 'form':1801 'formal':130 'format':1346,1352 'found':468,472,906 'framework':738,1876,1882,1905 'full':1170,1778 'ga':121 'generat':803 'get':936 'git':153 'github':714,808,910,1897 'github.com':718,812,892,914,1738,1764,1899 'github.com/alpic-ai/skybridge':1898 'github.com/jezweb/chatgpt-app-sdk':1763 'github.com/modelcontextprotocol/typescript-sdk':1737 'github.com/modelcontextprotocol/typescript-sdk/issues/1369)':717 'github.com/modelcontextprotocol/typescript-sdk/issues/273)':913 'github.com/modelcontextprotocol/typescript-sdk/issues/689)':811 'github.com/modelcontextprotocol/typescript-sdk/pull/844).':891 'global':696 'global.response':736 'good':614,1150 'greet':272 'grid':1158 'handl':941 'handler':112,709,1817 'happen':730,816,918,1023,1109,1237 'hasmor':1509 'heartbeat':425,570,580,588 'hello':150,262,296,308 'hello-world':149 'helper':1892 'hex':1077 'histori':1172,1178 'hmr':1884 'hono':64,71,165,210,212,229,695,731,1713,1804 'hono-rout':63 'hono/cors':216 'html':115,351,391,496,692 'html/js':21,98 'http':964,996 'hundr':1419 'id':1318,1490,1756 'ideal':1440 'ifram':102 'imag':1345 'image/jpeg':1348,1360 'image/png':1347,1359 'image/webp':1350,1361 'immedi':1521 'implement':28,1432,1783 'import':209,213,217,221,782,1210,1602,1607,1613,1619 'in-memori':922,957 'inact':577 'includ':1862 'incorrect':1563 'index.ts':554 'initi':637,935,1216,1239,1252,1303 'initialdata':311,659 'inlin':680 'input':795,1643 'inputschema':273,1294 'instal':138,162,170 'instanc':943,952,1118 'instanceof':746,1697 'instead':500,758,862,969,1135 'integr':1790 'interact':19,96 'intern':817 'introduc':994 'issu':433,440,441,462,488,523,555,592,631,664,693,715,791,809,894,911,1004,1085,1094,1163,1212,1831 'item':1155,1165,1193,1289,1293,1316 'itemid':1319 'join':1707 'json':108,1709,1780 'json-rpc':107,1779 'json.parse':1494 'json.stringify':586 'jsonc':179,544 'juli':1630 'keep':1140 'key':1072,1466,1469,1475,1812 'known':432,1830 'kv':1062 'kv/d1':1479 'lack':955 'lambda':931 'land':950 'larg':1184,1498 'largearray':1166 'last':53 'latest':67,143,1731 'librari':1755 'lightweight':1151 'limit':1328 'line':1788 'listresourcesrequestschema':515 'listtoolsrequestschema':222,258,1284,1620 'live':1769 'load':352,674 'localhost':1030,1050 'locat':623,660 'long':987,1573 'long-run':986,1572 'loos':1543,1600 'm2m':1591 'machin':1593,1595 'machine-to-machin':1592 'main':185,1132 'maintain':1896 'major':1628 'manual':1069 'map':626,630,657,1463 'mark':1247,1267,1277 'match':201,542 'may':939 'mcp':3,8,29,48,82,105,125,193,205,446,732,761,882,1015,1200,1258,1275,1527,1728,1735,1746,1776,1816,1847,1890 'mcpserver':243 'mcpserver.handlerequest':333 'mcpserver.setrequesthandler':257,290 'memori':924,959 'merg':1657 'messag':1123 'meta':310,658,1508 'meta.initialdata':397,647 'migrat':1597,1626,1680 'millisecond':1420 'mime':36,384,505 'mimetyp':520 'mode':1195 'model':83,1586 'modelcontextprotocol.io':1730 'modelcontextprotocol/inspector':363 'modelcontextprotocol/sdk':69,163,1537,1711 'modelcontextprotocol/sdk/server/index.js':220,785 'modelcontextprotocol/sdk/types.js':225,1616,1623 'mount':1204 'move':1514 'ms':1445 'must':200,234,370,378,386,507,541 'my-app':247 'my-app.workers.dev':365 'my-app.workers.dev/mcp':364 'my-mcp-server.workers.dev':1058 'my-mcp-server.workers.dev/callback':1057 'my-openai-app':144,157,181 'name':180,246,261,277,281,298,301,309,312,538,855,868,871,1287,1314 'namespac':1063 'need':1038,1451 'never':603 'new':228,244,317,776,1462,1568,1617 'next.js':41,700,710,742,768 'next.js/remix/sveltekit':767 'nextpag':1511 'nextrespons':745 'nodej':189 'non':982 'non-serverless':981 'note':1627,1903 'notic':1448 'nov':122,131 'npm':140,161,169,356 'npx':359,362 'oauth':1006,1024,1040,1046,1053,1588,1863 'object':275,1679 'offici':990,1426,1726,1908 'old':1609,1651 'one':1121,1843 'one-click':1842 'open':1760 'openai':1,44,146,159,183,283,485,1099,1227,1296,1333,1407,1740 'openssl':1075 'oper':1436,1575 'optim':1147,1452,1453 'option':66,1661 'orient':405,611 'origin':240,460,685 'orm':1811 'output':1646 'outputtempl':284,486,1297 'overrid':698 'overview':77 'overwrit':735 'pagin':1497 'paramet':861 'pars':1672 'parti':689 'pass':313,395,644,1398 'past':1387 'pattern':1826 'payload':1142 'perform':1093,1148,1162,1401,1437,1668 'permiss':1221 'persist':1112,1209 'ping':590 'plain':493 'platform':826,832 'polici':444,455 'port':765 'portfolio':1766 'post':947 'pr':889 'practic':1187,1325 'pre':1482,1854 'pre-built':1853 'pre-comput':1481 'prefault':1649 'prefix':381,479 'prevent':434,437,749,827,961,1043,1139,1266,1832 'previous':1557 'product':51,1032,1052,1758,1771,1836 'profil':1429 'progress':1577 'prohibit':822 'prompt':851,1546 'proper':887 'properti':276,532,1662 'protocol':85 'provid':874,1025 'python':1866 'quick':134 'r2':1396 'rand':1076 'react':1794,1806,1880 'react-focus':1879 'read':531 'readi':52 'recommend':753,1000,1428 'redirect':1027 'refer':1759 'refus':672 'regist':601 'registr':256,1042 'reli':920 'remix':743 'remot':1014 'remov':1542,1610 'render':100,502,1772 'request':293,866,938,949,1385,1585 'request.params.arguments':299,869 'request.params.name':295,321 'requir':191,280,368,881,1008,1061,1081,1244,1299,1556 'reset':1126 'resourc':114,254,392,517,1547,1786,1838 'respons':331,337,697,704,1441 'restart':1080 'result':1185,1471,1476,1489,1507 'return':303,335,465,641,649,706,1467,1493,1500,1520,1699 'root':1548 'rout':65,375,708 'router':712 'rpc':109,1781 'run':357,760,988,1574 's3':1395 'same-origin':683 'sampl':1549,1579,1587 'scaffold':137 'schema':856,1553,1564,1606 'schema.parse':1691 'script':667,675,681,686 'sdk':194,733,883,1101,1229,1335,1409,1528,1736,1742 'second':562 'see':270,621 'select':1192 'selectedid':1154 'send':424,569 'sep':128,1749 'separ':764,1010,1039 'serv':414 'server':9,30,49,86,206,218,245,762,1016,1201,1259,1276,1777,1848,1891 'server.elicitinput':850 'server.setrequesthandler':514,863,1283 'serverless':900,928,973,983,1003 'servic':1394 'session':925 'sessionid':908 'sessionidgener':778,789 'setinterv':581 'setrequesthandl':1559 'sf':661 'show':628 'silent':1265,1354,1365 'singl':1116 'skill':436 'skill-openai-apps-mcp' 'skybridg':389,510,522,1877 'slow':1435,1515 'sluggish':1423,1450 'solut':975 'sourc':497,713,807,909,1012,1097,1098,1225,1226,1331,1332,1405,1406,1761 'source-rkz91' 'specif':1552,1605,1729 'sqlite':1798 'src/index.ts':207 'src/widgets/hello.html':350 'sse':423,557,564,896,971,978 'sseservertransport':919 'stack':1803 'standard':1001 'start':135 'state':898,960,977,1066,1088,1111,1124,1141,1152,1191 'stateless':47 'status':50,116,880,991,1320 'storag':926,1067 'store':1169,1176,1188 'strategi':1454 'stream':565 'stream.writesse':584 'streamabl':963,995 'streamablehttpservertransport':777 'strict':1029 'string':279,302,805,858,872,1671 'submiss':1802 'submit':1129 'subsequ':946 'suggest':597,606 'superrefin':1659 'support':1343,1356,1363,1867,1885,1909 'sveltekit':744 'tailwind':1808 'target':1402,1438,1555 'task':1519,1570 'templat':1856 'text':306,307,494,652,653,1388,1503,1504,1702,1703 'text/csv':1371 'text/html':388,509,512,521 'third':688 'third-parti':687 'throw':316,1565 'tie':1119 'time':1442 'timeout':429,574 'token':1091,1145 'tool':17,94,111,253,255,260,288,320,401,598,600,860,876,1206,1217,1240,1246,1268,1278,1286,1312,1382,1400,1414,1550,1581,1583,1614,1785,1819,1840 'topic-agent-skills' 'topic-agents-md' 'topic-ai-agents' 'topic-claude-code' 'topic-codex' 'topic-cursor' 'topic-developer-tools' 'topic-llm-tools' 'topic-mcp' 'topic-pm-tools' 'topic-product-management' 'topic-productivity' 'track':888,1578 'transport':775,787,897,905,965 'tri':1688 'troubleshoot':32,1410 'true':1271,1306,1510 'ts':152 'two':1009 'type':37,148,274,278,305,385,506,550,587,651,857,1376,1502,1544,1566,1601,1644,1647,1701 'typeerror':529 'typesaf':1562 'typescript':202,208,483,513,543,549,578,613,648,771,840,1149,1274,1355,1455,1608,1684 'ui':22,99,1103,1190,1231,1337,1422 'undefin':527,535,642,779,790 'unexpect':567 'unknown':319 'unoffici':1893 'updat':54,1288,1291,1315,1522,1531,1603,1629,1681 'upgrad':750 'upload':1327,1393 'uri':287,377,518 'url':475,1028,1035,1049,1056,1399 'use':23,81,264,379,406,477,608,615,679,756,821,843,859,962,1205,1378,1391,1478,1551,1599,1648,1910 'user':267,409,618,794,873,1128,1386 'userinput':848 'v1.24':997 'v1.24.0':968,1571,1582 'v1.25.0':722 'v1.25.0-1.25.2':701,780 'v1.25.2':724 'v1.25.3':727,752,772 'v2':884 'v4':1809 'v6':819 'valid':820,1026,1682,1690 'var':1074 'vercel':835 'verifi':1827 'version':68,250,721 'via':396,399,646,875,1130,1242,1523 'view':1157,1194 'visibl':499 'vite':174,1722 'vps':985 'want':268,410,619 'webstandardstreamablehttpservertransport':757,783,788 'widget':20,34,97,113,286,315,349,376,380,393,415,464,474,478,490,503,633,666,1087,1110,1117,1137,1203,1215,1281,1302,1307,1322,1768,1789,1821,1887 'widget-cal':1280 'widget-initi':1214,1301 'widget/hello.html':285 'widget/item.html':1298 'widget/map.html':383,487,519 'widgetcal':1270,1305 'window.openai.calltool':1243,1313 'window.openai.getinitialdata':400,640 'window.openai.setwidgetstate':1153,1164 'window.openai.tooloutput':1793 'window.openai.uploadfile':1341,1357,1366,1369 'without':1220,1260 'wordpress':1791 'work':1516 'workaround':781,839 'worker':12,61,89,421,431,573,799,834,846,933,1851 'world':151 'wrangler':75,360,1724 'wrangler.jsonc':178,540,548 'wrapper':1864 'www.skybridge.tech':1902 'x':1530,1539,1541,1637,1639,1677,1687 'z.zoderror':1698 'zero':1833 'zod':73,167,1624,1635,1685,1715 'zoderror':1653 'zoom':662","prices":[{"id":"c65516ee-9b75-4bfc-9ea8-47e0ab17c32c","listingId":"75b00874-001b-4e61-a892-1e0a910e9c35","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"rkz91","category":"coco","install_from":"skills.sh"},"createdAt":"2026-05-18T13:21:40.750Z"}],"sources":[{"listingId":"75b00874-001b-4e61-a892-1e0a910e9c35","source":"github","sourceId":"rkz91/coco/openai-apps-mcp","sourceUrl":"https://github.com/rkz91/coco/tree/main/skills/openai-apps-mcp","isPrimary":false,"firstSeenAt":"2026-05-18T13:21:40.750Z","lastSeenAt":"2026-05-18T19:14:08.036Z"}],"details":{"listingId":"75b00874-001b-4e61-a892-1e0a910e9c35","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"rkz91","slug":"openai-apps-mcp","github":{"repo":"rkz91/coco","stars":7,"topics":["agent-skills","agents-md","ai","ai-agents","claude-code","codex","cursor","developer-tools","llm-tools","mcp","pm-tools","product-management","productivity","prompt-engineering","workflow-automation"],"license":"mit","html_url":"https://github.com/rkz91/coco","pushed_at":"2026-04-26T01:51:27Z","description":"Open-source library of AI superpowers — 59 skills, 34 commands, 10 agents + 24 GSD subagents, 3 system bundles. An entire team, wherever your AI lives. Vendor-neutral across Claude Code, Cursor, Codex, and any AGENTS.md tool.","skill_md_sha":"4f9f3fc997d85fbf9e6a8f7a796f4eb51f3df11d","skill_md_path":"skills/openai-apps-mcp/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/rkz91/coco/tree/main/skills/openai-apps-mcp"},"layout":"multi","source":"github","category":"coco","frontmatter":{"name":"OpenAI Apps MCP","description":"Build ChatGPT apps with MCP servers on Cloudflare Workers. Extend ChatGPT with custom tools and interactive widgets (HTML/JS 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."},"skills_sh_url":"https://skills.sh/rkz91/coco/openai-apps-mcp"},"updatedAt":"2026-05-18T19:14:08.036Z"}}