ChatGPT Apps
Overview
Scaffold ChatGPT Apps SDK implementations with a docs-first, example-first workflow, then generate code that follows current Apps SDK and MCP Apps bridge patterns.
This skill produces:
- A primary app-archetype classification and repo-shape decision
- A tool plan (names, schemas, annotations, outputs)
- An upstream starting-point recommendation (official example, ext-apps example, or local fallback scaffold)
- An MCP server scaffold (resource registration, tool handlers, metadata)
- A widget scaffold (MCP Apps bridge first,
window.openai compatibility/extensions second)
- A reusable Node +
@modelcontextprotocol/ext-apps starter scaffold for low-dependency fallbacks
- A validation report against the minimum working repo contract
- Local dev and connector setup steps
- A short stakeholder summary of what the app does (when requested)
When to Use
Use this skill when the task involves any of the following:
- Scaffolding a new ChatGPT App from scratch or adapting an upstream example
- Designing or refactoring MCP tools for a ChatGPT App (names, schemas, annotations,
structuredContent, _meta)
- Wiring the MCP Apps bridge in a widget (
ui/notifications/tool-result, tools/call, ui/update-model-context)
- Applying
window.openai compatibility APIs (file upload, modal, display mode, theme, locale)
- Setting resource metadata and security (
_meta.ui.csp, _meta.ui.domain, connectDomains, resourceDomains)
- Preparing an app for local testing in ChatGPT Developer Mode via HTTPS tunnel
- Preparing an app for public submission to the ChatGPT app directory
- Troubleshooting tool descriptors, widget rendering, CSP violations, or bridge communication
Trigger keywords: ChatGPT app, Apps SDK, MCP Apps bridge, widget, ext-apps, tool annotations, structuredContent, submission, connector, window.openai, MCP server scaffold.
Prerequisites
- Node.js (v18+ recommended) installed and available on
PATH
- PowerShell as the primary shell on the Windows host (
~\agent-skills\library\chatgpt-apps\)
$openai-docs skill or the OpenAI developer docs MCP server (mcp__openaiDeveloperDocs__search_openai_docs, mcp__openaiDeveloperDocs__fetch_openai_doc) for docs-first workflow
- ngrok or equivalent HTTPS tunneling tool for local ChatGPT testing
- ChatGPT Developer Mode access (Settings → Apps & Connectors → Advanced settings)
@modelcontextprotocol/ext-apps package when using the fallback scaffold or ext-apps examples
Procedure
0. Mandatory Docs-First Workflow
Before writing any code, fetch current Apps SDK documentation.
- Invoke
$openai-docs (preferred) or call the OpenAI docs MCP server directly.
- Fetch current Apps SDK docs before writing code, especially these baseline pages:
apps-sdk/build/mcp-server
apps-sdk/build/chatgpt-ui
apps-sdk/build/examples
apps-sdk/plan/tools
apps-sdk/reference
- Fetch
apps-sdk/quickstart when scaffolding a new app or generating a first-pass implementation. Check the official examples repo/page before inventing a scaffold from scratch.
- Fetch deployment/submission docs when the task includes local ChatGPT testing, hosting, or public launch:
apps-sdk/deploy
apps-sdk/deploy/submission
apps-sdk/app-submission-guidelines
- Cite the docs URLs you used when explaining design choices or generated scaffolds.
- Prefer current docs guidance over older repo patterns when they differ, and call out compatibility aliases explicitly.
- If doc search times out or returns poor matches, fetch the canonical Apps SDK pages directly by URL and continue. Do not let search failure block scaffolding.
Load these reference files at specific points:
| Reference File |
When to Load |
references/apps-sdk-docs-workflow.md |
At the start, for suggested doc queries and a compact checklist |
references/app-archetypes.md |
Before choosing examples or scaffolds, to classify the request into a supported app shape |
references/repo-contract-and-validation.md |
When generating or reviewing a repo, to keep output inside a stable "working app" contract |
references/search-fetch-standard.md |
When the app is connector-like, data-only, sync-oriented, or meant to work with company knowledge or deep research |
references/upstream-example-workflow.md |
When starting a greenfield app or deciding whether to adapt an upstream example or use the local fallback scaffold |
references/window-openai-patterns.md |
When the task needs ChatGPT-specific widget behavior or when translating repo examples that use wrapper-specific app.* helpers |
references/interactive-state-sync-patterns.md |
When the app has long-lived widget state, repeated interactions, or component-initiated tool calls (games, boards, maps, dashboards, editors) |
1. Classify the App Archetype
Before choosing examples, repo shape, or scaffolds, classify the request into one primary archetype and state it explicitly.
tool-only — no UI, just MCP tools
vanilla-widget — minimal HTML widget, MCP Apps bridge
react-widget — React widget with @modelcontextprotocol/ext-apps/react
interactive-decoupled — decoupled data/render tools, long-lived state
submission-ready — public directory launch target
Infer the archetype unless a missing detail is truly blocking. Use the archetype to choose:
- Whether a UI is needed at all
- Whether to preserve a split
server/ + web/ layout
- Whether to prefer official OpenAI examples, ext-apps examples, or the local fallback scaffold
- Which validation checks matter most
- Whether
search and fetch should be the default read-only tool surface
Escalate to submission-ready only when the user asks for public launch, directory submission, or review-ready deployment.
Load references/app-archetypes.md for the full decision rubric.
2. Plan Tools Before Code
Define the tool surface area from user intents.
- Use one job per tool.
- Write tool descriptions that start with "Use this when..." behavior cues.
- Make inputs explicit and machine-friendly (enums, required fields, bounds).
- Decide whether each tool is data-only, render-only, or both.
- Set annotations accurately:
readOnlyHint
destructiveHint
openWorldHint
idempotentHint (when true)
- If the app is connector-like, data-only, sync-oriented, or intended for company knowledge or deep research, default to the standard
search and fetch tools instead of inventing custom read-only equivalents.
- For educational/demo apps, prefer one concept per tool so the model can pick the right example cleanly.
- Group demo tools by learning objective: data into the widget, widget actions back into the conversation or tools, host/layout environment signals, and lifecycle/streaming behavior.
Load references/search-fetch-standard.md when search and fetch may be relevant.
3. Choose an App Architecture
Choose the simplest structure that fits the goal.
- Minimal demo pattern — for quick prototypes, workshops, or proofs of concept.
- Decoupled data/render pattern — for production UX so the widget does not re-render on every tool call.
Prefer the decoupled pattern for non-trivial apps:
- Data tools return reusable
structuredContent.
- Render tools attach
_meta.ui.resourceUri and optional _meta["openai/outputTemplate"].
- Render tool descriptions state prerequisites (for example, "Call
search first").
3a. Choose a Starting Point
For greenfield apps, prefer these starting points in order:
- Official OpenAI examples — when a close example already matches the requested stack or interaction pattern.
- Version-matched
@modelcontextprotocol/ext-apps examples — when the user needs a lower-level or more portable MCP Apps baseline.
scripts/scaffold_node_ext_apps.mjs — only when no close example fits, the user wants a tiny Node + vanilla starter, or network access/example retrieval is undesirable.
Do not generate a large custom scaffold from scratch if a close upstream example already exists. Copy the smallest matching example, remove unrelated demo code, then patch it to the current docs and the user request.
Load references/upstream-example-workflow.md for the selection and adaptation rubric.
3b. Use the Starter Script (Low-Dependency Fallback)
Use scripts/scaffold_node_ext_apps.mjs only when:
- The user wants a quick, greenfield Node starter
- A vanilla HTML widget is acceptable
- No upstream example is a better starting point
Skip it when:
- A close official example exists
- The user already has an existing app structure
- The user needs a non-Node stack
- The user explicitly wants React first
- The user only wants a plan/review instead of code
Run it only after fetching current docs, then reconcile the generated files with the docs you fetched.
node scripts/scaffold_node_ext_apps.mjs
The script generates a minimal @modelcontextprotocol/ext-apps server plus a vanilla HTML widget that uses the MCP Apps bridge by default. The generated widget keeps follow-up messaging on the standard ui/message bridge and only uses window.openai for optional host signals/extensions.
After running it, patch the generated output to match the current docs and the user request: adjust tool names/descriptions, annotations, resource metadata, URI versioning, and README/run instructions.
4. Scaffold the MCP Server
Generate a server that:
- Registers a widget resource/template with the MCP Apps UI MIME type (
text/html;profile=mcp-app) or the SDK constant (RESOURCE_MIME_TYPE) when using @modelcontextprotocol/ext-apps/server.
- Registers tools with clear names, schemas, titles, and descriptions.
- Returns
structuredContent (model + widget), content (model narration), and _meta (widget-only data) intentionally.
- Keeps handlers idempotent or documents non-idempotent behavior explicitly.
- Includes tool status strings (
openai/toolInvocation/*) when helpful in ChatGPT.
Keep structuredContent concise. Move large or sensitive widget-only payloads to _meta.
5. Scaffold the Widget UI
Use the MCP Apps bridge first for portability, then add ChatGPT-specific window.openai APIs when they materially improve UX.
- Listen for
ui/notifications/tool-result (JSON-RPC over postMessage).
- Render from
structuredContent.
- Use
tools/call for component-initiated tool calls.
- Use
ui/update-model-context only when UI state should change what the model sees.
Use window.openai for compatibility and extensions (file upload, modal, display mode, etc.), not as the only integration path for new apps.
API Surface Guardrails
Some examples wrap the bridge with an app object (for example, @modelcontextprotocol/ext-apps/react) and expose helper names like app.sendMessage(), app.callServerTool(), app.openLink(), or host getter methods.
- Treat those wrappers as implementation details or convenience layers, not the canonical public API to teach by default.
- For ChatGPT-facing guidance, prefer the current documented surface:
window.openai.callTool(...)
window.openai.sendFollowUpMessage(...)
window.openai.openExternal(...)
window.openai.requestDisplayMode(...)
- Direct globals:
window.openai.theme, window.openai.locale, window.openai.displayMode, window.openai.toolInput, window.openai.toolOutput, window.openai.toolResponseMetadata, window.openai.widgetState
- If you reference wrapper helpers from repo examples, map them back to the documented
window.openai or MCP Apps bridge primitives and call out that the wrapper is not the normative API surface.
Load references/window-openai-patterns.md for the wrapper-to-canonical mapping and for React helper extraction patterns.
6. Add Resource Metadata and Security
Set resource metadata deliberately on the widget resource/template:
_meta.ui.csp with exact connectDomains and resourceDomains.
_meta.ui.domain for app submission-ready deployments.
_meta.ui.prefersBorder (or OpenAI compatibility alias when needed).
- Optional
openai/widgetDescription to reduce redundant narration.
Avoid frameDomains unless iframe embeds are core to the product.
7. Enforce the Minimum Working Repo Contract
Every generated repo should satisfy a small, stable contract before you consider it done.
- The repo shape matches the chosen archetype.
- The MCP server and tools are wired to a reachable
/mcp endpoint.
- Tools have clear descriptions, accurate annotations, and UI metadata where needed.
- Connector-like, data-only, sync-oriented, and company-knowledge-style apps use the standard
search and fetch tool shapes when relevant.
- The widget uses the MCP Apps bridge correctly when a UI exists.
- The repo includes enough scripts or commands for a user to run and check it locally.
- The response explicitly says what validation was run and what was not run.
Load references/repo-contract-and-validation.md for the detailed checklist and validation ladder.
8. Validate the Local Loop
Validate against the minimum working repo contract, not just "did files get created."
Low-cost checks first:
- Static contract review.
- Syntax or compile checks when feasible.
- Local
/mcp health check when feasible.
# Example: check if the MCP server responds on the expected endpoint
curl http://localhost:3000/mcp
Runtime checks next:
- Verify tool descriptors and widget rendering in MCP Inspector.
- Test the app in ChatGPT developer mode through HTTPS tunneling.
- Exercise retries and repeated tool calls to confirm idempotent behavior.
- Check widget updates after host events and follow-up tool calls.
If you are only delivering a scaffold and are not installing dependencies, still run low-cost checks and say exactly what you did not run.
9. Connect and Test in ChatGPT (Developer Mode)
For local development, include explicit ChatGPT setup steps.
- Run the MCP server locally on
http://localhost:<port>/mcp.
- Expose the local server with a public HTTPS tunnel:
ngrok http <port>
- Use the tunneled HTTPS URL plus
/mcp path when connecting from ChatGPT.
- In ChatGPT, enable Developer Mode under Settings → Apps & Connectors → Advanced settings.
- In ChatGPT app settings, create a new app for the remote MCP server and paste the public MCP URL.
- Tell users to refresh the app after MCP tool/metadata changes so ChatGPT reloads the latest descriptors.
Note: Some docs/screenshots still use older "connector" terminology. Prefer current product wording ("app") while acknowledging both labels when giving step-by-step instructions.
10. Plan Production Hosting and Deployment
When the user asks to deploy or prepare for launch, generate hosting guidance for the MCP server (and widget assets if hosted separately).
- Host behind a stable public HTTPS endpoint (not a tunnel) with dependable TLS.
- Preserve low-latency streaming behavior on
/mcp.
- Configure secrets outside the repo (environment variables / secret manager).
- Add logging, request latency tracking, and error visibility for tool calls.
- Add basic observability (CPU, memory, request volume) and a troubleshooting path.
- Re-test the hosted endpoint in ChatGPT Developer Mode before submission.
11. Prepare Submission and Publish (Public Apps Only)
Only include these steps when the user intends a public directory listing.
- Use
apps-sdk/deploy/submission for the submission flow and apps-sdk/app-submission-guidelines for review requirements.
- Keep private/internal apps in Developer Mode instead of submitting.
- Confirm org verification and Owner-role prerequisites before submission work.
- Ensure the MCP server uses a public production endpoint (no localhost/testing URLs) and has submission-ready CSP configured.
- Prepare submission artifacts: app metadata, logo/screenshots, privacy policy URL, support contact, test prompts/responses, localization info.
- If auth is required, include review-safe demo credentials and test the login path end-to-end.
- Submit for review in the Platform dashboard, monitor review status, and publish only after approval.
Interactive State Guidance
Load references/interactive-state-sync-patterns.md when the app has long-lived widget state, repeated interactions, or component-initiated tool calls (for example, games, boards, maps, dashboards, editors).
Use it to choose patterns for:
- State snapshots plus monotonic event tokens (
stateVersion, resetCount, etc.)
- Idempotent retry-safe handlers
structuredContent vs _meta partitioning
- MCP Apps bridge-first update flows with optional
window.openai compatibility
- Decoupled data/render tool architecture for more complex interactive apps
Output Expectations
When using this skill to scaffold code, produce output in this order unless the user asks otherwise:
For direct scaffold requests, do not stop at the plan: give the brief plan, then create the files immediately.
- Primary app archetype chosen and why
- Tool plan and architecture choice (minimal vs decoupled)
- Upstream starting point chosen (official example, ext-apps example, or local fallback scaffold) and why
- Doc pages/URLs used from
$openai-docs
- File tree to create or modify
- Implementation (server + widget)
- Validation performed against the minimum working repo contract
- Local run/test instructions (including tunnel + ChatGPT Developer Mode app setup)
- Deployment/hosting guidance (if requested or implied)
- Submission-readiness checklist (for public launch requests)
- Risks, gaps, and follow-up improvements
Prompt Guidance
Use prompts that explicitly pair this skill with $openai-docs so the resulting scaffold is grounded in current docs.
Preferred prompt patterns:
Use $chatgpt-apps with $openai-docs to scaffold a ChatGPT app for <use case> with a <TS/Python> MCP server and <React/vanilla> widget.
Use $chatgpt-apps with $openai-docs to adapt the closest official Apps SDK example into a ChatGPT app for <use case>.
Use $chatgpt-apps and $openai-docs to refactor this Apps SDK demo into a production-ready structure with tool annotations, CSP, and URI versioning.
Use $chatgpt-apps with $openai-docs to plan tools first, then generate the MCP server and widget code.
When responding, ask for or infer these inputs before coding:
- Use case and primary user flows
- Read-only vs mutating tools
- Demo vs production target
- Private/internal use vs public directory submission
- Backend language and UI stack
- Auth requirements
- External API domains for CSP allowlists
- Hosting target and local dev approach
- Org ownership/verification readiness (for submission tasks)
Pitfalls
- Do not generate a large custom scaffold from scratch if a close upstream example already exists. Copy the smallest matching example, remove unrelated demo code, then patch it to current docs.
- Do not treat wrapper
app.* helpers as the canonical public API. They are implementation details. Map them back to window.openai or MCP Apps bridge primitives.
- Do not use
window.openai as the only integration path for new apps. Use the MCP Apps bridge first for portability.
- Do not let doc search failure block scaffolding. If search times out or returns poor matches, fetch canonical Apps SDK pages directly by URL.
- Do not prefer older repo patterns over current docs when they differ. Call out compatibility aliases explicitly.
- Do not skip validation. Even when only delivering a scaffold, run low-cost checks and state exactly what was not run.
- Do not submit private/internal apps to the public directory. Keep them in Developer Mode.
- Do not use localhost/testing URLs for submission. Ensure the MCP server uses a public production endpoint.
- Do not put large or sensitive payloads in
structuredContent. Move them to _meta.
- Do not use
frameDomains unless iframe embeds are core to the product.
- Do not mix multiple primary archetypes. Pick one and escalate only when the user explicitly asks for public launch.
- Do not forget to tell users to refresh the app after MCP tool/metadata changes so ChatGPT reloads the latest descriptors.
Verification
After generating or modifying a ChatGPT App, verify the following:
Static Checks
# Verify file tree matches the chosen archetype
Get-ChildItem -Recurse -Name | Select-String -Pattern "server|web|package.json"
# Verify MCP server file has no syntax errors (Node.js)
node --check server/index.ts
# or
npx tsc --noEmit
Local MCP Health Check
# Start the MCP server
npm run dev
# In a separate terminal, verify the /mcp endpoint responds
curl http://localhost:3000/mcp
Tool Descriptor Verification
- Confirm each tool has: name, description starting with "Use this when...", input schema, accurate annotations (
readOnlyHint, destructiveHint, openWorldHint, idempotentHint).
- Confirm
structuredContent, content, and _meta are returned intentionally per tool.
- Confirm widget resource/template uses MIME type
text/html;profile=mcp-app or RESOURCE_MIME_TYPE.
Widget Bridge Verification
- Confirm the widget listens for
ui/notifications/tool-result via postMessage.
- Confirm rendering comes from
structuredContent.
- Confirm
tools/call is used for component-initiated tool calls.
- Confirm
window.openai APIs are used only for extensions, not as the sole integration path.
CSP and Metadata Verification
- Confirm
_meta.ui.csp has exact connectDomains and resourceDomains.
- Confirm
_meta.ui.domain is set for submission-ready deployments.
- Confirm no
frameDomains unless iframe embeds are core to the product.
ChatGPT Developer Mode Verification
- Confirm the MCP server is running on
http://localhost:<port>/mcp.
- Confirm ngrok tunnel is active and the HTTPS URL is reachable.
- Confirm the app was created in ChatGPT with the tunneled HTTPS URL +
/mcp path.
- Confirm Developer Mode is enabled under Settings → Apps & Connectors → Advanced settings.
- Confirm tool calls execute and widget renders correctly in ChatGPT.
- Confirm repeated tool calls behave idempotently.
- Confirm widget updates after host events and follow-up tool calls.
Submission Readiness Verification (Public Apps Only)
References
references/app-archetypes.md — Classifying requests into supported app shapes
references/apps-sdk-docs-workflow.md — Doc queries, page targets, and code-generation checklist
references/interactive-state-sync-patterns.md — Reusable patterns for stateful or highly interactive widget apps
references/repo-contract-and-validation.md — Minimum working repo contract and lightweight validation ladder
references/search-fetch-standard.md — When and how to default to the standard search and fetch tools
references/upstream-example-workflow.md — Choosing between official examples, ext-apps examples, and the local fallback scaffold
references/window-openai-patterns.md — ChatGPT-specific extensions, wrapper API translation, and React helper patterns
scripts/scaffold_node_ext_apps.mjs — Minimal Node + @modelcontextprotocol/ext-apps fallback starter scaffold
Related Skills
$openai-docs — Fetch current OpenAI developer documentation; always pair with this skill for docs-first scaffolding
mcp__openaiDeveloperDocs__search_openai_docs — Direct docs search when $openai-docs is unavailable
mcp__openaiDeveloperDocs__fetch_openai_doc — Direct doc fetch when $openai-docs is unavailable
1---2name: chatgpt-apps3description: Scaffolds ChatGPT Apps SDK apps that pair an MCP server with a widget UI: tool plans, resource registration, MCP Apps bridge, window.openai extensions, CSP/domain metadata, and directory submission. Use when designing tools, wiring widgets, or producing a docs-aligned Apps SDK scaffold. Not for generic ChatGPT chat prompts or standalone MCP servers with no Apps SDK widget contract.4---5
6# ChatGPT Apps
7
8## Overview
9
10Scaffold ChatGPT Apps SDK implementations with a docs-first, example-first workflow, then generate code that follows current Apps SDK and MCP Apps bridge patterns.
11
12This skill produces:
13
14- A primary app-archetype classification and repo-shape decision
15- A tool plan (names, schemas, annotations, outputs)
16- An upstream starting-point recommendation (official example, ext-apps example, or local fallback scaffold)
17- An MCP server scaffold (resource registration, tool handlers, metadata)
18- A widget scaffold (MCP Apps bridge first, `window.openai` compatibility/extensions second)
19- A reusable Node + `@modelcontextprotocol/ext-apps` starter scaffold for low-dependency fallbacks
20- A validation report against the minimum working repo contract
21- Local dev and connector setup steps
22- A short stakeholder summary of what the app does (when requested)
23
24## When to Use
25
26Use this skill when the task involves any of the following:
27
28- **Scaffolding a new ChatGPT App** from scratch or adapting an upstream example
29- **Designing or refactoring MCP tools** for a ChatGPT App (names, schemas, annotations, `structuredContent`, `_meta`)
30- **Wiring the MCP Apps bridge** in a widget (`ui/notifications/tool-result`, `tools/call`, `ui/update-model-context`)
31- **Applying `window.openai` compatibility APIs** (file upload, modal, display mode, theme, locale)
32- **Setting resource metadata and security** (`_meta.ui.csp`, `_meta.ui.domain`, `connectDomains`, `resourceDomains`)
33- **Preparing an app for local testing** in ChatGPT Developer Mode via HTTPS tunnel
34- **Preparing an app for public submission** to the ChatGPT app directory
35- **Troubleshooting** tool descriptors, widget rendering, CSP violations, or bridge communication
36
37**Trigger keywords:** ChatGPT app, Apps SDK, MCP Apps bridge, widget, ext-apps, tool annotations, structuredContent, submission, connector, `window.openai`, MCP server scaffold.
38
39## Prerequisites
40
41- **Node.js** (v18+ recommended) installed and available on `PATH`
42- **PowerShell** as the primary shell on the Windows host (`~\agent-skills\library\chatgpt-apps\`)
43- **`$openai-docs` skill** or the OpenAI developer docs MCP server (`mcp__openaiDeveloperDocs__search_openai_docs`, `mcp__openaiDeveloperDocs__fetch_openai_doc`) for docs-first workflow
44- **ngrok** or equivalent HTTPS tunneling tool for local ChatGPT testing
45- **ChatGPT Developer Mode** access (Settings → Apps & Connectors → Advanced settings)
46- **`@modelcontextprotocol/ext-apps`** package when using the fallback scaffold or ext-apps examples
47
48## Procedure
49
50### 0. Mandatory Docs-First Workflow
51
52Before writing any code, fetch current Apps SDK documentation.
53
541. Invoke `$openai-docs` (preferred) or call the OpenAI docs MCP server directly.
552. Fetch current Apps SDK docs before writing code, especially these baseline pages:
56 - `apps-sdk/build/mcp-server`
57 - `apps-sdk/build/chatgpt-ui`
58 - `apps-sdk/build/examples`
59 - `apps-sdk/plan/tools`
60 - `apps-sdk/reference`
613. Fetch `apps-sdk/quickstart` when scaffolding a new app or generating a first-pass implementation. Check the official examples repo/page before inventing a scaffold from scratch.
624. Fetch deployment/submission docs when the task includes local ChatGPT testing, hosting, or public launch:
63 - `apps-sdk/deploy`
64 - `apps-sdk/deploy/submission`
65 - `apps-sdk/app-submission-guidelines`
665. Cite the docs URLs you used when explaining design choices or generated scaffolds.
676. Prefer current docs guidance over older repo patterns when they differ, and call out compatibility aliases explicitly.
687. If doc search times out or returns poor matches, fetch the canonical Apps SDK pages directly by URL and continue. Do not let search failure block scaffolding.
69
70**Load these reference files at specific points:**
71
72| Reference File | When to Load |
73|---|---|
74| `references/apps-sdk-docs-workflow.md` | At the start, for suggested doc queries and a compact checklist |
75| `references/app-archetypes.md` | Before choosing examples or scaffolds, to classify the request into a supported app shape |
76| `references/repo-contract-and-validation.md` | When generating or reviewing a repo, to keep output inside a stable "working app" contract |
77| `references/search-fetch-standard.md` | When the app is connector-like, data-only, sync-oriented, or meant to work with company knowledge or deep research |
78| `references/upstream-example-workflow.md` | When starting a greenfield app or deciding whether to adapt an upstream example or use the local fallback scaffold |
79| `references/window-openai-patterns.md` | When the task needs ChatGPT-specific widget behavior or when translating repo examples that use wrapper-specific `app.*` helpers |
80| `references/interactive-state-sync-patterns.md` | When the app has long-lived widget state, repeated interactions, or component-initiated tool calls (games, boards, maps, dashboards, editors) |
81
82### 1. Classify the App Archetype
83
84Before choosing examples, repo shape, or scaffolds, classify the request into one primary archetype and state it explicitly.
85
86- `tool-only` — no UI, just MCP tools
87- `vanilla-widget` — minimal HTML widget, MCP Apps bridge
88- `react-widget` — React widget with `@modelcontextprotocol/ext-apps/react`
89- `interactive-decoupled` — decoupled data/render tools, long-lived state
90- `submission-ready` — public directory launch target
91
92Infer the archetype unless a missing detail is truly blocking. Use the archetype to choose:
93
94- Whether a UI is needed at all
95- Whether to preserve a split `server/` + `web/` layout
96- Whether to prefer official OpenAI examples, ext-apps examples, or the local fallback scaffold
97- Which validation checks matter most
98- Whether `search` and `fetch` should be the default read-only tool surface
99
100Escalate to `submission-ready` only when the user asks for public launch, directory submission, or review-ready deployment.
101
102**Load `references/app-archetypes.md`** for the full decision rubric.
103
104### 2. Plan Tools Before Code
105
106Define the tool surface area from user intents.
107
1081. Use one job per tool.
1092. Write tool descriptions that start with "Use this when..." behavior cues.
1103. Make inputs explicit and machine-friendly (enums, required fields, bounds).
1114. Decide whether each tool is data-only, render-only, or both.
1125. Set annotations accurately:
113 - `readOnlyHint`
114 - `destructiveHint`
115 - `openWorldHint`
116 - `idempotentHint` (when true)
1176. If the app is connector-like, data-only, sync-oriented, or intended for company knowledge or deep research, default to the standard `search` and `fetch` tools instead of inventing custom read-only equivalents.
1187. For educational/demo apps, prefer one concept per tool so the model can pick the right example cleanly.
1198. Group demo tools by learning objective: data into the widget, widget actions back into the conversation or tools, host/layout environment signals, and lifecycle/streaming behavior.
120
121**Load `references/search-fetch-standard.md`** when `search` and `fetch` may be relevant.
122
123### 3. Choose an App Architecture
124
125Choose the simplest structure that fits the goal.
126
127- **Minimal demo pattern** — for quick prototypes, workshops, or proofs of concept.
128- **Decoupled data/render pattern** — for production UX so the widget does not re-render on every tool call.
129
130Prefer the decoupled pattern for non-trivial apps:
131
132- Data tools return reusable `structuredContent`.
133- Render tools attach `_meta.ui.resourceUri` and optional `_meta["openai/outputTemplate"]`.
134- Render tool descriptions state prerequisites (for example, "Call `search` first").
135
136### 3a. Choose a Starting Point
137
138For greenfield apps, prefer these starting points in order:
139
1401. **Official OpenAI examples** — when a close example already matches the requested stack or interaction pattern.
1412. **Version-matched `@modelcontextprotocol/ext-apps` examples** — when the user needs a lower-level or more portable MCP Apps baseline.
1423. **`scripts/scaffold_node_ext_apps.mjs`** — only when no close example fits, the user wants a tiny Node + vanilla starter, or network access/example retrieval is undesirable.
143
144Do not generate a large custom scaffold from scratch if a close upstream example already exists. Copy the smallest matching example, remove unrelated demo code, then patch it to the current docs and the user request.
145
146**Load `references/upstream-example-workflow.md`** for the selection and adaptation rubric.
147
148### 3b. Use the Starter Script (Low-Dependency Fallback)
149
150Use `scripts/scaffold_node_ext_apps.mjs` only when:
151
152- The user wants a quick, greenfield Node starter
153- A vanilla HTML widget is acceptable
154- No upstream example is a better starting point
155
156**Skip it when:**
157
158- A close official example exists
159- The user already has an existing app structure
160- The user needs a non-Node stack
161- The user explicitly wants React first
162- The user only wants a plan/review instead of code
163
164Run it only after fetching current docs, then reconcile the generated files with the docs you fetched.
165
166```powershell
167node scripts/scaffold_node_ext_apps.mjs
168```
169
170The script generates a minimal `@modelcontextprotocol/ext-apps` server plus a vanilla HTML widget that uses the MCP Apps bridge by default. The generated widget keeps follow-up messaging on the standard `ui/message` bridge and only uses `window.openai` for optional host signals/extensions.
171
172After running it, patch the generated output to match the current docs and the user request: adjust tool names/descriptions, annotations, resource metadata, URI versioning, and README/run instructions.
173
174### 4. Scaffold the MCP Server
175
176Generate a server that:
177
1781. Registers a widget resource/template with the MCP Apps UI MIME type (`text/html;profile=mcp-app`) or the SDK constant (`RESOURCE_MIME_TYPE`) when using `@modelcontextprotocol/ext-apps/server`.
1792. Registers tools with clear names, schemas, titles, and descriptions.
1803. Returns `structuredContent` (model + widget), `content` (model narration), and `_meta` (widget-only data) intentionally.
1814. Keeps handlers idempotent or documents non-idempotent behavior explicitly.
1825. Includes tool status strings (`openai/toolInvocation/*`) when helpful in ChatGPT.
183
184Keep `structuredContent` concise. Move large or sensitive widget-only payloads to `_meta`.
185
186### 5. Scaffold the Widget UI
187
188Use the MCP Apps bridge first for portability, then add ChatGPT-specific `window.openai` APIs when they materially improve UX.
189
1901. Listen for `ui/notifications/tool-result` (JSON-RPC over `postMessage`).
1912. Render from `structuredContent`.
1923. Use `tools/call` for component-initiated tool calls.
1934. Use `ui/update-model-context` only when UI state should change what the model sees.
194
195Use `window.openai` for compatibility and extensions (file upload, modal, display mode, etc.), not as the only integration path for new apps.
196
197#### API Surface Guardrails
198
199Some examples wrap the bridge with an `app` object (for example, `@modelcontextprotocol/ext-apps/react`) and expose helper names like `app.sendMessage()`, `app.callServerTool()`, `app.openLink()`, or host getter methods.
200
201- Treat those wrappers as implementation details or convenience layers, not the canonical public API to teach by default.
202- For ChatGPT-facing guidance, prefer the current documented surface:
203 - `window.openai.callTool(...)`
204 - `window.openai.sendFollowUpMessage(...)`
205 - `window.openai.openExternal(...)`
206 - `window.openai.requestDisplayMode(...)`
207 - Direct globals: `window.openai.theme`, `window.openai.locale`, `window.openai.displayMode`, `window.openai.toolInput`, `window.openai.toolOutput`, `window.openai.toolResponseMetadata`, `window.openai.widgetState`
208- If you reference wrapper helpers from repo examples, map them back to the documented `window.openai` or MCP Apps bridge primitives and call out that the wrapper is not the normative API surface.
209
210**Load `references/window-openai-patterns.md`** for the wrapper-to-canonical mapping and for React helper extraction patterns.
211
212### 6. Add Resource Metadata and Security
213
214Set resource metadata deliberately on the widget resource/template:
215
2161. `_meta.ui.csp` with exact `connectDomains` and `resourceDomains`.
2172. `_meta.ui.domain` for app submission-ready deployments.
2183. `_meta.ui.prefersBorder` (or OpenAI compatibility alias when needed).
2194. Optional `openai/widgetDescription` to reduce redundant narration.
220
221Avoid `frameDomains` unless iframe embeds are core to the product.
222
223### 7. Enforce the Minimum Working Repo Contract
224
225Every generated repo should satisfy a small, stable contract before you consider it done.
226
227- The repo shape matches the chosen archetype.
228- The MCP server and tools are wired to a reachable `/mcp` endpoint.
229- Tools have clear descriptions, accurate annotations, and UI metadata where needed.
230- Connector-like, data-only, sync-oriented, and company-knowledge-style apps use the standard `search` and `fetch` tool shapes when relevant.
231- The widget uses the MCP Apps bridge correctly when a UI exists.
232- The repo includes enough scripts or commands for a user to run and check it locally.
233- The response explicitly says what validation was run and what was not run.
234
235**Load `references/repo-contract-and-validation.md`** for the detailed checklist and validation ladder.
236
237### 8. Validate the Local Loop
238
239Validate against the minimum working repo contract, not just "did files get created."
240
241**Low-cost checks first:**
242
2431. Static contract review.
2442. Syntax or compile checks when feasible.
2453. Local `/mcp` health check when feasible.
246
247```powershell
248# Example: check if the MCP server responds on the expected endpoint
249curl http://localhost:3000/mcp
250```
251
252**Runtime checks next:**
253
2544. Verify tool descriptors and widget rendering in MCP Inspector.
2555. Test the app in ChatGPT developer mode through HTTPS tunneling.
2566. Exercise retries and repeated tool calls to confirm idempotent behavior.
2577. Check widget updates after host events and follow-up tool calls.
258
259If you are only delivering a scaffold and are not installing dependencies, still run low-cost checks and say exactly what you did not run.
260
261### 9. Connect and Test in ChatGPT (Developer Mode)
262
263For local development, include explicit ChatGPT setup steps.
264
2651. Run the MCP server locally on `http://localhost:<port>/mcp`.
2662. Expose the local server with a public HTTPS tunnel:
267
268```powershell
269ngrok http <port>
270```
271
2723. Use the tunneled HTTPS URL plus `/mcp` path when connecting from ChatGPT.
2734. In ChatGPT, enable Developer Mode under **Settings → Apps & Connectors → Advanced settings**.
2745. In ChatGPT app settings, create a new app for the remote MCP server and paste the public MCP URL.
2756. Tell users to refresh the app after MCP tool/metadata changes so ChatGPT reloads the latest descriptors.
276
277> **Note:** Some docs/screenshots still use older "connector" terminology. Prefer current product wording ("app") while acknowledging both labels when giving step-by-step instructions.
278
279### 10. Plan Production Hosting and Deployment
280
281When the user asks to deploy or prepare for launch, generate hosting guidance for the MCP server (and widget assets if hosted separately).
282
2831. Host behind a stable public HTTPS endpoint (not a tunnel) with dependable TLS.
2842. Preserve low-latency streaming behavior on `/mcp`.
2853. Configure secrets outside the repo (environment variables / secret manager).
2864. Add logging, request latency tracking, and error visibility for tool calls.
2875. Add basic observability (CPU, memory, request volume) and a troubleshooting path.
2886. Re-test the hosted endpoint in ChatGPT Developer Mode before submission.
289
290### 11. Prepare Submission and Publish (Public Apps Only)
291
292Only include these steps when the user intends a public directory listing.
293
2941. Use `apps-sdk/deploy/submission` for the submission flow and `apps-sdk/app-submission-guidelines` for review requirements.
2952. Keep private/internal apps in Developer Mode instead of submitting.
2963. Confirm org verification and Owner-role prerequisites before submission work.
2974. Ensure the MCP server uses a public production endpoint (no localhost/testing URLs) and has submission-ready CSP configured.
2985. Prepare submission artifacts: app metadata, logo/screenshots, privacy policy URL, support contact, test prompts/responses, localization info.
2996. If auth is required, include review-safe demo credentials and test the login path end-to-end.
3007. Submit for review in the Platform dashboard, monitor review status, and publish only after approval.
301
302## Interactive State Guidance
303
304**Load `references/interactive-state-sync-patterns.md`** when the app has long-lived widget state, repeated interactions, or component-initiated tool calls (for example, games, boards, maps, dashboards, editors).
305
306Use it to choose patterns for:
307
308- State snapshots plus monotonic event tokens (`stateVersion`, `resetCount`, etc.)
309- Idempotent retry-safe handlers
310- `structuredContent` vs `_meta` partitioning
311- MCP Apps bridge-first update flows with optional `window.openai` compatibility
312- Decoupled data/render tool architecture for more complex interactive apps
313
314## Output Expectations
315
316When using this skill to scaffold code, produce output in this order unless the user asks otherwise:
317
318> For direct scaffold requests, do not stop at the plan: give the brief plan, then create the files immediately.
319
3201. Primary app archetype chosen and why
3212. Tool plan and architecture choice (minimal vs decoupled)
3223. Upstream starting point chosen (official example, ext-apps example, or local fallback scaffold) and why
3234. Doc pages/URLs used from `$openai-docs`
3245. File tree to create or modify
3256. Implementation (server + widget)
3267. Validation performed against the minimum working repo contract
3278. Local run/test instructions (including tunnel + ChatGPT Developer Mode app setup)
3289. Deployment/hosting guidance (if requested or implied)
32910. Submission-readiness checklist (for public launch requests)
33011. Risks, gaps, and follow-up improvements
331
332## Prompt Guidance
333
334Use prompts that explicitly pair this skill with `$openai-docs` so the resulting scaffold is grounded in current docs.
335
336Preferred prompt patterns:
337
338- `Use $chatgpt-apps with $openai-docs to scaffold a ChatGPT app for <use case> with a <TS/Python> MCP server and <React/vanilla> widget.`
339- `Use $chatgpt-apps with $openai-docs to adapt the closest official Apps SDK example into a ChatGPT app for <use case>.`
340- `Use $chatgpt-apps and $openai-docs to refactor this Apps SDK demo into a production-ready structure with tool annotations, CSP, and URI versioning.`
341- `Use $chatgpt-apps with $openai-docs to plan tools first, then generate the MCP server and widget code.`
342
343When responding, ask for or infer these inputs before coding:
344
345- Use case and primary user flows
346- Read-only vs mutating tools
347- Demo vs production target
348- Private/internal use vs public directory submission
349- Backend language and UI stack
350- Auth requirements
351- External API domains for CSP allowlists
352- Hosting target and local dev approach
353- Org ownership/verification readiness (for submission tasks)
354
355## Pitfalls
356
357- **Do not generate a large custom scaffold from scratch if a close upstream example already exists.** Copy the smallest matching example, remove unrelated demo code, then patch it to current docs.
358- **Do not treat wrapper `app.*` helpers as the canonical public API.** They are implementation details. Map them back to `window.openai` or MCP Apps bridge primitives.
359- **Do not use `window.openai` as the only integration path for new apps.** Use the MCP Apps bridge first for portability.
360- **Do not let doc search failure block scaffolding.** If search times out or returns poor matches, fetch canonical Apps SDK pages directly by URL.
361- **Do not prefer older repo patterns over current docs when they differ.** Call out compatibility aliases explicitly.
362- **Do not skip validation.** Even when only delivering a scaffold, run low-cost checks and state exactly what was not run.
363- **Do not submit private/internal apps to the public directory.** Keep them in Developer Mode.
364- **Do not use localhost/testing URLs for submission.** Ensure the MCP server uses a public production endpoint.
365- **Do not put large or sensitive payloads in `structuredContent`.** Move them to `_meta`.
366- **Do not use `frameDomains` unless iframe embeds are core to the product.**
367- **Do not mix multiple primary archetypes.** Pick one and escalate only when the user explicitly asks for public launch.
368- **Do not forget to tell users to refresh the app** after MCP tool/metadata changes so ChatGPT reloads the latest descriptors.
369
370## Verification
371
372After generating or modifying a ChatGPT App, verify the following:
373
374### Static Checks
375
376```powershell
377# Verify file tree matches the chosen archetype
378Get-ChildItem -Recurse -Name | Select-String -Pattern "server|web|package.json"
379
380# Verify MCP server file has no syntax errors (Node.js)
381node --check server/index.ts
382# or
383npx tsc --noEmit
384```
385
386### Local MCP Health Check
387
388```powershell
389# Start the MCP server
390npm run dev
391
392# In a separate terminal, verify the /mcp endpoint responds
393curl http://localhost:3000/mcp
394```
395
396### Tool Descriptor Verification
397
398- Confirm each tool has: name, description starting with "Use this when...", input schema, accurate annotations (`readOnlyHint`, `destructiveHint`, `openWorldHint`, `idempotentHint`).
399- Confirm `structuredContent`, `content`, and `_meta` are returned intentionally per tool.
400- Confirm widget resource/template uses MIME type `text/html;profile=mcp-app` or `RESOURCE_MIME_TYPE`.
401
402### Widget Bridge Verification
403
404- Confirm the widget listens for `ui/notifications/tool-result` via `postMessage`.
405- Confirm rendering comes from `structuredContent`.
406- Confirm `tools/call` is used for component-initiated tool calls.
407- Confirm `window.openai` APIs are used only for extensions, not as the sole integration path.
408
409### CSP and Metadata Verification
410
411- Confirm `_meta.ui.csp` has exact `connectDomains` and `resourceDomains`.
412- Confirm `_meta.ui.domain` is set for submission-ready deployments.
413- Confirm no `frameDomains` unless iframe embeds are core to the product.
414
415### ChatGPT Developer Mode Verification
416
4171. Confirm the MCP server is running on `http://localhost:<port>/mcp`.
4182. Confirm ngrok tunnel is active and the HTTPS URL is reachable.
4193. Confirm the app was created in ChatGPT with the tunneled HTTPS URL + `/mcp` path.
4204. Confirm Developer Mode is enabled under **Settings → Apps & Connectors → Advanced settings**.
4215. Confirm tool calls execute and widget renders correctly in ChatGPT.
4226. Confirm repeated tool calls behave idempotently.
4237. Confirm widget updates after host events and follow-up tool calls.
424
425### Submission Readiness Verification (Public Apps Only)
426
427- [ ] Public production HTTPS endpoint (no localhost/tunnel URLs)
428- [ ] Submission-ready CSP configured
429- [ ] Org verification and Owner-role confirmed
430- [ ] App metadata, logo/screenshots, privacy policy URL, support contact prepared
431- [ ] Test prompts/responses documented
432- [ ] Localization info prepared
433- [ ] Review-safe demo credentials included (if auth required)
434- [ ] Login path tested end-to-end (if auth required)
435
436## References
437
438- `references/app-archetypes.md` — Classifying requests into supported app shapes
439- `references/apps-sdk-docs-workflow.md` — Doc queries, page targets, and code-generation checklist
440- `references/interactive-state-sync-patterns.md` — Reusable patterns for stateful or highly interactive widget apps
441- `references/repo-contract-and-validation.md` — Minimum working repo contract and lightweight validation ladder
442- `references/search-fetch-standard.md` — When and how to default to the standard `search` and `fetch` tools
443- `references/upstream-example-workflow.md` — Choosing between official examples, ext-apps examples, and the local fallback scaffold
444- `references/window-openai-patterns.md` — ChatGPT-specific extensions, wrapper API translation, and React helper patterns
445- `scripts/scaffold_node_ext_apps.mjs` — Minimal Node + `@modelcontextprotocol/ext-apps` fallback starter scaffold
446
447## Related Skills
448
449- `$openai-docs` — Fetch current OpenAI developer documentation; always pair with this skill for docs-first scaffolding
450- `mcp__openaiDeveloperDocs__search_openai_docs` — Direct docs search when `$openai-docs` is unavailable
451- `mcp__openaiDeveloperDocs__fetch_openai_doc` — Direct doc fetch when `$openai-docs` is unavailable