Skip to content

Add streamable_http_client which accepts httpx.AsyncClient instead of httpx_client_factory - #1177

Merged
maxisbey merged 13 commits into
mainfrom
rename-streamable-http
Dec 10, 2025
Merged

Add streamable_http_client which accepts httpx.AsyncClient instead of httpx_client_factory#1177
maxisbey merged 13 commits into
mainfrom
rename-streamable-http

Conversation

@Kludex

@KludexKludex commented Jul 21, 2025

Copy link
Copy Markdown
Member

@Kludex
Kludex requested review from a team, ihrpr and ochafikJuly 21, 2025 08:43
ihrpr
ihrpr previously requested changes Jul 21, 2025

@ihrprihrpr left a comment

Copy link
Copy Markdown
Contributor

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)

@Kludex

Copy link
Copy Markdown
MemberAuthor

I blame copilot. 😄

@Kludex

Kludex commented Jul 21, 2025

Copy link
Copy Markdown
MemberAuthor

(it's probably a good time to replace httpx_client_factory with an object)

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.

@ihrpr

Copy link
Copy Markdown
Contributor

(it's probably a good time to replace httpx_client_factory with an object)

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?

@Kludex

Copy link
Copy Markdown
MemberAuthor

(it's probably a good time to replace httpx_client_factory with an object)

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 streamable_http_client.

@dsp-ant

Copy link
Copy Markdown
Member

Should we do this and deprecate the old module?

@felixweinbergerfelixweinberger self-assigned this Sep 22, 2025
@felixweinbergerfelixweinberger added the needs maintainer Potentially serious issue - needs proactive fix and maintainer attention label Sep 22, 2025
@felixweinberger

Copy link
Copy Markdown
Contributor

FYI for viewers: on my todo list to update this

@felixweinbergerfelixweinberger added the needs more work Not ready to be merged yet, needs additional follow-up from the author(s). label Sep 23, 2025
@felixweinbergerfelixweinberger removed the needs more work Not ready to be merged yet, needs additional follow-up from the author(s). label Oct 1, 2025
@felixweinberger
felixweinberger dismissed ihrpr’s stale reviewOctober 1, 2025 18:18

outdated - pushed updated version making httpx_client a param instead of factory

@felixweinberger
felixweinberger requested review from ihrpr and removed request for ihrpr and ochafikOctober 1, 2025 18:24
Comment threadsrc/mcp/client/streamable_http.py Outdated
self.session_id = None
self.protocol_version = None
self.request_headers = {
**self.headers,

@felixweinbergerfelixweinbergerOct 1, 2025

Copy link
Copy Markdown
Contributor

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), for example accept: */*

Therefore the transport needs to override these headers now if they're present to be configured correctly.

Comment threadsrc/mcp/client/streamable_http.py Outdated
@felixweinberger

felixweinberger commented Oct 1, 2025

Copy link
Copy Markdown
Contributor

@Kludex@ihrpr@dsp-ant gave this a shot, trying to replace the factory with a client object.

Results in some ugly header mangling that I don't see how to avoid given the existing structure. Keen for thoughts.

@felixweinbergerfelixweinberger added needs more eyes and removed needs maintainer Potentially serious issue - needs proactive fix and maintainer attention labels Oct 1, 2025
@felixweinbergerfelixweinberger changed the title Rename streamablehttp_client to streamable_http_clientAdd streamable_http_client which accepts httpx.AsyncClient instead of httpx_client_factoryOct 1, 2025
# 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)

Copy link
Copy Markdown
Contributor

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.

felixweinbergerand others added 6 commits December 4, 2025 17:54
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.
…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.
@maxisbey
maxisbey merged commit a3a4b8d into mainDec 10, 2025
21 checks passed
@maxisbey
maxisbey deleted the rename-streamable-http branch December 10, 2025 16:39
@Kludex

Copy link
Copy Markdown
MemberAuthor

I didn't think this was going to be merged already. I've been thinking about this, and I'm unsure this is the cleanest approach.

I think this API is not going to last, I think the right approach is to create the MCPClient/Client that it will manage the lifecycle of the HTTP client there. Right now, most of the LLM providers are doing this thing of accepting the HTTP client, but I think what everybody should be doing is allowing to pass all the parameters (a bit contradictory to what was the motivation to this PR, since I'm saying the completely opposite).

I think the TestClient from Starlette is more or less how I see that happening (doesn't need to be with inheritance, it can be with composition).

@maxisbey

Copy link
Copy Markdown
Contributor

I didn't think this was going to be merged already. I've been thinking about this, and I'm unsure this is the cleanest approach.

I think this API is not going to last, I think the right approach is to create the MCPClient/Client that it will manage the lifecycle of the HTTP client there. Right now, most of the LLM providers are doing this thing of accepting the HTTP client, but I think what everybody should be doing is allowing to pass all the parameters (a bit contradictory to what was the motivation to this PR, since I'm saying the completely opposite).

I think the TestClient from Starlette is more or less how I see that happening (doesn't need to be with inheritance, it can be with composition).

Hmm, could you make an issue for this for v2 with some thoughts? would be good to capture it when going through the refactoring

@felixweinberger

Copy link
Copy Markdown
Contributor

I didn't think this was going to be merged already. I've been thinking about this, and I'm unsure this is the cleanest approach.
I think this API is not going to last, I think the right approach is to create the MCPClient/Client that it will manage the lifecycle of the HTTP client there. Right now, most of the LLM providers are doing this thing of accepting the HTTP client, but I think what everybody should be doing is allowing to pass all the parameters (a bit contradictory to what was the motivation to this PR, since I'm saying the completely opposite).
I think the TestClient from Starlette is more or less how I see that happening (doesn't need to be with inheritance, it can be with composition).

Hmm, could you make an issue for this for v2 with some thoughts? would be good to capture it when going through the refactoring

@maxisbey@Kludex made one here for discussion: #1773

@tsingfei

tsingfei commented Jan 5, 2026

Copy link
Copy Markdown

Will McpHttpClientFactory be deprecated in the future? I need to automatically recreate the connection to a streamable-http mcp srerver. But now it seems that only create the connection with an http.AsyncClient instance is supported. Well, I need to write the factory myself or I can reuse the McpHttpClientFactory to implement that logic?

@richardye101

richardye101 commented Jan 29, 2026

Copy link
Copy Markdown

For anyone like me who just ran into this SSL issue and is unclear about how to use this PR to get around it, this is all I had to do on the client to bypass the SSL issues:

async with httpx.AsyncClient(verify=False) as custom_httpx_client:
async with streamable_http_client(url="https://my-mcp.com/mcp", http_client=custom_httpx_client) as (
read_stream,
write_stream,
_,
):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
tools = await session.list_tools()
print(tools)

jairus-m added a commit to dbt-labs/dbt-mcp that referenced this pull request Apr 17, 2026
…lient (#727)
## Summary
<!-- Provide a brief description of the changes in this PR -->
MCP python-sdk [updated
streamable_http_client](modelcontextprotocol/python-sdk#1177),
now deprecated in favor of `streamable_http_client` with a slightly
different signature but returns the same exact tuple.
Basically a name change!
## What Changed
<!-- Describe the changes made in this PR -->
## Why
<!-- Explain the motivation for these changes -->
## Related Issues
<!-- Link any related issues using #issue_number -->
Closes #
Related to #
## Checklist
- [ ] I have performed a self-review of my code
- [ ] I have made corresponding changes to the documentation (in
https://github.com/dbt-labs/docs.getdbt.com) if required -- Mention it
here
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
## Additional Notes
<!-- Any additional information that would be helpful for reviewers -->
prashant9501 added a commit to prashant9501/SecureLife-MCP-Project that referenced this pull request May 30, 2026
The MCP Python SDK renamed the import from `streamablehttp_client` to
`streamable_http_client` (PR modelcontextprotocol/python-sdk#1177).
The old name is deprecated and has been removed from current main.
Pyright/Windsurf flags it as deprecated.
Signature is backward-compatible: `url` remains the only required arg
and the context manager still yields a 3-tuple, so the destructuring
`async with streamable_http_client(url) as (read, write, _):` still
works without further changes.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementRequest for a new feature that's not currently supported

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SSLV3_ALERT_HANDSHAKE_FAILURE when using private gateway

8 participants

@Kludex@ihrpr@dsp-ant@felixweinberger@fryguy04@maxisbey@tsingfei@richardye101