Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2109,6 +2109,18 @@ v1's internal client set `follow_redirects=True`; set it explicitly when supplyi
- `httpx_client_factory`: gone with no replacement — call your factory yourself and pass the result as `http_client`.
- `terminate_on_close`: unchanged (default `True`).

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`:

```python
from mcp.client.streamable_http import create_mcp_http_client, streamable_http_client

async with create_mcp_http_client(headers={"Authorization": "Bearer token"}) as http_client:
async with streamable_http_client(url="http://localhost:8000/mcp", http_client=http_client) as (read_stream, write_stream):
...
```

`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.

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.

### `get_session_id` callback removed from `streamable_http_client`
Expand Down
3 changes: 3 additions & 0 deletions src/mcp/client/streamable_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
from mcp.client._transport import TransportStreams
from mcp.shared._compat import resync_tracer
from mcp.shared._context_streams import ContextReceiveStream, ContextSendStream, create_context_streams
from mcp.shared._httpx_utils import MCP_DEFAULT_SSE_READ_TIMEOUT as MCP_DEFAULT_SSE_READ_TIMEOUT
from mcp.shared._httpx_utils import MCP_DEFAULT_TIMEOUT as MCP_DEFAULT_TIMEOUT
from mcp.shared._httpx_utils import McpHttpClientFactory as McpHttpClientFactory
from mcp.shared._httpx_utils import create_mcp_http_client
from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER
from mcp.shared.jsonrpc_dispatcher import cancelled_request_id_from_params
Expand Down
13 changes: 13 additions & 0 deletions tests/shared/test_httpx_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,16 @@ def test_custom_parameters():

assert client.headers["Authorization"] == "Bearer token"
assert client.timeout.connect == 60.0


def test_public_reexport_from_streamable_http():
"""The factory, protocol, and timeout defaults are importable from the
public ``mcp.client.streamable_http`` module, not only the private
``mcp.shared._httpx_utils``."""
import mcp.shared._httpx_utils as private_module
from mcp.client import streamable_http

assert streamable_http.create_mcp_http_client is private_module.create_mcp_http_client
assert streamable_http.McpHttpClientFactory is private_module.McpHttpClientFactory
assert streamable_http.MCP_DEFAULT_TIMEOUT == private_module.MCP_DEFAULT_TIMEOUT
assert streamable_http.MCP_DEFAULT_SSE_READ_TIMEOUT == private_module.MCP_DEFAULT_SSE_READ_TIMEOUT
Loading