Exclude MCP SDK 1.21.1 and add scope validation to InMemoryOAuthProvider#2422
Exclude MCP SDK 1.21.1 and add scope validation to InMemoryOAuthProvider#2422
Conversation
- Exclude MCP SDK 1.21.1 due to bug adding metadata URL to scopes - Update OAuth client tests to use valid scopes
…oint - Add scope validation to InMemoryOAuthProvider.register_client() to match MCP SDK behavior - Ensures unit tests catch scope-related bugs like the MCP SDK 1.21.1 issue - Remove debug breakpoint from OAuth client redirect_handler
WalkthroughAdds explicit runtime validation and presence checks for client identifiers and scopes across the in-memory auth provider and OAuth proxy. Client registration now validates requested scopes against configured valid_scopes and raises ValueError for invalid scopes or missing client_id. Authorization and token-exchange flows now validate client_id presence and raise AuthorizeError or TokenError as appropriate; redirect URI and token payload handling were made more robust to safely handle None values. Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Add null checks for client_id before using in OAuthTransaction, AuthorizationCode, AccessToken, RefreshToken - Add null check for redirect_uris before len() call - Import AuthorizeError from mcp.server.auth.provider
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/fastmcp/server/auth/oauth_proxy.py (1)
1184-1197: Move client_id validation before UpstreamTokenSet construction.There's a logic ordering issue: Line 1184 uses
client.client_id or ""as a fallback, but the validation check at lines 1196-1197 occurs afterward. Ifclient.client_idis None, you'll store an empty string inUpstreamTokenSetbefore the validation fails.Move the validation check from lines 1196-1197 to occur immediately after Line 1125 (before constructing
UpstreamTokenSet). Once validated, Line 1184 can safely useclient.client_idwithout the fallback:# Clean up client code (one-time use) await self._code_store.delete(key=authorization_code.code) + # Validate client_id presence before token operations + if client.client_id is None: + raise TokenError("invalid_client", "Client ID is required") + # Generate IDs for token storage upstream_token_id = secrets.token_urlsafe(32)Then at Line 1184, simplify to:
- client_id=client.client_id or "", + client_id=client.client_id,And remove the now-redundant check at lines 1196-1197:
- # Issue minimal FastMCP access token (just a reference via JTI) - if client.client_id is None: - raise TokenError("invalid_client", "Client ID is required") fastmcp_access_token = self._jwt_issuer.issue_access_token(
🧹 Nitpick comments (2)
src/fastmcp/server/auth/providers/in_memory.py (2)
69-81: LGTM! Scope validation correctly matches MCP SDK behavior.The scope validation logic properly validates requested scopes against configured valid_scopes, matching the MCP SDK registration handler behavior as intended per the PR objectives. The implementation correctly handles the preconditions and provides clear error messages listing invalid scopes.
If you'd like to address the static analysis hint, consider defining a custom exception class for scope validation errors:
class InvalidScopeError(ValueError): """Raised when requested scopes are not valid.""" passThen use it:
if invalid_scopes: - raise ValueError( - f"Requested scopes are not valid: {', '.join(invalid_scopes)}" - ) + raise InvalidScopeError( + f"Requested scopes are not valid: {', '.join(invalid_scopes)}" + )However, this is purely optional for cleaner exception handling patterns.
83-84: LGTM! Essential validation to prevent invalid registrations.This validation ensures that only clients with valid client_id values can be registered, preventing downstream errors in the OAuth flow.
Similar to the scope validation above, you could optionally define a custom exception class to address the static analysis hint, but this is not critical.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
tests/integration_tests/auth/test_github_provider_integration.pyis excluded by none and included by nonetests/server/auth/test_oidc_proxy.pyis excluded by none and included by nonetests/server/test_auth_integration.pyis excluded by none and included by none
📒 Files selected for processing (2)
src/fastmcp/server/auth/oauth_proxy.py(8 hunks)src/fastmcp/server/auth/providers/in_memory.py(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/fastmcp/server/auth/providers/in_memory.py (1)
src/fastmcp/server/context.py (1)
client_id(291-297)
src/fastmcp/server/auth/oauth_proxy.py (1)
src/fastmcp/server/context.py (1)
client_id(291-297)
🪛 Ruff (0.14.4)
src/fastmcp/server/auth/providers/in_memory.py
79-81: Avoid specifying long messages outside the exception class
(TRY003)
84-84: Avoid specifying long messages outside the exception class
(TRY003)
src/fastmcp/server/auth/oauth_proxy.py
946-946: Avoid specifying long messages outside the exception class
(TRY003)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (10)
src/fastmcp/server/auth/oauth_proxy.py (6)
47-47: LGTM - Import needed for OAuth error handling.The
AuthorizeErrorimport is correctly added to support OAuth 2.1-compliant error responses in authorization flows.
945-946: LGTM - Essential client_id validation added.The validation correctly prevents registration of clients without a client_id, which would cause downstream issues. The ValueError is appropriate for invalid input during registration.
Note: The PR objectives mention adding "scope validation to match MCP SDK registration handler behavior," but I don't see scope validation logic in this method. Is scope validation implemented elsewhere or still pending?
976-976: LGTM - Defensive programming for logging safety.The None check prevents potential AttributeError when logging redirect URI count. While the code at Line 950 provides a default value, this defensive check improves robustness.
1013-1016: LGTM - Appropriate OAuth error handling.The validation correctly raises
AuthorizeErrorwith the standard OAuth 2.1 error code "invalid_client" before creating the transaction. This prevents downstream issues in the authorization flow.
1095-1098: LGTM - Consistent OAuth error handling.The validation correctly raises
AuthorizeErrorbefore constructing theAuthorizationCodeobject. This is consistent with the validation pattern used in theauthorizemethod.
1393-1394: LGTM - Appropriate token endpoint error handling.The validation correctly raises
TokenErrorwith the OAuth-compliant "invalid_client" error code before issuing new tokens. This is properly placed before token operations.src/fastmcp/server/auth/providers/in_memory.py (4)
110-110: LGTM! Good defensive guard for redirect_uris check.Adding the guard to check whether
client.redirect_urisexists before checking membership prevents potential errors when redirect_uris is None or undefined.
129-132: LGTM! Defensive validation prevents downstream errors.This check ensures client_id is present before creating the AuthorizationCode object (line 135). While the registration validation (lines 83-84) prevents None client_id in registered clients, this provides defense-in-depth since the client parameter is passed from external callers.
189-190: LGTM! Consistent defensive validation in token exchange.This validation mirrors the pattern in the authorize method and prevents errors when creating AccessToken and RefreshToken objects that require client_id (lines 193, 199).
261-262: LGTM! Completes consistent validation across all token operations.This validation maintains consistency with the authorization code exchange method and ensures client_id is valid for creating new tokens during refresh token rotation (lines 265, 271).
This PR addresses the MCP SDK 1.21.1 bug that incorrectly adds the protected resource metadata URL to OAuth scopes.
Changes
!=1.21.1to prevent installation of the buggy versionTesting
1.21.1 is responsible for the integration test failures we've been seeing lately.
Related
This is a temporary workaround until the MCP SDK bug is fixed. Once fixed (or a new version is released), the version exclusion can be removed -- most likely when we raise the floor to account for the MCP protcol update in a couple weeks. NOTE: the changes to unit tests are not temporary; only the need to avoid this version.