-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Add streamable_http_client
which accepts httpx.AsyncClient
instead of httpx_client_factory
#1177
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?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The deprecated wrapper is missing the httpx_client_factory
and auth
parameters. This could break existing code using those parameters.
(it's probably a good time to replace httpx_client_factory with an object)
I blame copilot. 😄 |
I think the timeouts, the auth and the httpx_client_factory CAN be replaced. The timeouts I think can be subjective, but the auth and the httpx_client_factory I think need to be replaced. |
should we just replace it now, since we are deprecating one method, would be good to have a nice alternative and not to deprecate it twice? |
I didn't get what you said. I agree that it's a good moment to change the parameters of the new |
Should we do this and deprecate the old module? |
FYI for viewers: on my todo list to update this |
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
222b13f
to
74fa629
Compare
outdated - pushed updated version making httpx_client a param instead of factory
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.
self.session_id = None | ||
self.protocol_version = None | ||
self.request_headers = { | ||
**self.headers, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously we would have streamablehttp_client
create the httpx.AsyncClient
via a factory after initializing the transport. That means we could use these transport.request_headers
when creating the httpx.AsyncClient
to ensure the client and the transport have the same headers.
If we now accept httpx_client
as an argument to streamable_http_client
, that client might have custom headers! In fact by default httpx.AsyncClient
creates headers here that need to be overriden (hence moving the ACCEPT and CONTENT_TYPE after)
Therefore the transport needs to override these headers now if they're present to be configured correctly.
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.
# Sync client headers with transport's merged headers (includes MCP protocol requirements) | ||
client.headers.update(transport.request_headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to sync the headers on the client back so that they match with the transport after the transport has overriden them. If the user passes in a httpx_client
they created, that could have arbitrary headers that no longer match the headers on the transport.
Overall I find this quite horrible - but I'm not sure how else to make this work via an object instead of a factory. Open for suggestions...
streamablehttp_client
to streamable_http_client
streamable_http_client
which accepts httpx.AsyncClient
instead of httpx_client_factory
7a6c89d
to
43dec02
Compare
# Handle timeout | ||
if timeout is None: | ||
kwargs["timeout"] = httpx.Timeout(30.0) | ||
kwargs["timeout"] = httpx.Timeout(MCP_DEFAULT_TIMEOUT, read=MCP_DEFAULT_SSE_READ_TIMEOUT) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously we'd rely on the transport setting this to 60 * 5
when creating our client, but we can't rely on that anymore as the client may now be created before the transport.
from mcp.client.auth import OAuthClientProvider, TokenStorage | ||
from mcp.client.streamable_http import streamablehttp_client | ||
from mcp.client.streamable_http import streamable_http_client | ||
from mcp.shared._httpx_utils import create_mcp_http_client |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should have private methods being exposed in examples. 🤔
timeout: float | timedelta = 30, | ||
sse_read_timeout: float | timedelta = 60 * 5, | ||
*, | ||
httpx_client: httpx.AsyncClient | None = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
httpx_client: httpx.AsyncClient | None = None, | |
http_client: httpx.AsyncClient | None = None, |
The current name is not very intuitive considering the class name and the module.