branch:
README.md
2323 bytesRaw
# Authenticated MCP Server

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.

## What it demonstrates

- **OAuth 2.1 with MCP** — dynamic client registration, authorization code flow, and token exchange
- **`OAuthProvider`** — wrapping `createMcpHandler` with `@cloudflare/workers-oauth-provider`
- **`getMcpAuthContext()`** — accessing the authenticated user's identity inside tool handlers
- **Custom authorization UI** — a Hono-based approval page for the OAuth flow

## Running

First, create a KV namespace for OAuth state:

```sh
npx wrangler kv namespace create OAUTH_KV
```

Update the `kv_namespaces` binding in `wrangler.jsonc` with the returned ID, then:

```sh
npm install
npm run dev
```

Open the browser to see the server info page. To test the tools, use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) — it will handle the OAuth flow automatically.

## How it works

The `OAuthProvider` wraps the entire Worker. It intercepts OAuth endpoints (`/authorize`, `/oauth/token`, `/oauth/register`) and validates Bearer tokens on the API route (`/mcp`).

```typescript
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:

```typescript
server.registerTool("whoami", { description: "Who am I?" }, async () => {
  const auth = getMcpAuthContext();
  return {
    content: [{ type: "text", text: JSON.stringify(auth?.props) }]
  };
});
```

## Related examples

- [`mcp-worker`](../mcp-worker/) — same stateless pattern without authentication
- [`mcp-client`](../mcp-client/) — connecting to authenticated MCP servers as a client (handles OAuth automatically)