Skip to main content
Glama
route.ts4.93 kB
import { NextRequest, NextResponse } from "next/server"; import { clerkClient } from "@clerk/nextjs/server"; import { expandLocalhostUris } from "@/lib/auth-utils"; // Custom registration endpoint needed because Clerk doesn't support custom scopes // We only want "openid" scope instead of Clerk's default email/profile scopes export async function OPTIONS(): Promise<NextResponse> { return new NextResponse(null, { status: 204, headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization", }, }); } export async function POST(request: NextRequest): Promise<NextResponse> { const contentType = request.headers.get("content-type"); if (!contentType?.includes("application/json")) { return NextResponse.json( { error: "invalid_request", error_description: "Content-Type must be application/json", }, { status: 400 }, ); } let body; try { body = await request.json(); } catch { return NextResponse.json( { error: "invalid_request", error_description: "Invalid JSON body" }, { status: 400 }, ); } const { redirect_uris, client_name, client_uri, logo_uri, contacts, tos_uri, policy_uri, token_endpoint_auth_method = "none", // Default to PKCE for public clients grant_types = ["authorization_code"], response_types = ["code"], scope = "openid", } = body; // Validate required parameters if ( !redirect_uris || !Array.isArray(redirect_uris) || redirect_uris.length === 0 ) { return NextResponse.json( { error: "invalid_redirect_uri", error_description: "redirect_uris is required and must be a non-empty array", }, { status: 400 }, ); } // Validate redirect URIs for (const uri of redirect_uris) { try { const url = new URL(uri); // Allow HTTPS, localhost, and custom URI schemes (for native apps) if (url.protocol === "https:") { // HTTPS is always allowed continue; } else if ( url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1") ) { // HTTP localhost is allowed for development continue; } else if (url.protocol !== "http:" && url.protocol !== "https:") { // Custom URI schemes are allowed for native apps (e.g., cursor://, myapp://) continue; } else { // HTTP with non-localhost hostname is not allowed return NextResponse.json( { error: "invalid_redirect_uri", error_description: "HTTP redirect URIs are only allowed for localhost", }, { status: 400 }, ); } } catch { return NextResponse.json( { error: "invalid_redirect_uri", error_description: "Invalid redirect URI format", }, { status: 400 }, ); } } try { // Expand redirect_uris to include both localhost and 127.0.0.1 variants // This is needed because Vercel normalizes 127.0.0.1 to localhost in query params const expandedRedirectUris = expandLocalhostUris(redirect_uris); // Register the OAuth application with Clerk const clerk = await clerkClient(); const oauthApp = await clerk.oauthApplications.create({ name: client_name || "MCP Client", redirectUris: expandedRedirectUris, scopes: scope ? scope : "openid", public: true, }); // Create response in OAuth Dynamic Client Registration format const now = Math.floor(Date.now() / 1000); const registrationResponse = { client_id: oauthApp.clientId, client_secret: oauthApp.clientSecret || undefined, // May be null for public clients client_name: client_name || "MCP Client", client_uri, logo_uri, contacts, tos_uri, policy_uri, redirect_uris, token_endpoint_auth_method, grant_types, response_types, scope, client_id_issued_at: now, client_secret_expires_at: 0, // Public clients don't expire registration_access_token: oauthApp.id, // Use OAuth app ID as registration token registration_client_uri: `${request.nextUrl.origin}/register/${oauthApp.clientId}`, }; return NextResponse.json(registrationResponse, { headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization", }, }); } catch (error) { console.error("Client registration error:", error); return NextResponse.json( { error: "invalid_request", error_description: "Failed to register client with OAuth provider", }, { status: 500 }, ); } }

Latest Blog Posts

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/onkernel/kernel-mcp-server'

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