Overview
sunpeak provides three ways to run a production MCP server, from zero-config to full control:| Approach | Use case |
|---|---|
sunpeak start | Zero-config — loads tools, resources, and auth from dist/ automatically |
createMcpHandler | Node.js — mount the MCP handler on your own Express/Fastify/http server |
createHandler | Web Standard — Cloudflare Workers, Deno, Bun, Vercel Edge |
sunpeak start auto-discovers tools from dist/tools/, resources from dist/{name}/, and auth from dist/server.js, then passes them to startProductionHttpServer internally.
createMcpHandler and createHandler accept these as config objects — you load and pass them yourself. This gives full control over how tools, resources, and auth are provided.
All three use MCP Streamable HTTP transport on a single /mcp endpoint.
createMcpHandler
Creates a Node.js request handler for MCP over Streamable HTTP.
POST /mcp, GET /mcp, DELETE /mcp, and OPTIONS /mcp. For unmatched paths it does nothing, so you can chain it with your own routes.
Example: Express
ProductionServerConfig
Server name reported to hosts during MCP handshake.
Server version reported to hosts.
Full server identity. Overrides
name and version when provided. Supports additional fields like title, description, websiteUrl, and icons.Tool registrations. Each has a
name, tool config (optional resource link, title, description), optional schema (Zod shape for input validation), and handler function. Tools without a resource are registered as plain MCP tools (no UI).Resource registrations. Each has a
name, uri, html (self-contained HTML string), and optional _meta.Auth function called on every request. Return
AuthInfo to authenticate, null to reject with 401. See Server Entry.Public URL of the MCP server (e.g.
'https://example.com/mcp'). Used to auto-compute a default _meta.ui.domain for resources that don’t specify one. Without this, resources without an explicit domain may trigger host warnings (e.g. ChatGPT’s “Widget domain is not set”).Respond with JSON instead of SSE streams. Recommended for serverless environments (Lambda, Workers, Vercel Edge) where holding open SSE connections is unreliable. Set to
false if you need SSE streaming for long-running tool calls.Enable stateless mode for serverless and horizontally-scaled deployments. When
true, every request creates a fresh MCP server instance with no session tracking. The host is identified from HTTP headers (User-Agent, x-anthropic-client, x-openai-session) on every request. See Serverless Deployment and Horizontal Scaling.createHandler
Creates a Web Standard request handler for serverless and edge runtimes.
createMcpHandler, this handler does not do path matching — it handles every request it receives. Mount it behind your own router.
Example: Cloudflare Worker
Example: Hono
WebHandlerConfig
Same shape as ProductionServerConfig (including enableJsonResponse and stateless), except auth takes a Web Standard Request:
Auth function for Web Standard environments. Return
AuthInfo to authenticate, null to reject with 401.startProductionHttpServer
The built-in HTTP server used by sunpeak start. A convenience wrapper around createMcpHandler that adds a health check endpoint (/health), root page, favicon, and graceful shutdown.
HttpServerOptions object:
HTTP port to listen on.
Host/interface to bind to. Use
127.0.0.1 to restrict to localhost.sunpeak start instead of calling this directly.
setJsonLogging
Enable structured JSON logging for all production server log output.
{"ts":"...","level":"info","msg":"..."}) to stdout (info/warn) or stderr (errors). This is useful for log aggregation tools like Datadog, CloudWatch, or Loki. The sunpeak start --json-logs flag calls this automatically.
See Also
sunpeak start
Zero-config production server CLI.
Deployment Guide
Full deployment walkthrough with custom server examples.