Skip to content

fix(auth): merge configured and server-required OAuth scopes - #1102

Draft
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union
Draft

fix(auth): merge configured and server-required OAuth scopes#1102
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union

Conversation

@tsarlandie-oai

Copy link
Copy Markdown
Contributor

Motivation and Context

Reactive OAuth discovery records scopes required by the server's
WWW-Authenticate challenge and validated protected-resource metadata. However,
AuthorizationSession::new bypasses the existing scope-selection policy whenever
AuthorizationRequest::with_scopes supplies explicit client scopes:

if request.scopes.is_empty(){
request.scopes = auth_manager.select_scopes(None,&[]);}else{
auth_manager.add_offline_access_if_supported(&mut request.scopes);}

As a result, a client configured with configured shared does not request
challenge or resource when the Bearer challenge and protected-resource
metadata require them. Duplicate configured scopes are also preserved, and
authorization can fail even though the SDK discovered the scopes necessary for
the operation.

What changed

  • Route both configured and implicit scopes through the existing stable,
    deduplicating scope-selection policy.
  • Seed that policy with explicitly requested scopes before accumulating
    previously requested scopes, Bearer challenge scopes, and protected-resource
    scopes.
  • Keep authorization-server scopes_supported as a fallback only when no
    caller-requested or operationally required scopes exist.
  • Preserve the existing offline_access policy and apply the same resulting
    scope set to preregistered clients, dynamic registration, and the authorization
    URL.
  • Update public documentation to explain that configured scopes are combined
    with server-required scopes.

The public select_scopes API is unchanged. Discovery remains reactive,
consistent with #1052: this change adds no synthetic POST, ping, initialize
request, or additional discovery endpoint.

Regression coverage

Two end-to-end authorization tests cover preregistered and dynamically
registered clients with:

configured scopes: configured shared configured
Bearer challenge scopes: challenge shared
protected-resource scopes: resource shared
authorization-server scopes: fallback-only offline_access
requested scopes: configured shared challenge resource offline_access

The tests verify stable ordering, deduplication, exclusion of unrelated
authorization-server scopes, preserved offline_access, reactive GET-only
metadata discovery for preregistered clients, and identical scopes in the
dynamic-registration request and authorization URL.

Validation

  • cargo test -p rmcp --features auth --lib transport::auth::tests — 173 OAuth
    tests passed.
  • cargo test -p rmcp --features auth,client,transport-io — full package tests
    and doctests passed.
  • cargo clippy -p rmcp --features auth,client,transport-io --lib -- -D warnings.
  • rustfmt --check --edition 2024 crates/rmcp/src/transport/auth.rs.
  • git diff --check.

Breaking Changes

None. Existing public APIs, registration priority, metadata fallback, and
reactive discovery behavior are preserved.

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

Labels

T-coreCore library changesT-transportTransport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(auth): merge configured and server-required OAuth scopes by tsarlandie-oai · Pull Request #1102 · modelcontextprotocol/rust-sdk · GitHub
Skip to content

fix(auth): merge configured and server-required OAuth scopes - #1102

Draft
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union
Draft

fix(auth): merge configured and server-required OAuth scopes#1102
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union

Conversation

@tsarlandie-oai

Copy link
Copy Markdown
Contributor

Motivation and Context

Reactive OAuth discovery records scopes required by the server's
WWW-Authenticate challenge and validated protected-resource metadata. However,
AuthorizationSession::new bypasses the existing scope-selection policy whenever
AuthorizationRequest::with_scopes supplies explicit client scopes:

if request.scopes.is_empty(){
request.scopes = auth_manager.select_scopes(None,&[]);}else{
auth_manager.add_offline_access_if_supported(&mut request.scopes);}

As a result, a client configured with configured shared does not request
challenge or resource when the Bearer challenge and protected-resource
metadata require them. Duplicate configured scopes are also preserved, and
authorization can fail even though the SDK discovered the scopes necessary for
the operation.

What changed

  • Route both configured and implicit scopes through the existing stable,
    deduplicating scope-selection policy.
  • Seed that policy with explicitly requested scopes before accumulating
    previously requested scopes, Bearer challenge scopes, and protected-resource
    scopes.
  • Keep authorization-server scopes_supported as a fallback only when no
    caller-requested or operationally required scopes exist.
  • Preserve the existing offline_access policy and apply the same resulting
    scope set to preregistered clients, dynamic registration, and the authorization
    URL.
  • Update public documentation to explain that configured scopes are combined
    with server-required scopes.

The public select_scopes API is unchanged. Discovery remains reactive,
consistent with #1052: this change adds no synthetic POST, ping, initialize
request, or additional discovery endpoint.

Regression coverage

Two end-to-end authorization tests cover preregistered and dynamically
registered clients with:

configured scopes: configured shared configured
Bearer challenge scopes: challenge shared
protected-resource scopes: resource shared
authorization-server scopes: fallback-only offline_access
requested scopes: configured shared challenge resource offline_access

The tests verify stable ordering, deduplication, exclusion of unrelated
authorization-server scopes, preserved offline_access, reactive GET-only
metadata discovery for preregistered clients, and identical scopes in the
dynamic-registration request and authorization URL.

Validation

  • cargo test -p rmcp --features auth --lib transport::auth::tests — 173 OAuth
    tests passed.
  • cargo test -p rmcp --features auth,client,transport-io — full package tests
    and doctests passed.
  • cargo clippy -p rmcp --features auth,client,transport-io --lib -- -D warnings.
  • rustfmt --check --edition 2024 crates/rmcp/src/transport/auth.rs.
  • git diff --check.

Breaking Changes

None. Existing public APIs, registration priority, metadata fallback, and
reactive discovery behavior are preserved.

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

Labels

T-coreCore library changesT-transportTransport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(auth): merge configured and server-required OAuth scopes by tsarlandie-oai · Pull Request #1102 · modelcontextprotocol/rust-sdk · GitHub
Skip to content

fix(auth): merge configured and server-required OAuth scopes - #1102

Draft
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union
Draft

fix(auth): merge configured and server-required OAuth scopes#1102
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union

Conversation

@tsarlandie-oai

Copy link
Copy Markdown
Contributor

Motivation and Context

Reactive OAuth discovery records scopes required by the server's
WWW-Authenticate challenge and validated protected-resource metadata. However,
AuthorizationSession::new bypasses the existing scope-selection policy whenever
AuthorizationRequest::with_scopes supplies explicit client scopes:

if request.scopes.is_empty(){
request.scopes = auth_manager.select_scopes(None,&[]);}else{
auth_manager.add_offline_access_if_supported(&mut request.scopes);}

As a result, a client configured with configured shared does not request
challenge or resource when the Bearer challenge and protected-resource
metadata require them. Duplicate configured scopes are also preserved, and
authorization can fail even though the SDK discovered the scopes necessary for
the operation.

What changed

  • Route both configured and implicit scopes through the existing stable,
    deduplicating scope-selection policy.
  • Seed that policy with explicitly requested scopes before accumulating
    previously requested scopes, Bearer challenge scopes, and protected-resource
    scopes.
  • Keep authorization-server scopes_supported as a fallback only when no
    caller-requested or operationally required scopes exist.
  • Preserve the existing offline_access policy and apply the same resulting
    scope set to preregistered clients, dynamic registration, and the authorization
    URL.
  • Update public documentation to explain that configured scopes are combined
    with server-required scopes.

The public select_scopes API is unchanged. Discovery remains reactive,
consistent with #1052: this change adds no synthetic POST, ping, initialize
request, or additional discovery endpoint.

Regression coverage

Two end-to-end authorization tests cover preregistered and dynamically
registered clients with:

configured scopes: configured shared configured
Bearer challenge scopes: challenge shared
protected-resource scopes: resource shared
authorization-server scopes: fallback-only offline_access
requested scopes: configured shared challenge resource offline_access

The tests verify stable ordering, deduplication, exclusion of unrelated
authorization-server scopes, preserved offline_access, reactive GET-only
metadata discovery for preregistered clients, and identical scopes in the
dynamic-registration request and authorization URL.

Validation

  • cargo test -p rmcp --features auth --lib transport::auth::tests — 173 OAuth
    tests passed.
  • cargo test -p rmcp --features auth,client,transport-io — full package tests
    and doctests passed.
  • cargo clippy -p rmcp --features auth,client,transport-io --lib -- -D warnings.
  • rustfmt --check --edition 2024 crates/rmcp/src/transport/auth.rs.
  • git diff --check.

Breaking Changes

None. Existing public APIs, registration priority, metadata fallback, and
reactive discovery behavior are preserved.

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

Labels

T-coreCore library changesT-transportTransport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(auth): merge configured and server-required OAuth scopes by tsarlandie-oai · Pull Request #1102 · modelcontextprotocol/rust-sdk · GitHub
Skip to content

fix(auth): merge configured and server-required OAuth scopes - #1102

Draft
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union
Draft

fix(auth): merge configured and server-required OAuth scopes#1102
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union

Conversation

@tsarlandie-oai

Copy link
Copy Markdown
Contributor

Motivation and Context

Reactive OAuth discovery records scopes required by the server's
WWW-Authenticate challenge and validated protected-resource metadata. However,
AuthorizationSession::new bypasses the existing scope-selection policy whenever
AuthorizationRequest::with_scopes supplies explicit client scopes:

if request.scopes.is_empty(){
request.scopes = auth_manager.select_scopes(None,&[]);}else{
auth_manager.add_offline_access_if_supported(&mut request.scopes);}

As a result, a client configured with configured shared does not request
challenge or resource when the Bearer challenge and protected-resource
metadata require them. Duplicate configured scopes are also preserved, and
authorization can fail even though the SDK discovered the scopes necessary for
the operation.

What changed

  • Route both configured and implicit scopes through the existing stable,
    deduplicating scope-selection policy.
  • Seed that policy with explicitly requested scopes before accumulating
    previously requested scopes, Bearer challenge scopes, and protected-resource
    scopes.
  • Keep authorization-server scopes_supported as a fallback only when no
    caller-requested or operationally required scopes exist.
  • Preserve the existing offline_access policy and apply the same resulting
    scope set to preregistered clients, dynamic registration, and the authorization
    URL.
  • Update public documentation to explain that configured scopes are combined
    with server-required scopes.

The public select_scopes API is unchanged. Discovery remains reactive,
consistent with #1052: this change adds no synthetic POST, ping, initialize
request, or additional discovery endpoint.

Regression coverage

Two end-to-end authorization tests cover preregistered and dynamically
registered clients with:

configured scopes: configured shared configured
Bearer challenge scopes: challenge shared
protected-resource scopes: resource shared
authorization-server scopes: fallback-only offline_access
requested scopes: configured shared challenge resource offline_access

The tests verify stable ordering, deduplication, exclusion of unrelated
authorization-server scopes, preserved offline_access, reactive GET-only
metadata discovery for preregistered clients, and identical scopes in the
dynamic-registration request and authorization URL.

Validation

  • cargo test -p rmcp --features auth --lib transport::auth::tests — 173 OAuth
    tests passed.
  • cargo test -p rmcp --features auth,client,transport-io — full package tests
    and doctests passed.
  • cargo clippy -p rmcp --features auth,client,transport-io --lib -- -D warnings.
  • rustfmt --check --edition 2024 crates/rmcp/src/transport/auth.rs.
  • git diff --check.

Breaking Changes

None. Existing public APIs, registration priority, metadata fallback, and
reactive discovery behavior are preserved.

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

Labels

T-coreCore library changesT-transportTransport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(auth): merge configured and server-required OAuth scopes by tsarlandie-oai · Pull Request #1102 · modelcontextprotocol/rust-sdk · GitHub
Skip to content

fix(auth): merge configured and server-required OAuth scopes - #1102

Draft
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union
Draft

fix(auth): merge configured and server-required OAuth scopes#1102
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union

Conversation

@tsarlandie-oai

Copy link
Copy Markdown
Contributor

Motivation and Context

Reactive OAuth discovery records scopes required by the server's
WWW-Authenticate challenge and validated protected-resource metadata. However,
AuthorizationSession::new bypasses the existing scope-selection policy whenever
AuthorizationRequest::with_scopes supplies explicit client scopes:

if request.scopes.is_empty(){
request.scopes = auth_manager.select_scopes(None,&[]);}else{
auth_manager.add_offline_access_if_supported(&mut request.scopes);}

As a result, a client configured with configured shared does not request
challenge or resource when the Bearer challenge and protected-resource
metadata require them. Duplicate configured scopes are also preserved, and
authorization can fail even though the SDK discovered the scopes necessary for
the operation.

What changed

  • Route both configured and implicit scopes through the existing stable,
    deduplicating scope-selection policy.
  • Seed that policy with explicitly requested scopes before accumulating
    previously requested scopes, Bearer challenge scopes, and protected-resource
    scopes.
  • Keep authorization-server scopes_supported as a fallback only when no
    caller-requested or operationally required scopes exist.
  • Preserve the existing offline_access policy and apply the same resulting
    scope set to preregistered clients, dynamic registration, and the authorization
    URL.
  • Update public documentation to explain that configured scopes are combined
    with server-required scopes.

The public select_scopes API is unchanged. Discovery remains reactive,
consistent with #1052: this change adds no synthetic POST, ping, initialize
request, or additional discovery endpoint.

Regression coverage

Two end-to-end authorization tests cover preregistered and dynamically
registered clients with:

configured scopes: configured shared configured
Bearer challenge scopes: challenge shared
protected-resource scopes: resource shared
authorization-server scopes: fallback-only offline_access
requested scopes: configured shared challenge resource offline_access

The tests verify stable ordering, deduplication, exclusion of unrelated
authorization-server scopes, preserved offline_access, reactive GET-only
metadata discovery for preregistered clients, and identical scopes in the
dynamic-registration request and authorization URL.

Validation

  • cargo test -p rmcp --features auth --lib transport::auth::tests — 173 OAuth
    tests passed.
  • cargo test -p rmcp --features auth,client,transport-io — full package tests
    and doctests passed.
  • cargo clippy -p rmcp --features auth,client,transport-io --lib -- -D warnings.
  • rustfmt --check --edition 2024 crates/rmcp/src/transport/auth.rs.
  • git diff --check.

Breaking Changes

None. Existing public APIs, registration priority, metadata fallback, and
reactive discovery behavior are preserved.

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

Labels

T-coreCore library changesT-transportTransport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(auth): merge configured and server-required OAuth scopes by tsarlandie-oai · Pull Request #1102 · modelcontextprotocol/rust-sdk · GitHub
Skip to content

fix(auth): merge configured and server-required OAuth scopes - #1102

Draft
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union
Draft

fix(auth): merge configured and server-required OAuth scopes#1102
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union

Conversation

@tsarlandie-oai

Copy link
Copy Markdown
Contributor

Motivation and Context

Reactive OAuth discovery records scopes required by the server's
WWW-Authenticate challenge and validated protected-resource metadata. However,
AuthorizationSession::new bypasses the existing scope-selection policy whenever
AuthorizationRequest::with_scopes supplies explicit client scopes:

if request.scopes.is_empty(){
request.scopes = auth_manager.select_scopes(None,&[]);}else{
auth_manager.add_offline_access_if_supported(&mut request.scopes);}

As a result, a client configured with configured shared does not request
challenge or resource when the Bearer challenge and protected-resource
metadata require them. Duplicate configured scopes are also preserved, and
authorization can fail even though the SDK discovered the scopes necessary for
the operation.

What changed

  • Route both configured and implicit scopes through the existing stable,
    deduplicating scope-selection policy.
  • Seed that policy with explicitly requested scopes before accumulating
    previously requested scopes, Bearer challenge scopes, and protected-resource
    scopes.
  • Keep authorization-server scopes_supported as a fallback only when no
    caller-requested or operationally required scopes exist.
  • Preserve the existing offline_access policy and apply the same resulting
    scope set to preregistered clients, dynamic registration, and the authorization
    URL.
  • Update public documentation to explain that configured scopes are combined
    with server-required scopes.

The public select_scopes API is unchanged. Discovery remains reactive,
consistent with #1052: this change adds no synthetic POST, ping, initialize
request, or additional discovery endpoint.

Regression coverage

Two end-to-end authorization tests cover preregistered and dynamically
registered clients with:

configured scopes: configured shared configured
Bearer challenge scopes: challenge shared
protected-resource scopes: resource shared
authorization-server scopes: fallback-only offline_access
requested scopes: configured shared challenge resource offline_access

The tests verify stable ordering, deduplication, exclusion of unrelated
authorization-server scopes, preserved offline_access, reactive GET-only
metadata discovery for preregistered clients, and identical scopes in the
dynamic-registration request and authorization URL.

Validation

  • cargo test -p rmcp --features auth --lib transport::auth::tests — 173 OAuth
    tests passed.
  • cargo test -p rmcp --features auth,client,transport-io — full package tests
    and doctests passed.
  • cargo clippy -p rmcp --features auth,client,transport-io --lib -- -D warnings.
  • rustfmt --check --edition 2024 crates/rmcp/src/transport/auth.rs.
  • git diff --check.

Breaking Changes

None. Existing public APIs, registration priority, metadata fallback, and
reactive discovery behavior are preserved.

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

Labels

T-coreCore library changesT-transportTransport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(auth): merge configured and server-required OAuth scopes by tsarlandie-oai · Pull Request #1102 · modelcontextprotocol/rust-sdk · GitHub
Skip to content

fix(auth): merge configured and server-required OAuth scopes - #1102

Draft
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union
Draft

fix(auth): merge configured and server-required OAuth scopes#1102
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union

Conversation

@tsarlandie-oai

Copy link
Copy Markdown
Contributor

Motivation and Context

Reactive OAuth discovery records scopes required by the server's
WWW-Authenticate challenge and validated protected-resource metadata. However,
AuthorizationSession::new bypasses the existing scope-selection policy whenever
AuthorizationRequest::with_scopes supplies explicit client scopes:

if request.scopes.is_empty(){
request.scopes = auth_manager.select_scopes(None,&[]);}else{
auth_manager.add_offline_access_if_supported(&mut request.scopes);}

As a result, a client configured with configured shared does not request
challenge or resource when the Bearer challenge and protected-resource
metadata require them. Duplicate configured scopes are also preserved, and
authorization can fail even though the SDK discovered the scopes necessary for
the operation.

What changed

  • Route both configured and implicit scopes through the existing stable,
    deduplicating scope-selection policy.
  • Seed that policy with explicitly requested scopes before accumulating
    previously requested scopes, Bearer challenge scopes, and protected-resource
    scopes.
  • Keep authorization-server scopes_supported as a fallback only when no
    caller-requested or operationally required scopes exist.
  • Preserve the existing offline_access policy and apply the same resulting
    scope set to preregistered clients, dynamic registration, and the authorization
    URL.
  • Update public documentation to explain that configured scopes are combined
    with server-required scopes.

The public select_scopes API is unchanged. Discovery remains reactive,
consistent with #1052: this change adds no synthetic POST, ping, initialize
request, or additional discovery endpoint.

Regression coverage

Two end-to-end authorization tests cover preregistered and dynamically
registered clients with:

configured scopes: configured shared configured
Bearer challenge scopes: challenge shared
protected-resource scopes: resource shared
authorization-server scopes: fallback-only offline_access
requested scopes: configured shared challenge resource offline_access

The tests verify stable ordering, deduplication, exclusion of unrelated
authorization-server scopes, preserved offline_access, reactive GET-only
metadata discovery for preregistered clients, and identical scopes in the
dynamic-registration request and authorization URL.

Validation

  • cargo test -p rmcp --features auth --lib transport::auth::tests — 173 OAuth
    tests passed.
  • cargo test -p rmcp --features auth,client,transport-io — full package tests
    and doctests passed.
  • cargo clippy -p rmcp --features auth,client,transport-io --lib -- -D warnings.
  • rustfmt --check --edition 2024 crates/rmcp/src/transport/auth.rs.
  • git diff --check.

Breaking Changes

None. Existing public APIs, registration priority, metadata fallback, and
reactive discovery behavior are preserved.

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

Labels

T-coreCore library changesT-transportTransport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tsarlandie-oai
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(auth): merge configured and server-required OAuth scopes by tsarlandie-oai · Pull Request #1102 · modelcontextprotocol/rust-sdk · GitHub
Skip to content

fix(auth): merge configured and server-required OAuth scopes - #1102

Draft
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union
Draft

fix(auth): merge configured and server-required OAuth scopes#1102
tsarlandie-oai wants to merge 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-configured-scope-union

Conversation

@tsarlandie-oai

Copy link
Copy Markdown
Contributor

Motivation and Context

Reactive OAuth discovery records scopes required by the server's
WWW-Authenticate challenge and validated protected-resource metadata. However,
AuthorizationSession::new bypasses the existing scope-selection policy whenever
AuthorizationRequest::with_scopes supplies explicit client scopes:

if request.scopes.is_empty(){
request.scopes = auth_manager.select_scopes(None,&[]);}else{
auth_manager.add_offline_access_if_supported(&mut request.scopes);}

As a result, a client configured with configured shared does not request
challenge or resource when the Bearer challenge and protected-resource
metadata require them. Duplicate configured scopes are also preserved, and
authorization can fail even though the SDK discovered the scopes necessary for
the operation.

What changed

  • Route both configured and implicit scopes through the existing stable,
    deduplicating scope-selection policy.
  • Seed that policy with explicitly requested scopes before accumulating
    previously requested scopes, Bearer challenge scopes, and protected-resource
    scopes.
  • Keep authorization-server scopes_supported as a fallback only when no
    caller-requested or operationally required scopes exist.
  • Preserve the existing offline_access policy and apply the same resulting
    scope set to preregistered clients, dynamic registration, and the authorization
    URL.
  • Update public documentation to explain that configured scopes are combined
    with server-required scopes.

The public select_scopes API is unchanged. Discovery remains reactive,
consistent with #1052: this change adds no synthetic POST, ping, initialize
request, or additional discovery endpoint.

Regression coverage

Two end-to-end authorization tests cover preregistered and dynamically
registered clients with:

configured scopes: configured shared configured
Bearer challenge scopes: challenge shared
protected-resource scopes: resource shared
authorization-server scopes: fallback-only offline_access
requested scopes: configured shared challenge resource offline_access

The tests verify stable ordering, deduplication, exclusion of unrelated
authorization-server scopes, preserved offline_access, reactive GET-only
metadata discovery for preregistered clients, and identical scopes in the
dynamic-registration request and authorization URL.

Validation

  • cargo test -p rmcp --features auth --lib transport::auth::tests — 173 OAuth
    tests passed.
  • cargo test -p rmcp --features auth,client,transport-io — full package tests
    and doctests passed.
  • cargo clippy -p rmcp --features auth,client,transport-io --lib -- -D warnings.
  • rustfmt --check --edition 2024 crates/rmcp/src/transport/auth.rs.
  • git diff --check.

Breaking Changes

None. Existing public APIs, registration priority, metadata fallback, and
reactive discovery behavior are preserved.

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

Labels

T-coreCore library changesT-transportTransport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tsarlandie-oai