Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/client/oauth-clients.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
18 changes: 18 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/mcp/client/auth/extensions/client_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 0 additions & 4 deletions src/mcp/client/auth/oauth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
):
Expand All @@ -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.
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion tests/client/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
6 changes: 3 additions & 3 deletions tests/docs_src/test_oauth_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading