README.md•5.94 kB
# MCP Server for ChatGPT Apps
A Model Context Protocol (MCP) server with interactive React components for ChatGPT Apps.
## What is This?
This project implements an MCP server that works with ChatGPT Apps SDK, featuring:
- **SSE-based MCP server** - HTTP server using Server-Sent Events
- **Interactive React widgets** - Components that render inline in ChatGPT
- **Calculator example** - A fully functional calculator with history
- **Modern build system** - Vite + React + TypeScript + Tailwind CSS
## Project Structure
```
mcp-server/
├── server/
│ └── src/
│ └── server.ts # MCP server with SSE transport
├── web/
│ └── src/
│ ├── calculator/ # Calculator React component
│ ├── types.ts # TypeScript definitions for window.openai
│ ├── use-openai-global.ts # Hook for ChatGPT state
│ ├── use-widget-state.ts # Hook for widget persistence
│ └── index.css # Global styles
├── assets/ # Built components (generated)
├── scripts/
│ └── post-build.js # HTML generation script
├── vite.config.ts # Vite build configuration
├── tailwind.config.ts # Tailwind CSS config
└── package.json # Root dependencies
```
## Installation
### 1. Install Dependencies
```bash
# Install root dependencies (React, Vite, Tailwind)
npm install
# Install server dependencies (MCP SDK)
cd server && npm install && cd ..
```
### 2. Build Components
```bash
npm run build
```
This generates bundled React components in the `assets/` directory.
## Running the Server
### Start the MCP Server
```bash
npm run server
```
The server will start on `http://localhost:8000` with these endpoints:
- `GET /mcp` - SSE stream
- `POST /mcp/messages?sessionId=...` - Message handler
## Testing with ChatGPT
### Option 1: Local Testing with ngrok
1. **Start the server**:
```bash
npm run server
```
2. **Expose with ngrok**:
```bash
ngrok http 8000
```
3. **Add to ChatGPT**:
- Go to ChatGPT Settings > Connectors
- Add connector with URL: `https://YOUR-ID.ngrok-free.app/mcp`
4. **Use in ChatGPT**:
- Add the connector to your conversation
- Ask: "Show me a calculator" or "Open calculator"
- The interactive calculator widget will appear!
### Option 2: Production Deployment
1. **Set base URL for assets**:
```bash
export BASE_URL=https://your-cdn.com
npm run build
```
2. **Deploy**:
- Upload `assets/` to a CDN or static hosting (with CORS enabled)
- Deploy the MCP server with HTTPS
- Register in ChatGPT with your server URL
## Available Tools
### 1. Calculator (with React UI)
- **Tool**: `calculator`
- **Description**: Interactive calculator widget with history
- **Features**:
- Basic arithmetic operations
- Calculation history (last 5 calculations)
- Dark/light theme support
- State persistence across turns
### 2. Get Current Time
- **Tool**: `get_current_time`
- **Returns**: Current time in ISO format
### 3. Echo
- **Tool**: `echo`
- **Returns**: Echoes back your message
## Development
### Run Dev Server (with hot reload)
```bash
npm run dev
```
Visit `http://localhost:4444` to preview components.
### Adding New Components
1. **Create component** in `web/src/your-component/index.tsx`:
```tsx
import React from "react";
import { createRoot } from "react-dom/client";
import { useOpenAiGlobal } from "../use-openai-global";
function App() {
const toolOutput = useOpenAiGlobal("toolOutput");
const theme = useOpenAiGlobal("theme");
return (
<div className={theme === "dark" ? "dark" : ""}>
<h1>Your Component</h1>
{/* Your UI */}
</div>
);
}
createRoot(document.getElementById("your-component-root")!).render(<App />);
```
2. **Update Vite config** (`vite.config.ts`):
```typescript
input: {
calculator: path.resolve(__dirname, "web/src/calculator/index.tsx"),
"your-component": path.resolve(__dirname, "web/src/your-component/index.tsx"),
},
```
3. **Update post-build script** (`scripts/post-build.js`):
```javascript
const components = ["calculator", "your-component"];
```
4. **Add widget to server** (`server/src/server.ts`):
```typescript
const widgets: Widget[] = [
// ... existing widgets
{
id: "your-component",
title: "Your Component",
templateUri: "ui://widget/your-component.html",
invoking: "Loading your component",
invoked: "Component ready",
html: readWidgetHtml("your-component"),
responseText: "Component rendered!",
},
];
```
5. **Build and test**:
```bash
npm run build
npm run server
```
## Key Features
### window.openai API
Components have access to:
```typescript
window.openai.theme // "light" | "dark"
window.openai.displayMode // "inline" | "pip" | "fullscreen"
window.openai.toolOutput // Data from tool call
window.openai.widgetState // Persisted component state
window.openai.callTool(name, args) // Call MCP tools
window.openai.sendFollowUpMessage({ prompt }) // Send to ChatGPT
```
### Custom Hooks
```typescript
// Subscribe to ChatGPT state
const theme = useOpenAiGlobal("theme");
// Persist component state
const [state, setState] = useWidgetState({ count: 0 });
```
## Troubleshooting
### "Widget assets not found"
Run `npm run build` to generate component bundles.
### CORS errors
Ensure your server has CORS enabled (already configured in server.ts).
### Components not rendering
1. Check browser console for errors
2. Verify assets are accessible at BASE_URL
3. Ensure HTML files reference correct JS/CSS paths
## Resources
- [OpenAI Apps SDK Documentation](https://developers.openai.com/apps-sdk)
- [MCP Specification](https://modelcontextprotocol.io)
- [Example Apps Gallery](https://github.com/openai/openai-apps-sdk-examples)
## License
ISC