Skip to main content

Overview

createMCPServer is the main entry point for building an MCP server. It wires together your tool definitions, storage backends, authentication strategy, and HTTP routing into a single object that Cloudflare Workers can export directly.
function createMCPServer<TEnv = unknown>(options: MCPServerOptions<TEnv>): MCPServer<TEnv>
Cloudflare Workers is the primary supported runtime. The returned MCPServer object exposes a fetch method that matches the Workers module export interface, so you can use export default directly.Use the generic TEnv parameter to get type-safe access to your Cloudflare bindings in tool handlers.

Parameters

options
MCPServerOptions
required
Configuration object for the server.

Return value

Returns an MCPServer<TEnv> object:
interface MCPServer<TEnv = unknown> {
  fetch: (request: Request, env: TEnv) => Promise<Response>;
}
The fetch method is called by the Workers runtime on every incoming request. It reads environment variables from env, initializes storage (KV with memory fallback), parses the active auth strategy, and routes the request to the appropriate handler.

Usage

Basic server

import { createMCPServer, defineTool } from "@phake/mcp";
import { z } from "zod";

const greetTool = defineTool({
  name: "greet",
  description: "Returns a greeting for the given name",
  inputSchema: z.object({
    name: z.string().describe("Name to greet"),
  }),
  handler: async (args) => {
    return { message: `Hello, ${args.name}!` };
  },
});

const server = createMCPServer({
  tools: [greetTool],
});

export default server;

With type-safe environment bindings

Define your environment type and use it with createMCPServer to get type-safe bindings:
interface Env {
  TOKENS: KVNamespace;
  MY_DATABASE: D1Database;
  MY_BUCKET: R2Bucket;
}

const server = createMCPServer<Env>({
  tools: [greetTool],
});

export default server;
In your tool handlers, the context now includes your typed bindings:
const greetTool = defineTool({
  name: "greet",
  description: "Returns a greeting for the given name",
  inputSchema: z.object({
    name: z.string(),
  }),
  handler: async (args, context) => {
    // context.env is typed as Env
    const value = await context.env.MY_DATABASE.prepare("SELECT * FROM users").first();
    return { message: `Hello, ${args.name}!`, dbResult: value };
  },
});

Environment variables

createMCPServer reads all configuration from the Worker’s env object at request time. The following bindings and variables are used:
VariableRequiredDescription
TOKENSYesCloudflare KV namespace binding for token and session storage
RS_TOKENS_ENC_KEYYes (production)Base64url-encoded 32-byte AES-256-GCM encryption key
AUTH_STRATEGYNooauth | bearer | api_key | custom | none (inferred if unset)
If the TOKENS KV namespace binding is missing, the server returns 503 Service Unavailable on every request. Bind the namespace in your wrangler.toml or wrangler.jsonc before deploying.
If RS_TOKENS_ENC_KEY is not set in production, tokens are stored in KV unencrypted and a warning is logged. In local development this is acceptable.

Storage

createMCPServer sets up token and session storage automatically based on your environment:
  • If a TOKENS KV namespace binding is present, it uses Cloudflare KV with an in-memory fallback.
  • If RS_TOKENS_ENC_KEY is set, all stored values are encrypted with AES-256-GCM.
  • If TOKENS is missing, the server returns 503 Service Unavailable on every request.
You do not need to instantiate storage backends directly. See Storage backends for the full reference.