Skip to content

Latest commit

 

History

History

examples

README.md

Examples

Four runnable MCP servers built with the toolkit.

File What it shows
basic-auth-server.ts API key auth, JWT auth, reading the auth context in a handler
full-middleware-stack.ts All five packages composed on one server
production-server.ts Auth + rate limit + cache + logging with realistic tools and stats
shared-state-redis.ts Cache and rate limit in Redis / Valkey, shared by every replica

Prerequisites

Build the workspace first. The examples import the packages by their published names (@mcp-toolkit/auth and friends), which npm workspaces resolve to each package's dist/, so an unbuilt checkout fails at import time:

npm install
npm run build

The examples are run with tsx, which is not a dependency of this repo. npx tsx fetches it on demand:

npx tsx examples/basic-auth-server.ts

examples/ is typechecked by npm run typecheck:examples, which CI runs, so these files stay compilable against the current SDK.

Environment variables

Variable Used by Default
MCP_API_KEY all four dev-api-key-12345 (basic, full, shared-state), prod-key-abc123 (production)
JWT_SECRET basic-auth-server (jwt mode) super-secret-jwt-key
MCP_ALLOWED_ORIGINS full-middleware-stack unset, so the origin allowlist is skipped
LOG_LEVEL production-server info
LOG_FILE production-server unset, so logs go to stderr only
REDIS_URL shared-state-redis redis://localhost:6379

The defaults are development placeholders. Set real values before exposing any of these.

basic-auth-server.ts takes a mode argument: npx tsx examples/basic-auth-server.ts jwt starts the JWT variant instead of the API key one.

Exercising a running server

With the MCP Inspector

npx @modelcontextprotocol/inspector npx tsx examples/production-server.ts

The Inspector lists the tools and calls them interactively. Note that a call it sends carries no credential, so every tool answers with an auth error -- which is itself the useful signal that the middleware is active.

With raw JSON-RPC on stdin

To see auth accept and reject, the credential has to travel somewhere the server can read it. Under stdio that is the request's params._meta:

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"1.0.0"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"greet","arguments":{"name":"Sagar"},"_meta":{"x-api-key":"dev-api-key-12345"}}}' \
  '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"greet","arguments":{"name":"Sagar"}}}' \
  | npx tsx examples/basic-auth-server.ts

Request 2 returns the greeting; request 3 returns Missing API key in "x-api-key" header. On a Streamable HTTP or SSE transport the same credential arrives as a real HTTP header instead.

Two replicas, one cache and one limit

shared-state-redis.ts needs a Redis 5+ or Valkey server. Run two copies against the same REDIS_URL and call slow-square with the same n on each: the second answers from the shared cache, and its computedBy field still names the first process's pid. The 5-token bucket is shared too, so 5 calls in total go through before both copies answer Rate limited.

What these examples do not show

Every example uses StdioServerTransport, so:

  • Logs go to stderr. Writing them to stdout would corrupt the JSON-RPC stream, which is why the logger defaults to stderr.
  • withCors has no Origin to validate. full-middleware-stack.ts applies it only when MCP_ALLOWED_ORIGINS is set, because an allowlist with no origin present rejects every call by design.

See the root README for the full list of limitations.