A TypeScript library for building MCP (Model Context Protocol) servers - works on both Cloudflare Workers and Node.js.
- Multiple auth strategies: OAuth 2.1, Google/GitHub presets, Bearer, API Key, Custom headers
- Encrypted token storage: AES-256-GCM at rest + proactive refresh
- Cloudflare Workers native: KV storage, memory fallback, zero config
- Production-ready: CIMD validation, OAuth discovery, DNS rebinding protection
- Quick scaffold:
bun create @phake/mcpto get started
See Comparison Guide for full feature comparison with official SDK.
- Bun 1.x or Node.js 20+
- Wrangler CLI (for Cloudflare Workers deployments)
- A Cloudflare account with Workers and KV access (for Worker deployments)
bun add @phake/mcp
# or
npm install @phake/mcpScaffold a new MCP server with one command:
# Interactive (prompts for template)
bun create @phake/mcp
# With options
bun create @phake/mcp my-mcp-app --template cloudflare-workers --installAvailable templates:
| Template | Description |
|---|---|
cloudflare-workers |
Cloudflare Workers + Hono (default) |
cloudflare-workers-google |
Cloudflare Workers + Google OAuth |
node-hono |
Node.js + Bun + Hono |
Options:
| Option | Description |
|---|---|
-t, --template |
Template name |
-i, --install |
Auto install dependencies |
-p, --pm |
Package manager: npm, bun, yarn, pnpm |
wrangler kv namespace create TOKENSwrangler.toml
[[kv_namespaces]]
binding = "TOKENS"
id = "<your-kv-namespace-id>"openssl rand -base64 32 | tr '+/' '-_' | tr -d '='Production:
wrangler secret put RS_TOKENS_ENC_KEYLocal development - add to .dev.vars:
RS_TOKENS_ENC_KEY=<your-generated-key>import { createMCPServer } from "@phake/mcp";
const server = createMCPServer({
adapter: "worker",
tools: [/* your tools */],
});
export default server;For detailed setup, see Getting Started Guide.
import { z } from "zod";
import { defineTool } from "@phake/mcp";
const greetTool = defineTool({
name: "greet",
description: "Returns a greeting for the given name",
inputSchema: z.object({
name: z.string().describe("Name to greet"),
}),
outputSchema: z.object({
message: z.string().describe("The greeting message"),
}),
handler: async (args) => {
return { message: `Hello, ${args.name}!` };
},
});const profileTool = defineTool({
name: "get_profile",
description: "Fetch the authenticated user's profile",
inputSchema: z.object({}),
requiresAuth: true,
handler: async (_args, context) => {
const response = await fetch("https://api.example.com/me", {
headers: context.resolvedHeaders,
});
return await response.json();
},
});Access Cloudflare worker bindings (AI, Vectorize, D1, R2, KV, etc.) in your tools:
import { createMCPServer, defineTool, type ToolContext } from "@phake/mcp";
interface Env extends Cloudflare.Env {
AI: unknown;
VECTORIZE: unknown;
MY_BUCKET: unknown;
}
const searchTool = defineTool({
name: "search_vectors",
inputSchema: z.object({ query: z.string() }),
handler: async (args, context: ToolContext<Env>) => {
// Type-safe access to bindings
const ai = context.bindings?.AI;
const vectorize = context.bindings?.VECTORIZE;
return {
content: [{
type: "text",
text: `AI: ${!!ai}, Vectorize: ${!!vectorize}`
}]
};
},
});
const server = createMCPServer<Env>({
tools: [searchTool],
});Fetch user profile from OAuth providers using context.getUser():
const userTool = defineTool({
name: "get_user_info",
requiresAuth: true,
inputSchema: z.object({}),
handler: async (_, context) => {
// Get token with error handling
const { data: token, error: tokenError } = context.getToken();
if (tokenError || !token) {
return { content: [{ type: "text", text: tokenError }], isError: true };
}
// Get user info with error handling
const { data, error } = await context.getUser();
if (error || !data) {
return { content: [{ type: "text", text: error }], isError: true };
}
return {
content: [{
type: "text",
text: JSON.stringify({ email: data.email, name: data.name })
}]
};
},
});The getUser() method automatically detects the provider based on AUTH_STRATEGY:
google→https://www.googleapis.com/oauth2/v2/userinfogithub→https://api.github.com/user
| Strategy | Description |
|---|---|
oauth |
Full OAuth 2.1 PKCE flow with RS token => provider token mapping |
google |
Same as OAuth with Google preset endpoints |
github |
Same as OAuth with GitHub preset endpoints |
bearer |
Static Bearer token |
api_key |
Static API key via header |
custom |
Arbitrary custom headers |
none |
No authentication |
See Authentication Guide for detailed configuration.
Creates an MCP server instance.
const server = createMCPServer({
adapter: "worker",
tools: [greetTool, profileTool],
});Type-safe tool factory. See API Reference for the full field reference.
Creates a typed error factory with preset default fields:
const fail = toolFail({ ok: false, items: null });
return fail("spreadsheet_id is required");
// => { ok: false, items: null, error: "spreadsheet_id is required" }| Tool | Input | Output | Description |
|---|---|---|---|
echo |
{ message, uppercase? } |
{ echoed, length } |
Echoes back a message |
health |
{ verbose? } |
{ status, timestamp, runtime, uptime? } |
Reports server health |
| Export | Path | Description |
|---|---|---|
| Core | @phake/mcp |
defineTool, createMCPServer, helpers |
| Worker runtime | @phake/mcp/runtime/worker |
Cloudflare Workers adapter |
| Node runtime | @phake/mcp/runtime/node |
Node.js adapter (experimental) |
# Install dependencies
bun install
# Run tests
bun test
# Type check
bun run typecheck
# Build
bun run build