mirror of
https://github.com/Aider-AI/aider.git
synced 2025-06-02 02:34:59 +00:00
Configurable mcp servers
This commit is contained in:
parent
2c24084cb0
commit
16c4e622e3
6 changed files with 254 additions and 10 deletions
|
@ -723,6 +723,18 @@ def get_parser(default_config_files, git_root):
|
|||
default="platform",
|
||||
help="Line endings to use when writing files (default: platform)",
|
||||
)
|
||||
group.add_argument(
|
||||
"--mcp-servers",
|
||||
metavar="MCP_CONFIG_JSON",
|
||||
help="Specify MCP server configurations as a JSON string",
|
||||
default=None,
|
||||
)
|
||||
group.add_argument(
|
||||
"--mcp-servers-file",
|
||||
metavar="MCP_CONFIG_FILE",
|
||||
help="Specify a file path with MCP server configurations",
|
||||
default=None,
|
||||
)
|
||||
group.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
|
|
|
@ -198,6 +198,7 @@ class GUI:
|
|||
)
|
||||
|
||||
for fname in fnames:
|
||||
print(fname)
|
||||
if fname not in self.coder.get_inchat_relative_files():
|
||||
self.coder.add_rel_fname(fname)
|
||||
self.info(f"Added {fname} to the chat")
|
||||
|
|
|
@ -29,7 +29,7 @@ from aider.format_settings import format_settings, scrub_sensitive_info
|
|||
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.server import McpServer
|
||||
from aider.mcp 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
|
||||
|
@ -957,14 +957,11 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||
analytics.event("auto_commits", enabled=bool(args.auto_commits))
|
||||
|
||||
try:
|
||||
git_server = McpServer({"name": "git", "command": "uvx", "args": ["mcp-server-git"]})
|
||||
context_seven_server = McpServer(
|
||||
{
|
||||
"name": "context7",
|
||||
"command": "deno",
|
||||
"args": ["run", "--allow-net", "npm:@upstash/context7-mcp"],
|
||||
}
|
||||
)
|
||||
# Load MCP servers from config string or file
|
||||
mcp_servers = load_mcp_servers(args.mcp_servers, args.mcp_servers_file, io, args.verbose)
|
||||
|
||||
if not mcp_servers:
|
||||
mcp_servers = []
|
||||
|
||||
coder = Coder.create(
|
||||
main_model=main_model,
|
||||
|
@ -998,7 +995,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||
detect_urls=args.detect_urls,
|
||||
auto_copy_context=args.copy_paste,
|
||||
auto_accept_architect=args.auto_accept_architect,
|
||||
mcp_servers=[context_seven_server, git_server],
|
||||
mcp_servers=mcp_servers,
|
||||
)
|
||||
except UnknownEditFormat as err:
|
||||
io.tool_error(str(err))
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
import json
|
||||
|
||||
from aider.mcp.server import McpServer
|
||||
|
||||
|
||||
def _parse_mcp_servers_from_json_string(json_string, io, verbose=False):
|
||||
"""Parse MCP servers from a JSON string."""
|
||||
servers = []
|
||||
|
||||
try:
|
||||
config = json.loads(json_string)
|
||||
if verbose:
|
||||
io.tool_output("Loading MCP servers from provided JSON string")
|
||||
|
||||
if "mcpServers" in config:
|
||||
for name, server_config in config["mcpServers"].items():
|
||||
if verbose:
|
||||
io.tool_output(f"Loading MCP server: {name}")
|
||||
|
||||
# Create a server config with name included
|
||||
server_config["name"] = name
|
||||
servers.append(McpServer(server_config))
|
||||
|
||||
if verbose:
|
||||
io.tool_output(f"Loaded {len(servers)} MCP servers from JSON string")
|
||||
return servers
|
||||
else:
|
||||
io.tool_warning("No 'mcpServers' key found in MCP config JSON string")
|
||||
except json.JSONDecodeError:
|
||||
io.tool_error("Invalid JSON in MCP config string")
|
||||
except Exception as e:
|
||||
io.tool_error(f"Error loading MCP config from string: {e}")
|
||||
|
||||
return servers
|
||||
|
||||
|
||||
def _parse_mcp_servers_from_file(file_path, io, verbose=False):
|
||||
"""Parse MCP servers from a JSON file."""
|
||||
servers = []
|
||||
|
||||
try:
|
||||
with open(file_path, "r") as f:
|
||||
config = json.load(f)
|
||||
|
||||
if verbose:
|
||||
io.tool_output(f"Loading MCP servers from file: {file_path}")
|
||||
|
||||
if "mcpServers" in config:
|
||||
for name, server_config in config["mcpServers"].items():
|
||||
if verbose:
|
||||
io.tool_output(f"Loading MCP server: {name}")
|
||||
|
||||
# Create a server config with name included
|
||||
server_config["name"] = name
|
||||
servers.append(McpServer(server_config))
|
||||
|
||||
if verbose:
|
||||
io.tool_output(f"Loaded {len(servers)} MCP servers from {file_path}")
|
||||
return servers
|
||||
else:
|
||||
io.tool_warning(f"No 'mcpServers' key found in MCP config file: {file_path}")
|
||||
except FileNotFoundError:
|
||||
io.tool_warning(f"MCP config file not found: {file_path}")
|
||||
except json.JSONDecodeError:
|
||||
io.tool_error(f"Invalid JSON in MCP config file: {file_path}")
|
||||
except Exception as e:
|
||||
io.tool_error(f"Error loading MCP config from file: {e}")
|
||||
|
||||
return servers
|
||||
|
||||
|
||||
def load_mcp_servers(mcp_servers, mcp_servers_file, io, verbose=False):
|
||||
"""Load MCP servers from a JSON string or file."""
|
||||
servers = []
|
||||
|
||||
# First try to load from the JSON string (preferred)
|
||||
if mcp_servers:
|
||||
servers = _parse_mcp_servers_from_json_string(mcp_servers, io, verbose)
|
||||
if servers:
|
||||
return servers
|
||||
|
||||
# If JSON string failed or wasn't provided, try the file
|
||||
if mcp_servers_file:
|
||||
servers = _parse_mcp_servers_from_file(mcp_servers_file, io, verbose)
|
||||
|
||||
return servers
|
94
aider/website/docs/config/mcp.md
Normal file
94
aider/website/docs/config/mcp.md
Normal file
|
@ -0,0 +1,94 @@
|
|||
---
|
||||
parent: Configuration
|
||||
nav_order: 120
|
||||
description: Configure Model Control Protocol (MCP) servers for enhanced AI capabilities.
|
||||
---
|
||||
|
||||
# Model Control Protocol (MCP)
|
||||
|
||||
Model Control Protocol (MCP) servers extend aider's capabilities by providing additional tools and functionality to the AI models. MCP servers can add features like git operations, context retrieval, and other specialized tools.
|
||||
|
||||
## Configuring MCP Servers
|
||||
|
||||
Aider supports configuring MCP servers using the MCP Server Configuration schema. Please
|
||||
see the [Model Context Protocol documentation](https://modelcontextprotocol.io/introduction)
|
||||
for more information.
|
||||
|
||||
You have two ways of sharing your MCP server configuration with Aider.
|
||||
|
||||
### 1. Using a JSON string
|
||||
|
||||
You can specify MCP servers directly on the command line using the `--mcp-servers` option with a JSON string:
|
||||
|
||||
```bash
|
||||
aider --mcp-servers '{"mcpServers":{"git":{"command":"uvx","args":["mcp-server-git"]}}}'
|
||||
```
|
||||
|
||||
### 2. Using a configuration file
|
||||
|
||||
Alternatively, you can store your MCP server configurations in a JSON file and reference it with the `--mcp-servers-file` option:
|
||||
|
||||
```bash
|
||||
aider --mcp-servers-file mcp.json
|
||||
```
|
||||
|
||||
Example `mcp.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"git": {
|
||||
"command": "uvx",
|
||||
"args": ["mcp-server-git"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## YAML Configuration
|
||||
|
||||
You can also configure MCP servers in your `.aider.conf.yml` file:
|
||||
|
||||
```yaml
|
||||
mcp-servers: |
|
||||
{
|
||||
"mcpServers": {
|
||||
"git": {
|
||||
"command": "uvx",
|
||||
"args": ["mcp-server-git"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or specify a configuration file:
|
||||
|
||||
```yaml
|
||||
mcp-servers-file: /path/to/mcp.json
|
||||
```
|
||||
|
||||
These options are configurable in any of Aider's config file formats.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
You can also configure MCP servers using environment variables in your `.env` file:
|
||||
|
||||
```
|
||||
AIDER_MCP_SERVERS={"mcpServers":{"git":{"command":"uvx","args":["mcp-server-git"]}}}
|
||||
```
|
||||
|
||||
Or specify a configuration file:
|
||||
|
||||
```
|
||||
AIDER_MCP_SERVERS_FILE=/path/to/mcp.json
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter issues with MCP servers:
|
||||
|
||||
1. Use the `--verbose` flag to see detailed information about MCP server loading
|
||||
2. Check that the specified executables are installed and available in your PATH
|
||||
3. Verify that your JSON configuration is valid
|
||||
|
||||
For more information about specific MCP servers and their capabilities, refer to their respective documentation.
|
|
@ -1345,3 +1345,57 @@ class TestMain(TestCase):
|
|||
)
|
||||
for call in mock_io_instance.tool_warning.call_args_list:
|
||||
self.assertNotIn("Cost estimates may be inaccurate", call[0][0])
|
||||
|
||||
@patch("aider.coders.Coder.create")
|
||||
def test_mcp_servers_parsing(self, mock_coder_create):
|
||||
# Setup mock coder
|
||||
mock_coder_instance = MagicMock()
|
||||
mock_coder_create.return_value = mock_coder_instance
|
||||
|
||||
# Test with --mcp-servers option
|
||||
with GitTemporaryDirectory():
|
||||
main(
|
||||
[
|
||||
"--mcp-servers",
|
||||
'{"mcpServers":{"git":{"command":"uvx","args":["mcp-server-git"]}}}',
|
||||
"--exit",
|
||||
"--yes",
|
||||
],
|
||||
input=DummyInput(),
|
||||
output=DummyOutput(),
|
||||
)
|
||||
|
||||
# Verify that Coder.create was called with mcp_servers parameter
|
||||
mock_coder_create.assert_called_once()
|
||||
_, kwargs = mock_coder_create.call_args
|
||||
self.assertIn("mcp_servers", kwargs)
|
||||
self.assertIsNotNone(kwargs["mcp_servers"])
|
||||
# At least one server should be in the list
|
||||
self.assertTrue(len(kwargs["mcp_servers"]) > 0)
|
||||
# First server should have a name attribute
|
||||
self.assertTrue(hasattr(kwargs["mcp_servers"][0], "name"))
|
||||
|
||||
# Test with --mcp-servers-file option
|
||||
mock_coder_create.reset_mock()
|
||||
|
||||
with GitTemporaryDirectory():
|
||||
# Create a temporary MCP servers file
|
||||
mcp_file = Path("mcp_servers.json")
|
||||
mcp_content = {"mcpServers": {"git": {"command": "uvx", "args": ["mcp-server-git"]}}}
|
||||
mcp_file.write_text(json.dumps(mcp_content))
|
||||
|
||||
main(
|
||||
["--mcp-servers-file", str(mcp_file), "--exit", "--yes"],
|
||||
input=DummyInput(),
|
||||
output=DummyOutput(),
|
||||
)
|
||||
|
||||
# Verify that Coder.create was called with mcp_servers parameter
|
||||
mock_coder_create.assert_called_once()
|
||||
_, kwargs = mock_coder_create.call_args
|
||||
self.assertIn("mcp_servers", kwargs)
|
||||
self.assertIsNotNone(kwargs["mcp_servers"])
|
||||
# At least one server should be in the list
|
||||
self.assertTrue(len(kwargs["mcp_servers"]) > 0)
|
||||
# First server should have a name attribute
|
||||
self.assertTrue(hasattr(kwargs["mcp_servers"][0], "name"))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue