Replace httpx and httpx-sse with httpx2 (#2972)
Some checks failed
CI / checks (push) Has been cancelled
CI / all-green (push) Has been cancelled

Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
This commit is contained in:
Marcelo Trylesinski 2026-07-14 18:05:08 +02:00 • committed by GitHub
commit 2713b53b12
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -6,7 +6,7 @@ that don't follow SDK conventions.
import json
import httpx
import httpx2
import mcp_types as types
import pytest
from mcp_types import RootsListChangedNotification
@ -89,7 +89,7 @@ async def test_non_compliant_notification_response() -> None:
if isinstance(message, Exception):
returned_exception = message
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=_create_non_sdk_server_app())) as client:
async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=_create_non_sdk_server_app())) as client:
async with streamable_http_client("http://localhost/mcp", http_client=client) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream, message_handler=message_handler) as session:
await session.initialize()
@ -108,7 +108,7 @@ async def test_unexpected_content_type_sends_jsonrpc_error() -> None:
the client should send a JSONRPCError so the pending request resolves immediately
instead of hanging until timeout.
"""
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=_create_unexpected_content_type_app())) as client:
async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=_create_unexpected_content_type_app())) as client:
async with streamable_http_client("http://localhost/mcp", http_client=client) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session: # pragma: no branch
await session.initialize()
@ -142,9 +142,9 @@ async def test_http_error_status_sends_jsonrpc_error() -> None:
When a server returns a non-2xx status code (e.g. 500), the client should
send a JSONRPCError so the pending request resolves immediately instead of
raising an unhandled httpx.HTTPStatusError that causes the caller to hang.
raising an unhandled httpx2.HTTPStatusError that causes the caller to hang.
"""
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=_create_http_error_app(500))) as client:
async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=_create_http_error_app(500))) as client:
async with streamable_http_client("http://localhost/mcp", http_client=client) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session: # pragma: no branch
await session.initialize()
@ -160,7 +160,7 @@ async def test_http_error_on_notification_does_not_hang() -> None:
unblock, so the client should just return without sending a JSONRPCError.
"""
app = _create_http_error_app(500, error_on_notifications=True)
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app)) as client:
async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=app)) as client:
async with streamable_http_client("http://localhost/mcp", http_client=client) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session: # pragma: no branch
await session.initialize()
@ -195,7 +195,7 @@ async def test_invalid_json_response_sends_jsonrpc_error() -> None:
should send a JSONRPCError so the pending request resolves immediately
instead of hanging until timeout.
"""
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=_create_invalid_json_response_app())) as client:
async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=_create_invalid_json_response_app())) as client:
async with streamable_http_client("http://localhost/mcp", http_client=client) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session: # pragma: no branch
await session.initialize()
@ -233,7 +233,7 @@ async def test_client_surfaces_jsonrpc_error_from_non_2xx_body_with_correlated_i
{"jsonrpc": "2.0", "id": None, "error": {"code": types.METHOD_NOT_FOUND, "message": "nope"}}
).encode()
app = _create_non_2xx_json_body_app(400, body)
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app)) as client:
async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=app)) as client:
async with streamable_http_client("http://localhost/mcp", http_client=client) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session: # pragma: no branch
await session.initialize()
@ -247,7 +247,7 @@ async def test_client_falls_back_to_generic_error_when_non_2xx_body_is_a_jsonrpc
error) falls through to the generic ``INTERNAL_ERROR`` fallback rather than being
treated as the request's reply."""
app = _create_non_2xx_json_body_app(400, b'{"jsonrpc":"2.0","id":1,"result":{}}')
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app)) as client:
async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=app)) as client:
async with streamable_http_client("http://localhost/mcp", http_client=client) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session: # pragma: no branch
await session.initialize()
@ -261,7 +261,7 @@ async def test_client_falls_back_to_session_terminated_when_404_body_is_malforme
and the status-derived ``INVALID_REQUEST`` (session-terminated) fallback resolves the
pending request — the parse failure never propagates."""
app = _create_non_2xx_json_body_app(404, b"not valid json{{{")
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app)) as client:
async with httpx2.AsyncClient(transport=httpx2.ASGITransport(app=app)) as client:
async with streamable_http_client("http://localhost/mcp", http_client=client) as (read_stream, write_stream):
async with ClientSession(read_stream, write_stream) as session: # pragma: no branch
await session.initialize()