Skip to content

feat(auth): add initial authorization scope selection - #1352

Merged
DaleSeo merged 6 commits into
modelcontextprotocol:mainfrom
zamoshchin-openai:dev/alexz/rmcp-initial-login-scopes
Oct 16, 2026
Merged

DaleSeo merged 6 commits into
modelcontextprotocol:mainfrom
zamoshchin-openai:dev/alexz/rmcp-initial-login-scopes

Conversation

@zamoshchin-openai

@zamoshchin-openai zamoshchin-openai commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

An MCP server can challenge an initial request with scope="mcp.read" while its protected-resource metadata advertises scopes_supported: ["mcp.read", "mcp.write"]. Using select_scopes() for that login requests both scopes, even though the challenge only asks for read access. This can introduce unnecessary consent or an invalid_scope rejection when the client is not permitted to request the additional scope.

The MCP scope-selection guidance recommends preferring the initial challenge and using protected-resource metadata when that challenge provides no scopes. RMCP already discovers this information, but its existing selector intentionally accumulates scopes and its discovery result does not expose the separate scope sources. Clients therefore lack a way to choose initial-login precedence using the existing discovery APIs.

Add the opt-in AuthorizationManager::select_initial_scopes() API so callers can use that precedence without implementing discovery themselves or changing select_scopes() for existing consumers. It prefers the initial resource challenge, then protected-resource metadata, using only caller-provided defaults when neither supplies scopes. It deduplicates scopes and preserves offline_access handling. AuthorizationRequest::with_initial_scope_selection() exposes the same opt-in through OAuthState and AuthorizationSession; automatic authorization keeps the existing policy by default, and nonempty explicit scopes retain precedence.

Initial scopes are recorded separately so scope-only challenges work, later metadata challenges cannot replace the resource request's scopes, and repeated discovery does not reuse stale initial scopes.

Validation:

  • cargo +1.97.1 test -p rmcp --lib --features auth,client,reqwest transport::auth::tests:: --quiet: 228 passed.
  • cargo +1.97.1 test -p rmcp --doc --features auth,client,reqwest select_initial_scopes --quiet: passed.
  • cargo +1.96 clippy -p rmcp --all-targets --all-features -- -D warnings: passed.
  • cargo +nightly fmt --all -- --check: passed.

@github-actions github-actions Bot added T-core Core library changes T-transport Transport layer changes labels Oct 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@bolinfest is on triage rotation this week and is the reviewer for this one.

Automated classification did not run for this one, so it needs a full manual pass.

Automated triage. Labels are a first pass and the assigned maintainer has the final say.

@zamoshchin-openai
zamoshchin-openai marked this pull request as ready for review October 13, 2026 22:30
@zamoshchin-openai
zamoshchin-openai requested a review from a team as a code owner October 13, 2026 22:30
@zamoshchin-openai

Copy link
Copy Markdown
Contributor Author

@stefanoamorelli, was the scope union in #888 intended to apply to initial login too, or mainly to step-up authorization?

For example, an initial challenge requesting mcp.read currently also requests mcp.write if the resource metadata lists both. My reading of the MCP spec is that initial login should prefer the challenge scopes.

This PR adds a separate initial-login selector to preserve the existing union behavior. Would you prefer that approach, or changing the existing selector?

Comment thread crates/rmcp/src/transport/auth.rs
Comment thread crates/rmcp/src/transport/auth.rs Outdated
Comment thread crates/rmcp/src/transport/auth.rs Outdated
Comment thread crates/rmcp/src/transport/auth.rs

@DaleSeo DaleSeo left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing my feedback, @zamoshchin-openai. Merging now!

@DaleSeo
DaleSeo merged commit 98a5478 into modelcontextprotocol:main Oct 16, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-transport Transport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants