/** * OAuth 2.0 dynamic client registration (RFC 7591) — POST /oauth/register * * AI platforms call this once to register themselves and receive * client_id + client_secret. The plaintext secret is shown ONCE and * never stored — only its SHA-256 hash lives in mcp_clients. * * Two registration shapes are supported, in priority order: * * 1. RFC 7591-compliant (Anthropic Claude.ai sends this): * { * "client_name": "...", * "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"], * "grant_types": ["authorization_code", "refresh_token"], * "token_endpoint_auth_method": "none" | "client_secret_post", * "contact_email": "..." // non-standard, optional * } * * 2. Legacy ChatGPT Apps SDK shape (kept for backward compat): * { "client_name": "...", "contact_email": "..." } * Defaults: grant_types=["client_credentials"], * token_endpoint_auth_method="client_secret_post", * redirect_uris=[]. * * Validation rules: * - client_name: required, ≥ 2 chars. * - grant_types: subset of {authorization_code, refresh_token, * client_credentials}. If authorization_code is requested, * redirect_uris MUST be non-empty. * - redirect_uris: must be absolute https:// URLs OR the literal * loopback / custom-scheme patterns allowed by RFC 8252 §7. We do * a minimal "is parseable URL with a scheme" check here; the exact- * string allowlist match at /authorize is the real defence. * - token_endpoint_auth_method: one of {client_secret_post, * client_secret_basic, none}. `none` is only valid when PKCE is * used (i.e. authorization_code grant present). * * Public clients (token_endpoint_auth_method=none) still receive a * client_secret in the response so they can downgrade to a confidential * client later without re-registering — but they will fail authentication * if they try to send the secret on /oauth/token (see api/oauth.ts). */ import type { VercelRequest, VercelResponse } from "./_types.js"; export default function handler(req: VercelRequest, res: VercelResponse): Promise;