
ChatGPT Apps SDK Next.js Starter
A minimal Next.js application demonstrating how to build an OpenAI Apps SDK compatible MCP server with widget rendering in ChatGPT.
Overview
This project shows how to integrate a Next.js application with the ChatGPT Apps SDK using the Model Context Protocol (MCP). It includes a working MCP server that exposes tools and resources that can be called from ChatGPT, with responses rendered natively in ChatGPT.
Key Components
1. MCP Server Route (app/mcp/route.ts
)
The core MCP server implementation that exposes tools and resources to ChatGPT.
Key features:
- Tool registration with OpenAI-specific metadata
- Resource registration that serves HTML content for iframe rendering
- Cross-linking between tools and resources via
templateUri
OpenAI-specific metadata:
{"openai/outputTemplate": widget.templateUri, // Links to resource"openai/toolInvocation/invoking": "Loading...", // Loading state text"openai/toolInvocation/invoked": "Loaded", // Completion state text"openai/widgetAccessible": false, // Widget visibility"openai/resultCanProduceWidget": true // Enable widget rendering}
Full configuration options: OpenAI Apps SDK MCP Documentation
2. Asset Configuration (next.config.ts
)
Critical: Set assetPrefix
to ensure /_next/
static assets are fetched from the correct origin:
const nextConfig: NextConfig = {assetPrefix: baseURL, // Prevents 404s on /_next/ files in iframe};
Without this, Next.js will attempt to load assets from the iframe's URL, causing 404 errors.
3. CORS Middleware (middleware.ts
)
Handles browser OPTIONS preflight requests required for cross-origin RSC (React Server Components) fetching during client-side navigation:
export function middleware(request: NextRequest) {if (request.method === "OPTIONS") {// Return 204 with CORS headers}// Add CORS headers to all responses}
4. SDK Bootstrap (app/layout.tsx
)
The <NextChatSDKBootstrap>
component patches browser APIs to work correctly within the ChatGPT iframe:
What it patches:
history.pushState
/history.replaceState
- Prevents full-origin URLs in historywindow.fetch
- Rewrites same-origin requests to use the correct base URL<html>
attribute observer - Prevents ChatGPT from modifying the root element
Required configuration:
<html lang="en" suppressHydrationWarning><head><NextChatSDKBootstrap baseUrl={baseURL} /></head><body>{children}</body></html>
Note: suppressHydrationWarning
is currently required because ChatGPT modifies the initial HTML before the Next.js app hydrates, causing hydration mismatches.
Getting Started
Installation
npm install# orpnpm install
Development
npm run dev# orpnpm dev
Open http://localhost:3000 to see the app.
Testing the MCP Server
The MCP server is available at:
http://localhost:3000/mcp
Connecting from ChatGPT
- Deploy your app to Vercel
- In ChatGPT, navigate to Settings → Connectors → Create and add your MCP server URL with the
/mcp
path (e.g.,https://your-app.vercel.app/mcp
)
Note: Connecting MCP servers to ChatGPT requires developer mode access. See the connection guide for setup instructions.
Project Structure
app/├── mcp/│ └── route.ts # MCP server with tool/resource registration├── layout.tsx # Root layout with SDK bootstrap├── page.tsx # Homepage content└── globals.css # Global stylesmiddleware.ts # CORS handling for RSCnext.config.ts # Asset prefix configuration
How It Works
- Tool Invocation: ChatGPT calls a tool registered in
app/mcp/route.ts
- Resource Reference: Tool response includes
templateUri
pointing to a registered resource - Widget Rendering: ChatGPT fetches the resource HTML and renders it in an iframe
- Client Hydration: Next.js hydrates the app inside the iframe with patched APIs
- Navigation: Client-side navigation uses patched
fetch
to load RSC payloads
Learn More
- OpenAI Apps SDK Documentation
- OpenAI Apps SDK - MCP Server Guide
- Model Context Protocol
- Next.js Documentation
Deployment
This project is designed to work seamlessly with Vercel deployment. The baseUrl.ts
configuration automatically detects Vercel environment variables and sets the correct asset URLs.
The configuration automatically handles:
- Production URLs via
VERCEL_PROJECT_PRODUCTION_URL
- Preview/branch URLs via
VERCEL_BRANCH_URL
- Asset prefixing for correct resource loading in iframes