Add `streamable_http_client` which accepts `httpx.AsyncClient` instead of `httpx_client_factory` by Kludex · Pull Request #1177 · modelcontextprotocol/python-sdk

ihrpr

ihrpr previously requested changes Jul 21, 2025

felixweinberger

felixweinberger

@felixweinberger felixweinberger changed the title Rename streamablehttp_client to streamable_http_client Add streamable_http_client which accepts httpx.AsyncClient instead of httpx_client_factory

Oct 1, 2025

felixweinberger

@Kludex @felixweinberger

@Kludex @felixweinberger

@Kludex @felixweinberger

Modernize streamable_http_client API by accepting httpx.AsyncClient instances
directly instead of factory functions, following industry standards.

- New API: httpx_client: httpx.AsyncClient | None parameter
- Default client created with recommended timeouts if None
- Deprecated wrapper provides backward compatibility
- Updated examples to show custom client usage
- Add MCP_DEFAULT_TIMEOUT constants to _httpx_utils
This commit fixes all test failures introduced by the API change from
httpx_client_factory to direct httpx_client parameter:

1. Updated deprecated imports: Changed streamablehttp_client to
   streamable_http_client in test files

2. Fixed 307 redirect errors: Replaced httpx.AsyncClient with
   create_mcp_http_client which includes follow_redirects=True by default

3. Fixed test assertion: Updated test_session_group.py to mock
   create_mcp_http_client and verify the new API signature where
   streamable_http_client receives httpx_client parameter instead of
   individual headers/timeout parameters

4. Removed unused httpx import from main.py after inlining client creation

All tests now pass with the new API.
Added missing type annotation imports that were causing NameError and
preventing test collection:

- RequestResponder from mcp.shared.session
- SessionMessage from mcp.shared.message
- GetSessionIdCallback from mcp.client.streamable_http
- RequestContext from mcp.shared.context

This fixes 4 NameError collection failures and 10 F821 ruff errors,
allowing all 20 tests in the file to be properly collected and executed.
Restore type annotations on test function parameters in
test_streamable_http.py that were accidentally removed during the
function renaming from streamablehttp_client to streamable_http_client.

Added type annotations to:
- Fixture parameters: basic_server, basic_server_url,
  json_response_server, json_server_url, event_server, monkeypatch
- Test function parameters: initialized_client_session

This fixes all 61 pyright errors and ensures type safety matches
the main branch standards.
Remove complex mocking of create_mcp_http_client in the streamablehttp
test case. Instead, let the real create_mcp_http_client execute and
only verify that streamable_http_client receives the correct parameters
including a real httpx.AsyncClient instance.

This simplifies the test by:
- Removing 13 lines of mock setup code
- Removing 14 lines of mock verification code
- Removing 3 lines of mock cleanup code
- Trusting that create_mcp_http_client works (it has its own tests)

The test now focuses on verifying the integration between session_group
and streamable_http_client rather than re-testing create_mcp_http_client.
This commit addresses two API design concerns:

1. Remove private module usage in examples: Examples no longer import from
   the private mcp.shared._httpx_utils module. Instead, they create httpx
   clients directly using the public httpx library.

2. Rename httpx_client parameter to http_client: The 'httpx_client' parameter
   name was redundant since the type annotation already specifies it's an
   httpx.AsyncClient. Renaming to 'http_client' provides a cleaner, more
   concise API.

Changes:
- Updated oauth_client.py and simple-auth-client examples to use public APIs
- Renamed httpx_client to http_client in function signatures
- Updated all internal callers and tests
- Updated deprecated streamablehttp_client wrapper function
Remove client.headers.update() call that was unnecessarily mutating
user-provided httpx.AsyncClient instances. The mutation was defensive
but unnecessary since:

1. All transport methods pass headers explicitly to httpx requests
2. httpx merges request headers with client defaults, with request
   headers taking precedence
3. HTTP requests are identical with or without the mutation
4. Not mutating respects user's client object integrity

Add comprehensive test coverage for header behavior:
- Verify client headers are not mutated after use
- Verify MCP protocol headers override httpx defaults in requests
- Verify custom and MCP headers coexist correctly in requests

All existing tests pass, confirming no behavior change to actual
HTTP requests.

@Kludex @felixweinberger

…ner header handling

This commit addresses three issues identified in PR review:

1. **Restore RequestContext fields for backwards compatibility**
   - Re-add `headers` and `sse_read_timeout` fields as optional with None defaults
   - Mark them as deprecated in docstring since they're no longer used internally
   - Prevents breaking changes for any code accessing these fields

2. **Add runtime deprecation warnings for StreamableHTTPTransport constructor**
   - Use sentinel value pattern to detect when deprecated parameters are passed
   - Issue DeprecationWarning at runtime when headers, timeout, sse_read_timeout, or auth are provided
   - Complements existing @deprecated decorator for type checkers with actual runtime warnings
   - Improve deprecation message clarity

3. **Simplify header handling by removing redundant client parameter**
   - Remove `client` parameter from `_prepare_headers()` method
   - Stop extracting and re-passing client.headers since httpx automatically merges them
   - Only build MCP-specific headers (Accept, Content-Type, session headers)
   - httpx merges these with client.headers automatically, with our headers taking precedence
   - Reduces code complexity and eliminates unnecessary header extraction

The header handling change leverages httpx's built-in header merging behavior,
similar to how headers were handled before the refactoring but without the
redundant extraction-and-repass pattern.

@felixweinberger

maxisbey

@licux licux mentioned this pull request

Dec 13, 2025

1 task