Skip to content

Conversation

@Rodriguespn
Copy link

@Rodriguespn Rodriguespn commented Nov 28, 2025

Summary

This PR adds support for OAuth 2.0 Protected Resource Metadata discovery (RFC 9728) to properly discover authorization servers that are hosted on different domains than the MCP server. This is required for MCP Authorization Server Discovery compliance and enables interoperability with MCP servers like Supabase where the auth server (https://api.supabase.com) differs from the MCP server (https://mcp.supabase.com).

Problem

Previously, mcp-remote assumed the MCP server URL was the same as the authorization server URL. This broke authentication for servers like Supabase that return:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_request",
                         error_description="No access token was provided in this request",
                         resource_metadata="https://mcp.supabase.com/.well-known/oauth-protected-resource/mcp"

Changes

New Module: Protected Resource Metadata

  • Created protected-resource-metadata.ts module implementing RFC 9728
  • parseWWWAuthenticateHeader() - Extracts resource_metadata, scope, error, and error_description from 401 responses
  • buildProtectedResourceMetadataUrls() - Constructs well-known URLs with proper fallback order
  • discoverProtectedResourceMetadata() - Full discovery flow with header priority, then path-specific, then root well-known
  • getAuthorizationServerUrl() - Extracts first authorization server from metadata
  • Comprehensive test coverage (24 tests) including Supabase-specific scenarios

OAuth Server Discovery Flow

  • Added discoverOAuthServerInfo() function in utils.ts implementing the MCP spec discovery flow:
    1. Probe MCP server to get WWW-Authenticate header
    2. Parse resource_metadata URL from header (if present)
    3. Fetch Protected Resource Metadata from header URL or well-known endpoints
    4. Extract authorization_servers to find the actual OAuth server
    5. Fetch Authorization Server Metadata from discovered server

Enhanced Scope Resolution

Updated NodeOAuthClientProvider with new scope priority order:

  1. User-provided scope from static OAuth client metadata (highest)
  2. Scope from WWW-Authenticate header (new!)
  3. scopes_supported from Protected Resource Metadata (new!)
  4. Scope from client registration response
  5. scopes_supported from Authorization Server Metadata
  6. Fallback default (openid email profile)

Client & Proxy Integration

  • Both client.ts and proxy.ts now call discoverOAuthServerInfo() on startup
  • Pass discovered authorization server URL to NodeOAuthClientProvider
  • Pass Protected Resource Metadata and WWW-Authenticate scope for proper scope resolution
  • Added debug logging throughout the discovery flow
  • Graceful fallback to previous behavior if discovery fails

Benefits

  • Supabase compatibility: Now works correctly with Supabase MCP server and similar setups
  • RFC 9728 compliance: Implements Protected Resource Metadata discovery per spec
  • MCP spec compliance: Follows MCP Authorization Server Discovery requirements
  • Automatic scope discovery: Uses scopes_supported from PRM for proper authorization
  • Graceful degradation: Falls back to server URL if no PRM is found
  • Backwards compatible: Existing setups continue to work unchanged

Testing

  • Added 24 new unit tests for Protected Resource Metadata discovery
  • Tests cover WWW-Authenticate parsing, well-known URL construction, discovery fallbacks, and Supabase-specific scenarios
  • All 100 tests pass (76 existing + 24 new)

Test with Supabase MCP Server

npm run build
node dist/proxy.js https://mcp.supabase.com/mcp --debug

Closes #192

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Protected Resource Metadata Discovery (RFC 9728) for MCP Authorization compliance

1 participant