| name | chatgpt-app-builder |
| description | Build ChatGPT Apps using the Apps SDK and MCP. Use when users want to: (1) Evaluate if their product should become a ChatGPT App (2) Design and implement MCP servers with widgets (3) Test apps locally and in ChatGPT (4) Prepare for App Store submission Triggers: "ChatGPT app", "Apps SDK", "build for ChatGPT", "ChatGPT integration", "MCP server for ChatGPT", "submit to ChatGPT" |
| license | MIT |
| compatibility | Requires Node.js 18+, npm. Network access needed for testing with ngrok. Intended for Claude Code and similar filesystem-based agents. |
| metadata | [object Object] |
| allowed-tools | Bash(npm:*) Bash(npx:*) Bash(node:*) Bash(ngrok:*) Read Write Edit Glob Grep |
ChatGPT App Builder
Build production-ready ChatGPT Apps from concept to App Store submission.
Quick Start
New app? → Start at Phase 1 (Fit Evaluation)
Have app-spec.md? → Start at Phase 3 (Implementation)
App built? → Start at Phase 4 (Testing)
Ready to ship? → Start at Phase 5 (Deployment)
Phase 1: Fit Evaluation
Goal: Determine if a ChatGPT App is right for this product.
Step 1: Gather Context
Ask the user:
- What does your product do?
- Who are your users?
- What API/data does it expose?
- What actions can users take?
Step 2: Apply Know/Do/Show Framework
Evaluate against three value pillars (see fit_evaluation.md):
| Pillar | Question | Strong Signal |
|---|---|---|
| Know | Does it provide data ChatGPT lacks? | Live prices, user-specific data, internal metrics |
| Do | Can it take real actions? | Create, update, delete, send, schedule |
| Show | Can it display better than text? | Lists, charts, maps, media galleries |
Minimum requirement: At least one pillar must be strong.
Step 3: Check Blockers
Review fit_evaluation.md for:
- Prohibited categories (gambling, adult, crypto speculation)
- Data restrictions (no PCI, PHI, SSN, API keys)
- Age requirements (13+ audience)
Step 4: Create Golden Prompt Set
Draft prompts for discovery testing:
- 5 direct prompts: Explicitly name your product ("Show my TaskFlow tasks")
- 5 indirect prompts: Describe intent without naming ("What should I work on?")
- 3 negative prompts: Similar but shouldn't trigger ("Create a reminder")
Step 5: Write app-spec.md
Create the specification file:
# [Product Name] ChatGPT App Spec
## Product Context
- Name: [Product name]
- API Base: [API URL]
- Auth: [Bearer token / OAuth / None]
## Value Proposition
- Know: [What data does it provide?]
- Do: [What actions can it take?]
- Show: [What UI is needed?]
## Golden Prompts
### Direct (should trigger)
1. ...
### Indirect (should trigger)
1. ...
### Negative (should NOT trigger)
1. ...
Output: app-spec.md in project directory
Phase 2: App Design
Goal: Define complete technical specification.
Step 1: Define Tools (2-5)
Follow one-job-per-tool principle. See chatgpt_app_best_practices.md.
For each tool, specify:
name: service_verb_noun # e.g., taskflow_get_tasks
title: Human Readable Name
description: Use this when the user wants to... [be specific]
annotations:
readOnlyHint: true/false
destructiveHint: true/false
openWorldHint: true/false
inputSchema:
param1: type (required/optional)
param2: enum["a", "b", "c"]
outputStructure:
content: [text summary for model]
structuredContent: {machine-readable data}
_meta: {widget-only data}
Step 2: Decide Widget Needs
Does the app need custom UI?
| Use Case | Widget Needed? | Component Type |
|---|---|---|
| Task list with checkboxes | Yes | List with actions |
| Data display only | Maybe | Could use text |
| Maps, charts, media | Yes | Specialized |
| Multi-step workflow | Yes | Stateful widget |
See widget_development.md for patterns.
Step 3: Plan Authentication
If accessing user-specific data or write operations, auth is required.
See oauth_integration.md for:
- Well-known endpoint setup
- Provider-specific guides (Auth0, Stytch)
- Tool-level securitySchemes
Step 4: Update app-spec.md
Add technical specification:
## Tools
### 1. service_get_items
- **Annotations**: readOnlyHint: true
- **Input**: { status?: "active" | "completed", limit?: number }
- **Output**:
- content: "Found N items"
- structuredContent: { items: [...] }
- _meta: { fullData: [...] }
### 2. service_create_item
- **Annotations**: openWorldHint: true
- **Input**: { title: string, description?: string }
- **Output**: { id, title, created_at }
## Widget
- Type: List with action buttons
- Display modes: inline, fullscreen
- State: { selectedId, filter }
## Authentication
- Required: Yes
- Provider: Auth0
- Scopes: read:items, write:items
Output: Updated app-spec.md with full technical spec
Phase 3: Implementation
Goal: Generate complete working project.
Step 1: Initialize Project
Copy from assets and customize:
# Project structure
myapp-chatgpt/
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # MCP server entry
│ ├── tools/ # Tool handlers
│ ├── widget/ # Widget source
│ └── types/ # TypeScript types
└── scripts/
└── build-widget.ts # Widget bundler
See node_chatgpt_app.md for complete patterns.
Step 2: Implement MCP Server
Key components (from assets/server/):
- HTTP server with SSE transport (required for ChatGPT Apps):
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
// GET /mcp - SSE stream connection
// POST /mcp/messages - Message handling
- Tool definitions with JSON Schema:
const tools: Tool[] = [{
name: "service_get_items",
title: "Get Items",
description: "Use this when the user wants to see items...",
inputSchema: { type: "object", properties: {...} },
_meta: {
"openai/outputTemplate": "ui://widget/app.html",
"openai/widgetAccessible": true
},
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}];
- Handler registration:
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// Handle tool calls...
return {
content: [{ type: "text", text: `Found ${items.length} items` }],
structuredContent: { items: items.slice(0, 10) },
_meta: { fullItems: items }
};
});
Step 3: Implement Widget
Key patterns (from assets/widget/):
// Access data
const output = window.openai.toolOutput;
const meta = window.openai.toolResponseMetadata;
// Invoke tools
await window.openai.callTool("service_action", { id: "123" });
// Persist state
window.openai.setWidgetState({ selectedId: "123" });
// Layout control
window.openai.notifyIntrinsicHeight(400);
await window.openai.requestDisplayMode({ mode: "fullscreen" });
See widget_development.md for React hooks and patterns.
Step 4: Build
npm install
npm run build # Compiles server + bundles widget
Step 5: Implementation Checklist
Before moving to testing, verify:
Widget Requirements
- Uses Apps SDK UI design tokens (see apps_sdk_ui_tokens.md)
- Implements dark mode with CSS variable architecture
- Uses LoadingDots pattern for loading states (see widget_ui_patterns.md)
- Calls
notifyIntrinsicHeight()after all DOM changes - Includes copy button feedback for copyable content
- Has show more/less for long lists (>3 items)
- Works on mobile (test at 375px width)
- Loading UI guards against re-initialization (see widget_loading_patterns.md)
- SVG animations use
.styleproperty, notsetAttribute()(see widget_development.md)
Security Requirements
- All user input is validated (see security_patterns.md)
- HTML output uses safe DOM methods (textContent, createElement)
- External image URLs are proxied with domain whitelist and size limits (200KB)
- Rate limiting is implemented per session with LRU eviction
Server Requirements
-
/.well-known/openai-apps-challengeendpoint returns challenge token -
/privacyendpoint returns HTML privacy policy -
/termsendpoint returns HTML terms of service -
/mcpendpoint handles SSE connections -
/healthor/returns health check JSON - CORS configured for ChatGPT domains only
- Security headers set on all responses
- Session routing uses direct lookup by sessionId (CRITICAL - see troubleshooting.md)
- Response size under 300KB total (remove duplicates, limit images)
Production Readiness (if deploying to production)
- OAuth tokens stored in database, not in-memory (see oauth_integration.md)
- Mutation tools include idempotency checks (see chatgpt_app_best_practices.md)
- Disambiguation pattern for multi-match scenarios (see chatgpt_app_best_practices.md)
- Confirmation receipts for all mutations (see chatgpt_app_best_practices.md)
Output: Complete project in working directory
Phase 4: Testing
Goal: Verify the app works correctly.
Step 1: Local Testing with MCP Inspector
# Terminal 1: Start server
npm run dev
# Server runs at http://localhost:8000
# Terminal 2: Run inspector
npx @modelcontextprotocol/inspector@latest http://localhost:8000/mcp
Verify:
- All tools appear in inspector
- Tool calls return expected structure
- Widget renders without errors
Step 2: Create HTTPS Tunnel
ngrok http 8000
# Copy the https://xxx.ngrok.app URL
Step 3: Create ChatGPT Connector
- Go to ChatGPT → Settings → Connectors
- Enable Developer Mode (Settings → Apps & Connectors → Advanced)
- Create new connector:
- Name: Your app name
- Description: From app-spec.md
- URL:
https://xxx.ngrok.app/mcp
- Click Create and verify tools appear
Step 4: Test Golden Prompts
In a new ChatGPT conversation:
- Enable your connector (+ button → More → select connector)
- Test each golden prompt from app-spec.md
- Verify:
- Direct prompts trigger correctly
- Indirect prompts trigger correctly
- Negative prompts do NOT trigger
- Widget renders properly
- Actions work (if applicable)
Step 5: Iterate
If issues found:
- Fix code
- Rebuild:
npm run build - Refresh connector in ChatGPT settings
- Re-test
See troubleshooting.md for common issues and solutions.
Output: Working app tested in ChatGPT
Phase 5: Deployment & Submission
Goal: Ship to production and App Store.
Step 1: Deploy to Production
Generate deployment configs from assets/deploy/:
Fly.io (recommended):
fly launch
fly deploy
Vercel/Cloudflare: Ensure streaming HTTP support.
Step 2: Update Connector
- Replace ngrok URL with production URL
- Verify connection in ChatGPT settings
Step 3: Pre-Submission Checklist
See submission_requirements.md:
Required:
- Organization verified on OpenAI Platform
- All tools have clear descriptions
- Annotations correct (readOnlyHint, destructiveHint, openWorldHint)
- No prohibited content/commerce
- No restricted data collection
- Widget renders on mobile
- Test credentials with sample data prepared
If auth required:
- Well-known endpoints accessible
- Test account credentials documented
- OAuth flow completes successfully
Step 4: Submit
- Go to platform.openai.com/apps-manage
- Enter MCP server URL
- Add OAuth metadata (if applicable)
- Complete submission form
- Submit for review
Step 5: Post-Submission
- Monitor email for review status
- Address any reviewer feedback
- Click Publish after approval
Output: App live in ChatGPT App Store