Oai App Composer
MCP server: Oai App Composer
Installation
npx oai-app-composerAsk AI about Oai App Composer
Powered by Claude Β· Grounded in docs
I know everything about Oai App Composer. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
Component Development Tool
A Storybook-like IDE for developing and testing ChatGPT MCP (Model Context Protocol) components in isolation. Build, preview, and export custom UI components that integrate with ChatGPT Apps SDK.
Features
- π¨ Component Development: Build React components with hot-reload and live preview
- π¦ Monorepo Structure: Organize components in isolated directories with their own mock data
- π Mock Data Management: Create and edit multiple mock states using Monaco Editor
- π Sandboxed Preview: Test components in iframe with ChatGPT-like environment
- π window.openai API: Full implementation matching ChatGPT's behavior for state management
- π€ Export: Generate MCP server code (Node.js/Python) for production deployment
- πΎ State Management: Supports both ephemeral UI state and cross-session state patterns
Table of Contents
- Installation
- Quick Start
- Architecture
- Creating Components
- State Management
- Mock Data
- Exporting Components
- Project Structure
- API Reference
Additional Documentation
docs/GETTING_STARTED.mdβ environment setup and first run walkthroughdocs/COMPONENT_TEMPLATES.mdβ sample manifest and component blueprintsdocs/CONTRIBUTING.md&docs/AGENTS.mdβ contribution workflow and agent-specific guidancedocs/PROJECT_SUMMARY.md&docs/ENHANCED_API_SUMMARY.mdβ architecture overview and API surface
Installation
# Install dependencies
npm install
# Start development server
npm run dev
The tool will start on http://localhost:3000
Quick Start
1. Create Your First Component
- Click "New Component" in the sidebar
- Enter a name and description
- The tool will scaffold a complete component with:
manifest.json- Component metadatacomponent.tsx- React component boilerplatemocks/- Mock data directory with default states
2. Edit Mock Data
- Select your component from the sidebar
- Use the Mock Data Editor on the right to edit JSON data
- Switch between different mock states to test various scenarios
- Changes are persisted automatically
3. Preview Your Component
The component renders in real-time with the selected mock data, exactly as it would appear in ChatGPT.
4. Export for Production
- Click "Export" in the toolbar
- Choose Node.js or Python
- Copy the generated MCP server code
- Deploy to your production environment
Architecture
The tool is built with modern web technologies and follows the ChatGPT Apps SDK architecture:
βββββββββββββββββββββββββββββββββββββββββββββββ
β Development Tool (Host) β
β ββββββββββββββ ββββββββββββββββββββββββ β
β β Sidebar β β Canvas + Editor β β
β β β β β β
β β Component β β ββββββββββββββββββ β β
β β List β β β Sandboxed β β β
β β β β β iframe β β β
β β β β β β β β
β ββββββββββββββ β β window.openai β β β
β β β - toolOutput β β β
β β β - callTool β β β
β β β - getState β β β
β β β - setState β β β
β β ββββββββββββββββββ β β
β ββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββ
Key Components
- Component Registry: Auto-discovers components from
/componentsdirectory - Iframe Bridge: Implements
window.openaiAPI via PostMessage - Widget State Store: IndexedDB-based storage for ephemeral UI state
- Component Builder: Transforms TSX to runnable JavaScript bundle
- File System Service: Manages component files via Vite middleware
Creating Components
Component Structure
Each component lives in its own directory:
components/
my-component/
βββ manifest.json # Component metadata
βββ component.tsx # React component
βββ mocks/
βββ default.json # Default mock state
βββ empty.json # Empty state
βββ loaded.json # Loaded state
manifest.json
{
"name": "My Component",
"description": "A custom widget for ChatGPT",
"version": "1.0.0",
"entry": "component.tsx",
"metadata": {
"prefersBorder": true,
"csp": {
"connect_domains": [],
"resource_domains": []
}
}
}
component.tsx
import { useEffect, useState } from 'react';
import { useToolOutput } from '@/hooks/useOpenAiGlobal';
const DEFAULT_OUTPUT = { title: '', items: [] };
export default function MyComponent() {
const toolOutput = useToolOutput<typeof DEFAULT_OUTPUT>() ?? DEFAULT_OUTPUT;
const [widgetState, setWidgetState] = useState<any>({});
// Load widget state (ephemeral UI state)
useEffect(() => {
if (window.openai?.getWidgetState) {
window.openai.getWidgetState(window.openai.widgetId).then((state) => {
if (state) setWidgetState(state);
});
}
}, []);
// Save widget state when it changes
const updateWidgetState = (newState: any) => {
setWidgetState(newState);
window.openai?.setWidgetState?.(window.openai.widgetId, newState);
};
return (
<div>
<h1>{toolOutput.title || 'My Component'}</h1>
{/* Your component UI */}
</div>
);
}
State Management
The tool implements the ChatGPT Apps SDK state management patterns:
1. Business Data (toolOutput)
Source of truth data from the MCP server, available via window.openai.toolOutput and kept in sync through the openai:set_globals event. The toolkit provides a helper hook:
import { useToolOutput } from '@/hooks/useOpenAiGlobal';
const toolOutput = useToolOutput<MyData>();
2. UI State (Ephemeral)
Ephemeral UI state scoped to the widget instance, persisted in IndexedDB:
const [widgetState, setWidgetState] = useState({ selectedId: null });
// Load state
useEffect(() => {
window.openai.getWidgetState(window.openai.widgetId).then((state) => {
if (state) setWidgetState(state);
});
}, []);
// Save state
const updateState = (newState) => {
setWidgetState(newState);
window.openai.setWidgetState(window.openai.widgetId, newState);
};
3. Cross-Session State
For preferences that persist across sessions, use tool calls:
const savePreferences = async (prefs) => {
const result = await window.openai.callTool('save_preferences', { preferences: prefs });
// Handle result
};
Mock Data
Mock data files are JSON files that represent different states of your component's data:
default.json
{
"_description": "Default state with sample data",
"title": "My Component",
"items": [
{ "id": 1, "name": "Item 1" },
{ "id": 2, "name": "Item 2" }
]
}
empty.json
{
"_description": "Empty state with no data",
"title": "My Component",
"items": []
}
The _description field is optional metadata that doesn't appear in window.openai.toolOutput.
Creating New Mock States
- Click "+ New State" in the Mock Data Editor
- Enter a name (e.g., "error", "loading", "success")
- Edit the JSON data
- Click "Create"
Exporting Components
Generate MCP Server Code
-
Click "Export" in the component toolbar
-
Choose your framework:
- Node.js / TypeScript: Using
@modelcontextprotocol/sdk - Python: Using FastMCP
- Node.js / TypeScript: Using
-
The tool generates complete MCP server code including:
- Resource registration for your component HTML
- Tool definition with proper metadata
- Content Security Policy configuration
- Widget state management hooks
Deployment Steps
- Copy the generated code to your MCP server
- Build your component assets
- Place built files in your server's public directory
- Deploy to production (Heroku, Vercel, Railway, etc.)
- Connect from ChatGPT using your server URL
Reference: OpenAI Apps SDK Deployment Guide
Project Structure
/
βββ src/
β βββ components/ # UI components
β β βββ Sidebar.tsx # Component list navigation
β β βββ Canvas.tsx # Main preview area
β β βββ ComponentFrame.tsx # Sandboxed iframe
β β βββ MockDataEditor.tsx # JSON editor
β β βββ ComponentCreator.tsx # Component scaffolding
β β βββ ExportModal.tsx # Export functionality
β βββ hooks/ # React hooks
β β βββ useComponentLoader.ts
β β βββ useMockData.ts
β β βββ useHotReload.ts
β βββ services/ # Core services
β β βββ fileSystem.ts # File operations
β β βββ componentRegistry.ts # Component discovery
β β βββ componentBuilder.ts # Build pipeline
β β βββ iframeBridge.ts # PostMessage communication
β β βββ widgetStateStore.ts # IndexedDB storage
β βββ store/
β β βββ appStore.ts # Zustand global state
β βββ types/
β βββ component.ts # Component types
β βββ openai.ts # window.openai API types
βββ components/ # User components (monorepo)
β βββ example-kanban/ # Example component
βββ public/
βββ iframe-loader.html # Iframe bootstrap
API Reference
window.openai API
The tool provides a complete implementation of the ChatGPT Apps SDK window.openai API:
window.openai.toolOutput
const toolOutput: Record<string, any>
The structured data from the MCP server tool response.
window.openai.widgetId
const widgetId: string
Unique identifier for the widget instance.
window.openai.callTool(toolName, args)
async function callTool(
toolName: string,
args: Record<string, any>
): Promise<OpenAIToolCallResult>
Call a tool on the MCP server.
window.openai.getWidgetState(widgetId)
async function getWidgetState<T>(widgetId: string): Promise<T | null>
Get persisted UI state for this widget instance.
window.openai.setWidgetState(widgetId, state)
async function setWidgetState<T>(widgetId: string, state: T): Promise<void>
Save UI state for this widget instance.
window.openai.openExternal(url)
function openExternal(url: string): void
Open an external URL (punchout behavior).
File System Service
import { fileSystem } from '@/services/fileSystem';
// Read file
const content = await fileSystem.readFile('path/to/file.txt');
// Write file
await fileSystem.writeFile('path/to/file.txt', 'content');
// Read JSON
const data = await fileSystem.readJSON('path/to/file.json');
// Write JSON
await fileSystem.writeJSON('path/to/file.json', { key: 'value' });
// List directory
const files = await fileSystem.listDirectory('path/to/dir');
// Create directory
await fileSystem.createDirectory('path/to/dir');
Component Registry Service
import { componentRegistry } from '@/services/componentRegistry';
// Load all components
const registry = await componentRegistry.loadRegistry();
// Get specific component
const component = await componentRegistry.getComponent('component-id');
// Refresh registry
await componentRegistry.refresh();
// Add mock state
await componentRegistry.addMockState('component-id', mockState);
// Update mock state
await componentRegistry.updateMockState('component-id', 'state-name', data);
Development
Running Tests
npm run test
Building for Production
npm run build
Linting
npm run lint
Resources
- OpenAI Apps SDK Documentation
- Model Context Protocol Spec
- State Management Guide
- Custom UX Reference
License
MIT
Contributing
Contributions are welcome! Please read the contributing guidelines before submitting PRs.
Built with β€οΈ for the ChatGPT developer community
