# MCP Orchestrator [![PyPI Version](https://img.shields.io/pypi/v/mcp-orchestrator.svg)](https://pypi.org/project/mcp-orchestrator/) [![Python Version](https://img.shields.io/pypi/pyversions/mcp-orchestrator)](https://pypi.org/project/mcp-orchestrator/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Tests](https://github.com/rupinder2/mcp-orchestrator/actions/workflows/ci.yml/badge.svg)](https://github.com/rupinder2/mcp-orchestrator/actions) [![Contributions Welcome](https://img.shields.io/badge/Contributions-Welcome-blue.svg)](CONTRIBUTING.md) A 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. ## Features - **Config-based Server Registration**: Add downstream MCP servers via JSON config file - **Tool Namespacing**: Automatic `server_name__tool_name` format - **Tool Search**: Unified BM25/regex search with deferred loading support - **Flexible Authentication**: Static saved headers or token forwarding - **Multiple Transports**: stdio or HTTP - **Tool Definition Caching**: Cached definitions, raw result passthrough - **Storage Backends**: In-memory (development) or Redis (production) ## Quick Start ### Installation ```bash pip install mcp-orchestrator ``` ### Running the MCP Server ```bash # Run as stdio MCP server (for Claude Desktop, Cursor, etc.) mcp-orchestrator # Or run with Python directly python -m mcp_orchestrator.main ``` **HTTP Transport:** ```bash ORCHESTRATOR_TRANSPORT=http ORCHESTRATOR_PORT=8080 python -m mcp_orchestrator.main ``` This starts the server on `http://localhost:8080/mcp` with CORS enabled. ### Configuring Servers Add downstream MCP servers in `server_config.json`: ```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"] } ] } ``` ### Searching for Tools The orchestrator provides unified tool search (BM25 by default, regex optional): ```python # 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 }) ``` ## Architecture ``` ┌─────────────────────────────────────────────────────┐ │ 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 │ └─────────┘ └─────────┘ └─────────┘ ``` ## Configuration ### Environment Variables | 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 | ### Claude Desktop Integration Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`): ```json { "mcpServers": { "mcp-orchestrator": { "command": "mcp-orchestrator", "env": { "STORAGE_BACKEND": "memory", "ORCHESTRATOR_LOG_LEVEL": "INFO" } } } } ``` ## MCP Tools ### tool_search Search for tools using BM25 relevance ranking or regex pattern matching. ```python @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 Discover tools from a registered downstream server. ```python @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_remote_tool Call a tool directly on a downstream MCP server. ```python @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 """ ``` ## Tool Search Results The search tools return results in the format expected by Claude's tool search system: ```json { "success": true, "tool_references": [ { "type": "tool_reference", "tool_name": "server_name__tool_name" } ], "total_matches": 5, "query": "weather" } ``` ## Testing Run the test suite: ```bash uv run pytest ``` Run with coverage: ```bash uv run pytest --cov=mcp_orchestrator ``` ## Project Structure ``` 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) ``` ## License MIT License ## Contributing Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.