An MCP server protected by OAuth 2.1, using @cloudflare/workers-oauth-provider. Clients must complete the OAuth flow before calling tools — the auth context is then available inside tool handlers.
- OAuth 2.1 with MCP — dynamic client registration, authorization code flow, and token exchange
OAuthProvider— wrappingcreateMcpHandlerwith@cloudflare/workers-oauth-providergetMcpAuthContext()— accessing the authenticated user's identity inside tool handlers- Custom authorization UI — a Hono-based approval page for the OAuth flow
First, create a KV namespace for OAuth state:
npx wrangler kv namespace create OAUTH_KVUpdate the kv_namespaces binding in wrangler.jsonc with the returned ID, then:
npm install
npm run devOpen the browser to see the server info page. To test the tools, use the MCP Inspector — it will handle the OAuth flow automatically.
The OAuthProvider wraps the entire Worker. It intercepts OAuth endpoints (/authorize, /oauth/token, /oauth/register) and validates Bearer tokens on the API route (/mcp).
import { OAuthProvider } from "@cloudflare/workers-oauth-provider";
import { createMcpHandler, getMcpAuthContext } from "agents/mcp";
const apiHandler = {
async fetch(request, env, ctx) {
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
}
};
export default new OAuthProvider({
authorizeEndpoint: "/authorize",
tokenEndpoint: "/oauth/token",
clientRegistrationEndpoint: "/oauth/register",
apiRoute: "/mcp",
apiHandler,
defaultHandler: { fetch: (req, env, ctx) => AuthHandler.fetch(req, env, ctx) }
});Inside tool handlers, access the authenticated user:
server.registerTool("whoami", { description: "Who am I?" }, async () => {
const auth = getMcpAuthContext();
return {
content: [{ type: "text", text: JSON.stringify(auth?.props) }]
};
});mcp-worker— same stateless pattern without authenticationmcp-client— connecting to authenticated MCP servers as a client (handles OAuth automatically)