diff --git a/.changeset/spicy-dragons-relate.md b/.changeset/spicy-dragons-relate.md new file mode 100644 index 0000000..6990a88 --- /dev/null +++ b/.changeset/spicy-dragons-relate.md @@ -0,0 +1,5 @@ +--- +"@vercel/mcp-adapter": patch +--- + +Update auth logic to not throw error is missing bearerToken diff --git a/src/next/auth-wrapper.ts b/src/next/auth-wrapper.ts index d71de22..75e835d 100644 --- a/src/next/auth-wrapper.ts +++ b/src/next/auth-wrapper.ts @@ -4,7 +4,8 @@ import { withAuthContext } from "./auth-context"; export function withMcpAuth( handler: (req: Request) => Response | Promise, verifyToken: ( - req: Request + req: Request, + bearerToken?: string ) => AuthInfo | undefined | Promise, { required = false, @@ -17,13 +18,21 @@ export function withMcpAuth( return async (req: Request) => { const origin = new URL(req.url).origin; - const authInfo = await verifyToken(req); + const authHeader = req.headers.get("Authorization"); + const [type, token] = authHeader?.split(" ") || []; + + // Only support bearer token as per the MCP spec + // https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization#2-6-1-token-requirements + const bearerToken = type?.toLowerCase() === "bearer" ? token : undefined; + + const authInfo = await verifyToken(req, bearerToken); + if (required && !authInfo) { - return Response.json( - { + return new Response( + JSON.stringify({ error: "unauthorized_client", error_description: "No authorization provided", - }, + }), { status: 401, headers: { @@ -38,8 +47,11 @@ export function withMcpAuth( } if (authInfo.expiresAt && authInfo.expiresAt < Date.now() / 1000) { - return Response.json( - { error: "invalid_token", error_description: "Authorization expired" }, + return new Response( + JSON.stringify({ + error: "invalid_token", + error_description: "Authorization expired", + }), { status: 401, headers: {