Skip to main content
Glama
BruceWilliamChen

ChatGPT Apps MCP Server

README.md
# 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