Add MCP OAuth auth to Express.js (Scalekit)
Choose a mode
Ask: "Are we scaffolding a brand-new MCP server repo, or adding MCP auth into an existing Express app?"
- •Mode A: New project scaffold (recommended for demos/POCs)
- •Mode B: Retrofit existing Express app (recommended for real products)
Inputs to collect (ask if missing)
- •Server base URL and port; confirm whether trailing slash is required for the audience (example: http://localhost:3002/)
- •SK_ENV_URL, SK_CLIENT_ID, SK_CLIENT_SECRET
- •PROTECTED_RESOURCE_METADATA JSON (copied from Scalekit dashboard MCP server page)
- •EXPECTED_AUDIENCE (must match the Server URL registered in Scalekit)
Required outcomes
- •Public discovery endpoint: GET /.well-known/oauth-protected-resource (returns PROTECTED_RESOURCE_METADATA as JSON)
- •Public health endpoint: GET /health
- •Auth middleware: validates Authorization: Bearer <token>, returns 401 + WWW-Authenticate with resource_metadata URL on failure
- •MCP endpoint: POST / protected by middleware, handled via MCP SDK StreamableHTTPServerTransport
- •At least one tool registered with server.tool(...)
Mode A — Scaffold a new project
Steps
- •Create a folder and initialize dependencies using templates in:
- •assets/new-project/package.json
- •assets/new-project/tsconfig.json
- •assets/new-project/src/server.ts
- •
Create .env using assets/env.example (fill real values).
- •
Run:
- •npm install
- •npm run dev
Notes
- •Ensure EXPECTED_AUDIENCE exactly matches the Scalekit "Server URL" (including trailing slash if used).
- •Keep /.well-known/oauth-protected-resource public; MCP clients need it for discovery.
Mode B — Retrofit an existing Express app
Identify insertion points
Ask for:
- •Existing server entrypoint file (e.g., src/index.ts or src/server.ts)
- •Current app router structure and whether POST / is already used
- •Existing auth middlewares and CORS settings
Patch plan (minimal diffs)
- •Add env vars (SK_*, EXPECTED_AUDIENCE, PROTECTED_RESOURCE_METADATA).
- •Add routes:
- •GET /.well-known/oauth-protected-resource (public)
- •GET /health (public)
- •Add auth middleware (public-path exemptions + Bearer extraction + validateToken with audience).
- •Add MCP server + route:
- •Create McpServer + tools (assets/retrofit/mcp-server.ts)
- •Add POST handler (assets/retrofit/mcp-route.ts)
- •Route mounting:
- •If POST / is free, mount MCP at /
- •If POST / is used, mount MCP at /mcp and update RESOURCE_METADATA_URL accordingly (and ensure clients point to correct MCP URL)
Templates
- •Auth middleware: assets/retrofit/auth-middleware.ts
- •Well-known route: assets/retrofit/well-known-route.ts
- •MCP server + tool registration: assets/retrofit/mcp-server.ts
- •MCP POST handler: assets/retrofit/mcp-route.ts
Verification checklist
- •GET /.well-known/oauth-protected-resource works without Authorization header
- •POST MCP endpoint without token -> 401 + WWW-Authenticate (resource_metadata points to the well-known URL)
- •Valid token with correct audience -> MCP tool call succeeds
- •Wrong-audience token -> 401
See references/TROUBLESHOOTING.md for common misconfigurations.