modelcontextprotocol / modelcontextprotocol/python-sdk

RequireAuthMiddleware omits RFC 6750 scope in WWW-Authenticate on 401/403

Open Beginner friendly
#3,103 2 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

auth bug needs confirmation P2 v1 v2
Dominant language
Python
Stars
24.3k
Forks
4k
Avg merge
1d 1h
Merged PRs (30d)
31

Description

Initial Checks
Description

Summary

RequireAuthMiddleware._send_auth_error builds WWW-Authenticate with error and error_description (and optional resource_metadata), but never includes the scope parameter, even when required_scopes is configured.

This breaks RFC 6750 Section 3.1 and the MCP Authorization scope-selection / step-up flow. The SDK client already expects scope via extract_scope_from_www_auth() and uses it in get_client_metadata_scopes() as the highest-priority source when handling 403 insufficient_scope.

Actual behavior

For a server configured with required_scopes=["api.read"]:

401 (no/invalid token):

WWW-Authenticate: Bearer error="invalid_token", error_description="Authentication required"

403 (token missing required scope):

WWW-Authenticate: Bearer error="insufficient_scope", error_description="Required scope: api.read"

Neither response includes scope="api.read".

Expected behavior

When required_scopes is non-empty, the challenge should include the RFC 6750 scope parameter, e.g.:

WWW-Authenticate: Bearer error="insufficient_scope", error_description="Required scope: api.read", scope="api.read"

(and similarly for 401 when scopes are configured, so the client can request the correct scopes on initial authorization).

Impact

  • MCP clients cannot reliably discover required scopes from the challenge header.
  • Step-up authorization falls back to PRM scopes_supported (or omits scope), which is lower priority per the MCP scope selection strategy and can fail when PRM does not advertise scopes.
  • The SDK client already implements the correct consumer side; only server emission is missing.

Root cause

In src/mcp/server/auth/middleware/bearer_auth.py, _send_auth_error builds:

www_auth_parts = [f'error="{error}"', f'error_description="{description}"']
if self.resource_metadata_url:
    www_auth_parts.append(f'resource_metadata="{self.resource_metadata_url}"')

It never appends scope= from self.required_scopes, which is already available on the middleware instance.

Present on both v1.x and main (verified in current source).

Suggested fix

When self.required_scopes is non-empty, append:

www_auth_parts.append(f'scope="{" ".join(self.required_scopes)}"')

Happy to open a PR against v1.x once a maintainer assigns this issue to me. Please also advise whether a follow-up for main (v2) is desired.

Example Code

Python & MCP Python SDK
Python: 3.14.6
MCP Python SDK: 1.28.1 (also reproduced against current v1.x / main source of RequireAuthMiddleware._send_auth_error)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/mcp/server/auth/middleware/bearer_auth.py, focusing on RequireAuthMiddleware._send_auth_error and its existing WWW-Authenticate construction. Review the surrounding authentication response coverage, then verify both 401 and 403 cases with required_scopes configured; done means the challenge exposes the required scope while preserving existing parameters.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
authentication, backend, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.