Skip to main content
sunpeak API

Overview

sunpeak provides three ways to run a production MCP server, from zero-config to full control:
ApproachUse case
sunpeak startZero-config — loads tools, resources, and auth from dist/ automatically
createMcpHandlerNode.js — mount the MCP handler on your own Express/Fastify/http server
createHandlerWeb 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.
import { createMcpHandler } from 'sunpeak/mcp';
import type { ProductionServerConfig } from 'sunpeak/mcp';

const handler = createMcpHandler(config);
// handler: (req: IncomingMessage, res: ServerResponse) => Promise<void>
The handler responds to 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

import express from 'express';
import { createMcpHandler } from 'sunpeak/mcp';

const app = express();
app.get('/health', (req, res) => res.json({ ok: true }));

// tools: ProductionTool[], resources: ProductionResource[], auth: AuthFunction
const mcpHandler = createMcpHandler({ tools, resources, auth });
app.use((req, res, next) => {
  mcpHandler(req, res).then(() => {
    if (!res.headersSent) next();
  });
});

app.listen(3000);

ProductionServerConfig

name
string
default:"sunpeak-app"
Server name reported to hosts during MCP handshake.
version
string
default:"0.1.0"
Server version reported to hosts.
serverInfo
ServerConfig
Full server identity. Overrides name and version when provided. Supports additional fields like title, description, websiteUrl, and icons.
tools
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).
resources
ProductionResource[]
required
Resource registrations. Each has a name, uri, html (self-contained HTML string), and optional _meta.
auth
(req: IncomingMessage) => AuthInfo | null
Auth function called on every request. Return AuthInfo to authenticate, null to reject with 401. See Server Entry.
serverUrl
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”).
enableJsonResponse
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.
stateless
boolean
default:"false"
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.
import { createHandler } from 'sunpeak/mcp';
import type { WebHandlerConfig } from 'sunpeak/mcp';

const handler = createHandler(config);
// handler: (req: Request) => Promise<Response>
Unlike createMcpHandler, this handler does not do path matching — it handles every request it receives. Mount it behind your own router.

Example: Cloudflare Worker

import { createHandler } from 'sunpeak/mcp';

const handler = createHandler({ tools, resources, auth });
export default { fetch: handler };

Example: Hono

import { Hono } from 'hono';
import { createHandler } from 'sunpeak/mcp';

const app = new Hono();
const handler = createHandler({ tools, resources });
app.all('/mcp', (c) => handler(c.req.raw));
export default app;

WebHandlerConfig

Same shape as ProductionServerConfig (including enableJsonResponse and stateless), except auth takes a Web Standard Request:
auth
(req: Request) => AuthInfo | null
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.
import { startProductionHttpServer } from 'sunpeak/mcp';

// Simple: port only
startProductionHttpServer(config, 3000);

// Full options
startProductionHttpServer(config, { port: 3000, host: '127.0.0.1' });
The second argument accepts either a port number or an HttpServerOptions object:
port
number
default:"8000"
HTTP port to listen on.
host
string
default:"0.0.0.0"
Host/interface to bind to. Use 127.0.0.1 to restrict to localhost.
For most projects, use sunpeak start instead of calling this directly.

setJsonLogging

Enable structured JSON logging for all production server log output.
import { setJsonLogging } from 'sunpeak/mcp';

setJsonLogging(true);
When enabled, all log messages are written as JSON lines ({"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.