- 
                Notifications
    You must be signed in to change notification settings 
- Fork 2.7k
Add support to custom extensions in streamable http client #1479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add support to custom extensions in streamable http client #1479
Conversation
…ns' into feature/streamable_http_extensions
| Hi @OS-anasantos thank you for this contribution. IIUC you're trying to use  Do you mind taking a look at whether that pattern would resolve your use case? I'm closing this for now, but feel free to comment here if #1177 would not meet your requirements. | 
| Hey @felixweinberger. Unfortunately, #1177 does not seem to meet our requirements. In that PR, streamable_http_client allows us to create a custom httpx.AsyncClient, but "transport" is still a StreamableHTTPTransport instance, and it does not allow us to use custom extensions in _handle_post_request. | 
| 
 Understood, given #1177 will be landing soon, could you base your change on top of that? You can also wait for the land, but we'll want to land that before this change. | 
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.
…eature/streamable_http_extensions # Conflicts: # src/mcp/client/streamable_http.py # tests/shared/test_streamable_http.py
| @felixweinberger, I have updated the code to use #1177 as the base. Can you give me a timeframe as to when a merge for #1177 can be expected? | 
| 
 Targeting by next release (i.e. next Thursday) | 
Motivation and Context
At the moment when using a private gateway, streamablehttp_client will return error
ConnectError('[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1000)'). This is because it needs sni_hostname. According to this httpx documentation, not only "headers" must have the hostname value, but custom extensions must be sent(extensions = {"sni_hostname": "www.encode.io"})Unfortunately, streamable_http client does not allow sending custom extensions.
How Has This Been Tested?
Breaking Changes
Types of changes
Checklist
Additional context