Skip to content

Commit 3e831d6

Browse files
committed
Preserve the 1 MiB SSE event default
1 parent d3d0ff4 commit 3e831d6

4 files changed

Lines changed: 15 additions & 15 deletions

File tree

‎docs/client/transports.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Pass `max_sse_event_size` when a server sends a large tool result or notificatio
5454
--8<-- "docs_src/client_transports/tutorial005.py"
5555
```
5656

57-
The default is 16 MiB per event, measured in bytes before the event is parsed. The limit applies to
57+
The default is 1 MiB per event, measured in bytes before the event is parsed. The limit applies to
5858
POST responses, the GET stream, and resumed streams. An oversized event in a POST response or resumed
5959
stream fails that request with an error naming the limit. On the background GET stream, the client logs
6060
the error and retries the stream. Set `max_sse_event_size=None` to disable the cap when you trust the

‎docs/migration.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2104,7 +2104,7 @@ async with http_client:
21042104

21052105
v1's internal client set `follow_redirects=True`. You don't need it on your own client: the transport follows a method-preserving redirect within the endpoint's origin (a trailing-slash 307/308, say) itself, and does not follow one anywhere else, whatever the client is configured to do.
21062106

2107-
`streamable_http_client` itself keeps a small signature — `streamable_http_client(url, *, http_client=None, terminate_on_close=True, max_sse_event_size=16 * 1024 * 1024)` — and now yields a 2-tuple (next section). The removed function's other parameters map onto the client you build:
2107+
`streamable_http_client` itself keeps a small signature — `streamable_http_client(url, *, http_client=None, terminate_on_close=True, max_sse_event_size=1024 * 1024)` — and now yields a 2-tuple (next section). The removed function's other parameters map onto the client you build:
21082108

21092109
- `headers`, `timeout`, `sse_read_timeout`, `auth`: set them on the `httpx2.AsyncClient` as above. `streamablehttp_client` defaulted to `httpx.Timeout(30, read=300)`; a bare `httpx2.AsyncClient()` falls back to httpx2's flat 5-second timeout, too short for the long-lived GET stream, so set `timeout=httpx2.Timeout(30, read=300)` (as shown) to keep v1's values. Omitting `http_client` still gives you a default client with those timeouts.
21102110
- `httpx_client_factory`: gone with no replacement — call your factory yourself and pass the result as `http_client`.

‎src/mcp/client/streamable_http.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
# Reconnection defaults
5959
DEFAULT_RECONNECTION_DELAY_MS = 1000 # 1 second fallback when server doesn't provide retry
6060
MAX_RECONNECTION_ATTEMPTS = 2 # Max retry attempts before giving up
61-
DEFAULT_MAX_SSE_EVENT_SIZE = 16 * 1024 * 1024
61+
DEFAULT_MAX_SSE_EVENT_SIZE = 1024 * 1024
6262

6363

6464
class StreamableHTTPError(Exception):

‎tests/client/test_streamable_http.py‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@
5959

6060

6161
@pytest.mark.anyio
62-
async def test_default_sse_limit_accepts_a_tool_result_larger_than_one_mebibyte() -> None:
63-
"""SDK-defined: the Streamable HTTP default accepts a large tool result sent as one POST SSE event."""
62+
async def test_configured_sse_limit_accepts_a_tool_result_larger_than_one_mebibyte() -> None:
63+
"""SDK-defined: a larger SSE limit accepts a large tool result sent as one POST SSE event."""
6464
payload = "x" * (2 * 1024 * 1024)
6565

6666
async def list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult:
@@ -77,24 +77,27 @@ async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) ->
7777
async with (
7878
manager.run(),
7979
httpx2.AsyncClient(transport=StreamingASGITransport(app)) as http,
80-
Client(streamable_http_client("http://localhost/mcp", http_client=http), mode="legacy") as client,
80+
Client(
81+
streamable_http_client("http://localhost/mcp", http_client=http, max_sse_event_size=4 * 1024 * 1024),
82+
mode="legacy",
83+
) as client,
8184
):
8285
result = await client.call_tool("large", {})
8386

8487
assert result.content == [TextContent(text=payload)]
8588

8689

8790
@pytest.mark.anyio
88-
async def test_configured_sse_limit_fails_one_tool_call_and_keeps_the_session_usable() -> None:
89-
"""SDK-defined: an oversized POST SSE event fails its request once and does not poison the session."""
91+
async def test_default_sse_limit_fails_one_large_tool_call_and_keeps_the_session_usable() -> None:
92+
"""SDK-defined: the 1 MiB default rejects one oversized tool result without poisoning the session."""
9093
calls: list[str] = []
9194

9295
async def list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult:
9396
return ListToolsResult(tools=[Tool(name=name, input_schema={"type": "object"}) for name in ("large", "small")])
9497

9598
async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult:
9699
calls.append(params.name)
97-
return CallToolResult(content=[TextContent(text="x" * 2048 if params.name == "large" else "ok")])
100+
return CallToolResult(content=[TextContent(text="x" * (2 * 1024 * 1024) if params.name == "large" else "ok")])
98101

99102
manager = StreamableHTTPSessionManager(
100103
app=Server("bounded-result", on_list_tools=list_tools, on_call_tool=call_tool)
@@ -105,10 +108,7 @@ async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) ->
105108
async with (
106109
manager.run(),
107110
httpx2.AsyncClient(transport=StreamingASGITransport(app)) as http,
108-
Client(
109-
streamable_http_client("http://localhost/mcp", http_client=http, max_sse_event_size=1024),
110-
mode="legacy",
111-
) as client,
111+
Client(streamable_http_client("http://localhost/mcp", http_client=http), mode="legacy") as client,
112112
):
113113
with pytest.raises(MCPError) as exc_info:
114114
await client.call_tool("large", {})
@@ -224,11 +224,11 @@ async def test_streamable_http_rejects_nonpositive_sse_event_limits(limit: int)
224224

225225
@pytest.mark.anyio
226226
async def test_none_disables_the_sse_event_limit() -> None:
227-
"""SDK-defined: opting out accepts a POST SSE response larger than 16 MiB.
227+
"""SDK-defined: opting out accepts a POST SSE response larger than the 1 MiB default.
228228
229229
The raw peer forces SSE for the request; the typed server can choose a JSON response instead.
230230
"""
231-
payload = "x" * (16 * 1024 * 1024 + 1)
231+
payload = "x" * (2 * 1024 * 1024)
232232

233233
def handler(request: httpx2.Request) -> httpx2.Response:
234234
body = json.loads(request.content)

0 commit comments

Comments
 (0)