From fdf1cfe35ac21d3510ef129c69c541d5fd1222f5 Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:06:23 +0000 Subject: [PATCH] Remove the unused timeout parameter from OAuthClientProvider OAuthContext.timeout was assigned in the constructor but never read anywhere, so the parameter has never bounded anything since the OAuth client was added. Drop it from OAuthClientProvider and OAuthContext rather than keep a knob that silently does nothing. --- docs/client/oauth-clients.md | 2 +- docs/migration.md | 18 ++++++++++++++++++ .../auth/extensions/client_credentials.py | 4 ++-- src/mcp/client/auth/oauth2.py | 4 ---- tests/client/test_auth.py | 1 - tests/docs_src/test_oauth_clients.py | 6 +++--- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/docs/client/oauth-clients.md b/docs/client/oauth-clients.md index 2ce67f815a..08ccdca1df 100644 --- a/docs/client/oauth-clients.md +++ b/docs/client/oauth-clients.md @@ -83,7 +83,7 @@ The first time `Client` sends a request, the server answers `401`. The provider After that it is quiet. Tokens come out of storage, an expired access token is refreshed with the refresh token, and only when none of that works does it run the flow again. -You wrote none of it. Three keyword arguments remain (`timeout`, `client_metadata_url` and `validate_resource_url`), and this file needs none of them. `client_metadata_url` is the one worth knowing about; it gets its own section below. +You wrote none of it. Two keyword arguments remain (`client_metadata_url` and `validate_resource_url`), and this file needs neither. `client_metadata_url` is the one worth knowing about; it gets its own section below. ### Try it diff --git a/docs/migration.md b/docs/migration.md index ab7b8f089c..f6fd0d0eca 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -2034,6 +2034,24 @@ ClientCredentialsOAuthProvider(..., scopes="read write") ClientCredentialsOAuthProvider(..., scope="read write") ``` +### `timeout` parameter removed from `OAuthClientProvider` + +`OAuthClientProvider` no longer accepts a `timeout` argument, and `OAuthContext.timeout` is gone. The value was stored but never read, so it never bounded anything — removing it changes nothing at runtime. + +**Before (v1):** + +```python +provider = OAuthClientProvider(server_url, client_metadata, storage, timeout=120.0) +``` + +**After (v2):** + +```python +provider = OAuthClientProvider(server_url, client_metadata, storage) +``` + +If you passed `timeout` to bound how long you wait for the user to complete authorization, apply that bound where you actually wait — inside your `redirect_handler`/`callback_handler`, e.g. `with anyio.fail_after(120): ...`. + ### Client rejects authorization server metadata with a mismatched `issuer` During OAuth discovery, `OAuthClientProvider` now validates that the authorization server diff --git a/src/mcp/client/auth/extensions/client_credentials.py b/src/mcp/client/auth/extensions/client_credentials.py index 9969c9b1fc..29197bb504 100644 --- a/src/mcp/client/auth/extensions/client_credentials.py +++ b/src/mcp/client/auth/extensions/client_credentials.py @@ -63,7 +63,7 @@ def __init__( token_endpoint_auth_method=token_endpoint_auth_method, scope=scope, ) - super().__init__(server_url, client_metadata, storage, None, None, 300.0) + super().__init__(server_url, client_metadata, storage, None, None) # Store client_info to be set during _initialize - no dynamic registration needed self._fixed_client_info = OAuthClientInformationFull( redirect_uris=None, @@ -277,7 +277,7 @@ def __init__( token_endpoint_auth_method="private_key_jwt", scope=scope, ) - super().__init__(server_url, client_metadata, storage, None, None, 300.0) + super().__init__(server_url, client_metadata, storage, None, None) self._assertion_provider = assertion_provider # Store client_info to be set during _initialize - no dynamic registration needed self._fixed_client_info = OAuthClientInformationFull( diff --git a/src/mcp/client/auth/oauth2.py b/src/mcp/client/auth/oauth2.py index af7e3a49a7..07e224148a 100644 --- a/src/mcp/client/auth/oauth2.py +++ b/src/mcp/client/auth/oauth2.py @@ -103,7 +103,6 @@ class OAuthContext: storage: TokenStorage redirect_handler: Callable[[str], Awaitable[None]] | None callback_handler: Callable[[], Awaitable[AuthorizationCodeResult]] | None - timeout: float = 300.0 client_metadata_url: str | None = None # Discovered metadata @@ -233,7 +232,6 @@ def __init__( storage: TokenStorage, redirect_handler: Callable[[str], Awaitable[None]] | None = None, callback_handler: Callable[[], Awaitable[AuthorizationCodeResult]] | None = None, - timeout: float = 300.0, client_metadata_url: str | None = None, validate_resource_url: Callable[[str, str | None], Awaitable[None]] | None = None, ): @@ -245,7 +243,6 @@ def __init__( storage: Token storage implementation. redirect_handler: Handler for authorization redirects. callback_handler: Handler for authorization callbacks. - timeout: Timeout for the OAuth flow. client_metadata_url: URL-based client ID. When provided and the server advertises client_id_metadata_document_supported=True, this URL will be used as the client_id instead of performing dynamic client registration. @@ -271,7 +268,6 @@ def __init__( storage=storage, redirect_handler=redirect_handler, callback_handler=callback_handler, - timeout=timeout, client_metadata_url=client_metadata_url, ) self._validate_resource_url_callback = validate_resource_url diff --git a/tests/client/test_auth.py b/tests/client/test_auth.py index 9e9599f86c..6bea7bcf70 100644 --- a/tests/client/test_auth.py +++ b/tests/client/test_auth.py @@ -192,7 +192,6 @@ async def test_oauth_provider_initialization( assert oauth_provider.context.server_url == "https://api.example.com/v1/mcp" assert oauth_provider.context.client_metadata == client_metadata assert oauth_provider.context.storage == mock_storage - assert oauth_provider.context.timeout == 300.0 assert oauth_provider.context is not None def test_context_url_parsing(self, oauth_provider: OAuthClientProvider): diff --git a/tests/docs_src/test_oauth_clients.py b/tests/docs_src/test_oauth_clients.py index 817cbb415d..801fbb2ca1 100644 --- a/tests/docs_src/test_oauth_clients.py +++ b/tests/docs_src/test_oauth_clients.py @@ -80,11 +80,11 @@ async def test_client_credentials_provider_builds_its_own_metadata() -> None: assert metadata.scope == "user" -async def test_the_three_remaining_keyword_arguments_have_defaults() -> None: - """The page names `timeout`, `client_metadata_url` and `validate_resource_url` as the remainder.""" +async def test_the_two_remaining_keyword_arguments_have_defaults() -> None: + """The page names `client_metadata_url` and `validate_resource_url` as the remainder.""" parameters = inspect.signature(OAuthClientProvider.__init__).parameters supplied = ["server_url", "client_metadata", "storage", "redirect_handler", "callback_handler"] - remainder = ["timeout", "client_metadata_url", "validate_resource_url"] + remainder = ["client_metadata_url", "validate_resource_url"] assert list(parameters) == ["self", *supplied, *remainder] assert all(parameters[name].default is not inspect.Parameter.empty for name in remainder)