Skip to main content Startseite Ersteller diegosouzapw awesome-omni-skill chatgpt-app-builder
chatgpt-app-builder Build ChatGPT apps with interactive widgets using mcp-use and OpenAI Apps SDK. Use when creating ChatGPT apps, building MCP servers with widgets, defining React widgets, working with Apps SDK, or when user mentions ChatGPT widgets, mcp-use widgets, or Apps SDK development.
Zur Installation springen Skills Marktplatz Entdecken und erkunden Sie KI-Skills, die von der Community erstellt wurden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Prompt kopierenPrompt-Details anzeigen Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
npx skills add https://github.com/diegosouzapw/awesome-omni-skill --skill chatgpt-app-builderDer Befehl bleibt in einer Zeile. Scrollen Sie horizontal, um ihn vor dem Kopieren vollständig zu prüfen.
Sie bevorzugen eine lokale Kopie? Laden Sie die Dateien herunter, die SkillsMP derzeit vorliegen.
ZIP herunterladen Herunterladen... Mehr aus diesem Repository
Verwandte Berufe SOC
Basierend auf der SOC-Berufsklassifikation
name chatgpt-app-builder description Build ChatGPT apps with interactive widgets using mcp-use and OpenAI Apps SDK. Use when creating ChatGPT apps, building MCP servers with widgets, defining React widgets, working with Apps SDK, or when user mentions ChatGPT widgets, mcp-use widgets, or Apps SDK development.
ChatGPT App Builder
Build production-ready ChatGPT apps with interactive widgets using mcp-use. Zero-config widget development with automatic registration and built-in React hooks.
Quick Start
npx create-mcp-use-app my-chatgpt-app --template mcp-apps
cd my-chatgpt-app
npm install && npm run dev
Project structure:
my-chatgpt-app/
├── resources/ # React widgets (auto-registered!)
│ ├── weather-display.tsx # Example widget
│ └── product-card.tsx # Another widget
├── public/ # Static assets
│ └── images/
├── index.ts # MCP server entry
├── package.json
└── tsconfig.json
Why mcp-use for ChatGPT Apps?
Traditional OpenAI Apps SDK requires significant manual setup:
Separate project structure (server/ and web/ folders)
Manual esbuild/webpack configuration
Custom useWidgetState hook implementation
Manual React mounting code
Manual CSP configuration
Manual widget registration
mcp-use simplifies everything:
Single command setup
Drop widgets in resources/ folder - auto-registered
Built-in useWidget() hook with state, props, tool calls
Automatic bundling with hot reload
Automatic CSP configuration
Built-in Inspector for testing
Dual-protocol support (works with ChatGPT AND MCP Apps clients)
MCP Apps vs ChatGPT Apps SDK
Protocol Use Case Compatibility Status MCP Apps (type: "mcpApps")Maximum compatibility ChatGPT + MCP Apps clients Recommended ChatGPT Apps SDK (type: "appsSdk")ChatGPT-only features ChatGPT only Supported
Why MCP Apps? It's the official standard (SEP-1865) for interactive widgets:
Universal : Works with ChatGPT, Claude Desktop, Goose, and all MCP Apps clients
Future-proof : Based on open specification
Zero config : With type: "mcpApps", mcp-use generates metadata for BOTH protocols automatically
Creating Widgets
Simple Widget (Single File) Create resources/weather-display.tsx:
import { McpUseProvider , useWidget, type WidgetMetadata } from "mcp-use/react" ;
import { z } from "zod" ;
export const widgetMetadata : WidgetMetadata = {
description : "Display current weather for a city" ,
props : z.object ({
city : z.string ().describe ("City name" ),
temperature : z.number ().describe ("Temperature in Celsius" ),
conditions : z.string ().describe ("Weather conditions" ),
humidity : z.number ().describe ("Humidity percentage" ),
}),
};
const WeatherDisplay : React .FC = () => {
const { props, isPending } = useWidget ();
if (isPending) {
return (
<McpUseProvider autoSize >
<div className ="animate-pulse p-4" > Loading weather...</div >
</McpUseProvider >
);
}
return (
<McpUseProvider autoSize >
<div className ="weather-card p-4 rounded-lg shadow" >
<h2 className ="text-2xl font-bold" > {props.city}</h2 >
<div className ="temp text-4xl" > {props.temperature}°C</div >
<p className ="conditions" > {props.conditions}</p >
<p className ="humidity" > Humidity: {props.humidity}%</p >
</div >
</McpUseProvider >
);
};
export default WeatherDisplay ;
Registered as MCP tool weather-display
Registered as MCP resource ui://widget/weather-display.html
Bundled for Apps SDK compatibility
Complex Widget (Folder Structure) For widgets with multiple components:
resources/
└── product-search/
├── widget.tsx # Entry point (required name)
├── components/
│ ├── ProductCard.tsx
│ └── FilterBar.tsx
├── hooks/
│ └── useFilter.ts
└── types.ts
Entry point must be named widget.tsx and export widgetMetadata + default component.
Widget Metadata export const widgetMetadata : WidgetMetadata = {
description : "Display weather information" ,
props : z.object ({
city : z.string ().describe ("City name" ),
temperature : z.number (),
}),
exposeAsTool : true ,
metadata : {
csp : {
connectDomains : ["https://api.weather.com" ],
resourceDomains : ["https://cdn.weather.com" ],
},
prefersBorder : true ,
autoResize : true ,
widgetDescription : "Interactive weather display" ,
},
};
description: Used for tool and resource descriptions
props: Zod schema defines widget input parameters
exposeAsTool: Set to false if only using widget via custom tools
metadata: Unified configuration for both protocols (recommended)
Content Security Policy (CSP) Control external resources your widget can access:
export const widgetMetadata : WidgetMetadata = {
description : "Weather widget" ,
props : z.object ({ city : z.string () }),
metadata : {
csp : {
connectDomains : ["https://api.weather.com" , "https://backup.weather.com" ],
resourceDomains : ["https://cdn.weather.com" ],
frameDomains : ["https://embed.weather.com" ],
scriptDirectives : ["'unsafe-inline'" ],
},
},
};
Specify exact domains: https://api.weather.com
Avoid wildcards in production
Never use 'unsafe-eval' unless necessary
useWidget Hook const {
props,
isPending,
state,
setState,
theme,
callTool,
displayMode,
requestDisplayMode,
output,
} = useWidget<MyPropsType , MyOutputType >();
Props and Loading States Critical: Widgets render BEFORE tool execution completes. Always handle isPending:
const { props, isPending } = useWidget<WeatherProps >();
if (isPending) {
return <div > Loading...</div > ;
}
return (
<div >
{isPending ? <LoadingSpinner /> : <div > {props.city}</div > }
</div >
);
return (
<div >
<h1 > {props.city ?? "Loading..."}</h1 >
</div >
);
Widget State Persist data across widget interactions:
const { state, setState } = useWidget ();
const addFavorite = async (city : string ) => {
await setState ({
favorites : [...(state?.favorites || []), city],
});
};
await setState ((prev ) => ({
...prev,
count : (prev?.count || 0 ) + 1 ,
}));
Calling MCP Tools Widgets can call other tools:
const { callTool } = useWidget ();
const refreshData = async ( ) => {
try {
const result = await callTool ("get-weather" , { city : "Tokyo" });
console .log ("Result:" , result.content );
} catch (error) {
console .error ("Tool call failed:" , error);
}
};
Display Mode Control const { displayMode, requestDisplayMode } = useWidget ();
const goFullscreen = async ( ) => {
await requestDisplayMode ("fullscreen" );
};
console .log (displayMode);
Custom Tools with Widgets Create tools that return widgets:
import { MCPServer , widget, text } from "mcp-use/server" ;
import { z } from "zod" ;
const server = new MCPServer ({
name : "weather-app" ,
version : "1.0.0" ,
baseUrl : process.env .MCP_URL || "http://localhost:3000" ,
});
server.tool (
{
name : "get-weather" ,
description : "Get current weather for a city" ,
schema : z.object ({
city : z.string ().describe ("City name" ),
}),
widget : {
name : "weather-display" ,
invoking : "Fetching weather..." ,
invoked : "Weather data loaded" ,
},
},
async ({ city }) => {
const data = await fetchWeatherAPI (city);
return widget ({
props : {
city,
temperature : data.temp ,
conditions : data.conditions ,
humidity : data.humidity ,
},
output : text (`Weather in ${city} : ${data.temp} °C` ),
message : `Current weather for ${city} ` ,
});
}
);
server.listen ();
baseUrl in server config enables proper asset loading
widget: { name, invoking, invoked } on tool definition
widget({ props, output }) helper returns runtime data
props passed to widget, output shown to model
Static Assets Use public/ folder for images, fonts:
my-app/
├── resources/
├── public/
│ ├── images/
│ │ ├── logo.svg
│ │ └── banner.png
│ └── fonts/
└── index.ts
import { Image } from "mcp-use/react" ;
function MyWidget ( ) {
return (
<div >
{/* Paths relative to public/ folder */}
<Image src ="/images/logo.svg" alt ="Logo" />
<img src ={window.__getFile?.( "images /banner.png ")} alt ="Banner" />
</div >
);
}
Components
McpUseProvider Unified provider combining all common setup:
import { McpUseProvider } from "mcp-use/react" ;
function MyWidget ( ) {
return (
<McpUseProvider
autoSize // Auto-resize widget
viewControls // Add debug /fullscreen buttons
debug // Show debug info
>
<div > Widget content</div >
</McpUseProvider >
);
}
Image Component Handles both data URLs and public paths:
import { Image } from "mcp-use/react" ;
<Image src ="/images/photo.jpg" alt ="Photo" />
<Image src ="data:image/png;base64,..." alt ="Data URL" />
ErrorBoundary import { ErrorBoundary } from "mcp-use/react" ;
<ErrorBoundary
fallback ={ <div > Something went wrong</div > }
onError={(error) => console.error(error)}
>
<MyComponent />
</ErrorBoundary >
Testing
Using the Inspector
Start development: npm run dev
Open http://localhost:3000/inspector
Click Tools tab → Find your widget → Enter parameters → Execute
Debug with browser console, RPC logs, state inspection
Testing in ChatGPT
Enable Developer Mode: Settings → Connectors → Advanced → Developer mode
Add your server: Connectors tab → Add remote MCP server URL
Test: Select Developer Mode from Plus menu → Choose connector → Use tools
Be explicit: "Use the weather-app connector's get-weather tool..."
Disallow alternatives: "Do not use built-in tools, only use my connector"
Specify input: "Call get-weather with { city: 'Tokyo' }"
Dual-protocol note: With type: "mcpApps", widgets work in both ChatGPT and MCP Apps clients without code changes.
Best Practices
Schema Design
const schema = z.object ({
city : z.string ().describe ("City name (e.g., Tokyo, Paris)" ),
temperature : z.number ().min (-50 ).max (60 ).describe ("Temp in Celsius" ),
});
const schema = z.object ({
city : z.string (),
temp : z.number (),
});
Theme Support const { theme } = useWidget ();
const bgColor = theme === "dark" ? "bg-gray-900" : "bg-white" ;
const textColor = theme === "dark" ? "text-white" : "text-gray-900" ;
Loading States Always check isPending first:
const { props, isPending } = useWidget<MyProps >();
if (isPending) return <LoadingSpinner /> ;
return <div > {props.field}</div > ;
Widget Focus Keep widgets focused on one thing:
export const widgetMetadata : WidgetMetadata = {
description : "Display weather for a city" ,
props : z.object ({ city : z.string () }),
};
export const widgetMetadata : WidgetMetadata = {
description : "Weather, forecast, map, news, and more" ,
props : z.object ({ }),
};
Error Handling const { callTool } = useWidget ();
const fetchData = async ( ) => {
try {
const result = await callTool ("fetch-data" , { id : "123" });
if (result.isError ) {
console .error ("Tool returned error" );
}
} catch (error) {
console .error ("Tool call failed:" , error);
}
};
Configuration
Production Setup const server = new MCPServer ({
name : "my-app" ,
version : "1.0.0" ,
baseUrl : process.env .MCP_URL || "https://myserver.com" ,
});
Environment Variables MCP_URL=https://myserver.com
MCP_SERVER_URL=https://myserver.com/api
CSP_URLS=https://cdn.example.com,https://api.example.com
Deployment npx mcp-use login
npm run deploy
Troubleshooting
Widget Not Appearing
Ensure .tsx extension
Export widgetMetadata object
Export default React component
Check server logs for errors
Verify widget name matches file/folder name
Props Not Received
Check isPending first (props empty while pending)
Use useWidget() hook (not React props)
Verify widgetMetadata.props is valid Zod schema
Check tool parameters match schema
CSP Errors
Set baseUrl in server config
Add domains to CSP via metadata.csp
Use HTTPS for all resources
Check browser console for CSP violations
Protocol Compatibility
Use type: "mcpApps" for dual-protocol support
Set baseUrl correctly in server config
Use metadata (camelCase) not appsSdkMetadata for dual-protocol
Test in Inspector which supports both protocols
Quick Reference
npx create-mcp-use-app my-app --template mcp-apps - Bootstrap
npm run dev - Development with hot reload
npm run build - Build for production
npm start - Run production server
npm run deploy - Deploy to mcp-use Cloud
resources/widget-name.tsx - Single file widget
resources/widget-name/widget.tsx - Folder-based widget entry
public/ - Static assets
description - Widget description (required)
props - Zod schema for input (required)
exposeAsTool - Auto-register as tool (default: true)
metadata - Unified config (dual-protocol)
metadata.csp - Content Security Policy
props - Widget input parameters
isPending - Loading state flag
state, setState - Persistent state
callTool - Call other tools
theme - Current theme (light/dark)
displayMode, requestDisplayMode - Display control
References
Learn More