Skip to content

Commit 96e5327

Browse files
authored
add a timeout arguments on per-request basis (as per MCP specifications) (#601)
1 parent 697b6e8 commit 96e5327

File tree

2 files changed

+18
-9
lines changed

2 files changed

+18
-9
lines changed

src/mcp/client/session.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,10 @@ async def unsubscribe_resource(self, uri: AnyUrl) -> types.EmptyResult:
254254
)
255255

256256
async def call_tool(
257-
self, name: str, arguments: dict[str, Any] | None = None
257+
self,
258+
name: str,
259+
arguments: dict[str, Any] | None = None,
260+
read_timeout_seconds: timedelta | None = None,
258261
) -> types.CallToolResult:
259262
"""Send a tools/call request."""
260263
return await self.send_request(
@@ -265,6 +268,7 @@ async def call_tool(
265268
)
266269
),
267270
types.CallToolResult,
271+
request_read_timeout_seconds=read_timeout_seconds,
268272
)
269273

270274
async def list_prompts(self) -> types.ListPromptsResult:

src/mcp/shared/session.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def __init__(
185185
self._request_id = 0
186186
self._receive_request_type = receive_request_type
187187
self._receive_notification_type = receive_notification_type
188-
self._read_timeout_seconds = read_timeout_seconds
188+
self._session_read_timeout_seconds = read_timeout_seconds
189189
self._in_flight = {}
190190

191191
self._exit_stack = AsyncExitStack()
@@ -213,10 +213,12 @@ async def send_request(
213213
self,
214214
request: SendRequestT,
215215
result_type: type[ReceiveResultT],
216+
request_read_timeout_seconds: timedelta | None = None,
216217
) -> ReceiveResultT:
217218
"""
218219
Sends a request and wait for a response. Raises an McpError if the
219-
response contains an error.
220+
response contains an error. If a request read timeout is provided, it
221+
will take precedence over the session read timeout.
220222
221223
Do not use this method to emit notifications! Use send_notification()
222224
instead.
@@ -243,12 +245,15 @@ async def send_request(
243245

244246
await self._write_stream.send(JSONRPCMessage(jsonrpc_request))
245247

248+
# request read timeout takes precedence over session read timeout
249+
timeout = None
250+
if request_read_timeout_seconds is not None:
251+
timeout = request_read_timeout_seconds.total_seconds()
252+
elif self._session_read_timeout_seconds is not None:
253+
timeout = self._session_read_timeout_seconds.total_seconds()
254+
246255
try:
247-
with anyio.fail_after(
248-
None
249-
if self._read_timeout_seconds is None
250-
else self._read_timeout_seconds.total_seconds()
251-
):
256+
with anyio.fail_after(timeout):
252257
response_or_error = await response_stream_reader.receive()
253258
except TimeoutError:
254259
raise McpError(
@@ -257,7 +262,7 @@ async def send_request(
257262
message=(
258263
f"Timed out while waiting for response to "
259264
f"{request.__class__.__name__}. Waited "
260-
f"{self._read_timeout_seconds} seconds."
265+
f"{timeout} seconds."
261266
),
262267
)
263268
)

0 commit comments

Comments
 (0)