Skip to content

fix: remove scope registration check from authorize handler#2301

Draft
maxisbey wants to merge 1 commit intomainfrom
fix/remove-scope-registration-check
Draft

fix: remove scope registration check from authorize handler#2301
maxisbey wants to merge 1 commit intomainfrom
fix/remove-scope-registration-check

Conversation

@maxisbey
Copy link
Contributor

Supersedes #2220 and #2224. Fixes #2216.

Motivation and Context

OAuthClientMetadata.validate_scope() rejected any requested scope not in the client's registered scope field. When scope was None (no registration restriction), this converted to an empty allow-list and rejected everything — the bug reported in #2216. Both #2220 and #2224 fix that narrow case.

But the check itself breaks the MCP spec's step-up authorization flow. The Python client already implements step-up (client/auth/oauth2.py): on 403 insufficient_scope it re-authorizes with the expanded scopes from WWW-Authenticate. The server then rejects those scopes because they weren't in the original registration, so step-up against a Python MCP server is currently broken.

RFC 7591 §2 defines registered scope as values the client "can use" — there's no language restricting requests to that set. The TypeScript SDK removed the equivalent check in typescript-sdk#983 for the same reason.

Scope policy enforcement belongs in OAuthAuthorizationServerProvider.authorize(), which can already raise AuthorizeError(error="invalid_scope", ...) — the handler already catches this.

How Has This Been Tested?

  • ./scripts/test — full suite passes with 100% branch coverage
  • test_authorize_invalid_scope removed (tested the behavior being removed, same as typescript-sdk#983)
  • New unit tests cover both branches of the simplified validate_scope

Breaking Changes

  • mcp.shared.auth.InvalidScopeError removed (only raised from the deleted code path)
  • OAuthClientMetadata.validate_scope() no longer raises — it only parses
  • Servers relying on the SDK to reject scopes should move enforcement into provider.authorize() (example in docs/migration.md)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Thanks to @nik1097 for the original report, and to @shivama205 (#2224) and @lavish0000 (#2220) for the initial fixes that surfaced the broader issue.

AI Disclaimer

The check in validate_scope rejected any requested scope not in the
client's registered metadata. This broke the MCP spec's step-up
authorization flow: when a server returns 403 insufficient_scope with
a WWW-Authenticate challenge containing expanded scopes, the client
(see client/auth/oauth2.py) re-authorizes with those scopes and the
server would reject them.

RFC 7591 Section 2 defines the scope field as scopes the client "can
use", with no language restricting requests to that set. Scope policy
enforcement belongs in OAuthAuthorizationServerProvider.authorize(),
which can already raise AuthorizeError(error="invalid_scope", ...).

The TypeScript SDK removed this check in #983 for the same reason.

InvalidScopeError is removed as it was only raised from this path.

Reported-by: nik1097
Github-Issue: #2216
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.

Bug: validate_scope rejects client scopes when required scopes in None

1 participant