diff --git a/astrbot/core/agent/mcp_client.py b/astrbot/core/agent/mcp_client.py index 6cedc2202..dad9e5be4 100644 --- a/astrbot/core/agent/mcp_client.py +++ b/astrbot/core/agent/mcp_client.py @@ -9,6 +9,7 @@ from datetime import timedelta from pathlib import Path, PureWindowsPath from typing import Any, Generic +import httpx from tenacity import ( before_sleep_log, retry, @@ -102,12 +103,22 @@ except (ModuleNotFoundError, ImportError): "Warning: Missing 'mcp' dependency, MCP services will be unavailable." ) +streamable_http_client_legacy = None +streamable_http_client = None + try: - from mcp.client.streamable_http import streamablehttp_client -except (ModuleNotFoundError, ImportError): - logger.warning( - "Warning: Missing 'mcp' dependency or MCP library version too old, Streamable HTTP connection unavailable.", + from mcp.client.streamable_http import ( + streamablehttp_client as streamable_http_client_legacy, ) +except (ModuleNotFoundError, ImportError): + try: + from mcp.client.streamable_http import ( + streamable_http_client as streamable_http_client, + ) + except (ModuleNotFoundError, ImportError): + logger.warning( + "Warning: Missing 'mcp' dependency or MCP library version too old, Streamable HTTP connection unavailable.", + ) def _prepare_config(config: dict) -> dict: @@ -459,17 +470,38 @@ class MCPClient: ), ) else: - timeout = timedelta(seconds=cfg.get("timeout", 30)) - sse_read_timeout = timedelta( - seconds=cfg.get("sse_read_timeout", 60 * 5), - ) - self._streams_context = streamablehttp_client( - url=cfg["url"], - headers=cfg.get("headers", {}), - timeout=timeout, - sse_read_timeout=sse_read_timeout, - terminate_on_close=cfg.get("terminate_on_close", True), - ) + timeout_seconds = cfg.get("timeout", 30) + sse_read_timeout_seconds = cfg.get("sse_read_timeout", 60 * 5) + if streamable_http_client_legacy: + timeout = timedelta(seconds=timeout_seconds) + sse_read_timeout = timedelta(seconds=sse_read_timeout_seconds) + self._streams_context = streamable_http_client_legacy( + url=cfg["url"], + headers=cfg.get("headers", {}), + timeout=timeout, + sse_read_timeout=sse_read_timeout, + terminate_on_close=cfg.get("terminate_on_close", True), + ) + elif streamable_http_client: + http_client = await self.exit_stack.enter_async_context( + httpx.AsyncClient( + headers=cfg.get("headers", {}), + timeout=httpx.Timeout( + timeout_seconds, + read=sse_read_timeout_seconds, + ), + follow_redirects=True, + ), + ) + self._streams_context = streamable_http_client( + url=cfg["url"], + http_client=http_client, + terminate_on_close=cfg.get("terminate_on_close", True), + ) + else: + raise RuntimeError( + "Streamable HTTP transport is not available in the installed MCP library version." + ) read_s, write_s, _ = await self.exit_stack.enter_async_context( self._streams_context, ) diff --git a/pyproject.toml b/pyproject.toml index c44d65fd5..e4f85c5da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ dependencies = [ "google-genai>=1.56.0", "httpx[socks]>=0.28.1", "lark-oapi>=1.4.15", - "mcp>=1.8.0", + "mcp>=1.8.0,<2", "openai>=1.78.0", "ormsgpack>=1.9.1", "pillow>=11.2.1", diff --git a/requirements.txt b/requirements.txt index 1cf2fdc19..cdcf434ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ filelock>=3.18.0 google-genai>=1.56.0 httpx[socks]>=0.28.1 lark-oapi>=1.4.15 -mcp>=1.8.0 +mcp>=1.8.0,<2 openai>=1.78.0 ormsgpack>=1.9.1 pillow>=11.2.1