MCP Apps Server

MCP Apps Server

An MCP server template integrated with the OpenAI Apps SDK for building ChatGPT-compatible widgets with automatic tool registration. It provides a suite of interactive UI components and ecommerce examples for creating type-safe, theme-aware widgets.

Category
访问服务器

README

MCP Apps Server

Deploy to mcp-use

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 useWidget hook
  • 🛠️ 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 <div>Loading...</div>;
  }

  // Now props are safely available
  // props are validated and typed based on your schema
  // theme is automatically set by ChatGPT

  return (
    <div className={theme === 'dark' ? 'dark-theme' : 'light-theme'}>
      <h1>{props.title}</h1>
      <p>Count: {props.count}</p>
    </div>
  );
};

Note: Widgets render before the tool execution completes. On first render, props will be empty {} and isPending will be true. Always check isPending before 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 ChatGPT's theme:

const { theme } = useWidget();

const bgColor = theme === 'dark' ? 'bg-gray-900' : 'bg-white';
const textColor = theme === 'dark' ? 'text-gray-100' : 'text-gray-800';

Official UI Components

This template uses the OpenAI Apps SDK UI component library for building consistent, accessible widgets. The library provides:

  • Button: Primary, secondary, and outline button variants
  • Card: Container component for content sections
  • Carousel: Image and content carousel with transitions
  • Input: Form input fields
  • Icon: Consistent iconography
  • Transition: Smooth animations and transitions

Import components like this:

import {
  Button,
  Card,
  Carousel,
  CarouselItem,
  Transition,
  Icon,
  Input,
} from '@openai/apps-sdk-ui';

Ecommerce Widgets

This template includes a complete ecommerce example with four widgets:

1. Ecommerce Carousel (ecommerce-carousel.tsx)

A product carousel widget featuring:

  • Title and description
  • Carousel of product items with placeholder images
  • Info button and Add to Cart button for each item
  • Uses official Carousel, Card, Button, Icon, and Transition components
  • Integrates with callTool for cart operations
  • Persistent state management

2. Product Search Result (product-search-result.tsx)

A search results widget with:

  • Search input with real-time filtering
  • Price range filters and stock status filter
  • Grid layout of product cards
  • Uses callTool to perform searches
  • Uses sendFollowUpMessage to update conversation
  • Persistent filter state

3. Stores Locations Map (stores-locations-map.tsx)

A store locator widget featuring:

  • Interactive map display (placeholder)
  • List of store locations with details
  • Distance calculation
  • Get directions functionality
  • Store details on click
  • Uses callTool for directions and store info

4. Order Confirmation (order-confirmation.tsx)

An order confirmation widget with:

  • Order summary and items list
  • Shipping information
  • Order status tracking
  • Track order and view receipt actions
  • Uses callTool for order tracking

Brand Info Tool

The template includes a get-brand-info tool (normal MCP tool, not a widget) that returns brand information:

// Call the tool
await client.callTool('get-brand-info', {});

// Returns brand details including:
// - Company name, tagline, description
// - Mission and values
// - Contact information
// - Social media links

Example: Weather Widget

The included display-weather.tsx widget demonstrates:

  1. Schema Definition: Zod schema for validation
  2. Metadata Export: Widget registration info
  3. Theme Detection: Dark/light mode support
  4. Type Safety: Full TypeScript support
// Get props from OpenAI Apps SDK
const { props, theme } = useWidget<WeatherProps>();

// props.city, props.weather, props.temperature are validated

Using Widgets in ChatGPT

Via Tool Call

await client.callTool('display-weather', {
  city: 'San Francisco',
  weather: 'sunny',
  temperature: 22
});

Via Resource Access

await client.readResource('ui://widget/display-weather');

Customization Guide

Adding New Widgets

  1. Create a React component in resources/my-widget.tsx:
import React from 'react';
import { z } from 'zod';
import { useWidget, type WidgetMetadata } from 'mcp-use/react';

const propSchema = z.object({
  message: z.string().describe('Message to display'),
});

export const widgetMetadata: WidgetMetadata = {
  description: 'Display a message',
  props: propSchema,
};

type Props = z.infer<typeof propSchema>;

const MyWidget: React.FC = () => {
  const { props, theme } = useWidget<Props>();

  return (
    <div>
      <h1>{props.message}</h1>
    </div>
  );
};

export default MyWidget;
  1. The widget is automatically registered!

Adding Traditional MCP Tools

You can mix Apps SDK widgets with regular MCP tools:

import { text } from 'mcp-use/server';

server.tool({
  name: 'get-data',
  description: 'Fetch data from API',
  cb: async () => {
    return text('Data');
  },
});

Testing Your Widgets

Via Inspector UI

  1. Start the server: npm run dev
  2. Open: http://localhost:3000/inspector
  3. Test widgets interactively

Direct Browser Access

Visit: http://localhost:3000/mcp-use/widgets/display-weather

Via MCP Client

import { createMCPClient } from 'mcp-use/client';

const client = createMCPClient({
  serverUrl: 'http://localhost:3000/mcp',
});

await client.connect();

// Call widget as tool
const result = await client.callTool('display-weather', {
  city: 'London',
  weather: 'rain',
  temperature: 15
});

Apps SDK vs Other Widget Types

Feature Apps SDK External URL Remote DOM
ChatGPT Compatible ✅ Yes ❌ No ❌ No
Theme Detection ✅ Automatic ❌ Manual ❌ Manual
Props Validation ✅ Zod Schema ❌ Manual ❌ Manual
React Support ✅ Full ✅ Full ❌ Limited
OpenAI Metadata ✅ Yes ❌ No ❌ No

Benefits of Apps SDK

ChatGPT Native - Works seamlessly in ChatGPT ✅ Theme Aware - Automatic dark/light mode ✅ Type Safe - Full TypeScript with Zod validation ✅ Simple API - One hook for all props ✅ Auto Registration - Export metadata and done

Troubleshooting

Widget Not Loading

  • Ensure widget has widgetMetadata export
  • Check Zod schema is valid
  • Verify widget exists in dist/resources/mcp-use/widgets/

Props Not Passed

  • Ensure schema includes all props
  • Check .describe() for each prop
  • Verify useWidget hook is called

Theme Not Applied

  • Theme is only available in ChatGPT
  • Use theme from useWidget() hook
  • Test in actual ChatGPT interface

Migration from Other Templates

Moving from starter to mcp-apps:

// Before: Manual props handling
const params = new URLSearchParams(window.location.search);
const city = params.get('city');

// After: Apps SDK hook
const { props } = useWidget();
const city = props.city;

Using Widget Capabilities

The widgets in this template demonstrate the full capabilities of the Apps SDK:

Calling Tools (callTool)

Widgets can call other MCP tools:

const { callTool } = useWidget();

const handleAction = async () => {
  const result = await callTool('add-to-cart', {
    productId: '123',
    productName: 'Product Name',
    price: 29.99
  });
};

Sending Follow-up Messages (sendFollowUpMessage)

Widgets can send messages to the ChatGPT conversation:

const { sendFollowUpMessage } = useWidget();

await sendFollowUpMessage('Product added to cart successfully!');

Persistent State (setState)

Widgets can maintain state across interactions:

const { setState, state } = useWidget();

// Save state
await setState({ cart: [...cart, newItem] });

// Read state
const savedCart = state?.cart || [];

Component Library Note

This template uses the OpenAI Apps SDK UI component library. The exact component API may vary based on the library version. If you encounter import errors, check the official documentation for the correct component names and props.

If the official library is not available, you can replace the imports with custom React components or other UI libraries while maintaining the same widget structure.

Learn More

Happy building! 🚀

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选