Build for Production
Build your app for production using the sunpeak CLI:- pnpm
- npm
- yarn
dist/ directory with built resources and compiled tools:
.html file is a complete, self-contained app bundle — no external scripts or stylesheets. Tool files are compiled to standard ESM JavaScript with node_modules resolved at runtime.
Start the Production Server
- pnpm
- npm
- yarn
dist/, registers them with the MCP protocol, and starts a Streamable HTTP server on port 8000.
- pnpm
- npm
- yarn
- Registers each tool from
dist/tools/with its real handler and Zod schema (input validation) - Serves pre-built resource HTML from
dist/{name}/{name}.html - Calls your
auth()function fromsrc/server.tson every request (if present) - Uses MCP Streamable HTTP transport on a single
/mcpendpoint - Provides a
/healthendpoint for load balancer probes and uptime monitoring - Handles graceful shutdown on SIGTERM/SIGINT
Testing Locally
Usesunpeak build and sunpeak start to test production behavior locally:
- pnpm
- npm
- yarn
ngrok http 8000) and connect from ChatGPT.
Custom Server Setup
For full control over the HTTP server (custom middleware, CORS, health checks, etc.), usecreateMcpHandler to mount the MCP protocol handler on your own server.
Unlike sunpeak start which auto-discovers from dist/, these handlers accept pre-loaded config objects — you load tools, resources, and auth yourself. See Production Server API for config types.
req.auth, and CORS preflights for the /mcp path. For unmatched routes it does nothing, so your server handles them.
Serverless / Edge Deployment
For serverless and edge runtimes (Cloudflare Workers, Deno, Bun, Vercel Edge), usecreateHandler with stateless: true:
createMcpHandler, the web handler does not do path matching — it handles every request it receives. Mount it behind your own router.
Stateless vs Stateful Mode
By default, bothcreateHandler and createMcpHandler use stateful mode: they track MCP sessions in an in-memory map and route requests by the mcp-session-id header. This works for single-instance servers and multi-instance deployments with sticky sessions (see Horizontal Scaling).
Stateless mode (stateless: true) creates a fresh MCP server and transport for every request. No session map, no session ID validation. Use this when:
- Serverless (Lambda, Workers, Vercel Edge) where each request may hit a different instance with no shared memory
- Horizontally scaled deployments where configuring sticky sessions isn’t practical
POST is supported (no SSE streaming via GET). JSON responses are used by default, which is the right choice for serverless anyway.
Domain Resolution in Stateless Mode
sunpeak auto-computes resource domains fromserverUrl based on which host is connecting (ChatGPT and Claude use different sandbox domain schemes). This requires knowing the host’s identity.
ChatGPT sends user-agent: openai-mcp/1.0.0 and Claude sends user-agent: Claude-User on every MCP HTTP request. sunpeak detects these automatically, so domain resolution works in stateless mode without any extra configuration:
detectClientFromHeaders is exported from sunpeak/mcp so you can build your own detection logic in a custom handler.
Both createMcpHandler and createHandler use JSON responses by default (enableJsonResponse: true), which works reliably across all environments including serverless. If you need SSE streaming (e.g., for progress updates on long-running tool calls), set enableJsonResponse: false in your config. See Production Server API for details.
For fully custom MCP server setups,
sunpeak/mcp also re-exports registerAppTool, registerAppResource, and RESOURCE_MIME_TYPE from the MCP Apps SDK. See Server Helpers for the full list.Production Operations
Health Checks
The built-in server exposes aGET /health endpoint that returns:
Structured Logging
Pass--json-logs to output structured JSON lines instead of human-readable logs:
- pnpm
- npm
- yarn
ts, level, and msg fields, plus contextual data:
Binding Interface
By default, the server binds to0.0.0.0 (all interfaces). To restrict it to localhost only:
- pnpm
- npm
- yarn
Process Management
sunpeak start handles SIGTERM and SIGINT for graceful shutdown (5-second drain timeout). It works with any process manager:
Reverse Proxy
For TLS, rate limiting, and request size limits, putsunpeak start behind a reverse proxy. The server’s CORS headers allow all origins by default, which is correct for MCP servers called by AI hosts from various origins.
Horizontal Scaling
MCP uses session-based communication by default. Eachinitialize handshake creates a session, and all subsequent requests include an mcp-session-id header that must route back to the same server instance.
For multi-instance deployments behind a load balancer, you have two options:
Sticky sessions (recommended for long-lived servers) — Configure your load balancer to route requests with the same mcp-session-id header to the same backend instance:
- AWS ALB: Enable target group stickiness
- nginx: Use
ip_hashorsticky cookie - Cloudflare: Enable session affinity
- Kubernetes: Set session affinity annotations on your Ingress
- pnpm
- npm
- yarn
What Not to Worry About
These are handled at the infrastructure layer, not bysunpeak start:
- TLS/HTTPS — Use a reverse proxy (nginx, Caddy, Cloudflare)
- Rate limiting — Configure at the proxy or CDN level
- Request body limits — Handled by the proxy or platform
createMcpHandler to mount the MCP handler on your own Express, Fastify, or http server.
Publish to ChatGPT
ChatGPT apps are now submitted and published as plugins. The app itself remains an MCP-backed app, so your sunpeak server, tools, and UI resources do not change. The plugin is the package used for review and distribution. Use the OpenAI Platform plugin submission portal when your production server is ready. OpenAI’s plugin submission guide is the source of truth for the review flow.Prerequisites
- Your MCP server is deployed at a public production HTTPS URL.
- Your resources are built with
sunpeak build. - The submitter has Apps Management: Write access for the OpenAI Platform organization. Organization owners already have this access.
- The publisher has completed individual or business identity verification in the same organization and project used for submission.
- You can serve the domain verification token at
/.well-known/openai-apps-challengeon the MCP hostname or an allowed parent hostname. - If sign-in is required, reviewer credentials work without MFA, SMS, email confirmation, or private-network access.
For local development, first enable Developer mode from the bottom-left user menu under
Settings > Security and login > Developer mode. Then create the app from the ChatGPT homepage under Plugins > +. Public submission is a separate flow in the OpenAI Platform. If the app already exists in ChatGPT, submit its MCP server from scratch. The portal does not accept an existing ChatGPT app ID.Prepare the submission
Collect these materials before opening the portal:- Plugin name, short and long descriptions, logo, category, website, support URL, privacy policy URL, and terms URL.
- Production MCP server URL, authentication details, and reviewer credentials when needed.
- A content security policy listing the exact domains the app fetches from.
- Clear tool names, descriptions, input schemas, output shapes, and accurate
readOnlyHint,openWorldHint, anddestructiveHintannotations for every tool. - Starter prompts that show realistic workflows.
- Exactly five positive test cases and three negative test cases, including expected behavior and any fixture data.
- Country or region availability and release notes.
Submit the MCP-backed plugin
- Open the plugin submission portal and select Create plugin.
- Choose With MCP for an app-only plugin or an app-plus-skills plugin.
- Complete the public listing and publisher identity fields.
- Enter the production MCP server URL, configure authentication, and provide demo credentials when required.
- Add the app’s content security policy.
- Complete domain verification if the portal asks for it. The challenge endpoint must return only the exact token as plain text.
- Select Scan Tools, fix any server or metadata errors, deploy the fixes, and scan again.
- Add starter prompts, five positive tests, three negative tests, availability, release notes, and policy attestations.
- Select Submit for Review.
Production Checklist
- Set an exact CSP (Content Security Policy) for your resources.
- Configure CORS if serving from a different domain.
- Set up HTTPS for secure communication.
- Verify that tool annotations match real behavior.
- Confirm that the domain challenge and reviewer credentials work from the public internet.
- Check that listing URLs and publisher identity match.