This is a meta-layer server that sits above your other MCP servers and aggregates their tools into a single interface. Instead of Claude loading every tool from every server upfront, you get deferred loading through a unified search system. It uses BM25 ranking for natural language queries or regex for pattern matching, returns tool references that Claude can then load on demand, and routes calls back to the appropriate downstream server. Configure servers via JSON (supporting both stdio and HTTP transports), and it handles namespacing, caching, and authentication. Useful when you have many MCP servers and want to avoid context bloat while maintaining discoverability across your entire tool ecosystem.
Public tool metadata for what this MCP can expose to an agent.
orchestrator.server_catalogReturn MCP endpoints and recommended server keys.Return MCP endpoints and recommended server keys.
No parameter schema in public metadata yet.
orchestrator_server_catalogReturn MCP endpoints and recommended server keys.Return MCP endpoints and recommended server keys.
No parameter schema in public metadata yet.
orchestrator.landing_routesPublic no-token routes to start the experience.Public no-token routes to start the experience.
No parameter schema in public metadata yet.
orchestrator_landing_routesPublic no-token routes to start the experience.Public no-token routes to start the experience.
No parameter schema in public metadata yet.
orchestrator.summaryCross-domain snapshot: which goals/wealth areas have data + tokenized deep links.1 paramsCross-domain snapshot: which goals/wealth areas have data + tokenized deep links.
tokenstringorchestrator_summaryCross-domain snapshot: which goals/wealth areas have data + tokenized deep links.1 paramsCross-domain snapshot: which goals/wealth areas have data + tokenized deep links.
tokenstringorchestrator.onboardingOnboarding payload (requires token). Returns profile/shared_data + next deep links.1 paramsOnboarding payload (requires token). Returns profile/shared_data + next deep links.
tokenstringorchestrator_onboardingOnboarding payload (requires token). Returns profile/shared_data + next deep links.1 paramsOnboarding payload (requires token). Returns profile/shared_data + next deep links.
tokenstringorchestrator.send_magic_linkSend magic link email so the user can open a path with their token (uses /api/auth/send-magic-link).3 paramsSend magic link email so the user can open a path with their token (uses /api/auth/send-magic-link).
emailstringconfirm_sendbooleanredirectPathstringorchestrator_send_magic_linkSend magic link email so the user can open a path with their token (uses /api/auth/send-magic-link).3 paramsSend magic link email so the user can open a path with their token (uses /api/auth/send-magic-link).
emailstringconfirm_sendbooleanredirectPathstringA central hub that connects to multiple downstream MCP servers, aggregates their tools, and provides unified access with powerful tool search capabilities.
Built around deferred tool loading — search across all your servers without blowing Claude's context window.
server_name__tool_name formatpip install mcp-orchestrator
# Run as stdio MCP server (for Claude Desktop, Cursor, etc.)
mcp-orchestrator
# Or run with Python directly
python -m mcp_orchestrator.main
HTTP Transport:
ORCHESTRATOR_TRANSPORT=http ORCHESTRATOR_PORT=8080 python -m mcp_orchestrator.main
This starts the server on http://localhost:8080/mcp with CORS enabled.
Add downstream MCP servers in server_config.json:
{
"servers": [
{
"name": "my-server",
"url": "http://localhost:8080/mcp",
"transport": "http",
"auth_type": "static",
"auth_headers": {
"Authorization": "Bearer my-token"
}
},
{
"name": "my-stdio-server",
"url": "server.py",
"transport": "stdio",
"command": "uv",
"args": ["run", "python", "server.py"]
}
]
}
The orchestrator provides unified tool search (BM25 by default, regex optional):
# BM25 search (default - natural language)
results = await mcp_client.call_tool("tool_search", {
"query": "get weather information",
"max_results": 3
})
# Regex search (set use_regex=true)
results = await mcp_client.call_tool("tool_search", {
"query": "weather|forecast",
"use_regex": true,
"max_results": 3
})
┌─────────────────────────────────────────────────────┐
│ MCP Orchestrator │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ FastMCP Server │ │
│ │ ┌─────────────┐ ┌──────────────────┐ │ │
│ │ │ tool_search │ │ call_remote_tool │ │ │
│ │ └─────────────┘ └──────────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Server │ │ Tool │ │ Storage │ │
│ │ Registry │ │ Search │ │(Memory/Redis)│ │
│ └──────────┘ └──────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ MCP Svr │ │ MCP Svr │ │ MCP Svr │
│ #1 │ │ #2 │ │ #N │
└─────────┘ └─────────┘ └─────────┘
| Variable | Default | Description |
|---|---|---|
STORAGE_BACKEND | memory | Storage backend (memory or redis) |
REDIS_URL | redis://localhost:6379/0 | Redis connection URL |
MCP_ORCHESTRATOR_TOOL_CACHE_TTL | 300 | Tool schema cache TTL in seconds |
MCP_ORCHESTRATOR_DEFAULT_CONNECTION_MODE | stateless | Default connection mode |
MCP_ORCHESTRATOR_CONNECTION_TIMEOUT | 30.0 | Connection timeout in seconds |
MCP_ORCHESTRATOR_MAX_RETRIES | 3 | Maximum retry attempts |
ORCHESTRATOR_TRANSPORT | stdio | MCP transport (stdio or http) |
ORCHESTRATOR_PORT | 8080 | Port for HTTP transport |
ORCHESTRATOR_HOST | 0.0.0.0 | Host for HTTP transport |
ORCHESTRATOR_LOG_LEVEL | INFO | Logging level |
SERVER_CONFIG_PATH | server_config.json | Path to server configuration file |
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"mcp-orchestrator": {
"command": "mcp-orchestrator",
"env": {
"STORAGE_BACKEND": "memory",
"ORCHESTRATOR_LOG_LEVEL": "INFO"
}
}
}
}
Search for tools using BM25 relevance ranking or regex pattern matching.
@mcp.tool()
async def tool_search(
query: str,
max_results: int = 3,
use_regex: bool = False,
) -> dict:
"""Search for tools using BM25 or regex.
By default uses BM25 natural language search. Set use_regex=True
to search using Python regex patterns instead.
"""
Discover tools from a registered downstream server.
@mcp.tool()
async def discover_tools(
server_name: str,
) -> dict:
"""Discover tools from a registered server and index them for search.
Returns the list of discovered tools with their schemas.
"""
Call a tool directly on a downstream MCP server.
@mcp.tool()
async def call_remote_tool(
tool_name: str,
arguments: Optional[dict] = None,
auth_header: Optional[str] = None,
) -> Any:
"""Call a tool on a downstream server.
Args:
tool_name: Namespaced tool name (server_name__tool_name)
arguments: Tool arguments
auth_header: Optional auth header to override server's configured auth
"""
The search tools return results in the format expected by Claude's tool search system:
{
"success": true,
"tool_references": [
{
"type": "tool_reference",
"tool_name": "server_name__tool_name"
}
],
"total_matches": 5,
"query": "weather"
}
Run the test suite:
uv run pytest
Run with coverage:
uv run pytest --cov=mcp_orchestrator
mcp-orchestrator/
├── src/mcp_orchestrator/
│ ├── __init__.py
│ ├── main.py # Entry point
│ ├── models.py # Pydantic models
│ ├── mcp_server.py # FastMCP server
│ ├── config_loader.py # Config file loader
│ ├── server/
│ │ └── registry.py # Server registry
│ ├── tools/
│ │ ├── router.py # Tool router
│ │ └── search.py # Tool search service
│ └── storage/
│ ├── base.py # Storage interface
│ ├── memory.py # In-memory backend
│ └── redis.py # Redis backend
├── tests/
│ ├── test_registry.py
│ ├── test_search.py
│ ├── test_storage.py
│ ├── test_models.py
│ └── test_integration.py
├── server_config.json # Pre-configured downstream servers
├── pyproject.toml
├── README.md
└── .env # Environment variables (not committed)
MIT License
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
STORAGE_BACKENDStorage backend for tool cache. Use 'memory' for development or 'redis' for production.
REDIS_URLRedis connection URL. Required only when STORAGE_BACKEND=redis.
SERVER_CONFIG_PATHPath to the server_config.json file listing downstream MCP servers.
ORCHESTRATOR_PORTPort for the HTTP transport server (default: 8080).
com.mcparmory/google-search
io.github.pipeworx-io/brave-search
marcopesani/mcp-server-serper
brave/brave-search-mcp-server
com.mcparmory/google-search-console
acamolese/google-search-console-mcp