create-rule
Create Cursor rules for persistent AI guidance. Use when the user wants to create a rule, add coding standards, set up project…
Build ChatGPT apps with MCP servers on Cloudflare Workers. Extend ChatGPT with custom tools and interactive widgets (HTML/JS UI). Use when: developing ChatGPT extensions, implementing MCP servers, or troubleshooting CORS, widget 404s, MIME types, ASSETS binding errors, Next.js
Claude Code
$ npx -y skills add coco-research/coco --skill openai-apps-mcp --agent claude-code
Codex
$ npx skills add coco-research/coco --skill openai-apps-mcp -a codex -y
Cursor
$ npx skills add coco-research/coco --skill openai-apps-mcp -a cursor -y
Gemini CLI
$ npx skills add coco-research/coco --skill openai-apps-mcp -a gemini-cli -y
GitHub Copilot
$ npx skills add coco-research/coco --skill openai-apps-mcp -a github-copilot -y
OpenCode
$ npx skills add coco-research/coco --skill openai-apps-mcp -a opencode -y
Windsurf
$ npx skills add coco-research/coco --skill openai-apps-mcp -a windsurf -y
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). 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. user-invocable: true allowed-tools: [Read, Write, Edit, Bash, Glob, Grep] domain: engineering
**Status**: Production Ready **Last Updated**: 2026-01-21 **Dependencies**: `cloudflare-worker-base`, `hono-routing` (optional) **Latest Versions**: @modelcontextprotocol/sdk@1.25.3, hono@4.11.3, zod@4.3.5, wrangler@4.58.0
---
Build **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).
**Architecture**: ChatGPT → MCP endpoint (JSON-RPC 2.0) → Tool handlers → Widget resources (HTML)
**Status**: Apps available to Business/Enterprise/Edu (GA Nov 13, 2025). MCP Apps Extension (SEP-1865) formalized Nov 21, 2025.
---
npm create cloudflare@latest my-openai-app -- --type hello-world --ts --git --deploy false cd my-openai-app npm install @modelcontextprotocol/sdk@1.25.3 hono@4.11.3 zod@4.3.5 npm install -D @cloudflare/vite-plugin@1.17.1 vite@7.2.4
{
"name": "my-openai-app",
"main": "dist/index.js",
"compatibility_flags": ["nodejs_compat"], // Required for MCP SDK
"assets": {
"directory": "dist/client",
"binding": "ASSETS" // Must match TypeScript
}
}import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
const app = new Hono<{ Bindings: { ASSETS: Fetcher } }>();
// CRITICAL: Must allow chatgpt.com
app.use('/mcp/*', cors({ origin: 'https://chatgpt.com' }));
const mcpServer = new Server(
{ name: 'my-app', version: '1.0.0' },
{ capabilities: { tools: {}, resources: {} } }
);
// Tool registration
mcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: 'hello',
description: 'Use this when user wants to see a greeting',
inputSchema: {
type: 'object',
properties: { name: { type: 'string' } },
required: ['name']
},
annotations: {
openai: { outputTemplate: 'ui://widget/hello.html' } // Widget URI
}
}]
}));
// Tool execution
mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'hello') {
const { name } = request.params.arguments as { name: string };
return {
content: [{ type: 'text', text: `Hello, ${name}!` }],
_meta: { initialData: { name } } // Passed to widget
};
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
app.post('/mcp', async (c) => {
const body = await c.req.json();
const response = await mcpServer.handleRequest(body);
return c.json(response);
});
app.get('/widgets/*', async (c) => c.env.ASSETS.fetch(c.req.raw));
export default app;<!DOCTYPE html>
<html>
<head>
<style>
body { margin: 0; padding: 20px; font-family: system-ui; }
</style>
</head>
<body>
<div id="greeting">Loading...</div>
<script>
if (window.openai && window.openai.getInitialData) {
const data = window.openai.getInitialData();
document.getElementById('greeting').textContent = `Hello, ${data.name}! 👋`;
}
</script>
</body>
</html>npm run build npx wrangler deploy npx @modelcontextprotocol/inspector https://my-app.workers.dev/mcp
---
**CORS**: Must allow `https://chatgpt.com` on `/mcp/*` routes **Widget URI**: Must use `ui://widget/` prefix (e.g., `ui://widget/map.html`) **MIME Type**: Must be `text/html+skybridge` for HTML resources **Widget Data**: Pass via `_meta.initialData` (accessed via `window.openai.getInitialData()`) **Tool Descriptions**: Action-oriented ("Use this when user wants to...") **ASSETS Binding**: Serve widgets from ASSETS, not bundled in worker code **SSE**: Send heartbeat every 30s (100s timeout on Workers)
---
This skill prevents **14** documented issues:
**Error**: `Access to fetch blocked by CORS policy` **Fix**: `app.use('/mcp/*', cors({ origin: 'https://chatgpt.com' }))`
**Error**: `404 (Not Found)` for widget URL **Fix**: Use `ui://widget/` prefix (not `resource://` or `/widgets/`)
annotations: { openai: { outputTemplate: 'ui://widget/map.html' } }**Error**: HTML source code visible instead of rendered widget **Fix**: MIME type must be `text/html+skybridge` (not `text/html`)
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [{ uri: 'ui://widget/map.html', mimeType: 'text/html+skybridge' }]
}));**Error**: `TypeError: Cannot read property 'fetch' of undefined` **Fix**: Binding name in wrangler.jsonc must match TypeScript
{ "assets": { "binding": "ASSETS" } } // wrangler.jsonctype Bindings = { ASSETS: Fetcher }; // index.ts**Error**: SSE stream closes unexpectedly **Fix**: Send heartbeat every 30s (Workers timeout at 100s inactivity)
const heartbeat = setInterval(async () => {
await stream.writeSSE({ data: JSON.stringify({ type: 'heartbeat' }), event: 'ping' });
}, 30000);495 personas across 13 departments, 229 skills[^installed] and 388 commands in the full catalog (coco 1.9.0, 2026-10-21). Agent skills, slash commands and subagents for Claude Code, Cursor, Codex and other AI coding agents.
Create Cursor rules for persistent AI guidance. Use when the user wants to create a rule, add coding standards, set up project…
Guides users through creating effective Agent Skills for Cursor. Use when the user wants to create, write, or author a new skill,…
Create custom subagents for specialized AI tasks. Use when the user wants to create a new type of subagent, set up task-specific…
Convert 'Applied intelligently' Cursor rules (.cursor/rules/*.mdc) and slash commands (.cursor/commands/*.md) to Agent Skills…
Modify Cursor/VSCode user settings in settings.json. Use when the user wants to change editor settings, preferences,…
Train and optimize AI agents using Microsoft's Agent Lightning framework with reinforcement learning. Use when setting up agent…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.