Add OAuth 2.1 authentication flow for MCP server#21
Add OAuth 2.1 authentication flow for MCP server#21andrewdmontgomery wants to merge 34 commits intomainfrom
Conversation
…client The fetch api client has a bug that causes it to fail to throw an error on non-2xx responses
| // Initialize the Hono app with routes for the OAuth Provider | ||
| const app = new Hono<{ Bindings: Env & { OAUTH_PROVIDER: any } }>(); | ||
| app.get("/authorize", authorize); | ||
| app.post("/authorize/consent", confirmConsent); | ||
| app.get("/callback", callback); | ||
| app.post("/register", registerClient); | ||
|
|
||
| // Root endpoint for basic server info | ||
| app.get("/", (c) => { | ||
| const serverInfo = getServerInfo(); | ||
| return c.json({ | ||
| name: serverInfo.name, | ||
| version: serverInfo.version, | ||
| endpoints: { | ||
| mcp: "/mcp", | ||
| sse: "/sse", | ||
| oauth_authorize: "/authorize", | ||
| oauth_callback: "/callback", | ||
| oauth_token: "/token", | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| function createOAuthProviderIfConfigured(env: Env) { | ||
| // Only create OAuth provider if all required variables are set | ||
| if (!env.OAUTH_CLIENT_ID || !env.OAUTH_CLIENT_SECRET || !env.OAUTH_AUTHORIZATION_ENDPOINT) { | ||
| return null; | ||
| } | ||
|
|
||
| return new OAuthProvider({ | ||
| apiHandlers: { | ||
| // @ts-ignore | ||
| "/mcp": GravatarMcpServer.serve("/mcp"), | ||
| // @ts-ignore | ||
| "/sse": GravatarMcpServer.serveSSE("/sse"), | ||
| }, | ||
| // @ts-ignore | ||
| defaultHandler: app, | ||
| authorizeEndpoint: "/authorize", | ||
| tokenEndpoint: "/token", | ||
| tokenExchangeCallback: (options: any) => tokenExchangeCallback(options), | ||
| clientRegistrationEndpoint: "/register", | ||
| }); | ||
| } | ||
|
|
There was a problem hiding this comment.
This is the main part of OAuth support. Most of the rest of the changes are there to support this.
| // @ts-ignore | ||
| "/mcp": GravatarMcpServer.serve("/mcp"), | ||
| // @ts-ignore | ||
| "/sse": GravatarMcpServer.serveSSE("/sse"), |
There was a problem hiding this comment.
These // @ts-ignore are here because I couldn't figure out why TypeScript was complaining. Specifically, the error (which is hard to parse) says that the GravatarMcpServer.serve() function (and the other one) are returning a function signature that is slightly different from what the apiHandlers closure expects.
Yet this exact code checks out fine in Cloudflare's own production MCP servers:
return new OAuthProvider({
apiHandlers: {
'/mcp': DNSAnalyticsMCP.serve('/mcp'),
'/sse': DNSAnalyticsMCP.serveSSE('/sse'),
},
// @ts-ignore
defaultHandler: createAuthHandlers({ scopes: AnalyticsScopes, metrics }),
authorizeEndpoint: '/oauth/authorize',
tokenEndpoint: '/token',
tokenExchangeCallback: (options) =>
handleTokenExchangeCallback(
options,
env.CLOUDFLARE_CLIENT_ID,
env.CLOUDFLARE_CLIENT_SECRET
),
// Cloudflare access token TTL
accessTokenTTL: 3600,
clientRegistrationEndpoint: '/register',
}).fetch(req, env, ctx)
},Note, however that the defaultHandler parameter has a similar problem in Cloudflare's servers, requiring the same comment.
| if (oauthProvider) { | ||
| return oauthProvider.fetch(request, env, ctx); | ||
| } |
There was a problem hiding this comment.
This conditional logic probably isn't necessary, useful, or even desirable. If the proper OAuth settings aren't available, we should fix that problem rather than booting up a server that just doesn't support OAuth. Especially since many tools require it.
Summary
Key Changes
OAuth Authentication System:
Development Environment:
.dev.vars.exampletemplate with detailed setup instructions.dev.vars) and configuration (wrangler.jsonc)Tool System Improvements:
search-profiles-by-verified-accounttoolConfiguration Management:
wrangler.jsoncTest Plan
npm run dev🤖 Generated with Claude Code