Merge pull request #609 from jlowin/pr-607-settings

Add init_timeout for mcp client (incl settings)
This commit is contained in:
Jeremiah Lowin 2025-05-27 08:53:01 -04:00 committed by GitHub
commit 163ea02365
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 27 additions and 19 deletions

View file

@ -9,6 +9,7 @@ from exceptiongroup import catch
from mcp import ClientSession
from pydantic import AnyUrl
import fastmcp
from fastmcp.client.logging import (
LogHandler,
MessageHandler,
@ -45,8 +46,8 @@ class Client:
MCP client that delegates connection management to a Transport instance.
The Client class is responsible for MCP protocol logic, while the Transport
handles connection establishment and management. Client provides methods
for working with resources, prompts, tools and other MCP capabilities.
handles connection establishment and management. Client provides methods for
working with resources, prompts, tools and other MCP capabilities.
Args:
transport: Connection source specification, which can be:
@ -62,20 +63,18 @@ class Client:
message_handler: Optional handler for protocol messages
progress_handler: Optional handler for progress notifications
timeout: Optional timeout for requests (seconds or timedelta)
init_timeout: Optional timeout for initial connection (seconds or
timedelta)
init_timeout: Optional timeout for initial connection (seconds or timedelta).
Set to 0 to disable. If None, uses the value in the FastMCP global settings.
Examples:
```python
# Connect to FastMCP server
client = Client("http://localhost:8080")
```python # Connect to FastMCP server client =
Client("http://localhost:8080")
async with client:
# List available resources
resources = await client.list_resources()
# List available resources resources = await client.list_resources()
# Call a tool
result = await client.call_tool("my_tool", {"param": "value"})
# Call a tool result = await client.call_tool("my_tool", {"param":
"value"})
```
"""
@ -95,7 +94,7 @@ class Client:
message_handler: MessageHandler | None = None,
progress_handler: ProgressHandler | None = None,
timeout: datetime.timedelta | float | int | None = None,
init_timeout: datetime.timedelta | float | int = 1,
init_timeout: datetime.timedelta | float | int | None = None,
):
self.transport = infer_transport(transport)
self._session: ClientSession | None = None
@ -114,14 +113,16 @@ class Client:
if isinstance(timeout, int | float):
timeout = datetime.timedelta(seconds=timeout)
if isinstance(init_timeout, int):
self._init_timeout = float(init_timeout)
elif isinstance(init_timeout, datetime.timedelta):
self._init_timeout = float(init_timeout.total_seconds())
elif isinstance(init_timeout, float):
self._init_timeout = init_timeout
# handle init handshake timeout
if init_timeout is None:
init_timeout = fastmcp.settings.settings.client_init_timeout
if isinstance(init_timeout, datetime.timedelta):
init_timeout = init_timeout.total_seconds()
elif not init_timeout:
init_timeout = None
else:
raise ValueError("init_timeout must be int, float or datetime.timedelta")
init_timeout = float(init_timeout)
self._init_timeout = init_timeout
self._session_kwargs: SessionKwargs = {
"sampling_callback": None,

View file

@ -87,6 +87,13 @@ class Settings(BaseSettings):
),
] = False
client_init_timeout: Annotated[
float | None,
Field(
description="The timeout for the client's initialization handshake, in seconds. Set to None or 0 to disable.",
),
] = None
@model_validator(mode="after")
def setup_logging(self) -> Self:
"""Finalize the settings."""