Skip to content

Commit f9bde3a

Browse files
Expose the httpx client factory as public API in mcp.client.streamable_http
Customizing the streamable-HTTP client's headers/auth/timeout requires building an httpx2.AsyncClient, but the standardized factory (create_mcp_http_client), the McpHttpClientFactory protocol, and the default timeout constants only lived in the private mcp.shared._httpx_utils module. Re-export them from the public mcp.client.streamable_http module — where streamable_http_client itself lives — so building a custom client does not require importing a private module. Add a regression test asserting the public names resolve to the same objects as the private ones, and point the migration guide at the public import path. Refs #3238
1 parent a4f4ccd commit f9bde3a

3 files changed

Lines changed: 48 additions & 1 deletion

File tree

docs/migration.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2109,6 +2109,18 @@ v1's internal client set `follow_redirects=True`; set it explicitly when supplyi
21092109
- `httpx_client_factory`: gone with no replacement — call your factory yourself and pass the result as `http_client`.
21102110
- `terminate_on_close`: unchanged (default `True`).
21112111

2112+
If you'd rather not hand-build the `httpx2.AsyncClient`, the standardized factory and its defaults are re-exported from the public `mcp.client.streamable_http` module, so you don't need to touch the private `mcp.shared._httpx_utils`:
2113+
2114+
```python
2115+
from mcp.client.streamable_http import create_mcp_http_client, streamable_http_client
2116+
2117+
async with create_mcp_http_client(headers={"Authorization": "Bearer token"}) as http_client:
2118+
async with streamable_http_client(url="http://localhost:8000/mcp", http_client=http_client) as (read_stream, write_stream):
2119+
...
2120+
```
2121+
2122+
`create_mcp_http_client(headers=..., timeout=..., auth=...)` applies the MCP defaults (`follow_redirects=True` and `Timeout(30, read=300)`) for you; the `McpHttpClientFactory` protocol and the `MCP_DEFAULT_TIMEOUT` / `MCP_DEFAULT_SSE_READ_TIMEOUT` constants are available from the same module.
2123+
21122124
Client-side stream resumption is also unchanged: the transport reconnects a dropped GET stream with `Last-Event-ID` on its own, and `session.send_request(..., metadata=ClientMessageMetadata(resumption_token=..., on_resumption_token_update=...))` (from `mcp.shared.message`) works as in v1.
21132125

21142126
### `get_session_id` callback removed from `streamable_http_client`

src/mcp/client/streamable_http.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,33 @@
3333
from mcp.client._transport import TransportStreams
3434
from mcp.shared._compat import resync_tracer
3535
from mcp.shared._context_streams import ContextReceiveStream, ContextSendStream, create_context_streams
36-
from mcp.shared._httpx_utils import create_mcp_http_client
36+
from mcp.shared._httpx_utils import (
37+
MCP_DEFAULT_SSE_READ_TIMEOUT,
38+
MCP_DEFAULT_TIMEOUT,
39+
McpHttpClientFactory,
40+
create_mcp_http_client,
41+
)
3742
from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER
3843
from mcp.shared.jsonrpc_dispatcher import cancelled_request_id_from_params
3944
from mcp.shared.message import ClientMessageMetadata, SessionMessage
4045

46+
__all__ = [
47+
"LAST_EVENT_ID",
48+
"MAX_RECONNECTION_ATTEMPTS",
49+
"MCP_DEFAULT_SSE_READ_TIMEOUT",
50+
"MCP_DEFAULT_TIMEOUT",
51+
"MCP_SESSION_ID",
52+
"McpHttpClientFactory",
53+
"RequestContext",
54+
"ResumptionError",
55+
"StreamReader",
56+
"StreamWriter",
57+
"StreamableHTTPError",
58+
"StreamableHTTPTransport",
59+
"create_mcp_http_client",
60+
"streamable_http_client",
61+
]
62+
4163
logger = logging.getLogger(__name__)
4264

4365

tests/shared/test_httpx_utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,16 @@ def test_custom_parameters():
2222

2323
assert client.headers["Authorization"] == "Bearer token"
2424
assert client.timeout.connect == 60.0
25+
26+
27+
def test_public_reexport_from_streamable_http():
28+
"""The factory, protocol, and timeout defaults are importable from the
29+
public ``mcp.client.streamable_http`` module, not only the private
30+
``mcp.shared._httpx_utils``."""
31+
import mcp.shared._httpx_utils as private_module
32+
from mcp.client import streamable_http
33+
34+
assert streamable_http.create_mcp_http_client is private_module.create_mcp_http_client
35+
assert streamable_http.McpHttpClientFactory is private_module.McpHttpClientFactory
36+
assert streamable_http.MCP_DEFAULT_TIMEOUT == private_module.MCP_DEFAULT_TIMEOUT
37+
assert streamable_http.MCP_DEFAULT_SSE_READ_TIMEOUT == private_module.MCP_DEFAULT_SSE_READ_TIMEOUT

0 commit comments

Comments
 (0)