Skip to content

fix(mcp): use scopes from protected resource metadata (RFC 9728)#2212

Merged
pomelo-nwu merged 2 commits intoQwenLM:mainfrom
xuewenjie123:fix/mcp-oauth-scope-from-protected-resource
Mar 10, 2026
Merged

fix(mcp): use scopes from protected resource metadata (RFC 9728)#2212
pomelo-nwu merged 2 commits intoQwenLM:mainfrom
xuewenjie123:fix/mcp-oauth-scope-from-protected-resource

Conversation

@xuewenjie123
Copy link
Copy Markdown
Collaborator

Problem

When discovering OAuth configuration for MCP servers, the scopes_supported field from Protected Resource Metadata was being ignored. According to RFC 9728, Protected Resource Metadata defines scopes specific to that resource, which should take precedence over Authorization Server Metadata scopes.

This caused OAuth authorization links to be generated without the scope parameter when connecting to MCP servers like ModelScope, where scopes_supported is defined in Protected Resource Metadata but not in Authorization Server Metadata.

Root Cause

The discoverOAuthConfig method only extracted scopes from Authorization Server Metadata via metadataToOAuthConfig(), ignoring the scopes_supported field in Protected Resource Metadata.

Example - ModelScope Configuration:

Protected Resource Metadata (/.well-known/oauth-protected-resource):

{
  "scopes_supported": ["openid", "profile", "list-operational-mcp", "manage-mcp-deployment"]
}

Authorization Server Metadata (/.well-known/oauth-authorization-server):

{
  // No scopes_supported field
}

Solution

  1. Added scopes_supported field to OAuthProtectedResourceMetadata interface
  2. Modified discoverOAuthConfig to use scopes from Protected Resource Metadata when available
  3. Modified discoverOAuthFromWWWAuthenticate with the same fix

Scope Priority Logic:

Protected Resource scopes Authorization Server scopes Result
Present Absent Use Protected Resource scopes
Present Present Use Protected Resource scopes (priority)
Absent Present Use Authorization Server scopes
Absent Absent Empty scopes array

Screenshots

Before Fix

2222

OAuth authorization link missing scope parameter.

After Fix

1111

OAuth authorization link now includes scope parameter with correct scopes.

Changes

  • packages/core/src/mcp/oauth-utils.ts: Added scopes_supported to interface and scope merging logic
  • packages/core/src/mcp/oauth-utils.test.ts: Added 2 test cases for the fix

Testing

  • All 3892 tests pass
  • Added 2 new test cases:
    • should use scopes from protected resource metadata when available
    • should prefer protected resource scopes over auth server scopes

References

When discovering OAuth configuration for MCP servers, the scopes_supported
field from Protected Resource Metadata was being ignored. According to
RFC 9728, Protected Resource Metadata defines scopes specific to that
resource, which should take precedence over Authorization Server Metadata
scopes.

This fix ensures that scopes from Protected Resource Metadata are used
when available, while maintaining backward compatibility by falling back
to Authorization Server Metadata scopes.

Fixes OAuth authorization links missing scope parameter for ModelScope
MCP server.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 9, 2026

📋 Review Summary

This PR fixes an important OAuth compliance issue where scopes_supported from Protected Resource Metadata (RFC 9728) was being ignored during OAuth configuration discovery. The implementation correctly prioritizes Protected Resource scopes over Authorization Server scopes, which aligns with the OAuth 2.0 specification. The changes are minimal, well-tested, and address a real-world issue with MCP servers like ModelScope.

🔍 General Feedback

  • Clean, focused implementation: The changes are minimal and surgical - only adding the necessary scopes_supported field and the logic to use it.
  • Good RFC compliance: Properly implements RFC 9728 (Protected Resource Metadata) scope handling, which is the correct behavior per the OAuth 2.0 specification.
  • Well-tested: Two new test cases cover both the primary use case (scopes only in Protected Resource) and the priority case (scopes in both, Protected Resource takes precedence).
  • Good documentation: Comments clearly explain the scope priority logic and reference RFC 9728.
  • Consistent pattern: The same fix is applied to both discoverOAuthConfig and discoverOAuthFromWWWAuthenticate methods, ensuring consistent behavior.

🎯 Specific Feedback

🔵 Low

  • File: packages/core/src/mcp/oauth-utils.ts:255-257 - The comment says "Merge scopes" but the implementation actually replaces scopes rather than merging them. While the current behavior (Protected Resource scopes take precedence) is correct per RFC 9728, the comment could be more accurate. Consider changing "Merge scopes from protected resource metadata" to "Use scopes from protected resource metadata" or "Override scopes with protected resource metadata scopes".

  • File: packages/core/src/mcp/oauth-utils.ts:38 - Consider adding a JSDoc comment to the scopes_supported field explaining its purpose and RFC 9728 reference, similar to other interface fields. For example:

    /**
     * Scopes supported by the protected resource (RFC 9728)
     */
    scopes_supported?: string[];

✅ Highlights

  • Excellent test coverage: The test cases are well-structured with clear comments explaining the scenario being tested. The first test specifically notes "This test verifies the fix for the issue where scopes from protected resource metadata were not being used."
  • Real-world problem solving: This fix directly addresses an issue with ModelScope MCP servers where OAuth authorization links were being generated without the scope parameter.
  • Correct priority implementation: The implementation correctly gives priority to Protected Resource Metadata scopes, which is the semantically correct behavior since protected resources define their own access requirements.
  • All tests pass: All 27 tests in the test file pass, including the 2 new test cases.
  • Clean build: No TypeScript compilation errors.

The tests used fixed wait times (200ms) that were too close to the
auto-advance timeout (150ms). In CI environments (especially Windows
with Node 20), timing can be less predictable due to event loop
scheduling differences, causing race conditions.

Changes:
- Increased wait(200) to wait(300) for auto-advance tests
- Increased wait() to wait(100) for navigation tests in custom input state test

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Copy link
Copy Markdown
Collaborator

@pomelo-nwu pomelo-nwu left a comment

Choose a reason for hiding this comment

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

LGTM!

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.

2 participants