loading…
Search for a command to run...
loading…
A flexible proxy server that aggregates multiple backend MCP servers into a single interface using STDIO or SSE transports. It supports dynamic server managemen
A flexible proxy server that aggregates multiple backend MCP servers into a single interface using STDIO or SSE transports. It supports dynamic server management via an HTTP API and utilizes namespacing to prevent tool conflicts across connected services.
A production-ready MCP proxy server that aggregates multiple backend MCP servers into a single endpoint — with lazy loading, per-tool filtering, and a unified YAML config that doubles as a live control plane.
All your AI tools → multi-mcp → github, obsidian, exa, tavily, context7, ...
Most MCP setups require configuring every server individually in every tool (Claude Code, Codex, Cursor, etc.). Each server starts eagerly at boot. You have no easy way to disable specific tools from a server you otherwise want.
multi-mcp solves all three:
enabled: false on any individual tool in a YAML fileserver::tool_name prevents conflicts across serversRequirements: Python 3.10+, uv
git clone https://github.com/itstanner5216/multi-mcp
cd multi-mcp
uv sync
First run — auto-discovers all your servers and writes ~/.config/multi-mcp/servers.yaml:
uv run python main.py start
Or refresh manually to re-discover tools and update the YAML:
uv run python main.py refresh
On first run, multi-mcp creates ~/.config/multi-mcp/servers.yaml by connecting to every server you've configured, fetching its tool list, then disconnecting. The resulting file looks like:
servers:
github:
command: /path/to/run-github.sh
always_on: true # stays connected at all times
idle_timeout_minutes: 5
tools:
search_repositories:
enabled: true
delete_repository:
enabled: false # hidden from all AI tools
create_gist:
enabled: false
exa:
url: https://mcp.exa.ai/mcp?tools=web_search_exa,get_code_context_exa
always_on: false # lazy: connects only when called
idle_timeout_minutes: 5
tools:
web_search_exa:
enabled: true
linkedin_search_exa:
enabled: false # don't need this
obsidian:
command: /path/to/run-obsidian.sh
always_on: true
tools: {} # auto-populated on first run
Tool control rules:
| State | Behavior |
|---|---|
enabled: true |
Exposed to AI |
enabled: false |
Hidden — setting is never overwritten by refresh |
| Tool disappears from server | Marked stale: true, your setting preserved |
stale: true + enabled: false |
Cleaned up on next refresh |
No tools key |
All tools pass through (default) |
To disable a tool, just set enabled: false and save. Takes effect on next multi-mcp start.
# Start the proxy (stdio mode — used by Claude Code, Codex, etc.)
uv run python main.py start
# Start in SSE mode (network accessible)
uv run python main.py start --transport sse --port 8085
# Re-discover tools from all servers, smart-merge into YAML
uv run python main.py refresh
# Re-discover tools from one server only
uv run python main.py refresh github
# Show server status and tool counts
uv run python main.py status
# List all tools with enabled/disabled status
uv run python main.py list
# Filter to one server
uv run python main.py list --server github
# Show only disabled tools
uv run python main.py list --disabled
Once multi-mcp is running, replace all individual server entries in your tool configs with a single entry:
Claude Code / Cursor / any JSON-based config:
{
"mcpServers": {
"multi-mcp": {
"type": "stdio",
"command": "uv",
"args": ["run", "--project", "/path/to/multi-mcp", "python", "main.py", "start"]
}
}
}
SSE mode (if running as a background service):
{
"mcpServers": {
"multi-mcp": {
"type": "sse",
"url": "http://localhost:8085/sse"
}
}
}
multi-mcp connects to backend servers over any transport:
| Transport | Backend config | Notes |
|---|---|---|
| stdio | command: /path/to/server |
Local subprocess |
| Streamable HTTP | url: https://... |
Current MCP spec (POST) |
| SSE | url: https://... |
Legacy SSE (GET), auto-fallback |
For url-based servers, multi-mcp tries Streamable HTTP first and falls back to legacy SSE automatically.
When running with --transport sse, a management API is available:
# List active servers
GET /mcp_servers
# Add a server at runtime
POST /mcp_servers
{"name": "new-server", "command": "/path/to/server"}
# Remove a server
DELETE /mcp_servers/{name}
# List all tools by server
GET /mcp_tools
# Health check
GET /health
Authenticate with Authorization: Bearer <key> (set MULTI_MCP_API_KEY env var to enable).
┌──────────────────────────────────────────────────┐
│ Claude Code / Codex / Cursor / any MCP client │
└─────────────────────┬────────────────────────────┘
│ stdio or SSE
┌───────▼────────┐
│ multi-mcp │
│ │
│ • YAML config │
│ • namespacing │
│ • tool filter │
│ • lazy loading │
│ • audit log │
└──┬──────┬──────┘
│ │
┌────────────┘ └──────────────┐
│ │
┌───▼──────────┐ ┌────────▼──────┐
│ always_on │ │ lazy │
│ │ │ │
│ github │ │ exa (SSE) │
│ obsidian │ │ tavily │
│ │ │ context7 │
│ (connected │ │ seq-thinking │
│ always) │ │ │
└──────────────┘ │ (connects on │
│ first call, │
│ disconnects │
│ after idle) │
└───────────────┘
# Run tests
uv run python -m pytest
# Run specific test file
uv run python -m pytest tests/test_cache_manager.py -v
# Check what's configured
uv run python main.py status
uv run python main.py list
Test coverage: 30 tests across YAML config, merge logic, startup discovery, idle timeout, startup flow, CLI, and reconnect behavior.
| Variable | Description |
|---|---|
MULTI_MCP_API_KEY |
Bearer token for SSE API auth |
MULTI_MCP_HOST |
SSE bind host (default: 127.0.0.1) |
MULTI_MCP_PORT |
SSE bind port (default: 8085) |
MULTI_MCP_LOG_LEVEL |
Log level: DEBUG, INFO, WARNING, ERROR |
MIT
Добавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"multi-mcp": {
"command": "npx",
"args": []
}
}
}