From cc4ac199b3b54b3cc9ff608e712dcb1d209e2a72 Mon Sep 17 00:00:00 2001 From: Guillaume Raille Date: Tue, 29 Apr 2025 07:56:19 +0200 Subject: [PATCH 1/3] add a timeout arguments on per-request basis --- src/mcp/shared/session.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/mcp/shared/session.py b/src/mcp/shared/session.py index 05fd3ce37..4bb9a4b8c 100644 --- a/src/mcp/shared/session.py +++ b/src/mcp/shared/session.py @@ -185,7 +185,7 @@ def __init__( self._request_id = 0 self._receive_request_type = receive_request_type self._receive_notification_type = receive_notification_type - self._read_timeout_seconds = read_timeout_seconds + self._session_read_timeout_seconds = read_timeout_seconds self._in_flight = {} self._exit_stack = AsyncExitStack() @@ -213,6 +213,7 @@ async def send_request( self, request: SendRequestT, result_type: type[ReceiveResultT], + request_read_timeout_seconds: timedelta | None = None ) -> ReceiveResultT: """ Sends a request and wait for a response. Raises an McpError if the @@ -243,12 +244,15 @@ async def send_request( await self._write_stream.send(JSONRPCMessage(jsonrpc_request)) + # request read timeout takes precedence over session read timeout + timeout = None + if request_read_timeout_seconds is not None: + timeout = request_read_timeout_seconds.total_seconds() + elif self._session_read_timeout_seconds is not None: + timeout = self._session_read_timeout_seconds.total_seconds() + try: - with anyio.fail_after( - None - if self._read_timeout_seconds is None - else self._read_timeout_seconds.total_seconds() - ): + with anyio.fail_after(timeout): response_or_error = await response_stream_reader.receive() except TimeoutError: raise McpError( @@ -257,7 +261,7 @@ async def send_request( message=( f"Timed out while waiting for response to " f"{request.__class__.__name__}. Waited " - f"{self._read_timeout_seconds} seconds." + f"{timeout} seconds." ), ) ) From 8f423c550901bce90405bfd7d9642b83d2bfaf58 Mon Sep 17 00:00:00 2001 From: Guillaume Raille Date: Tue, 29 Apr 2025 08:04:32 +0200 Subject: [PATCH 2/3] add documentation --- src/mcp/shared/session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mcp/shared/session.py b/src/mcp/shared/session.py index 4bb9a4b8c..25b438c3a 100644 --- a/src/mcp/shared/session.py +++ b/src/mcp/shared/session.py @@ -217,7 +217,8 @@ async def send_request( ) -> ReceiveResultT: """ Sends a request and wait for a response. Raises an McpError if the - response contains an error. + response contains an error. If a request read timeout is provided, it + will take precedence over the session read timeout. Do not use this method to emit notifications! Use send_notification() instead. From 365d1906891d531ed31ec5d64b40a05b16a3c298 Mon Sep 17 00:00:00 2001 From: Guillaume Raille Date: Tue, 29 Apr 2025 08:10:39 +0200 Subject: [PATCH 3/3] add read timeout on tool call request --- src/mcp/client/session.py | 6 +++++- src/mcp/shared/session.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mcp/client/session.py b/src/mcp/client/session.py index e29797d17..fc86f0110 100644 --- a/src/mcp/client/session.py +++ b/src/mcp/client/session.py @@ -254,7 +254,10 @@ async def unsubscribe_resource(self, uri: AnyUrl) -> types.EmptyResult: ) async def call_tool( - self, name: str, arguments: dict[str, Any] | None = None + self, + name: str, + arguments: dict[str, Any] | None = None, + read_timeout_seconds: timedelta | None = None, ) -> types.CallToolResult: """Send a tools/call request.""" return await self.send_request( @@ -265,6 +268,7 @@ async def call_tool( ) ), types.CallToolResult, + request_read_timeout_seconds=read_timeout_seconds, ) async def list_prompts(self) -> types.ListPromptsResult: diff --git a/src/mcp/shared/session.py b/src/mcp/shared/session.py index 25b438c3a..90ad92e33 100644 --- a/src/mcp/shared/session.py +++ b/src/mcp/shared/session.py @@ -213,7 +213,7 @@ async def send_request( self, request: SendRequestT, result_type: type[ReceiveResultT], - request_read_timeout_seconds: timedelta | None = None + request_read_timeout_seconds: timedelta | None = None, ) -> ReceiveResultT: """ Sends a request and wait for a response. Raises an McpError if the