vercel/ai · error · MCPClientOAuthError

OAuth protected resource metadata URL ${resourceMetadataUrl.

Error message

OAuth protected resource metadata URL ${resourceMetadataUrl.href} must have the same origin as the MCP server URL ${expectedOrigin}

What it means

assertResourceMetadataUrlSameOrigin enforces RFC 9728 hardening: the `resource_metadata` URL returned in a WWW-Authenticate challenge must be same-origin with the MCP server URL, otherwise a malicious server could point the client at attacker-controlled metadata. When the origins differ, MCPClientOAuthError is thrown listing both the metadata URL and the expected origin. It runs in authInternal when the MCP server responds 401 with a resource-metadata URL.

Source

Thrown at packages/mcp/src/tool/oauth.ts:311

      ),
    );
    return true;
  }

  return false;
}

function assertResourceMetadataUrlSameOrigin(
  serverUrl: string | URL,
  resourceMetadataUrl?: URL,
): void {
  if (!resourceMetadataUrl) {
    return;
  }

  const expectedOrigin = new URL(serverUrl).origin;
  if (resourceMetadataUrl.origin !== expectedOrigin) {
    throw new MCPClientOAuthError({
      message: `OAuth protected resource metadata URL ${resourceMetadataUrl.href} must have the same origin as the MCP server URL ${expectedOrigin}`,
    });
  }
}

function assertAuthorizationServerInformationMatches({
  storedAuthorizationServerInformation,
  currentAuthorizationServerInformation,
}: {
  storedAuthorizationServerInformation: OAuthAuthorizationServerInformation;
  currentAuthorizationServerInformation: OAuthAuthorizationServerInformation;
}): void {
  if (
    (storedAuthorizationServerInformation.issuer != null &&
      currentAuthorizationServerInformation.issuer != null &&
      storedAuthorizationServerInformation.issuer !==
        currentAuthorizationServerInformation.issuer) ||
    storedAuthorizationServerInformation.authorizationServerUrl !==

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Fix the MCP server (or its reverse proxy) so the resource_metadata URL in the 401 challenge is served from the same origin as the MCP server URL itself.
  2. If metadata legitimately lives elsewhere, host a same-origin metadata document (or route /​.well-known/oauth-protected-resource on the server origin) per RFC 9728.
  3. Check scheme and port match: https server must advertise https metadata on the same port; localhost dev should use the same port for server and metadata.
  4. Inspect the two URLs in the error message — align the metadata URL host/scheme/port with the serverUrl origin you passed to the client.
  5. If a proxy injects the wrong absolute URL, configure it to emit relative or same-origin absolute resource_metadata URLs.

Example fix

// before: challenge from https://api.example.com points metadata elsewhere
WWW-Authenticate: Bearer resource_metadata="https://auth.example.com/.well-known/oauth-protected-resource"
// after: same-origin metadata URL
WWW-Authenticate: Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"
Defensive patterns

Strategy: validation

Validate before calling

export function isSameOriginResourceMetadata(serverUrl: string, resourceMetadataUrl?: string): boolean {
  if (!resourceMetadataUrl) return true;
  try {
    return new URL(resourceMetadataUrl).origin === new URL(serverUrl).origin;
  } catch {
    return false;
  }
}
// run after parsing the 401 WWW-Authenticate challenge, before continuing the auth flow

Try / catch

import { MCPClientOAuthError } from './oauth';
try {
  await client.auth();
} catch (error) {
  if (MCPClientOAuthError.isInstance(error) && error.message.includes('must have the same origin')) {
    // fix the server's resource_metadata URL (or host metadata same-origin) and retry
  } else {
    throw error;
  }
}

Prevention

When it happens

Trigger: An MCP server returns a 401 challenge whose `resource_metadata` header/field points at a different origin than the server URL (CDN host, different subdomain, another port); a reverse proxy rewrites the metadata URL to an external host; the server advertises `http://` metadata while the client uses `https://` (different origin due to scheme); server configured with the wrong absolute URL for its own metadata document.

Common situations: MCP gateways where auth metadata lives on a separate domain (e.g. server at api.example.com, metadata at auth.example.com); localhost dev with mismatched ports (localhost:3000 server advertising localhost:8080 metadata — different origin); misconfigured proxy setting absolute URLs to a public domain; self-hosted servers with hand-written WWW-Authenticate headers.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/34a4aa027e444c2e. Report an issue: GitHub.