From c05d5773cd19475fff71f357344dc2c2307f0f4a Mon Sep 17 00:00:00 2001 From: dvd233 <111864431+dvd233@users.noreply.github.com> Date: Fri, 18 Sep 2026 17:46:38 -0700 Subject: [PATCH] fix(mcp): support mcp 2 streamable http client --- mini_agent/tools/mcp_loader.py | 39 +++++++++++++++++++++++++++++++--- tests/test_mcp.py | 10 +++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/mini_agent/tools/mcp_loader.py b/mini_agent/tools/mcp_loader.py index c89b0790..ac0647a2 100644 --- a/mini_agent/tools/mcp_loader.py +++ b/mini_agent/tools/mcp_loader.py @@ -10,7 +10,20 @@ from mcp import ClientSession, StdioServerParameters from mcp.client.sse import sse_client from mcp.client.stdio import stdio_client -from mcp.client.streamable_http import streamablehttp_client + +try: + from mcp.client.streamable_http import ( + streamable_http_client as _streamable_http_client, + ) +except ImportError: # mcp < 2.0 + _streamable_http_client = None + +try: + from mcp.client.streamable_http import ( + streamablehttp_client as _legacy_streamable_http_client, + ) +except ImportError: # mcp >= 2.0 + _legacy_streamable_http_client = None from .base import Tool, ToolResult @@ -255,9 +268,29 @@ async def _connect_streamable_http(self): connect_timeout = self._get_connect_timeout() sse_read_timeout = self._get_sse_read_timeout() - # streamablehttp_client returns (read, write, get_session_id) + if _streamable_http_client is not None: + # mcp 2.x renamed the transport and accepts a configured httpx2 client. + import httpx2 + + timeout = httpx2.Timeout(connect_timeout, read=sse_read_timeout) + http_client = await self.exit_stack.enter_async_context( + httpx2.AsyncClient( + headers=self.headers or None, + timeout=timeout, + follow_redirects=True, + ) + ) + read_stream, write_stream = await self.exit_stack.enter_async_context( + _streamable_http_client(url=self.url, http_client=http_client) + ) + return read_stream, write_stream + + if _legacy_streamable_http_client is None: + raise ImportError("The installed mcp package has no Streamable HTTP client") + + # mcp 1.x returns (read, write, get_session_id) and accepts timeout kwargs. read_stream, write_stream, _ = await self.exit_stack.enter_async_context( - streamablehttp_client( + _legacy_streamable_http_client( url=self.url, headers=self.headers if self.headers else None, timeout=connect_timeout, diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 2ea27bda..ac58aebf 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -80,6 +80,16 @@ def test_unknown_type_with_url_defaults_to_streamable_http(self): assert _determine_connection_type(config) == "streamable_http" +def test_streamable_http_transport_api_is_available(): + """Support both the mcp 1.x and 2.x Streamable HTTP API names.""" + from mini_agent.tools import mcp_loader + + assert ( + mcp_loader._streamable_http_client is not None + or mcp_loader._legacy_streamable_http_client is not None + ) + + # ============================================================================= # MCPServerConnection Initialization Tests # =============================================================================