Repository navigation
oauth client cannot exclude a problematic auto-discovered scope #5
Description
Description
rmcp's OAuth authorization-code client flow (crates/rmcp/src/transport/auth.rs, auth feature) offers only all-or-nothing scope control:
- Leave
AuthorizationRequest::scopes/OAuthClientConfig::scopesempty and the SDK auto-selects and requests every scope discovered from the server (WWW-Authenticatechallenge, protected-resource metadata, authorization-server metadata) viaAuthorizationManager::select_scopes. - Supply
with_scopes(...)and the caller must specify the full list themselves, losing the SDK's discovery/offline_access (SEP-2207)/step-up (SEP-2350)/dedup logic.
There is no way to exclude a single problematic scope the server advertises while still keeping the rest of auto-discovery.
This matters because it's not hypothetical: Google's Gmail MCP server advertises gmail.metadata in scopes_supported, but the Gmail API rejects the q search parameter on any token carrying gmail.metadata — even when gmail.readonly is also granted (a long-documented Gmail restriction, see googleapis/google-api-python-client#582). A client that lets rmcp auto-select scopes ends up with a token that silently breaks search, with no client-side remedy short of abandoning auto-discovery and hand-replicating its offline_access/step-up/dedup logic.
The Go SDK just added exactly this capability: modelcontextprotocol/go-sdk#1044 adds an optional ScopeFilter func(discovered []string) []string hook to AuthorizationCodeHandlerConfig, run after scope discovery but before the offline_access/step-up union logic, letting a caller narrow, extend, or reorder the discovered set.
There is also an open (not yet merged) upstream PR, modelcontextprotocol/rust-sdk#1102, that changes rmcp's scope handling to union configured scopes with server-required ones through a stable dedup policy — but it only adds scopes; it does not add any exclusion mechanism, so this gap persists regardless of whether/when modelcontextprotocol#1102 lands.
Reproduction Steps
- Configure an
AuthorizationRequest/OAuthClientConfigagainst a server whose discoveredscopes_supported(orWWW-Authenticatechallenge) includes a scope that is mutually exclusive with another required scope (e.g. Gmail'sgmail.metadatavs.gmail.readonly). - Leave
scopesempty (or usewith_scopesonly to add required scopes) to benefit from auto-discovery. - Observe: the SDK requests the full discovered set, including the problematic scope, with no way to filter it out short of replacing auto-discovery entirely.
Expected Behavior
A caller can supply an optional callback/hook that receives the SDK's discovered scope set and returns the set to actually request, composing with (not bypassing) the existing offline_access/step-up union logic and whatever scope-merge policy modelcontextprotocol/rust-sdk#1102 eventually lands with. No hook set → unchanged (fully backward compatible) behavior.
Actual Behavior
No such hook exists (grep -n "ScopeFilter\|scope_filter" crates/rmcp/src/transport/auth.rs returns nothing). The only escape hatch, with_scopes(...), fully replaces auto-discovery rather than filtering it.
Environment
- Version: rmcp 3.1.2 (commit 02c62ae)
- Features:
auth
Upstream
modelcontextprotocol/go-sdk#1044 — MERGED, adds the ScopeFilter hook this issue asks to mirror.
modelcontextprotocol/rust-sdk#1102 — OPEN, additive scope-union PR that this capability should compose with once merged; does not itself add an exclusion mechanism, so this finding is not a duplicate of it.
No existing upstream rust-sdk issue/PR found proposing a scope-filter/exclusion hook specifically (checked via gh issue list / gh pr list --search "scope filter" and related terms).
Logs / Evidence
N/A — competitive-parity research finding, not a live failure reproduction.
Spec
.local/specs/001-oauth-scope-filter-hook/spec.md
Activity
github-actions commentedon Oct 8, 2026
This issue has had no activity for 60 days and is now marked as stale. It will be closed in 14 days if there is no further activity. Add the
keeplabel to keep it open.