Description
Inconsistent timeout
and sse_read_timeout
Types in sse_client
and streamablehttp_client
Description
In the modelcontextprotocol/python-sdk
repository (version 1.9.3), the timeout
and sse_read_timeout
parameters in mcp/client/sse.py
and mcp/client/streamable_http.py
have inconsistent types, causing confusion and runtime errors in downstream applications:
-
mcp/client/sse.py
(sse_client
):@asynccontextmanager async def sse_client( url: str, headers: dict[str, Any] | None = None, timeout: float = 5, sse_read_timeout: float = 60 * 5, ... )
timeout
andsse_read_timeout
are typed asfloat
(seconds).
-
mcp/client/streamable_http.py
(streamablehttp_client
):@asynccontextmanager async def streamablehttp_client( url: str, headers: dict[str, Any] | None = None, timeout: timedelta = timedelta(seconds=30), sse_read_timeout: timedelta = timedelta(seconds=60 * 5), ... )
timeout
andsse_read_timeout
are typed astimedelta
.
This inconsistency forces developers to handle different types for similar parameters across MCP transport implementations, leading to errors. For example, in the openai-agents-python
project, passing a float
timeout to MCPServerStreamableHttp
(which uses streamablehttp_client
) causes a TaskGroup
exception:
2025-06-11 19:17:20,478 server.py:132 - mcp_client - [MainThread:5432] - ERROR - Error initializing MCP server: unhandled errors in a TaskGroup (1 sub-exception)
The lack of clear error messages from streamablehttp_client
(e.g., type mismatch) further complicates debugging.
Steps to Reproduce
- Configure an
MCPServerStreamableHttp
(fromopenai-agents-python
) with afloat
timeout (e.g.,timeout=30
). - Call
server.connect()
, which invokesstreamablehttp_client
. - Observe a
TaskGroup
exception due totimedelta
type mismatch. - Compare with
MCPServerSse
, which acceptsfloat
without issues.
Expected Behavior
Both sse_client
and streamablehttp_client
should use consistent types for timeout
and sse_read_timeout
(preferably float
, as it’s more intuitive for configuration files and aligns with sse_client
). The functions should either handle type conversion internally or provide clear TypeError
messages for invalid types.
Proposed Solution
-
Unify Parameter Types:
- Update
streamablehttp_client
to accepttimeout
andsse_read_timeout
asfloat
(seconds), matchingsse_client
. - Internally convert
float
totimedelta
instreamablehttp_client
if required by the underlying HTTP client (e.g.,httpx
).
- Update
-
Add Type Validation:
- In both
sse_client
andstreamablehttp_client
, validate thattimeout
andsse_read_timeout
areint
orfloat
, raising a clearTypeError
otherwise.
- In both
-
Update Documentation:
- Clearly document that
timeout
andsse_read_timeout
are in seconds (float
) for both functions. - Specify default values and any internal conversions.
- Clearly document that
-
Ensure Backward Compatibility:
- Allow
streamablehttp_client
to accepttimedelta
for a transition period, logging a deprecation warning and converting tofloat
internally.
- Allow
Example implementation for streamablehttp_client
:
from datetime import timedelta
from typing import Union
@asynccontextmanager
async def streamablehttp_client(
url: str,
headers: dict[str, Any] | None = None,
timeout: Union[float, timedelta] = 30.0, # Accept float, support timedelta temporarily
sse_read_timeout: Union[float, timedelta] = 60 * 5.0,
terminate_on_close: bool = True,
httpx_client_factory: McpHttpClientFactory = create_mcp_http_client,
auth: httpx.Auth | None = None,
):
if isinstance(timeout, timedelta):
logger.warning("Using timedelta for timeout is deprecated; use float (seconds) instead")
timeout = timeout.total_seconds()
if isinstance(sse_read_timeout, timedelta):
logger.warning("Using timedelta for sse_read_timeout is deprecated; use float (seconds) instead")
sse_read_timeout = sse_read_timeout.total_seconds()
if not isinstance(timeout, (int, float)):
raise TypeError(f"timeout must be float, got {type(timeout)}")
if not isinstance(sse_read_timeout, (int, float)):
raise TypeError(f"sse_read_timeout must be float, got {type(sse_read_timeout)}")
# Convert to timedelta for internal use if needed
timeout_td = timedelta(seconds=float(timeout))
sse_read_timeout_td = timedelta(seconds=float(sse_read_timeout))
# Proceed with streamablehttp_client logic
...
Additional Notes
- This issue was discovered while using
MCPServerStreamableHttp
with a server athttp://localhost:3001/mcp
(confirmed reachable viacurl
). - The type inconsistency propagates to downstream projects like
openai-agents-python
, causing errors inMCPServerStreamableHttp
initialization. - Suggest updating
MCPServerSseParams
andMCPServerStreamableHttpParams
inopenai-agents-python
to align with the unifiedfloat
type after this change. - Clearer error messages in
streamablehttp_client
(e.g., for type mismatches or HTTP errors) would greatly improve debugging.
Environment
- Package version:
mcp
1.9.3 - Python version: [Specify your Python version, e.g., 3.10]
- Operating system: [Specify your OS, e.g., macOS]
Please let me know if you need additional details or assistance to resolve this issue!