Skip to main content
The createHTTPServer function creates an Express-based HTTP server that exposes your MCP server over HTTP. It supports both stateless (Lambda/serverless) and stateful (long-running) modes.

Function Signature

async function createHTTPServer(
  serverInput: HTTPServerInput,
  options?: HTTPServerOptions
): Promise<{ listener: http.Server; port: number }>

Parameters

serverInput
MCPServerConstructorOptions | MCPServerFactory
required
Either server constructor options or a factory function that returns a Server instance.Recommended: Use MCPServerConstructorOptions for simplified API.Legacy: Use MCPServerFactory for advanced custom server creation.
options
HTTPServerOptions
HTTP server configuration (only used with legacy factory pattern)

Return Value

listener
http.Server
The Node.js HTTP server instance. Keep this reference to prevent process exit.
port
number
The actual port the server is listening on (may differ from requested port).

Examples

import { createHTTPServer } from '@leanmcp/core';

const { listener, port } = await createHTTPServer({
  name: 'my-mcp-server',
  version: '1.0.0',
  port: 3001,
  cors: true,
  logging: true,
});

console.log(`Server running on http://localhost:${port}`);

With CORS Configuration

await createHTTPServer({
  name: 'my-mcp-server',
  version: '1.0.0',
  port: 3001,
  cors: {
    origin: ['https://example.com', 'https://app.example.com'],
    credentials: true,
  },
});

Stateful Mode with Session Store

import { createHTTPServer, DynamoDBSessionStore } from '@leanmcp/core';

await createHTTPServer({
  name: 'my-mcp-server',
  version: '1.0.0',
  stateless: false,
  sessionStore: new DynamoDBSessionStore({
    tableName: 'my-sessions',
    ttlSeconds: 3600,
  }),
});

With OAuth/Auth (MCP Authorization Spec)

await createHTTPServer({
  name: 'my-mcp-server',
  version: '1.0.0',
  auth: {
    resource: 'https://api.example.com',
    authorizationServers: ['https://auth.example.com'],
    scopesSupported: ['read:data', 'write:data'],
    documentationUrl: 'https://docs.example.com/auth',
    enableOAuthServer: true,
    oauthServerOptions: {
      sessionSecret: process.env.SESSION_SECRET!,
      tokenTTL: 3600,
      enableDCR: true,
      upstreamProvider: {
        id: 'github',
        authorizationEndpoint: 'https://github.com/login/oauth/authorize',
        tokenEndpoint: 'https://github.com/login/oauth/access_token',
        clientId: process.env.GITHUB_CLIENT_ID!,
        clientSecret: process.env.GITHUB_CLIENT_SECRET!,
        scopes: ['read:user'],
      },
    },
  },
});

Legacy Factory Pattern

import { createHTTPServer, MCPServer } from '@leanmcp/core';

await createHTTPServer(
  async () => {
    const server = new MCPServer({
      name: 'my-mcp-server',
      version: '1.0.0',
    });
    // Custom initialization
    return server.getServer();
  },
  {
    port: 3001,
    cors: true,
  }
);

Endpoints

The HTTP server exposes the following endpoints:

MCP Protocol Endpoint

POST /mcp Main MCP protocol endpoint for tool/prompt/resource requests. Headers:
  • Content-Type: application/json
  • mcp-session-id: <session-id> (stateful mode only)
  • Authorization: Bearer <token> (if auth enabled)
Request Body:
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "getCurrentTime",
    "arguments": {}
  },
  "id": 1
}

Dashboard UI

GET / Serves the interactive MCP dashboard UI (if dashboard: true). GET /mcp Serves dashboard UI or handles SSE streaming (stateful mode).

Health Check

GET /health Returns server health status. Response:
{
  "status": "ok",
  "mode": "stateless",
  "activeSessions": 0,
  "uptime": 123.45
}

OAuth Metadata (if auth enabled)

GET /.well-known/oauth-protected-resource RFC 9728: Protected Resource Metadata GET /.well-known/oauth-authorization-server RFC 8414: Authorization Server Metadata (if enableOAuthServer: true)

Stateless vs Stateful Mode

Stateless Mode (Default)

Optimized for Lambda/serverless deployments:
  • Fresh server instance per request
  • No session IDs in headers
  • No session persistence
  • Automatic cleanup after each request
  • DELETE /mcp returns 405 Method Not Allowed
await createHTTPServer({
  name: 'my-server',
  version: '1.0.0',
  stateless: true, // Default
});

Stateful Mode

Long-running servers with session support:
  • Single server instance
  • Session IDs in mcp-session-id header
  • Session persistence with optional backend store
  • SSE support for streaming
  • DELETE /mcp for session cleanup
await createHTTPServer({
  name: 'my-server',
  version: '1.0.0',
  stateless: false,
});

Port Selection

If the requested port is unavailable, the server will automatically try the next port (up to 20 attempts):
const { listener, port } = await createHTTPServer({
  name: 'my-server',
  version: '1.0.0',
  port: 3001, // May use 3002, 3003, etc. if 3001 is taken
});

console.log(`Server running on port ${port}`);

Graceful Shutdown

The server automatically handles SIGINT and SIGTERM signals:
const { listener } = await createHTTPServer({
  name: 'my-server',
  version: '1.0.0',
});

// Press Ctrl+C to trigger graceful shutdown
// - Closes all MCP transports
// - Closes HTTP server
// - Releases port

Build docs developers (and LLMs) love