diff --git a/aider/coders/agent_coder.py b/aider/coders/agent_coder.py index 7cb60a16eaf..78e3d4a8373 100644 --- a/aider/coders/agent_coder.py +++ b/aider/coders/agent_coder.py @@ -14,14 +14,8 @@ from datetime import datetime from pathlib import Path -from litellm import experimental_mcp_client - from aider import urls, utils - -# Import the change tracker from aider.change_tracker import ChangeTracker - -# Import similarity functions for tool usage analysis from aider.helpers.similarity import ( cosine_similarity, create_bigram_vector, @@ -30,7 +24,7 @@ # Import skills helper for skills from aider.helpers.skills import SkillsManager -from aider.mcp.server import LocalServer +from aider.mcp_support.server import LocalServer from aider.repo import ANY_GIT_ERROR # Import tool modules for registry @@ -77,6 +71,25 @@ from .editblock_coder import do_replace, find_original_update_blocks, find_similar_lines +class _ExperimentalMCPClientProxy: + """Lazy proxy to defer importing litellm.experimental_mcp_client.""" + + _client = None + + def _get_client(self): + if self._client is None: + from litellm import experimental_mcp_client as client + + self._client = client + return self._client + + def __getattr__(self, name): + return getattr(self._get_client(), name) + + +experimental_mcp_client = _ExperimentalMCPClientProxy() + + class AgentCoder(Coder): """Mode where the LLM autonomously manages which files are in context.""" diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 62c64bd35ba..44eff7e3a3c 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -29,7 +29,6 @@ from typing import List import httpx -from litellm import experimental_mcp_client from litellm.types.utils import ModelResponse from prompt_toolkit.patch_stdout import patch_stdout from rich.console import Console @@ -42,7 +41,7 @@ from aider.io import ConfirmGroup, InputOutput from aider.linter import Linter from aider.llm import litellm -from aider.mcp.server import LocalServer +from aider.mcp_support.server import LocalServer from aider.models import RETRY_TIMEOUT from aider.reasoning_tags import ( REASONING_TAG, @@ -61,6 +60,25 @@ from .chat_chunks import ChatChunks +class _ExperimentalMCPClientProxy: + """Lazy proxy to defer importing litellm.experimental_mcp_client.""" + + _client = None + + def _get_client(self): + if self._client is None: + from litellm import experimental_mcp_client as client + + self._client = client + return self._client + + def __getattr__(self, name): + return getattr(self._get_client(), name) + + +experimental_mcp_client = _ExperimentalMCPClientProxy() + + class UnknownEditFormat(ValueError): def __init__(self, edit_format, valid_formats): self.edit_format = edit_format diff --git a/aider/main.py b/aider/main.py index a5b79e7137b..107e7a100f9 100644 --- a/aider/main.py +++ b/aider/main.py @@ -48,7 +48,7 @@ from aider.history import ChatSummary from aider.io import InputOutput from aider.llm import litellm # noqa: F401; properly init litellm on launch -from aider.mcp import load_mcp_servers +from aider.mcp_support import load_mcp_servers from aider.models import ModelSettings from aider.onboarding import offer_openrouter_oauth, select_default_model from aider.repo import ANY_GIT_ERROR, GitRepo diff --git a/aider/mcp/__init__.py b/aider/mcp_support/__init__.py similarity index 88% rename from aider/mcp/__init__.py rename to aider/mcp_support/__init__.py index eea87122003..26a6bd44b41 100644 --- a/aider/mcp/__init__.py +++ b/aider/mcp_support/__init__.py @@ -1,7 +1,17 @@ import json from pathlib import Path -from aider.mcp.server import HttpStreamingServer, McpServer, SseServer + +def _create_server_from_config(server_config, transport): + from .server import HttpStreamingServer, McpServer, SseServer + + if transport == "stdio": + return McpServer(server_config) + if transport == "http": + return HttpStreamingServer(server_config) + if transport == "sse": + return SseServer(server_config) + return None def _parse_mcp_servers_from_json_string(json_string, io, verbose=False, mcp_transport="stdio"): @@ -21,12 +31,9 @@ def _parse_mcp_servers_from_json_string(json_string, io, verbose=False, mcp_tran # Create a server config with name included server_config["name"] = name transport = server_config.get("transport", mcp_transport) - if transport == "stdio": - servers.append(McpServer(server_config)) - elif transport == "http": - servers.append(HttpStreamingServer(server_config)) - elif transport == "sse": - servers.append(SseServer(server_config)) + server = _create_server_from_config(server_config, transport) + if server: + servers.append(server) if verbose: io.tool_output(f"Loaded {len(servers)} MCP servers from JSON string") @@ -120,10 +127,9 @@ def _parse_mcp_servers_from_file(file_path, io, verbose=False, mcp_transport="st # Create a server config with name included server_config["name"] = name transport = server_config.get("transport", mcp_transport) - if transport == "stdio": - servers.append(McpServer(server_config)) - elif transport == "http": - servers.append(HttpStreamingServer(server_config)) + server = _create_server_from_config(server_config, transport) + if server: + servers.append(server) if verbose: io.tool_output(f"Loaded {len(servers)} MCP servers from {file_path}") @@ -169,6 +175,7 @@ def load_mcp_servers(mcp_servers, mcp_servers_file, io, verbose=False, mcp_trans # and maybe it is actually prompt_toolkit's fault # but this hack works swimmingly because ??? # so sure! why not - servers = [McpServer(json.loads('{"aider_default": {}}'))] + default_server = _create_server_from_config(json.loads('{"aider_default": {}}'), "stdio") + servers = [default_server] if default_server else [] return servers diff --git a/aider/mcp/server.py b/aider/mcp_support/server.py similarity index 100% rename from aider/mcp/server.py rename to aider/mcp_support/server.py