Describe the bug
Ran into this wiring up an OAuth-protected MCP client. When a 401 WWW-Authenticate challenge carries several auth-params, extract_field_from_www_auth (mcp/client/auth/utils.py) matches the requested field name as a substring of another param's name, so a different param shadows the real value.
The pattern is rf'{field_name}=(?:"([^"]+)"|([^\s,]+))' searched with re.search, with no boundary before field_name.
To reproduce
import httpx
from mcp.client.auth.utils import extract_field_from_www_auth, extract_resource_metadata_from_www_auth
def r(h):
return httpx.Response(401, headers={"WWW-Authenticate": h},
request=httpx.Request("GET", "https://api.example.com/"))
# A real `scope` is present, but `error_scope` shadows it:
print(extract_field_from_www_auth(r('Bearer error_scope="decoy", scope="read write"'), "scope"))
# -> 'decoy' (expected 'read write')
# Only a decoy param exists; no real `scope`:
print(extract_field_from_www_auth(r('Bearer custom_scope="leaked"'), "scope"))
# -> 'leaked' (expected None)
# Same for resource_metadata, which drives discovery URL selection:
print(extract_resource_metadata_from_www_auth(r('Bearer x_resource_metadata="https://decoy.example.com"')))
# -> 'https://decoy.example.com' (expected None)
Expected behavior
The field name should match only as a complete auth-param name (at the header start or after a whitespace/comma separator), so scope doesn't match inside error_scope. This matters most for resource_metadata, since the client uses it to pick the protected-resource-metadata discovery URL — reading it from the wrong param points discovery at the wrong place.
Environment
mcp main (reproduces on v1.x as well)
I have a small fix (anchor the param name to the header start or a separator) plus regression cases added to the existing test_extract_field_from_www_auth_* parametrized tests, and can open a PR if this looks right.
Reviewed by AI
Describe the bug
Ran into this wiring up an OAuth-protected MCP client. When a 401
WWW-Authenticatechallenge carries several auth-params,extract_field_from_www_auth(mcp/client/auth/utils.py) matches the requested field name as a substring of another param's name, so a different param shadows the real value.The pattern is
rf'{field_name}=(?:"([^"]+)"|([^\s,]+))'searched withre.search, with no boundary beforefield_name.To reproduce
Expected behavior
The field name should match only as a complete auth-param name (at the header start or after a whitespace/comma separator), so
scopedoesn't match insideerror_scope. This matters most forresource_metadata, since the client uses it to pick the protected-resource-metadata discovery URL — reading it from the wrong param points discovery at the wrong place.Environment
mcpmain(reproduces onv1.xas well)I have a small fix (anchor the param name to the header start or a separator) plus regression cases added to the existing
test_extract_field_from_www_auth_*parametrized tests, and can open a PR if this looks right.Reviewed by AI