Overview
sunpeak provides three ways to run a production MCP server, from zero-config to full control:sunpeak start auto-discovers tools from dist/tools/, resources from dist/{name}/, and the auth and oauth exports 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
string
default:"sunpeak-app"
Server name reported to hosts during MCP handshake.
string
default:"0.1.0"
Server version reported to hosts.
ServerConfig
Full server identity. Overrides
name and version when provided. Supports additional fields
like title, description, websiteUrl, icons, and instructions (sent in the MCP
initialize response so hosts can inject it into the model’s system prompt). See Server
Entry for the full field list.ProductionTool[]
required
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).ProductionResource[]
required
Resource registrations. Each has a
name, uri, html (self-contained HTML string), and
optional _meta.(req: IncomingMessage, context: AuthorizationContext) => AuthInfo | AuthorizationFailure | null
Auth function called on every request. Return
AuthInfo to authenticate, null for an initial
401, or AuthorizationFailure for an invalid-token or insufficient-scope challenge.
context.body contains the parsed MCP request. See
Authorization.OAuthProtectedResourceConfig
Protected-resource discovery and bearer challenge configuration. When set, the handler serves RFC
9728 metadata at the root and endpoint-specific well-known paths and adds
resource_metadata to
WWW-Authenticate.string
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”).boolean
default:"true"
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.number
default:"15000"
Interval in milliseconds between SSE keep-alive comments when
enableJsonResponse is false. Set
to 0 to disable keep-alives.boolean
default:"false"
Enable stateless v1 transport mode for serverless and horizontally-scaled deployments. When
true, every request creates a fresh MCP server instance with no session tracking. This does not
enable the core MCP 2026-07-28 wire protocol. The host is identified from HTTP headers
(User-Agent, x-anthropic-client, x-openai-session) on every request. See Serverless
Deployment, Horizontal
Scaling, and MCP 2026-07-28 for MCP
Apps.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 oauth, enableJsonResponse, sseKeepAliveMs, and stateless), except auth takes a Web Standard Request:
(req: Request, context: AuthorizationContext) => AuthInfo | AuthorizationFailure | null
Auth function for Web Standard environments. The return behavior matches the Node handler.
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:
number
default:"8000"
HTTP port to listen on.
string
default:"0.0.0.0"
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.