Skip to content

oauth client cannot exclude a problematic auto-discovered scope #5

Open

Description

@bug-ops

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::scopes empty and the SDK auto-selects and requests every scope discovered from the server (WWW-Authenticate challenge, protected-resource metadata, authorization-server metadata) via AuthorizationManager::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

  1. Configure an AuthorizationRequest / OAuthClientConfig against a server whose discovered scopes_supported (or WWW-Authenticate challenge) includes a scope that is mutually exclusive with another required scope (e.g. Gmail's gmail.metadata vs. gmail.readonly).
  2. Leave scopes empty (or use with_scopes only to add required scopes) to benefit from auto-discovery.
  3. 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

  1. added
    enhancementNew feature or request
    P3Medium: suboptimal behavior, minor inconsistency
    on Aug 8, 2026
  2. github-actions commented on Oct 8, 2026

    @github-actions

    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 keep label to keep it open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Medium: suboptimal behavior, minor inconsistencyStaleenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions