Help us improve
Share bugs, ideas, or general feedback.
From openai
This skill should be used when the user asks to "build a ChatGPT app", "create a ChatGPT plugin", "implement MCP server for ChatGPT", "build ChatGPT UI widget", "add OAuth to ChatGPT app", "use window.openai API", "create ChatGPT component", "implement tool for ChatGPT", or mentions ChatGPT Apps SDK, skybridge, or widget development for ChatGPT.
npx claudepluginhub tarqd/skills --plugin openaiHow this skill is triggered — by the user, by Claude, or both
Slash command
/openai:chatgpt-app-developmentThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Build apps that extend ChatGPT using the Apps SDK. Apps combine an MCP server (tools and data), UI widgets (rendered in iframes), and structured responses that the model can read and act upon.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Create and present web-based slidedecks using Slidev with Markdown, Vue components, code highlighting, animations, interactive demos, LaTeX, and Mermaid for technical presentations, conference talks, code walkthroughs, and teaching materials.
Share bugs, ideas, or general feedback.
Build apps that extend ChatGPT using the Apps SDK. Apps combine an MCP server (tools and data), UI widgets (rendered in iframes), and structured responses that the model can read and act upon.
A ChatGPT app has three components:
Data flows: User request → Model selects tool → MCP server executes → Returns structuredContent + _meta → Widget renders UI
Register tools with JSON schemas and metadata:
server.registerTool(
"show-board",
{
title: "Show Kanban Board",
inputSchema: { workspace: z.string() },
_meta: {
"openai/outputTemplate": "ui://widget/board.html",
"openai/toolInvocation/invoking": "Loading board...",
"openai/toolInvocation/invoked": "Board ready"
}
},
async ({ workspace }) => ({
structuredContent: boardSummary, // Model + widget sees this
content: [{ type: "text", text: "Drag cards to update" }],
_meta: { fullData: allTasks } // Widget-only, hidden from model
})
);
structuredContent - Concise JSON for model and widget (keep under 4k tokens)content - Optional markdown/text narration_meta - Large or sensitive data for widget only (model never sees this)Required annotations for ChatGPT's safety system:
{
"annotations": {
"readOnlyHint": true, // Required: read-only operation
"destructiveHint": false, // Required: deletes/overwrites data
"openWorldHint": false // Required: publishes externally
}
}
Register UI templates with text/html+skybridge MIME type:
server.registerResource(
"widget",
"ui://widget/board.html",
{},
async () => ({
contents: [{
uri: "ui://widget/board.html",
mimeType: "text/html+skybridge",
text: `<div id="root"></div><script type="module" src="..."></script>`,
_meta: {
"openai/widgetPrefersBorder": true,
"openai/widgetCSP": {
connect_domains: ["https://api.example.com"],
resource_domains: ["https://*.oaistatic.com"]
}
}
}]
})
);
window.openai APIWidgets communicate with ChatGPT through the injected window.openai bridge:
Reading Data:
window.openai.toolInput - Arguments passed to the toolwindow.openai.toolOutput - The structuredContent from serverwindow.openai.toolResponseMetadata - The _meta payloadwindow.openai.widgetState - Persisted UI stateActions:
callTool(name, args) - Invoke another MCP toolsendFollowUpMessage({ prompt }) - Insert a message into conversationuploadFile(file) - Upload PNG/JPEG/WebP imagesgetFileDownloadUrl({ fileId }) - Get temporary download URLrequestDisplayMode("pip" | "fullscreen") - Change layoutsetWidgetState(state) - Persist UI state (shown to model)Context:
theme - Current visual themelocale - User's BCP 47 localedisplayMode - Current display contextmaxHeight - Maximum widget heightThree state categories:
| Type | Owner | Lifetime | Example |
|---|---|---|---|
| Business | MCP Server | Persistent | Tasks, documents |
| UI | Widget | Message-scoped | Selected items, expanded panels |
| Cross-session | Your backend | Persistent | Saved filters, preferences |
Persist UI state with setWidgetState() - anything passed is visible to the model. Keep payloads under 4k tokens.
Request layout changes with requestDisplayMode():
Mobile may coerce pip to fullscreen.
Apps requiring user data must implement OAuth 2.1:
/.well-known/oauth-protected-resource:{
"resource": "https://api.example.com",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["read", "write"]
}
"_meta": {
"securitySchemes": [
{ "type": "oauth2", "scopes": ["read", "write"] }
]
}
Use noauth for anonymous access, oauth2 for authenticated endpoints.
The MCP server must verify every request:
iss, aud, exp, nbf claims401 with WWW-Authenticate header on failureTrigger OAuth UI by returning _meta["mcp/www_authenticate"] with error details.
structuredContent, content, _meta, or widget stateuserAgent or userLocation for authorizationdestructiveHint: trueesbuild src/component.tsx --bundle --format=esm --outfile=dist/component.js
node dist/index.jsngrok http 3000http://localhost:3000/mcpDeploy to HTTPS hosts with low latency (Cloudflare Workers, Vercel, Fly.io). ChatGPT requires HTTPS for all connections.
Apps must:
Prohibited: digital subscriptions, adult content, gambling, counterfeit goods, malware.
For detailed implementation guidance:
references/window-openai-api.md - Complete window.openai API referencereferences/mcp-implementation.md - MCP server setup and tool patternsreferences/authentication.md - OAuth 2.1 implementation details