Skip to main content
Glama

Muni-MCP

by leafsicle
google-handler.ts4.05 kB
import type { AuthRequest, OAuthHelpers } from '@cloudflare/workers-oauth-provider' import { Hono, Context } from 'hono' import { fetchUpstreamAuthToken, getUpstreamAuthorizeUrl, Props } from './oauth' 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: { provider: "google", name: 'MCP Boilerplate', logo: "https://avatars.githubusercontent.com/u/314135?s=200&v=4", description: 'This is a boilerplate MCP that you can use to build your own remote MCP server, with Stripe integration for paid tools and Google/Github authentication.', }, 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, { status: 302, headers: { ...headers, location: getUpstreamAuthorizeUrl({ upstream_url: 'https://accounts.google.com/o/oauth2/v2/auth', scope: 'email profile', client_id: c.env.GOOGLE_CLIENT_ID, redirect_uri: new URL('/callback/google', c.req.raw.url).href, state: btoa(JSON.stringify(oauthReqInfo)), hosted_domain: c.env.HOSTED_DOMAIN, }), }, }) } /** * 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/google', 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({ upstream_url: 'https://accounts.google.com/o/oauth2/token', client_id: c.env.GOOGLE_CLIENT_ID, client_secret: c.env.GOOGLE_CLIENT_SECRET, code, redirect_uri: new URL('/callback/google', c.req.url).href, grant_type: 'authorization_code', }) 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({ request: oauthReqInfo, userId: id, metadata: { label: name, }, scope: oauthReqInfo.scope, props: { name, email, accessToken, userEmail: email, } as Props, }) 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/leafsicle/muni-mcp'

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