You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Port the httpx2 migration notes into the restructured docs
The migration guide and client docs were reorganized on main after the
swap branched: re-add the httpx2 section under Packaging with rows in
the dependency table, convert the After-v2 examples that arrived with
the reorganization (Before-v1 blocks keep httpx), and surface the
system-trust-store TLS note on the transports page.
No-Verification-Needed: documentation-only change
Copy file name to clipboardExpand all lines: docs/client/transports.md
+16-9Lines changed: 16 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,7 @@ Pass a URL string and you get **Streamable HTTP**, the transport you deploy behi
29
29
--8<--"docs_src/client_transports/tutorial002.py"
30
30
```
31
31
32
-
That is the whole production client. `Client` wraps the URL in `streamable_http_client(...)` for you, on top of an `httpx.AsyncClient` configured the way MCP needs: `follow_redirects=True`, a 30-second timeout for connect/write/pool, and a 300-second read timeout because the server may hold a response stream open.
32
+
That is the whole production client. `Client` wraps the URL in `streamable_http_client(...)` for you, on top of an `httpx2.AsyncClient` configured the way MCP needs: `follow_redirects=True`, a 30-second timeout for connect/write/pool, and a 300-second read timeout because the server may hold a response stream open.
33
33
34
34
!!! check
35
35
A `Client` you have constructed is **not** connected. Construction only picks the transport;
@@ -41,19 +41,25 @@ That is the whole production client. `Client` wraps the URL in `streamable_http_
41
41
42
42
Nothing was resolved, fetched or spawned when you wrote `Client("http://...")`. That line is free.
43
43
44
-
### Bring your own `httpx.AsyncClient`
44
+
### Bring your own `httpx2.AsyncClient`
45
45
46
-
The moment you need an `Authorization` header, a cookie, a proxy, mTLS, or a different timeout, build the `httpx.AsyncClient` yourself and hand it to `streamable_http_client`:
46
+
The moment you need an `Authorization` header, a cookie, a proxy, mTLS, or a different timeout, build the `httpx2.AsyncClient` yourself and hand it to `streamable_http_client`:
47
47
48
48
```python title="client.py" hl_lines="8-14"
49
49
--8<--"docs_src/client_transports/tutorial003.py"
50
50
```
51
51
52
52
Two things to notice:
53
53
54
-
* You own the `httpx.AsyncClient`, so **you** enter and exit it. The SDK never closes a client it didn't create.
54
+
* You own the `httpx2.AsyncClient`, so **you** enter and exit it. The SDK never closes a client it didn't create.
55
55
*`streamable_http_client(url, http_client=...)` returns a transport, and `Client(transport)` accepts it like anything else.
56
56
57
+
One TLS note: `httpx2` verifies certificates against the operating system trust store (via
58
+
[`truststore`](https://pypi.org/project/truststore/)), not a bundled CA list. In an environment with
59
+
no usable system CA store - some minimal containers - pass an explicit `verify=ssl_context` to your
60
+
`httpx2.AsyncClient` (background in
61
+
[`httpx` and `httpx-sse` replaced by `httpx2`](../migration.md#httpx-and-httpx-sse-replaced-by-httpx2)).
62
+
57
63
!!! warning
58
64
`streamable_http_client` used to take `headers=` and `timeout=` directly. It does not any more:
59
65
its only parameters are `url`, `http_client` and `terminate_on_close`. Reach for `headers=` out
@@ -63,12 +69,13 @@ Two things to notice:
63
69
TypeError: streamable_http_client() got an unexpected keyword argument 'headers'
64
70
```
65
71
66
-
Everything HTTP-shaped now lives on the one `httpx.AsyncClient` you pass in.
72
+
Everything HTTP-shaped now lives on the one `httpx2.AsyncClient` you pass in.
67
73
68
74
!!! info
69
-
If you know `httpx`, you already know how to do auth, proxies, event hooks, retries and connection
70
-
limits here. The SDK adds nothing on top and takes nothing away. It is also where OAuth plugs in:
71
-
`httpx.AsyncClient(auth=OAuthClientProvider(...))`. That whole flow is **[OAuth clients](oauth-clients.md)**.
75
+
`httpx2` keeps the familiar `httpx` API, so if you know `httpx` you already know how to do auth,
76
+
proxies, event hooks, retries and connection limits here. The SDK adds nothing on top and takes
77
+
nothing away. It is also where OAuth plugs in:
78
+
`httpx2.AsyncClient(auth=OAuthClientProvider(...))`. That whole flow is **[OAuth clients](oauth-clients.md)**.
72
79
73
80
## stdio
74
81
@@ -106,7 +113,7 @@ A **transport** is any async context manager that yields a `(read, write)` pair
106
113
107
114
*`Client(mcp)` (the server object) connects in memory. Use it for tests and for embedding.
108
115
*`Client("http://.../mcp")` (a URL) connects over Streamable HTTP, the production transport.
109
-
* Headers, auth, proxies and timeouts belong on an `httpx.AsyncClient` you pass to `streamable_http_client(url, http_client=...)`. There is no `headers=` keyword.
116
+
* Headers, auth, proxies and timeouts belong on an `httpx2.AsyncClient` you pass to `streamable_http_client(url, http_client=...)`. There is no `headers=` keyword.
110
117
* stdio is `Client(stdio_client(StdioServerParameters(...)))`, never the parameters object alone.
111
118
* The subprocess gets an allow-listed environment, not yours; `env=` adds to it.
112
119
* A transport is anything you can `async with x as (read, write)`. `Client` hands anything that isn't a server object or a URL straight to that protocol.
| opentelemetry-api | not a dependency |`>=1.28.0`| new required dependency |
69
69
| mcp-types | not a dependency |`==<exact mcp version>`| new, exact-pinned |
70
+
| httpx |`>=0.27.1,<1.0.0`| removed | see [`httpx` and `httpx-sse` replaced by `httpx2`](#httpx-and-httpx-sse-replaced-by-httpx2)|
71
+
| httpx-sse |`>=0.4`| removed | see [`httpx` and `httpx-sse` replaced by `httpx2`](#httpx-and-httpx-sse-replaced-by-httpx2)|
72
+
| httpx2 | not a dependency |`>=2.5.0`| new required dependency |
70
73
|`ws` extra |`websockets>=15.0.1`| removed | see [WebSocket transport removed](#websocket-transport-removed)|
71
74
72
75
**Before (v1):**
@@ -89,6 +92,46 @@ dependencies = [
89
92
90
93
Relax or bump any conflicting pins when upgrading. sse-starlette jumps two majors, so a project that imports `sse_starlette` itself must also work through that library's own breaking changes to co-install with mcp v2. `opentelemetry-api` is a new hard dependency because every outbound request now carries a `_meta` envelope used for OpenTelemetry trace propagation; see [Every outbound request now carries a `_meta` envelope](#every-outbound-request-now-carries-a-_meta-envelope-opentelemetry-is-on-by-default). `mcp-types` is exact-pinned to the SDK version; nothing in a v1 tree can conflict with it, but do not pin `mcp-types` independently of `mcp`.
91
94
95
+
### `httpx` and `httpx-sse` replaced by `httpx2`
96
+
97
+
The SDK now depends on [`httpx2`](https://pypi.org/project/httpx2/) instead of
98
+
`httpx` and `httpx-sse`. `httpx2` is the next-generation HTTP client (a fork of
99
+
`httpx`) with server-sent events support built in, so the separate `httpx-sse`
100
+
dependency is gone.
101
+
102
+
The swap itself does not change any SDK signatures - `streamable_http_client`
103
+
and `sse_client` accept the same arguments as elsewhere in v2 - but the client
104
+
type they expect is now `httpx2.AsyncClient`. If you construct your own client to pass as
105
+
`http_client` (or build an `httpx2.Auth` subclass for `auth`), import from
`httpx2` is API-compatible with `httpx`, so usually only the import name
125
+
changes. To consume SSE directly, use `httpx2.EventSource` (or
126
+
`AsyncClient.sse()`) instead of the `httpx-sse` helpers.
127
+
128
+
TLS verification also changes: `httpx` validated certificates against the
129
+
bundled `certifi` CA list, while `httpx2` validates against the operating
130
+
system trust store via [`truststore`](https://pypi.org/project/truststore/).
131
+
If your environment has no usable system CA store (some minimal containers),
132
+
or you relied on certifi's bundle specifically, pass an explicit
133
+
`verify=ssl_context` to your `httpx2.AsyncClient`.
134
+
92
135
### `mcp dev` and `mcp install` pin the spawned environment to your SDK version
93
136
94
137
Both commands run your server through a fresh `uv run --with ...` environment. In v1 the
@@ -111,7 +154,7 @@ unchanged. Only the `mcp.types` submodule and `mcp.shared.version` were removed.
111
154
package's API reference is at [`mcp_types`](api/mcp_types/index.md).
112
155
113
156
**Why:** keeping the wire types in their own package lets tooling and lightweight clients
114
-
depend on the protocol schema without pulling in `httpx`, `starlette`, `uvicorn`, and the
157
+
depend on the protocol schema without pulling in `httpx2`, `starlette`, `uvicorn`, and the
115
158
rest of the server/transport stack.
116
159
117
160
**Before (v1):**
@@ -1594,13 +1637,13 @@ async with streamablehttp_client(
1594
1637
**After (v2):**
1595
1638
1596
1639
```python
1597
-
importhttpx
1640
+
importhttpx2
1598
1641
from mcp.client.streamable_http import streamable_http_client
1599
1642
1600
-
# Configure headers, timeout, and auth on the httpx.AsyncClient
1601
-
http_client =httpx.AsyncClient(
1643
+
# Configure headers, timeout, and auth on the httpx2.AsyncClient
1644
+
http_client =httpx2.AsyncClient(
1602
1645
headers={"Authorization": "Bearer token"},
1603
-
timeout=httpx.Timeout(30, read=300),
1646
+
timeout=httpx2.Timeout(30, read=300),
1604
1647
auth=my_auth,
1605
1648
follow_redirects=True,
1606
1649
)
@@ -1613,15 +1656,15 @@ async with http_client:
1613
1656
...
1614
1657
```
1615
1658
1616
-
v1's internal client set `follow_redirects=True`; set it explicitly when supplying your own `httpx.AsyncClient` to preserve that behavior.
1659
+
v1's internal client set `follow_redirects=True`; set it explicitly when supplying your own `httpx2.AsyncClient` to preserve that behavior.
1617
1660
1618
1661
### `get_session_id` callback removed from `streamable_http_client`
1619
1662
1620
1663
The `get_session_id` callback (third element of the returned tuple) has been removed from `streamable_http_client`. The function now returns a 2-tuple `(read_stream, write_stream)` instead of a 3-tuple.
1621
1664
1622
1665
The `GetSessionIdCallback` type alias is gone as well, so `from mcp.client.streamable_http import GetSessionIdCallback` now raises `ImportError`. Drop the annotation, or inline `Callable[[], str | None]` if your own wrapper code still needs the type.
1623
1666
1624
-
If you need to capture the session ID (e.g., for session resumption testing), you can use httpx event hooks to capture it from the response headers:
1667
+
If you need to capture the session ID (e.g., for session resumption testing), you can use httpx2 event hooks to capture it from the response headers:
1625
1668
1626
1669
**Before (v1):**
1627
1670
@@ -1637,23 +1680,23 @@ async with streamable_http_client(url) as (read_stream, write_stream, get_sessio
1637
1680
**After (v2):**
1638
1681
1639
1682
```python
1640
-
importhttpx
1683
+
importhttpx2
1641
1684
from mcp.client.streamable_http import streamable_http_client
1642
1685
1643
1686
# Option 1: Simply ignore if you don't need the session ID
1644
1687
asyncwith streamable_http_client(url) as (read_stream, write_stream):
1645
1688
asyncwith ClientSession(read_stream, write_stream) as session:
1646
1689
await session.initialize()
1647
1690
1648
-
# Option 2: Capture session ID via httpx event hooks if needed
1691
+
# Option 2: Capture session ID via httpx2 event hooks if needed
The `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters have been removed from `StreamableHTTPTransport`. Configure these on the `httpx.AsyncClient` instead (see example above).
1713
+
The `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters have been removed from `StreamableHTTPTransport`. Configure these on the `httpx2.AsyncClient` instead (see example above).
1671
1714
1672
1715
Note: `sse_client` retains its `headers`, `timeout`, `sse_read_timeout`, and `auth` parameters — only the streamable HTTP transport changed.
1673
1716
@@ -1730,7 +1773,7 @@ async with streamable_http_client(url) as (read, write):
1730
1773
raise
1731
1774
```
1732
1775
1733
-
Move HTTP-status failure handling from around the transport context to around the individual calls, catching `MCPError` (see [`McpError` renamed to `MCPError`](#mcperror-renamed-to-mcperror)). Connect-level failures such as `httpx.ConnectError` still escape the transport context as before; keep context-level handling for those only.
1776
+
Move HTTP-status failure handling from around the transport context to around the individual calls, catching `MCPError` (see [`McpError` renamed to `MCPError`](#mcperror-renamed-to-mcperror)). Connect-level failures such as `httpx2.ConnectError` still escape the transport context as before; keep context-level handling for those only.
0 commit comments