An MCP server template with OpenAI Apps SDK integration for ChatGPT widgets.
MCP Apps Server
An MCP server template with OpenAI Apps SDK integration for ChatGPT-compatible widgets.
Features
- š¤ OpenAI Apps SDK: Full compatibility with ChatGPT widgets
- šØ Official UI Components: Integrated OpenAI Apps SDK UI components for consistent, accessible widgets
- š Ecommerce Widgets: Complete ecommerce example with carousel, search, map, and order confirmation
- š Automatic Registration: Widgets auto-register from
resources/folder - š¦ Props Schema: Zod schema validation for widget props
- š Theme Support: Dark/light theme detection via
useWidgethook - š ļø TypeScript: Complete type safety
- š§ Widget Capabilities: Full support for
callTool,sendFollowUpMessage, and persistent state
What's New: Apps SDK Integration
This template demonstrates how to build ChatGPT-compatible widgets using OpenAI's Apps SDK:
import { useWidget } from 'mcp-use/react';
const MyWidget: React.FC = () => {
const { props, theme } = useWidget<MyProps>();
// props contains validated inputs from OpenAI
// theme is 'dark' or 'light' based on ChatGPT setting
}
Getting Started
Development
# Install dependencies
npm install
# Start development server
npm run dev
This starts:
- MCP server on port 3000
- Widget serving at
/mcp-use/widgets/* - Inspector UI at
/inspector
Production
# Build the server and widgets
npm run build
# Run the built server
npm start
Project Structure
mcp-apps/
āāā resources/ # React widget components
ā āāā display-weather.tsx # Weather widget example
ā āāā ecommerce-carousel.tsx # Ecommerce product carousel
ā āāā product-search-result.tsx # Product search with filters
ā āāā stores-locations-map.tsx # Store locations map
ā āāā order-confirmation.tsx # Order confirmation widget
āāā index.ts # Server entry point (includes brand info tool)
āāā package.json
āāā tsconfig.json
āāā README.md
How Automatic Registration Works
All React components in the resources/ folder are automatically registered as MCP tools and resources when they export widgetMetadata:
import { z } from 'zod';
import type { WidgetMetadata } from 'mcp-use/react';
const propSchema = z.object({
city: z.string().describe('The city name'),
temperature: z.number().describe('Temperature in Celsius'),
});
export const widgetMetadata: WidgetMetadata = {
description: 'My widget description',
props: propSchema,
};
const MyWidget: React.FC = () => {
const { props } = useWidget<z.infer<typeof propSchema>>();
// Your widget implementation
};
export default MyWidget;
This automatically creates:
- Tool:
display-weather- Accepts parameters via OpenAI - Resource:
ui://widget/display-weather- Static access
Building Widgets with Apps SDK
Using the `useWidget` Hook
import { useWidget } from 'mcp-use/react';
interface MyProps {
title: string;
count: number;
}
const MyWidget: React.FC = () => {
const { props, theme, isPending } = useWidget<MyProps>();
// IMPORTANT: Widgets render before tool execution completes
// Always check isPending to handle the loading state
if (isPending) {
return Loading...;
}
// Now props are safely available
// props are validated and typed based on your schema
// theme is automatically set by ChatGPT
return (
<h1>{props.title}</h1>
Count: {props.count}
);
};
Note: Widgets render before the tool execution completes. On first render,
propswill be empty{}andisPendingwill betrue. Always checkisPendingbefore accessing props. See the Widget Lifecycle documentation for more details.
Defining Widget Metadata
Use Zod schemas to define widget inputs:
import { z } from 'zod';
import type { WidgetMetadata } from 'mcp-use/react';
const propSchema = z.object({
name: z.string().describe('Person name'),
age: z.number().min(0).max(120).describe('Age in years'),
email: z.string().email().describe('Email address'),
});
export const widgetMetadata: WidgetMetadata = {
description: 'Display user information',
props: propSchema,
};
Theme Support
Automatically adapt to ChatGP
Tools (1)
display-weatherDisplays weather information based on city and temperature inputs.Configuration
{"mcpServers": {"mcp-apps": {"command": "node", "args": ["/path/to/mcp-apps/index.js"]}}}