🐞 Bug Summary
When a request arrives without an OAuth token and the backend MCP server requires OAuth authentication, the 401 response's WWW-Authenticate header is missing the resource_metadata attribute. This breaks MCP OAuth discovery for clients that rely on this attribute to locate the resource metadata endpoint (per the MCP OAuth specification).
The root cause is a code ordering issue in _auth_no_token(): the strict-mode and empty-bearer checks return 401 before _check_server_oauth_enforcement() is called, so the resource_metadata URL is never included in the response.
🧩 Affected Component
🔁 Steps to Reproduce
- Configure a gateway with a backend MCP server that has OAuth enabled
- Send a request to a tool on that server without an
Authorization header
- Observe the 401 response's
WWW-Authenticate header
- The header contains
Bearer realm="mcp-gateway" but is missing the resource_metadata attribute
🤔 Expected Behavior
The 401 WWW-Authenticate header should include the resource_metadata attribute pointing to the server's OAuth resource metadata endpoint, e.g.:
Bearer realm="mcp-gateway", resource_metadata="https://gateway.example.com/.well-known/oauth-protected-resource/server-name"
This allows MCP clients to discover the OAuth configuration and initiate the authorization flow.
📓 Logs / Error Output
No crash — the gateway returns 401 but with an incomplete WWW-Authenticate header. Clients that follow the MCP OAuth spec cannot discover the resource metadata and fail to authenticate.
🧠 Environment Info
| Key |
Value |
| Version or commit |
1.0.0-RC2 |
| Runtime |
Python 3.12, Gunicorn |
| Platform / OS |
Linux (Kubernetes) |
| Container |
Docker |
🧩 Additional Context
The fix reorders _auth_no_token() in streamablehttp_transport.py:
- Call
_check_server_oauth_enforcement() first to determine if the server requires OAuth
- Build the
www_auth string with the resource_metadata attribute when applicable
- Use the enriched
www_auth in all subsequent 401 responses (strict-mode check, empty-bearer check)
This ensures every 401 response includes the full OAuth discovery information regardless of which check triggers the rejection.
🐞 Bug Summary
When a request arrives without an OAuth token and the backend MCP server requires OAuth authentication, the 401 response's
WWW-Authenticateheader is missing theresource_metadataattribute. This breaks MCP OAuth discovery for clients that rely on this attribute to locate the resource metadata endpoint (per the MCP OAuth specification).The root cause is a code ordering issue in
_auth_no_token(): the strict-mode and empty-bearer checks return 401 before_check_server_oauth_enforcement()is called, so theresource_metadataURL is never included in the response.🧩 Affected Component
🔁 Steps to Reproduce
AuthorizationheaderWWW-AuthenticateheaderBearer realm="mcp-gateway"but is missing theresource_metadataattribute🤔 Expected Behavior
The 401
WWW-Authenticateheader should include theresource_metadataattribute pointing to the server's OAuth resource metadata endpoint, e.g.:This allows MCP clients to discover the OAuth configuration and initiate the authorization flow.
📓 Logs / Error Output
No crash — the gateway returns 401 but with an incomplete
WWW-Authenticateheader. Clients that follow the MCP OAuth spec cannot discover the resource metadata and fail to authenticate.🧠 Environment Info
1.0.0-RC2🧩 Additional Context
The fix reorders
_auth_no_token()instreamablehttp_transport.py:_check_server_oauth_enforcement()first to determine if the server requires OAuthwww_authstring with theresource_metadataattribute when applicablewww_authin all subsequent 401 responses (strict-mode check, empty-bearer check)This ensures every 401 response includes the full OAuth discovery information regardless of which check triggers the rejection.