Mcp streamable http detached httpx client
Skill kjuhwa/skills-hub/skills/integration/mcp-streamable-http-detached-httpx-client
Inject a caller-owned httpx.AsyncClient into the official MCP streamable_http transport via httpx_client_factory, using a no-op async-context-manager so the transport never closes the underlying client on exit.From its SKILL.md
npx -y skills add kjuhwa/skills-hub --skill mcp-streamable-http-detached-httpx-clientAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
3.3 KB, 605 tokens by cl100k_base, as published. Nobody here has run it
MCP Streamable HTTP with a Detached httpx Client
When to use
You want to share one httpx.AsyncClient (with custom auth, proxies, retries, connection pool) across many MCP server connections, but mcp.client.streamable_http.streamablehttp_client only accepts an httpx_client_factory callable that constructs a new client (and that the transport will close on exit). You need a shim that hands back the existing client without taking ownership of its lifecycle.
How it works
_DetachExitAsyncClientCMis a tinyasync withcontext manager that yields the pre-builthttpx.AsyncClienton__aenter__and does NOTHING on__aexit__— no close, no shutdown._httpx_factory_for_client(client)returns the factory function the MCP SDK expects; the factory ignores the SDK's headers/timeout/auth args (they're already configured on the client) and returns the wrapper CM.- The public
streamable_http_clientfunction passes the factory into the official transport, so application code can use one client across many MCP servers.
Example
class _DetachExitAsyncClientCM:
__slots__ = ("_client",)
def __init__(self, client: httpx.AsyncClient): self._client = client
async def __aenter__(self): return self._client
async def __aexit__(self, *_): return None
def _httpx_factory_for_client(http_client):
def _factory(headers=None, timeout=None, auth=None):
del headers, timeout, auth # already configured on the shared client
return cast(httpx.AsyncClient, _DetachExitAsyncClientCM(http_client))
return _factory
@asynccontextmanager
async def streamable_http_client(url, *, http_client, headers=None,
timeout=30.0, sse_read_timeout=300.0,
terminate_on_close=True):
async with _streamablehttp_client(
url, headers=headers or {}, timeout=timeout,
sse_read_timeout=sse_read_timeout, terminate_on_close=terminate_on_close,
httpx_client_factory=_httpx_factory_for_client(http_client),
) as triple:
yield triple
Gotchas
cast(httpx.AsyncClient, _DetachExitAsyncClientCM(...))is a type-checker workaround — the SDK type-hints want anAsyncClient, but it actually only usesasync withsemantics on the result.- This pattern is a maintenance burden: when the upstream SDK changes its transport contract, this shim must be revisited.
- Use
__slots__on the wrapper so it has zero per-instance overhead. - Pair with a single
httpx.AsyncClient(timeout=httpx.Timeout(...), limits=httpx.Limits(...))constructed at app startup so all MCP traffic shares one connection pool.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.