-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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, ... )
timeoutandsse_read_timeoutare 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), ... )
timeoutandsse_read_timeoutare 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 afloattimeout (e.g.,timeout=30). - Call
server.connect(), which invokesstreamablehttp_client. - Observe a
TaskGroupexception due totimedeltatype mismatch. - Compare with
MCPServerSse, which acceptsfloatwithout 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_clientto accepttimeoutandsse_read_timeoutasfloat(seconds), matchingsse_client. - Internally convert
floattotimedeltainstreamablehttp_clientif required by the underlying HTTP client (e.g.,httpx).
- Update
-
Add Type Validation:
- In both
sse_clientandstreamablehttp_client, validate thattimeoutandsse_read_timeoutareintorfloat, raising a clearTypeErrorotherwise.
- In both
-
Update Documentation:
- Clearly document that
timeoutandsse_read_timeoutare in seconds (float) for both functions. - Specify default values and any internal conversions.
- Clearly document that
-
Ensure Backward Compatibility:
- Allow
streamablehttp_clientto accepttimedeltafor a transition period, logging a deprecation warning and converting tofloatinternally.
- 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
MCPServerStreamableHttpwith a server athttp://localhost:3001/mcp(confirmed reachable viacurl). - The type inconsistency propagates to downstream projects like
openai-agents-python, causing errors inMCPServerStreamableHttpinitialization. - Suggest updating
MCPServerSseParamsandMCPServerStreamableHttpParamsinopenai-agents-pythonto align with the unifiedfloattype after this change. - Clearer error messages in
streamablehttp_client(e.g., for type mismatches or HTTP errors) would greatly improve debugging.
Environment
- Package version:
mcp1.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!