import type { AuthRequest, OAuthHelpers } from "@cloudflare/workers-oauth-provider"; import { Hono } from "hono"; interface Env { OAUTH_PROVIDER: OAuthHelpers; } const app = new Hono<{ Bindings: Env }>(); /** * GET /authorize - OAuth authorization endpoint * * This endpoint is called when an MCP client wants to authorize. * In a full implementation, this would: * 1. Parse the OAuth request * 2. Check if the client is already approved (via cookie) * 3. Show an approval dialog or redirect to external OAuth provider */ app.get("/authorize", async (c) => { const oauthReqInfo: AuthRequest = await c.env.OAUTH_PROVIDER.parseAuthRequest( c.req.raw ); const clientInfo = await c.env.OAUTH_PROVIDER.lookupClient( oauthReqInfo.clientId ); if (!clientInfo) { return c.text("Invalid client_id", 400); } // For this demo, we'll show a simple HTML approval page // In a real implementation, you might: // - Check cookies to see if this client was previously approved // - Redirect to an external OAuth provider (GitHub, Google, etc.) // - Show a custom approval UI const approvalPage = ` Authorize ${clientInfo.clientName || "MCP Client"}

Authorization Request

${clientInfo.clientName || "An MCP Client"} is requesting access to the MCP server.

Client ID: ${clientInfo.clientId}

${clientInfo.clientUri ? `

Website: ${clientInfo.clientUri}

` : ""}

Requested Scopes: ${oauthReqInfo.scope.join(", ") || "none"}

If you approve, this client will be able to:

`; return c.html(approvalPage); }); /** * POST /authorize - Handle authorization approval * * This endpoint is called when the user approves the authorization. * It completes the OAuth flow by creating a grant and redirecting back to the client. */ app.post("/authorize", async (c) => { const formData = await c.req.formData(); const state = formData.get("state"); if (!state || typeof state !== "string") { return c.text("Missing state parameter", 400); } let oauthReqInfo: AuthRequest; try { oauthReqInfo = JSON.parse(atob(state)); } catch { return c.text("Invalid state parameter", 400); } // For this demo, we'll use a static user ID // In a real implementation, you would: // 1. Have already authenticated the user (via external OAuth, session, etc.) // 2. Use their actual user ID and profile information const userId = "demo-user"; const userProfile = { userId: "demo-user", username: "Demo User", email: "demo@example.com" }; // Complete the authorization by creating a grant const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({ request: oauthReqInfo, userId: userId, metadata: { label: "MCP Server Access", clientName: (await c.env.OAUTH_PROVIDER.lookupClient(oauthReqInfo.clientId)) ?.clientName || "Unknown Client" }, scope: oauthReqInfo.scope, props: userProfile }); // Redirect back to the client with the authorization code return c.redirect(redirectTo, 302); }); /** * GET / - Home page * * Shows information about the OAuth server */ app.get("/", (c) => { return c.html(` MCP OAuth Server

MCP OAuth Server

This is an authenticated MCP server that uses OAuth 2.1 for authorization.

Available Endpoints

/mcp - MCP server endpoint (requires Bearer token)
/authorize - OAuth authorization endpoint
/token - OAuth token endpoint
/register - Client registration endpoint
/.well-known/oauth-authorization-server - OAuth metadata

Getting Started

To connect to this MCP server:

  1. Register your MCP client via the /register endpoint
  2. Initiate the OAuth flow by redirecting to /authorize
  3. Exchange the authorization code for an access token at /token
  4. Use the access token to access the /mcp endpoint
`); }); export { app as AuthHandler };