LEARN CHATGPT APPS DEEP DIVE
Learn ChatGPT Apps: From Zero to Published App Developer
Goal: Deeply understand the ChatGPT Apps platform—why it exists, how to build apps using the Apps SDK and Model Context Protocol (MCP), create rich interactive UIs, and publish apps that reach 800+ million ChatGPT users.
What Are ChatGPT Apps?
ChatGPT Apps are interactive applications that extend ChatGPT’s capabilities by connecting it to external tools, data sources, and rich user interfaces. They allow ChatGPT to:
- Know: Access live data, user-specific information, and external systems
- Do: Take real actions (create records, schedule events, trigger workflows)
- Show: Present information through rich, interactive UIs beyond plain text
Apps are built using the Apps SDK, which builds on the Model Context Protocol (MCP)—an open standard that lets AI models connect to external tools and data.
Why Do ChatGPT Apps Exist?
- Extend ChatGPT’s Reach: Connect to real-time data and external services
- Rich Interactions: Go beyond text with maps, charts, forms, and 3D visualizations
- Developer Opportunity: Reach 800+ million ChatGPT users
- Open Standard: Built on MCP, apps work across platforms that adopt the standard
- Enterprise Ready: Available to Business, Enterprise, and Edu customers
The Ecosystem at a Glance
┌─────────────────────────────────────────────────────────────────────┐
│ CHATGPT APPS ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ChatGPT UI │ │
│ │ [Chat Messages] [Your App's Widget in iframe] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ window.openai │
│ │ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Your Web Component │ │
│ │ React/Vue/Vanilla JS │ Tailwind CSS │ @openai/apps-sdk-ui│ │
│ │ Rendered in iframe │ Accesses window.openai API │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ MCP (Model Context Protocol) │
│ │ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Your MCP Server │ │
│ │ Python (FastMCP) or Node.js │ Defines Tools & Resources │ │
│ │ OAuth 2.1 Authentication │ Connects to Your Backend │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ Your Backend Services │
│ (Databases, APIs, External Services) │
│ │
└─────────────────────────────────────────────────────────────────────┘
Core Concept Analysis
The Apps SDK Stack
The Apps SDK has two core components:
- Web Component (UI): An HTML/CSS/JavaScript interface rendered in an iframe within ChatGPT
- MCP Server (Backend): Exposes your app’s capabilities through tools and resources
Model Context Protocol (MCP)
MCP is the “USB-C for AI applications”—a universal connector between LLMs and external systems.
MCP Core Primitives:
├── Tools: Functions the model can execute
│ └── Example: get_weather(city), create_ticket(title, body)
│
├── Resources: Structured documents the model can read
│ └── Example: user_profile, company_settings
│
└── Prompts: Instructions or templates for the model
└── Example: "When user asks about orders, always include status"
The window.openai API
Your widget communicates with ChatGPT through the injected window.openai object:
// Key APIs available in your widget:
// Read data from tool execution
window.openai.toolOutput // Data your MCP server returned
window.openai.toolInput // Parameters passed to the tool
// Take actions
window.openai.callTool(name, params) // Invoke another tool
window.openai.sendFollowUpMessage(text) // Send a message as user
// State management
window.openai.widgetState // Persisted widget state
window.openai.setWidgetState(state) // Save state across turns
// UI controls
window.openai.requestFullscreen() // Expand to fullscreen
window.openai.exitFullscreen() // Return to inline view
// File handling
window.openai.uploadFile(file) // Upload image/file
window.openai.getFileDownloadUrl(id) // Get file preview URL
Tools Design Best Practices
Tools are the interface between ChatGPT and your backend:
Good Tool Design:
├── Single Responsibility: One action per tool
│ ✓ get_order_status, cancel_order, update_shipping
│ ✗ manage_order (too broad)
│
├── Clear Naming: Descriptive verb phrases
│ ✓ search_products, add_to_cart, checkout
│ ✗ products, cart, buy (too vague)
│
├── Accurate Descriptions: Start with "Use this when..."
│ ✓ "Use this when user wants to find products by category"
│ ✗ "Product search" (not helpful for model routing)
│
├── Proper Annotations:
│ readOnlyHint: true // Safe operations (no side effects)
│ destructiveHint: true // Deletes data (requires confirmation)
│
└── Structured Outputs: Include IDs, timestamps, status
✓ { orderId: "123", status: "shipped", eta: "2025-12-21" }
✗ "Your order is on the way!" (not reusable)
Component Types
Components are the UI elements users interact with:
| Type | Purpose | Example |
|---|---|---|
| Viewer | Display read-only data | Order status, analytics dashboard |
| Editor | Modify data with forms | Create/edit record, settings |
| Workspace | Complex multi-step flows | Document editor, design tool |
| List | Dynamic collections | Search results, notifications |
| Map | Geographic visualization | Store locator, delivery tracking |
| Carousel | Swipeable content | Product gallery, recommendations |
| Shop | E-commerce flows | Product browsing, checkout |
Authentication (OAuth 2.1)
ChatGPT Apps use OAuth 2.1 with PKCE for secure authentication:
OAuth Flow:
1. User clicks "Connect" in ChatGPT
2. ChatGPT redirects to your authorization server
3. User logs in and grants permissions
4. Your server issues access token
5. ChatGPT uses token for MCP requests
6. Your MCP server validates token on every call
Project List
Projects are ordered from understanding the basics to building production-ready apps.
Project 1: MCP Protocol Explorer
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: Python
- Alternative Programming Languages: TypeScript/Node.js
- Coolness Level: Level 2: Practical but Forgettable
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 1: Beginner
- Knowledge Area: MCP Protocol / Tool Design
- Software or Tool: FastMCP, MCP Inspector
- Main Book: “Building APIs You Won’t Hate” by Phil Sturgeon
What you’ll build: A simple MCP server with 3-5 tools that you test with MCP Inspector, understanding how tools are defined, called, and how data flows between ChatGPT and your server.
Why it teaches ChatGPT Apps: Before building UIs, you need to understand MCP—the protocol that powers everything. This project teaches you tool definitions, parameter schemas, and the request/response cycle.
Core challenges you’ll face:
- Setting up FastMCP → maps to Python MCP SDK basics
- Defining tool schemas → maps to JSON Schema, parameter types
- Testing with MCP Inspector → maps to debugging MCP servers
- Understanding the protocol flow → maps to how ChatGPT invokes tools
Key Concepts:
- MCP Specification: Model Context Protocol
- FastMCP (Python SDK): FastMCP GitHub
- MCP Inspector: Testing tool for MCP servers
- Tool Design: OpenAI Apps SDK - Tools
Difficulty: Beginner Time estimate: Weekend Prerequisites: Basic Python, understanding of REST APIs
Real world outcome:
$ uvicorn my_mcp_server:app --port 8000
MCP Server running on http://localhost:8000
Available Tools:
1. get_weather
Description: "Use this when user asks about weather conditions"
Parameters: { city: string, units?: "celsius" | "fahrenheit" }
2. convert_currency
Description: "Use this when user wants to convert between currencies"
Parameters: { amount: number, from: string, to: string }
3. get_stock_price
Description: "Use this when user asks about stock prices"
Parameters: { symbol: string }
# In MCP Inspector:
> invoke get_weather {"city": "San Francisco", "units": "celsius"}
Response:
{
"temperature": 18,
"condition": "Partly Cloudy",
"humidity": 65,
"wind": "12 km/h NW"
}
> invoke convert_currency {"amount": 100, "from": "USD", "to": "EUR"}
Response:
{
"amount": 100,
"from": "USD",
"to": "EUR",
"result": 92.45,
"rate": 0.9245,
"timestamp": "2025-12-20T10:30:00Z"
}
Implementation Hints:
MCP server structure (Python with FastMCP):
from fastmcp import FastMCP
mcp = FastMCP("My First MCP Server")
@mcp.tool()
def get_weather(city: str, units: str = "celsius") -> dict:
"""Use this when user asks about weather conditions for a city."""
# Call your weather API here
return {
"temperature": 18,
"condition": "Partly Cloudy",
"humidity": 65
}
@mcp.tool()
def convert_currency(amount: float, from_currency: str, to_currency: str) -> dict:
"""Use this when user wants to convert money between currencies."""
# Call your currency API here
rate = get_exchange_rate(from_currency, to_currency)
return {
"amount": amount,
"from": from_currency,
"to": to_currency,
"result": amount * rate,
"rate": rate
}
# Run with: uvicorn main:mcp.app --port 8000
Testing with MCP Inspector:
# Install MCP Inspector
npx @anthropic/mcp-inspector
# Connect to your server
# Enter: http://localhost:8000
# Browse and test each tool
Questions to explore:
- How does ChatGPT decide which tool to call?
- What happens if a tool returns an error?
- How do you validate input parameters?
- What’s the difference between tools and resources?
Learning milestones:
- Server starts and lists tools → You understand MCP server basics
- Inspector can invoke tools → You understand the protocol
- Tools return structured data → You understand output design
- You add error handling → You understand robust tool design
Project 2: Hello World Widget
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: TypeScript/React
- Alternative Programming Languages: Vanilla JavaScript, Vue
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 1: Beginner
- Knowledge Area: Web Components / window.openai API
- Software or Tool: Vite, React, @openai/apps-sdk-ui
- Main Book: “Learning React” by Eve Porcello & Alex Banks
What you’ll build: A simple widget that displays data from a tool call, uses the window.openai API to read tool output, and can trigger follow-up actions—your first complete ChatGPT app.
Why it teaches ChatGPT Apps: This is the “Hello World” of ChatGPT apps. You’ll understand how widgets are built, how they receive data, and how they communicate with ChatGPT.
Core challenges you’ll face:
- Setting up the build pipeline → maps to Vite configuration for widgets
- Using window.openai API → maps to host communication
- Rendering in ChatGPT’s iframe → maps to sandbox constraints
- Connecting widget to MCP server → maps to full app architecture
Key Concepts:
- Apps SDK UI: @openai/apps-sdk-ui
- window.openai Reference: ChatGPT UI Guide
- Vite Build: Vite Documentation
- Widget Examples: openai-apps-sdk-examples
Difficulty: Beginner Time estimate: Weekend Prerequisites: Project 1, basic React knowledge
Real world outcome:
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ You: What's the weather in Tokyo? │
│ │
│ ChatGPT: Let me check the weather for Tokyo. │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🌤️ Weather in Tokyo │ │
│ │ │ │
│ │ Temperature: 15°C │ │
│ │ Condition: Partly Cloudy │ │
│ │ Humidity: 72% │ │
│ │ Wind: 8 km/h E │ │
│ │ │ │
│ │ [🔄 Refresh] [📍 Change City] [📊 5-Day Forecast] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ The current weather in Tokyo is 15°C and partly cloudy. │
│ │
└─────────────────────────────────────────────────────────────────┘
Implementation Hints:
Widget component structure:
// src/WeatherWidget.tsx
import { useEffect, useState } from 'react';
import { Button, Badge } from '@openai/apps-sdk-ui';
export function WeatherWidget() {
const [weather, setWeather] = useState(null);
useEffect(() => {
// Read data from tool output
const data = window.openai?.toolOutput;
if (data) {
setWeather(data);
}
}, []);
const handleRefresh = () => {
// Call the tool again
window.openai?.callTool('get_weather', {
city: weather?.city,
units: 'celsius'
});
};
const handleForecast = () => {
// Send a follow-up message
window.openai?.sendFollowUpMessage(
`Show me the 5-day forecast for ${weather?.city}`
);
};
if (!weather) return <div>Loading...</div>;
return (
<div className="p-4 space-y-4">
<h2 className="text-xl font-bold">
🌤️ Weather in {weather.city}
</h2>
<div className="grid grid-cols-2 gap-2">
<div>Temperature: {weather.temperature}°C</div>
<div>Condition: {weather.condition}</div>
<div>Humidity: {weather.humidity}%</div>
<div>Wind: {weather.wind}</div>
</div>
<div className="flex gap-2">
<Button onClick={handleRefresh}>🔄 Refresh</Button>
<Button onClick={handleForecast}>📊 5-Day Forecast</Button>
</div>
</div>
);
}
Vite configuration for widget bundle:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteSingleFile } from 'vite-plugin-singlefile';
export default defineConfig({
plugins: [react(), viteSingleFile()],
build: {
outDir: 'dist',
rollupOptions: {
output: {
// Single self-contained HTML file
inlineDynamicImports: true,
},
},
},
});
MCP server returning widget:
@mcp.tool()
def get_weather(city: str, units: str = "celsius") -> dict:
"""Use this when user asks about weather conditions."""
weather_data = fetch_weather_api(city, units)
return {
"structuredContent": weather_data, # Goes to window.openai.toolOutput
"uiComponent": {
"type": "iframe",
"url": "https://your-app.com/weather-widget.html"
}
}
Learning milestones:
- Widget renders in ChatGPT → You understand iframe embedding
- Widget reads toolOutput → You understand data flow
- Buttons trigger actions → You understand callTool and sendFollowUpMessage
- Full app works end-to-end → You’ve built your first ChatGPT app!
Project 3: Interactive List & Search App
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: TypeScript/React
- Alternative Programming Languages: Vue, Svelte
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 2. The “Micro-SaaS / Pro Tool”
- Difficulty: Level 2: Intermediate
- Knowledge Area: State Management / List Components
- Software or Tool: React, TanStack Query, @openai/apps-sdk-ui
- Main Book: “React Query Essentials” (TanStack documentation)
What you’ll build: A searchable product catalog app with list view, filters, pagination, and detail views—demonstrating how to build data-rich apps that maintain state across conversation turns.
Why it teaches ChatGPT Apps: Most real apps involve lists, search, and navigation. This project teaches state management, pagination, and complex UI patterns within the ChatGPT widget environment.
Core challenges you’ll face:
- Persisting state across turns → maps to widgetState API
- Handling pagination → maps to efficient data loading
- Implementing search/filter → maps to tool parameter design
- Detail view navigation → maps to widget state management
Key Concepts:
- Widget State:
window.openai.setWidgetState()for persistence - List Component: Apps SDK UI Components
- Pagination Patterns: Cursor vs. offset pagination
- Search UX: Debouncing, loading states
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Projects 1-2, React state management
Real world outcome:
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ You: Show me running shoes under $100 │
│ │
│ ChatGPT: Here are running shoes under $100: │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🔍 [Search... ] [Price ▼] [Brand ▼] [Rating ▼] │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ 🏃 Nike Air Zoom Pegasus 40 │ │ │
│ │ │ ⭐ 4.5 (2,341 reviews) · $89.99 │ │ │
│ │ │ [View Details] [Add to Cart] │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ 🏃 Adidas Ultraboost Light │ │ │
│ │ │ ⭐ 4.7 (1,892 reviews) · $94.99 │ │ │
│ │ │ [View Details] [Add to Cart] │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Showing 1-10 of 47 results [← Prev] [1] [2] [3] [Next →] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Implementation Hints:
State persistence pattern:
function ProductList() {
// Load persisted state on mount
const [state, setState] = useState(() => {
return window.openai?.widgetState || {
searchQuery: '',
filters: {},
page: 1,
selectedProductId: null
};
});
// Persist state changes
useEffect(() => {
window.openai?.setWidgetState(state);
}, [state]);
const handleSearch = (query: string) => {
setState(prev => ({ ...prev, searchQuery: query, page: 1 }));
// Trigger new search via tool call
window.openai?.callTool('search_products', {
query,
filters: state.filters,
page: 1
});
};
// ... rest of component
}
MCP tools for list operations:
@mcp.tool()
def search_products(
query: str = "",
category: str = None,
min_price: float = None,
max_price: float = None,
page: int = 1,
per_page: int = 10
) -> dict:
"""Use this when user wants to search or browse products."""
products = search_database(query, category, min_price, max_price)
return {
"structuredContent": {
"products": products[(page-1)*per_page : page*per_page],
"total": len(products),
"page": page,
"perPage": per_page,
"hasMore": page * per_page < len(products)
},
"uiComponent": {
"type": "iframe",
"url": "https://your-app.com/product-list.html"
}
}
@mcp.tool()
def get_product_details(product_id: str) -> dict:
"""Use this when user wants to see details of a specific product."""
product = get_product_by_id(product_id)
return {
"structuredContent": product,
"uiComponent": {
"type": "iframe",
"url": "https://your-app.com/product-detail.html"
}
}
Pagination component:
function Pagination({ page, total, perPage, onPageChange }) {
const totalPages = Math.ceil(total / perPage);
return (
<div className="flex items-center gap-2">
<Button
disabled={page === 1}
onClick={() => onPageChange(page - 1)}
>
← Prev
</Button>
{[...Array(Math.min(5, totalPages))].map((_, i) => (
<Button
key={i}
variant={page === i + 1 ? 'primary' : 'secondary'}
onClick={() => onPageChange(i + 1)}
>
{i + 1}
</Button>
))}
<Button
disabled={page === totalPages}
onClick={() => onPageChange(page + 1)}
>
Next →
</Button>
</div>
);
}
Learning milestones:
- List renders from API data → You understand structured content
- State persists across turns → You understand widgetState
- Pagination works → You understand multi-page data
- Search/filter work → You understand parameterized tools
Project 4: Map & Location-Based App
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: TypeScript/React
- Alternative Programming Languages: Vue, Svelte
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 2: Intermediate
- Knowledge Area: Maps / Geolocation / Visualization
- Software or Tool: Mapbox GL JS, Leaflet, or Google Maps
- Main Book: “Mapping Hacks” by Schuyler Erle
What you’ll build: A store locator / delivery tracker app with interactive maps, marker clustering, location search, and detail panes—demonstrating geographic visualization in ChatGPT.
Why it teaches ChatGPT Apps: Maps are one of the most visually impressive component types. This project teaches you how to integrate complex third-party libraries and handle geographic data.
Core challenges you’ll face:
- Integrating map libraries in iframe → maps to third-party library constraints
- Marker clustering and interaction → maps to performance optimization
- Geolocation and search → maps to location-aware tools
- Responsive map sizing → maps to ChatGPT widget dimensions
Key Concepts:
- Mapbox GL JS: Mapbox Documentation
- Geocoding APIs: Converting addresses to coordinates
- Marker Clustering: Handling many points efficiently
- Map Component: Pizza example in apps-sdk-examples
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Projects 1-3, basic geographic concepts
Real world outcome:
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ You: Find coffee shops near Union Square, San Francisco │
│ │
│ ChatGPT: Here are coffee shops near Union Square: │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 📍 Coffee Shops near Union Square │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ [Interactive Map with Markers] │ │ │
│ │ │ ☕ │ │ │
│ │ │ ☕ 📍 You are here │ │ │
│ │ │ ☕ ☕ │ │ │
│ │ │ ☕ │ │ │
│ │ │ [+] [-] │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Selected: Blue Bottle Coffee │ │
│ │ ⭐ 4.6 · $$ · 0.2 mi · Open until 7 PM │ │
│ │ [Directions] [Call] [Website] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ I found 12 coffee shops within 0.5 miles of Union Square. │
│ │
└─────────────────────────────────────────────────────────────────┘
Implementation Hints:
Map component with Mapbox:
import { useEffect, useRef, useState } from 'react';
import mapboxgl from 'mapbox-gl';
mapboxgl.accessToken = 'your-mapbox-token';
function CoffeeShopMap() {
const mapContainer = useRef(null);
const map = useRef(null);
const [selectedShop, setSelectedShop] = useState(null);
const data = window.openai?.toolOutput;
useEffect(() => {
if (map.current || !data) return;
map.current = new mapboxgl.Map({
container: mapContainer.current,
style: 'mapbox://styles/mapbox/streets-v12',
center: [data.center.lng, data.center.lat],
zoom: 14
});
// Add markers for each shop
data.shops.forEach(shop => {
const marker = new mapboxgl.Marker()
.setLngLat([shop.lng, shop.lat])
.addTo(map.current);
marker.getElement().addEventListener('click', () => {
setSelectedShop(shop);
});
});
}, [data]);
return (
<div className="flex flex-col h-full">
<div ref={mapContainer} className="flex-1 min-h-[300px]" />
{selectedShop && (
<div className="p-4 border-t">
<h3 className="font-bold">{selectedShop.name}</h3>
<p>⭐ {selectedShop.rating} · {selectedShop.distance} mi</p>
<div className="flex gap-2 mt-2">
<Button onClick={() => openDirections(selectedShop)}>
Directions
</Button>
<Button onClick={() => window.openai?.callTool('get_shop_details', { id: selectedShop.id })}>
Details
</Button>
</div>
</div>
)}
</div>
);
}
MCP tool for location search:
@mcp.tool()
def find_nearby_places(
query: str,
location: str,
radius_miles: float = 1.0,
limit: int = 20
) -> dict:
"""Use this when user wants to find places near a location."""
# Geocode the location
coords = geocode(location)
# Search for places
places = search_places_api(
query=query,
lat=coords['lat'],
lng=coords['lng'],
radius=radius_miles * 1609 # Convert to meters
)
return {
"structuredContent": {
"center": coords,
"shops": places[:limit],
"total": len(places),
"query": query,
"location": location
},
"uiComponent": {
"type": "iframe",
"url": "https://your-app.com/map-widget.html"
}
}
Learning milestones:
- Map renders with markers → You understand map integration
- Markers are interactive → You understand event handling
- Selection shows details → You understand component composition
- Actions trigger tools → You understand full interaction flow
Project 5: Form-Based Data Entry App
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: TypeScript/React
- Alternative Programming Languages: Vue, Svelte
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 2: Intermediate
- Knowledge Area: Forms / Validation / Data Entry
- Software or Tool: React Hook Form, Zod
- Main Book: “Form Design Patterns” by Adam Silver
What you’ll build: A CRM-style app for creating and editing contacts/leads with multi-step forms, validation, and confirmation flows—demonstrating write operations in ChatGPT.
Why it teaches ChatGPT Apps: Many apps need to create or edit data. This project teaches form design, validation, destructive action handling, and the confirmation flows required by OpenAI’s guidelines.
Core challenges you’ll face:
- Form validation in widgets → maps to client-side validation patterns
- Handling destructive actions → maps to destructiveHint annotations
- Multi-step forms → maps to widget state progression
- Error handling → maps to graceful failure UX
Key Concepts:
- React Hook Form: React Hook Form Documentation
- Zod Validation: Zod Documentation
- Destructive Hints: OpenAI requires confirmation for delete/update operations
- Form Accessibility: ARIA labels, error messages
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Projects 1-3, form handling experience
Real world outcome:
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ You: Add a new lead named John Smith from Acme Corp │
│ │
│ ChatGPT: I'll help you add this lead. Please complete the form: │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ➕ Add New Lead │ │
│ │ │ │
│ │ Step 1 of 3: Basic Information │ │
│ │ ━━━━━━━━━━━━●○○ │ │
│ │ │ │
│ │ First Name * │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ John │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Last Name * │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Smith │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Company │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Acme Corp │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ [Cancel] [Next →] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Implementation Hints:
Form with validation:
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button, Input } from '@openai/apps-sdk-ui';
const leadSchema = z.object({
firstName: z.string().min(1, "First name is required"),
lastName: z.string().min(1, "Last name is required"),
email: z.string().email("Invalid email").optional(),
company: z.string().optional(),
phone: z.string().optional(),
});
function LeadForm() {
const [step, setStep] = useState(1);
const prefill = window.openai?.toolInput; // Pre-filled from ChatGPT
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(leadSchema),
defaultValues: prefill
});
const onSubmit = (data) => {
// Call the create tool
window.openai?.callTool('create_lead', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)} className="p-4 space-y-4">
<h2>➕ Add New Lead</h2>
<div>
<label>First Name *</label>
<Input {...register('firstName')} />
{errors.firstName && (
<span className="text-red-500">{errors.firstName.message}</span>
)}
</div>
<div>
<label>Last Name *</label>
<Input {...register('lastName')} />
{errors.lastName && (
<span className="text-red-500">{errors.lastName.message}</span>
)}
</div>
<div>
<label>Email</label>
<Input type="email" {...register('email')} />
</div>
<div className="flex gap-2">
<Button type="button" variant="secondary" onClick={handleCancel}>
Cancel
</Button>
<Button type="submit">Create Lead</Button>
</div>
</form>
);
}
MCP tool with destructive hint:
@mcp.tool(
annotations={
"readOnlyHint": False,
"destructiveHint": False # Create is not destructive
}
)
def create_lead(
first_name: str,
last_name: str,
email: str = None,
company: str = None,
phone: str = None
) -> dict:
"""Use this when user wants to create a new lead in the CRM."""
lead = create_lead_in_database(first_name, last_name, email, company, phone)
return {
"structuredContent": {
"success": True,
"lead": lead,
"message": f"Lead {first_name} {last_name} created successfully"
}
}
@mcp.tool(
annotations={
"readOnlyHint": False,
"destructiveHint": True # Delete IS destructive - requires confirmation
}
)
def delete_lead(lead_id: str) -> dict:
"""Use this when user wants to delete a lead. This action is irreversible."""
# ChatGPT will ask for confirmation before calling this
delete_lead_from_database(lead_id)
return {
"structuredContent": {
"success": True,
"message": "Lead deleted successfully"
}
}
Learning milestones:
- Form renders with pre-filled data → You understand toolInput
- Validation shows errors → You understand client-side validation
- Submit calls create tool → You understand write operations
- Delete requires confirmation → You understand destructiveHint
Project 6: OAuth-Protected Integration App
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: TypeScript/Node.js
- Alternative Programming Languages: Python (FastAPI)
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 4. The “Open Core” Infrastructure
- Difficulty: Level 3: Advanced
- Knowledge Area: OAuth 2.1 / Authentication / Security
- Software or Tool: Passport.js, Auth0, or custom OAuth server
- Main Book: “OAuth 2 in Action” by Justin Richer & Antonio Sanso
What you’ll build: A GitHub-connected app that uses OAuth to access the user’s repositories, list issues, create PRs—demonstrating the full authentication flow required for real integrations.
Why it teaches ChatGPT Apps: Real apps connect to user accounts. This project teaches OAuth 2.1 with PKCE, token management, and how ChatGPT’s authentication flow works.
Core challenges you’ll face:
- Implementing OAuth server metadata → maps to well-known endpoints
- Dynamic client registration → maps to DCR flow
- Token validation → maps to JWT verification
- Handling auth failures → maps to WWW-Authenticate responses
Key Concepts:
- Apps SDK Auth Guide: Authentication Documentation
- OAuth 2.1: Updated OAuth specification with PKCE
- JWT Validation: Verifying tokens on every request
- Auth0/Okta Integration: Using existing identity providers
Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Projects 1-5, OAuth experience helpful
Real world outcome:
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ You: Show my GitHub repositories │
│ │
│ ChatGPT: To access your GitHub repositories, you'll need to │
│ connect your account. │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🔗 Connect to GitHub │ │
│ │ │ │
│ │ This app needs access to: │ │
│ │ • View your repositories │ │
│ │ • Read and write issues │ │
│ │ • Create pull requests │ │
│ │ │ │
│ │ [Connect GitHub Account] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ --- After connecting --- │
│ │
│ You: Show my GitHub repositories │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 📦 Your Repositories (12) │ │
│ │ │ │
│ │ ⭐ my-awesome-project │ │
│ │ TypeScript · Updated 2 hours ago · 3 open issues │ │
│ │ │ │
│ │ 📁 dotfiles │ │
│ │ Shell · Updated 1 week ago · 0 issues │ │
│ │ │ │
│ │ [View on GitHub] [Show Issues] [Create Issue] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Implementation Hints:
OAuth metadata endpoint:
# /.well-known/oauth-protected-resource
@app.get("/.well-known/oauth-protected-resource")
def oauth_metadata():
return {
"resource": "https://your-mcp-server.com",
"authorization_servers": [
"https://your-auth-server.com"
],
"bearer_methods_supported": ["header"],
"scopes_supported": ["read:repos", "write:issues", "read:user"]
}
Dynamic Client Registration:
# Your auth server must support DCR
@app.post("/oauth/register")
def register_client(request: ClientRegistrationRequest):
# ChatGPT registers itself as a client
client_id = generate_client_id()
client_secret = generate_client_secret() if request.token_endpoint_auth_method != "none" else None
save_client(client_id, client_secret, request.redirect_uris)
return {
"client_id": client_id,
"client_secret": client_secret,
"redirect_uris": request.redirect_uris,
"token_endpoint_auth_method": request.token_endpoint_auth_method
}
Token validation on every request:
from jose import jwt
def validate_token(token: str) -> dict:
try:
# Verify signature
payload = jwt.decode(
token,
get_public_key(),
algorithms=["RS256"],
audience="your-mcp-server",
issuer="https://your-auth-server.com"
)
# Check expiration
if payload["exp"] < time.time():
raise HTTPException(status_code=401, detail="Token expired")
# Check scopes
if "read:repos" not in payload.get("scope", "").split():
raise HTTPException(status_code=403, detail="Insufficient scope")
return payload
except jwt.JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
@mcp.tool(
annotations={"securitySchemes": ["oauth2"]}
)
def list_repositories(token: str = Header(...)) -> dict:
"""Use this when user wants to see their GitHub repositories."""
user = validate_token(token)
repos = fetch_github_repos(user["github_token"])
return {"structuredContent": {"repositories": repos}}
Handling auth failures:
@app.exception_handler(HTTPException)
def auth_exception_handler(request, exc):
if exc.status_code == 401:
return JSONResponse(
status_code=401,
content={
"error": "unauthorized",
"_meta": {
"mcp/www_authenticate": {
"scheme": "Bearer",
"realm": "github-integration",
"error": "invalid_token"
}
}
}
)
raise exc
Learning milestones:
- OAuth metadata is served → You understand discovery
- DCR creates clients → You understand dynamic registration
- Auth flow completes → You understand the full OAuth dance
- Protected tools work → You understand token validation
Project 7: Real-Time Dashboard App
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: TypeScript/React
- Alternative Programming Languages: Vue, Svelte
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 3: Advanced
- Knowledge Area: Data Visualization / Real-Time Updates
- Software or Tool: Recharts, D3.js, WebSockets
- Main Book: “The Visual Display of Quantitative Information” by Edward Tufte
What you’ll build: An analytics dashboard showing real-time metrics, charts, and KPIs—demonstrating data visualization and how to handle updating data in ChatGPT widgets.
Why it teaches ChatGPT Apps: Dashboards are a common use case. This project teaches charting, real-time updates, and how to present complex data in the widget format.
Core challenges you’ll face:
- Charting in iframe constraints → maps to library compatibility
- Real-time data updates → maps to polling vs. push strategies
- Responsive chart sizing → maps to widget dimension handling
- Data aggregation → maps to backend analytics patterns
Key Concepts:
- Recharts: Recharts Documentation
- D3.js: D3 Documentation
- Dashboard Design: Layout patterns for analytics
- Widget Fullscreen:
window.openai.requestFullscreen()
Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Projects 1-5, data visualization experience
Real world outcome:
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ You: Show me our website analytics for this week │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 📊 Analytics Dashboard - Dec 14-20, 2025 │ │
│ │ [⛶ Full] │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 👁️ Views │ │ 👤 Visitors │ │ ⏱️ Avg Time │ │ │
│ │ │ 45,231 │ │ 12,847 │ │ 3m 42s │ │ │
│ │ │ ↑ 12.3% │ │ ↑ 8.7% │ │ ↓ 5.2% │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ │ Daily Visitors │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ ▄ │ │ │
│ │ │ ▄█▄ ▄ │ │ │
│ │ │ ▄███ ▄█▄ ▄▄ │ │ │
│ │ │ ▄████▄ ▄███▄ ▄██▄ ▄▄ │ │ │
│ │ │▄██████▄▄█████▄▄████▄▄██▄▄ │ │ │
│ │ │ Mon Tue Wed Thu Fri Sat Sun │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ [📊 More Charts] [📥 Export] [🔄 Refresh] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ This week saw 45,231 page views with a 12.3% increase from │
│ last week. Your most popular page was /products. │
│ │
└─────────────────────────────────────────────────────────────────┘
Implementation Hints:
Dashboard with Recharts:
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
import { Button } from '@openai/apps-sdk-ui';
function AnalyticsDashboard() {
const data = window.openai?.toolOutput;
const [isFullscreen, setIsFullscreen] = useState(false);
const handleFullscreen = () => {
if (isFullscreen) {
window.openai?.exitFullscreen();
} else {
window.openai?.requestFullscreen();
}
setIsFullscreen(!isFullscreen);
};
return (
<div className="p-4">
<div className="flex justify-between items-center mb-4">
<h2>📊 Analytics Dashboard</h2>
<Button onClick={handleFullscreen}>
{isFullscreen ? '↙️ Exit' : '⛶ Full'}
</Button>
</div>
{/* KPI Cards */}
<div className="grid grid-cols-3 gap-4 mb-6">
<KPICard
title="Views"
value={data.totalViews}
change={data.viewsChange}
icon="👁️"
/>
<KPICard
title="Visitors"
value={data.uniqueVisitors}
change={data.visitorsChange}
icon="👤"
/>
<KPICard
title="Avg Time"
value={formatDuration(data.avgSessionTime)}
change={data.timeChange}
icon="⏱️"
/>
</div>
{/* Chart */}
<div className="h-64">
<h3>Daily Visitors</h3>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={data.dailyData}>
<XAxis dataKey="day" />
<YAxis />
<Tooltip />
<Bar dataKey="visitors" fill="#3b82f6" />
</BarChart>
</ResponsiveContainer>
</div>
<div className="flex gap-2 mt-4">
<Button onClick={() => window.openai?.callTool('get_detailed_analytics', {})}>
📊 More Charts
</Button>
<Button onClick={() => window.openai?.callTool('export_analytics', { format: 'csv' })}>
📥 Export
</Button>
</div>
</div>
);
}
MCP tool for analytics:
@mcp.tool()
def get_analytics(
start_date: str,
end_date: str,
metrics: list[str] = ["views", "visitors", "session_time"]
) -> dict:
"""Use this when user wants to see website analytics."""
analytics = fetch_analytics_data(start_date, end_date, metrics)
return {
"structuredContent": {
"totalViews": analytics.total_views,
"viewsChange": calculate_change(analytics.views, analytics.prev_views),
"uniqueVisitors": analytics.unique_visitors,
"visitorsChange": calculate_change(analytics.visitors, analytics.prev_visitors),
"avgSessionTime": analytics.avg_session_time,
"timeChange": calculate_change(analytics.time, analytics.prev_time),
"dailyData": [
{"day": "Mon", "visitors": 1800},
{"day": "Tue", "visitors": 2100},
# ...
]
},
"uiComponent": {
"type": "iframe",
"url": "https://your-app.com/analytics-dashboard.html"
}
}
Learning milestones:
- Charts render correctly → You understand visualization in widgets
- Fullscreen toggle works → You understand display modes
- KPIs update from data → You understand dynamic content
- Export/refresh work → You understand tool chaining
Project 8: E-Commerce Shopping App
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: TypeScript/React
- Alternative Programming Languages: Vue, Next.js
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 4. The “Open Core” Infrastructure
- Difficulty: Level 3: Advanced
- Knowledge Area: E-Commerce / Cart / Checkout
- Software or Tool: Stripe (for demo), Cart state management
- Main Book: “Designing Web Usability” by Jakob Nielsen
What you’ll build: A complete shopping experience: browse products, add to cart, view cart, and checkout—demonstrating the Shop component pattern and commerce restrictions.
Why it teaches ChatGPT Apps: E-commerce is a primary use case for ChatGPT Apps (physical goods only, per guidelines). This project teaches cart state, multi-step flows, and commerce compliance.
Core challenges you’ll face:
- Cart state persistence → maps to widgetSessionId pattern
- Multi-widget checkout flow → maps to tool chaining
- Commerce restrictions → maps to OpenAI guidelines compliance
- Order confirmation → maps to destructive action patterns
Key Concepts:
- Shopping Cart Example: apps-sdk-examples
- Widget Session ID: Persisting cart across widget instances
- Commerce Guidelines: Physical goods only, no digital products
- Stripe Integration: Payment handling (demo mode)
Difficulty: Advanced Time estimate: 3-4 weeks Prerequisites: Projects 1-7, e-commerce experience helpful
Real world outcome:
┌─────────────────────────────────────────────────────────────────┐
│ You: I want to buy some running shoes │
│ │
│ [Product search widget showing shoes] │
│ │
│ You: Add the Nike Pegasus to my cart │
│ │
│ ChatGPT: Added to cart! Here's your current cart: │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🛒 Your Cart (1 item) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ [IMG] Nike Air Zoom Pegasus 40 │ │ │
│ │ │ Size: 10 · Color: Black │ │ │
│ │ │ $89.99 [- 1 +] [🗑️] │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ─────────────────────────────────────────────────────── │ │
│ │ Subtotal: $89.99 │ │
│ │ Shipping: $5.99 │ │
│ │ Tax: $7.92 │ │
│ │ ─────────────────────────────────────────────────────── │ │
│ │ Total: $103.90 │ │
│ │ │ │
│ │ [Continue Shopping] [Proceed to Checkout →] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ You: Checkout │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 📦 Checkout │ │
│ │ │ │
│ │ Shipping Address: │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ John Doe │ │ │
│ │ │ 123 Main St, Apt 4 │ │ │
│ │ │ San Francisco, CA 94102 │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Payment: •••• 4242 [Change] │ │
│ │ │ │
│ │ [← Back] [Place Order] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Implementation Hints:
Cart state with widgetSessionId:
function ShoppingCart() {
// widgetSessionId persists across widget instances in same conversation
const sessionId = window.openai?.widgetSessionId;
const [cart, setCart] = useState([]);
useEffect(() => {
// Load cart from server using session ID
if (sessionId) {
fetch(`/api/cart/${sessionId}`)
.then(res => res.json())
.then(setCart);
}
}, [sessionId]);
const updateQuantity = async (itemId, quantity) => {
// Update on server
await fetch(`/api/cart/${sessionId}/items/${itemId}`, {
method: 'PATCH',
body: JSON.stringify({ quantity })
});
// Refresh cart
const updated = await fetch(`/api/cart/${sessionId}`).then(r => r.json());
setCart(updated);
};
const checkout = () => {
window.openai?.callTool('start_checkout', {
sessionId,
items: cart.items
});
};
// ... render cart UI
}
MCP tools for shopping:
@mcp.tool()
def add_to_cart(
session_id: str,
product_id: str,
quantity: int = 1,
size: str = None,
color: str = None
) -> dict:
"""Use this when user wants to add a product to their cart."""
cart = get_or_create_cart(session_id)
cart.add_item(product_id, quantity, size, color)
return {
"structuredContent": {
"success": True,
"cart": cart.to_dict(),
"message": f"Added {quantity} item(s) to cart"
},
"uiComponent": {
"type": "iframe",
"url": f"https://your-app.com/cart.html?session={session_id}"
}
}
@mcp.tool(
annotations={
"readOnlyHint": False,
"destructiveHint": True # Placing order is irreversible
}
)
def place_order(
session_id: str,
shipping_address_id: str,
payment_method_id: str
) -> dict:
"""Use this when user confirms they want to place their order."""
cart = get_cart(session_id)
order = create_order(cart, shipping_address_id, payment_method_id)
# Clear cart after successful order
clear_cart(session_id)
return {
"structuredContent": {
"success": True,
"orderId": order.id,
"total": order.total,
"estimatedDelivery": order.estimated_delivery,
"message": "Order placed successfully!"
}
}
Learning milestones:
- Cart persists across turns → You understand widgetSessionId
- Add/remove items work → You understand cart operations
- Checkout flow completes → You understand multi-step processes
- Order confirmation works → You understand commerce patterns
Project 9: Full App Store Submission
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: TypeScript + Python
- Alternative Programming Languages: N/A
- Coolness Level: Level 5: Pure Magic
- Business Potential: 5. The “Industry Disruptor”
- Difficulty: Level 4: Expert
- Knowledge Area: Production Deployment / App Store
- Software or Tool: Vercel/Railway, monitoring, analytics
- Main Book: “Release It!” by Michael Nygard
What you’ll build: Take one of your previous projects to production quality: proper error handling, logging, monitoring, tests, documentation, and submit to the ChatGPT app store.
Why it teaches ChatGPT Apps: The final step is shipping to real users. This project teaches production readiness, the review process, and reaching 800+ million ChatGPT users.
Core challenges you’ll face:
- Meeting submission guidelines → maps to quality bar requirements
- Developer verification → maps to identity and contact info
- Error handling and reliability → maps to production resilience
- Review process → maps to addressing feedback
Key Concepts:
- Submission Guidelines: App Submission Guidelines
- What Makes a Great App: Best Practices
- Developer Verification: OpenAI Platform Dashboard
- Production Monitoring: Error tracking, uptime monitoring
Difficulty: Expert Time estimate: 2-4 weeks Prerequisites: All previous projects, production deployment experience
Real world outcome:
App Submission Checklist ✓
═══════════════════════════════════════════════════
PRE-SUBMISSION
══════════════
✓ Developer identity verified on OpenAI Platform
✓ Customer support email configured
✓ Privacy policy URL active
✓ Terms of service URL active
FUNCTIONALITY
═════════════
✓ App serves clear purpose beyond native ChatGPT
✓ All tools work reliably without crashes
✓ Error states handled gracefully
✓ Loading states shown appropriately
✓ No demo/placeholder content
TOOL QUALITY
════════════
✓ Tool names are descriptive verbs
✓ Descriptions start with "Use this when..."
✓ Parameters use appropriate types/enums
✓ Annotations correctly mark read-only vs destructive
✓ Outputs are structured and reusable
SECURITY
════════
✓ OAuth 2.1 implemented correctly (if applicable)
✓ Token validation on every request
✓ No sensitive data in tool outputs
✓ Minimal data collection
✓ No payment card info, health data, or API keys
COMMERCE (if applicable)
════════════════════════
✓ Physical goods only
✓ No prohibited items
✓ Clear pricing displayed
TESTING
═══════
✓ Tested on ChatGPT web
✓ Tested on ChatGPT mobile
✓ Tested both direct and indirect prompts
✓ Tested error scenarios
✓ Tested with real user accounts
SUBMISSION
══════════
✓ App name and description written
✓ Icon/logo uploaded
✓ Category selected
✓ Screenshots captured
[Submit for Review]
───────────────────────────────────────────────────
Review Status: Under Review
Submitted: Dec 20, 2025
Expected response: 5-10 business days
Update (Dec 24, 2025):
✓ App Approved!
🎉 Now available to 800M+ ChatGPT users
Implementation Hints:
Submission preparation checklist:
1. VERIFY DEVELOPER IDENTITY
- Go to platform.openai.com
- Complete identity verification
- Add customer support email
2. PREPARE METADATA
- App name (unique, descriptive)
- Short description (1-2 sentences)
- Long description (features, use cases)
- Category selection
- Icon (512x512px)
- Screenshots (1280x720px)
3. LEGAL REQUIREMENTS
- Privacy policy URL
- Terms of service URL
- Third-party license compliance
4. QUALITY ASSURANCE
- Test all tools thoroughly
- Verify error handling
- Check mobile compatibility
- Test authentication flows
- Validate destructive actions
Common rejection reasons to avoid:
1. TOOL ANNOTATIONS
✗ Missing readOnlyHint/destructiveHint
✗ Incorrect annotations (marking delete as read-only)
2. RELIABILITY
✗ Crashes or hangs
✗ Inconsistent behavior
✗ Unhandled errors
3. QUALITY
✗ Demo/placeholder content
✗ Incomplete features
✗ Misleading descriptions
4. SECURITY
✗ Missing OAuth token validation
✗ Overly broad data requests
✗ Sensitive data exposure
5. COMPLIANCE
✗ Digital products for sale
✗ Age-inappropriate content
✗ Prohibited items
Production infrastructure:
# Example deployment architecture
Frontend (Widget):
- Vercel / Cloudflare Pages
- CDN for assets
- Error tracking (Sentry)
Backend (MCP Server):
- Railway / Render / AWS
- Auto-scaling
- Health checks
- Logging (Datadog/Logtail)
Monitoring:
- Uptime monitoring
- Error rate alerts
- Response time tracking
- Usage analytics
Learning milestones:
- All guidelines met → You understand quality requirements
- Submission accepted → You pass initial review
- Feedback addressed → You handle revision requests
- App published → You’ve reached 800M+ users!
Project Comparison Table
| Project | Difficulty | Time | Depth of Understanding | Fun Factor |
|---|---|---|---|---|
| 1. MCP Protocol Explorer | Beginner | Weekend | ⭐⭐⭐ | ⭐⭐⭐ |
| 2. Hello World Widget | Beginner | Weekend | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 3. List & Search App | Intermediate | 1-2 weeks | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 4. Map & Location App | Intermediate | 1-2 weeks | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 5. Form-Based Data Entry | Intermediate | 1-2 weeks | ⭐⭐⭐ | ⭐⭐⭐ |
| 6. OAuth Integration | Advanced | 2-3 weeks | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 7. Real-Time Dashboard | Advanced | 2-3 weeks | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 8. E-Commerce App | Advanced | 3-4 weeks | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 9. App Store Submission | Expert | 2-4 weeks | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
Recommended Learning Path
If you’re new to ChatGPT Apps:
Start with: Project 1 → 2 → 3
This path teaches MCP fundamentals, then your first widget, then list patterns—the foundation for any app.
If you’re a frontend developer:
Start with: Project 2 → 3 → 4 → 7
Focus on widget development, skipping deeper backend topics initially.
If you’re a backend developer:
Start with: Project 1 → 6 → 5 → 8
Focus on MCP servers, authentication, and data operations.
If you want to publish an app ASAP:
Fast path: Projects 1 → 2 → 3 → 9
Build fundamentals quickly, then polish one app for submission.
Final Capstone Project: AI-Powered Productivity Suite
- File: LEARN_CHATGPT_APPS_DEEP_DIVE.md
- Main Programming Language: TypeScript + Python
- Alternative Programming Languages: N/A
- Coolness Level: Level 5: Pure Magic
- Business Potential: 5. The “Industry Disruptor”
- Difficulty: Level 5: Master
- Knowledge Area: Full-Stack AI Integration
- Software or Tool: All previous + AI APIs
- Main Book: “Designing AI Products” + all previous
What you’ll build: A comprehensive productivity app connecting multiple services (calendar, email, tasks, notes) with AI-powered features like smart scheduling, email drafting, and task prioritization—the ultimate ChatGPT app.
Why this is the capstone: This combines everything: OAuth for multiple services, complex UIs, real-time updates, forms, commerce (for premium features), and production deployment.
Summary
| # | Project | Main Language |
|---|---|---|
| 1 | MCP Protocol Explorer | Python |
| 2 | Hello World Widget | TypeScript/React |
| 3 | Interactive List & Search App | TypeScript/React |
| 4 | Map & Location-Based App | TypeScript/React |
| 5 | Form-Based Data Entry App | TypeScript/React |
| 6 | OAuth-Protected Integration App | TypeScript/Node.js |
| 7 | Real-Time Dashboard App | TypeScript/React |
| 8 | E-Commerce Shopping App | TypeScript/React |
| 9 | Full App Store Submission | TypeScript + Python |
| Capstone | AI-Powered Productivity Suite | Full Stack |
Additional Resources
Official Documentation
- Apps SDK Home - Main documentation
- Apps SDK Quickstart - Getting started
- App Submission Guidelines - Requirements
- What Makes a Great App - Best practices
GitHub Repositories
- openai-apps-sdk-examples - Official examples
- apps-sdk-ui - UI component library
Model Context Protocol
- MCP Specification - Official protocol
- MCP GitHub - SDKs and tools
- Anthropic MCP Course - Learning resource
Tutorials
- MCPcat Quickstart (Python) - 15-minute guide
- MCPcat Quickstart (TypeScript) - 15-minute guide
- DeepWiki Examples Analysis - Code walkthrough
Community
- OpenAI Developer Forum - Q&A and discussions
- MCP Discord - Protocol community
Generated for your ChatGPT Apps learning journey. Build something amazing and reach 800 million users!