Quick Start
Start with the Apps SDK template which includes automatic widget registration:Folder Structure
Widgets can be organized in two ways: single-file widgets or folder-based widgets. Choose the organization style that best fits your widget’s complexity. For simple widgets, a single file is sufficient:.tsx file in the resources/ folder becomes a widget. The widget name is derived from the filename (without extension).
For complex widgets with multiple components, hooks, or utilities, organize them in folders:
- The folder name becomes the widget name (e.g.,
product-search-result) - The entry point must be named
widget.tsx(notindex.tsx) - You can organize sub-components, hooks, utilities, and types within the folder
- The
widget.tsxfile should exportwidgetMetadataand the default component
Widget Metadata
Contains the information that the MCP resource (and the tool that exposes it) will use when are automatically built by mcp-use.The
inputs field expects a Zod schema that defines the shape of the parameters passed to your widget. This schema is used to generate the tool’s input validation and to type-check the props in your widget component via useWidget<T>().exposeAsTool: true). You can control this behavior:
-
Auto-registered (default): Widget is callable as a tool directly by the model
-
Custom tool only: Widget is only accessible through custom tools you define
*.oaistatic.com, *.oaiusercontent.com, *.openai.com, and your server’s base URL). This ensures your widget can access both your custom resources and OpenAI’s required domains.
Components & Hooks
mcp-use provides a comprehensive set of React components and hooks for building OpenAI Apps SDK widgets. These components handle common setup tasks like theme management, error handling, routing, and debugging.Components
| Component | Description | Link |
|---|---|---|
| McpUseProvider | Unified provider that combines all common React setup (StrictMode, ThemeProvider, BrowserRouter, WidgetControls, ErrorBoundary) | McpUseProvider → |
| WidgetControls | Debug button and view controls (fullscreen/pip) with customizable positioning | WidgetControls → |
| ErrorBoundary | Error boundary component for graceful error handling in widgets | ErrorBoundary → |
| Image | Image component that handles both data URLs and public file paths | Image → |
| ThemeProvider | Theme provider for consistent theme management across widgets | ThemeProvider → |
Hooks
| Hook | Description | Link |
|---|---|---|
| useWidget | Main hook providing type-safe access to all widget capabilities (props, state, theme, actions) | useWidget → |
Static Assets
Widgets can use static assets from apublic/ folder. The framework automatically serves these assets and copies them during build.
Folder Structure
/mcp-use/public/. In production, they’re copied to dist/public/ during build.
Using the Image Component:
window.__mcpPublicUrl: Base URL for public assets (e.g.,http://localhost:3000/mcp-use/public)window.__getFile: Helper function to get file URLs
Patterns
Accessing Tool Input
Widgets are called by the model in the same way as tools. TheuseWidget hook provides access to the tool input, typed based on widget metadata schmea you defined.
Widget State
Widgets can maintain state across interactions. State is persisted by the host, for example in ChatGPT:Calling Other Tools
Widgets can call other MCP tools directly usingcallTool:
Display Mode Control
Request different display modes (inline, pip, or fullscreen):'inline'- Default embedded view in conversation'pip'- Picture-in-Picture floating window'fullscreen'- Full browser window (on mobile, PiP coerces to fullscreen)
<WidgetControls /> to automatically add controls to your widget.
Custom Tools with Widgets
You can create custom tools that return widgets instead of relying solely on automatic widget registration. This is useful when you need to:- Fetch data before displaying the widget
- Use different tool parameters than widget props
- Have multiple tools use the same widget
- Add custom logic or validation
Using the widget() Helper
Thewidget() helper returns runtime data for a widget. You must combine it with the widget config on the tool definition to set up all registration-time metadata.
Important: The widget configuration is split between two places:
- Tool definition (
widget: { name, invoking, ... }) - Registration-time metadata - Helper return (
widget({ props, output, ... })) - Runtime data
widget: { name, invoking, invoked, ... }on tool definition - Configures all widget metadata at registration timewidget({ props, output, message })helper - Returns runtime data only:props- Widget-only data passed touseWidget().props(hidden from model)output- Optional response helper result (text(), object(), etc.) that the model seesmessage- Optional text message override
- Widget must exist - The widget name must match a
.tsxfile or folder inresources/ - Disable auto-registration - Set
exposeAsTool: falsein the widget’s metadata if you only want it accessible through custom tools:
widget config option.
Configuration
Base URL for Production
Set theMCP_URL environment variable or pass baseUrl:
- Widget URLs use the correct domain
- Apps SDK CSP automatically includes your server
baseUrl option to the MCPServer constructor or by setting the MCP_URL environment variable.
Environment Variables
MCP_URL: Base URL for widget assets and public files. Used by Vite’sbaseoption during build. Also used by the server to configure CSP.MCP_SERVER_URL: (Optional) MCP server URL for API calls. When set, URLs are injected at build time for static deployments where widgets are served from storage rather than the MCP server.CSP_URLS: (Optional) Additional domains to whitelist in widget Content Security Policy. Supports comma-separated list. For Supabase, use the base project URL without path (e.g.,https://nnpumlykjksvxivhywwo.supabase.co). Required for static deployments where widget assets are served from different domains.
Static Deployments: Set
MCP_URL (for assets), MCP_SERVER_URL (for API calls), and CSP_URLS (for CSP whitelisting) when deploying to platforms like Supabase where widgets are served from static storage.Alternative CSP Configuration: Instead of using the global CSP_URLS environment variable, you can configure CSP per-widget in your widget’s appsSdkMetadata['openai/widgetCSP'] (see Apps SDK Metadata section above).Testing
Using the Inspector
The MCP Inspector provides full support for testing widgets during development:- Start your server:
npm run dev - Open Inspector:
http://localhost:3000/inspector - Test widgets: Execute tools to see widgets render
- Debug interactions: Use console logs and inspector features
- Test API methods: Verify
callTool,setState, etc. work correctly
Testing in ChatGPT
You need to enable the Developer Mode in ChatGPT to test widgets.- Enable developer mode: Go to Settings → Connectors → Advanced → Developer mode.
- Import MCPs: In the Connectors tab, add your remote MCP server. It will appear in the composer’s “Developer Mode” tool later during conversations.
-
Use connectors in conversations: Choose Developer mode from the Plus menu and select connectors. You may need to explore different prompting techniques to call the correct tools. For example:
- Be explicit: “Use the “Acme CRM” connector’s “update_record” tool to …”. When needed, include the server label and tool name.
- Disallow alternatives to avoid ambiguity: “Do not use built-in browsing or other tools; only use the Acme CRM connector.”
- Disambiguate similar tools: “Prefer Calendar.create_event for meetings; do not use Reminders.create_task for scheduling.”
- Specify input shape and sequencing: “First call Repo.read_file with { path: ”…” }. Then call Repo.write_file with the modified content. Do not call other tools.”
- If multiple connectors overlap, state preferences up front (e.g., “Use CompanyDB for authoritative data; use other sources only if CompanyDB returns no results”).
- Developer mode does not require search/fetch tools. Any tools your connector exposes (including write actions) are available, subject to confirmation settings.
- See more guidance in Using tools and Prompting.
- Improve tool selection with better tool descriptions: In your MCP server, write action-oriented tool names and descriptions that include “Use this when…” guidance, note disallowed/edge cases, and add parameter descriptions (and enums) to help the model choose the right tool among similar ones and avoid built-in tools when inappropriate.
Next Steps
- Creating Apps SDK Server - Complete guide
- Debugging ChatGPT Apps - Test widgets with Inspector
- Project Templates - Explore available templates