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 submissionTriggers: "ChatGPT app", "Apps SDK", "build for ChatGPT","ChatGPT integration", "MCP server for ChatGPT", "submit to ChatGPT"
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
skills listSkill Instructions
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: author: Bayram Annakov (onsa.ai) version: "1.0.0" category: development 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
External Resources
More by BayramAnnakov
View allclaude-reflect: Self-learning system that captures corrections during sessions and reminds users to run /reflect to update CLAUDE.md. Use when discussing learnings, corrections, or when the user mentions remembering something for future sessions.
Self-learning system that captures corrections during sessions and reminds users to run /reflect to update CLAUDE.md. Use when discussing learnings, corrections, or when the user mentions remembering something for future sessions.
Audit UX waiting states for web applications with long-running operations (30+ seconds). Use when asked to evaluate, audit, or analyze a product's loading states, wait times, progress indicators, or user experience during slow operations. Requires browser automation (Chrome MCP tools). Generates comprehensive reports with screenshots, checklist evaluation, and prioritized recommendations.
eval-coach: Guide users through building comprehensive AI evaluation strategies using Evaluation-Driven Development (EDD)