Skip to main content
Glama

MCP Starter for Puch AI

by TurboML-Inc
google-handler.ts3.87 kB
import type { AuthRequest, OAuthHelpers } from '@cloudflare/workers-oauth-provider' import { type Context, Hono } from 'hono' import { fetchUpstreamAuthToken, getUpstreamAuthorizeUrl, type Props } from './utils' import { clientIdAlreadyApproved, parseRedirectApproval, renderApprovalDialog } from './workers-oauth-utils' const app = new Hono<{ Bindings: Env & { OAUTH_PROVIDER: OAuthHelpers } }>() app.get('/authorize', async (c) => { const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw) const { clientId } = oauthReqInfo if (!clientId) { return c.text('Invalid request', 400) } if (await clientIdAlreadyApproved(c.req.raw, oauthReqInfo.clientId, c.env.COOKIE_ENCRYPTION_KEY)) { return redirectToGoogle(c, oauthReqInfo) } return renderApprovalDialog(c.req.raw, { client: await c.env.OAUTH_PROVIDER.lookupClient(clientId), server: { description: 'This MCP Server is a demo for Google OAuth.', name: 'Google OAuth Demo', }, state: { oauthReqInfo }, }) }) app.post('/authorize', async (c) => { const { state, headers } = await parseRedirectApproval(c.req.raw, c.env.COOKIE_ENCRYPTION_KEY) if (!state.oauthReqInfo) { return c.text('Invalid request', 400) } return redirectToGoogle(c, state.oauthReqInfo, headers) }) async function redirectToGoogle(c: Context, oauthReqInfo: AuthRequest, headers: Record<string, string> = {}) { return new Response(null, { headers: { ...headers, location: getUpstreamAuthorizeUrl({ clientId: c.env.GOOGLE_CLIENT_ID, hostedDomain: c.env.HOSTED_DOMAIN, redirectUri: new URL('/callback', c.req.raw.url).href, // scope: 'email profile', scope: 'email profile https://www.googleapis.com/auth/gmail.send', state: btoa(JSON.stringify(oauthReqInfo)), upstreamUrl: 'https://accounts.google.com/o/oauth2/v2/auth', }), }, status: 302, }) } /** * OAuth Callback Endpoint * * This route handles the callback from Google after user authentication. * It exchanges the temporary code for an access token, then stores some * user metadata & the auth token as part of the 'props' on the token passed * down to the client. It ends by redirecting the client back to _its_ callback URL */ app.get('/callback', async (c) => { // Get the oathReqInfo out of KV const oauthReqInfo = JSON.parse(atob(c.req.query('state') as string)) as AuthRequest if (!oauthReqInfo.clientId) { return c.text('Invalid state', 400) } // Exchange the code for an access token const code = c.req.query('code') if (!code) { return c.text('Missing code', 400) } const [accessToken, googleErrResponse] = await fetchUpstreamAuthToken({ clientId: c.env.GOOGLE_CLIENT_ID, clientSecret: c.env.GOOGLE_CLIENT_SECRET, code, grantType: 'authorization_code', redirectUri: new URL('/callback', c.req.url).href, upstreamUrl: 'https://accounts.google.com/o/oauth2/token', }) if (googleErrResponse) { return googleErrResponse } // Fetch the user info from Google const userResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', { headers: { Authorization: `Bearer ${accessToken}`, }, }) if (!userResponse.ok) { return c.text(`Failed to fetch user info: ${await userResponse.text()}`, 500) } const { id, name, email } = (await userResponse.json()) as { id: string name: string email: string } // Return back to the MCP client a new token const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({ metadata: { label: name, }, props: { accessToken, email, name, } as Props, request: oauthReqInfo, scope: oauthReqInfo.scope, userId: id, }) return Response.redirect(redirectTo) }) export { app as GoogleHandler }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TurboML-Inc/mcp-starter'

If you have feedback or need assistance with the MCP directory API, please join our Discord server