Skip to main content
Glama
rupinder2

mcp-orchestrator

by rupinder2

MCP Orchestrator

PyPI Version Python Version License: MIT Tests Contributions Welcome

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)

Related MCP server: super-mcp

Quick Start

Installation

pip install mcp-orchestrator

Running the MCP Server

# 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.

Configuring Servers

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"]
    }
  ]
}

Searching for Tools

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
})

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):

{
  "mcpServers": {
    "mcp-orchestrator": {
      "command": "mcp-orchestrator",
      "env": {
        "STORAGE_BACKEND": "memory",
        "ORCHESTRATOR_LOG_LEVEL": "INFO"
      }
    }
  }
}

MCP Tools

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

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_remote_tool

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
    """

Tool Search Results

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"
}

Testing

Run the test suite:

uv run pytest

Run with coverage:

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 for guidelines.

Available Tools

2 tools
call_remote_toolA

Call a tool directly on a registered remote MCP server through the orchestrator.

This tool allows direct invocation of any tool on a downstream MCP server. The tool name should be in the format 'server_name__tool_name' (e.g., 'context7__query-docs').

Args: tool_name: Full tool name in format 'server_name__tool_name' arguments: Tool arguments as a dictionary (optional) auth_header: Optional auth header to use for this call (overrides registered auth)

Returns: Raw tool call result from the remote server

ParametersJSON Schema
NameRequiredDescriptionDefault
argumentsNo
tool_nameYes
auth_headerNo

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden. It discloses that the result is a raw remote call result and that auth_header overrides registered auth. It does not mention error behavior, side effects, or the fact that arbitrary remote tool calls may be destructive, which would be useful for such a generic passthrough tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is organized with a short introductory statement, Args, and Returns sections. It is readable and information-dense. There is minor redundancy between 'Call a tool directly' and 'allows direct invocation of any tool,' but it does not hurt comprehension.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is a generic arbitrary remote invocation with no output schema and no annotations, so the description needs to cover a lot. It covers the naming convention, arguments, auth override, and raw return. It does not explain how to discover registered servers/tools, how to handle errors, or warn about side effects, leaving some gaps for an agent invoking arbitrary downstream tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Despite 0% schema description coverage, the description explains all three parameters: tool_name format, arguments as an optional dictionary, and auth_header as an optional override. The concrete example ('context7__query-docs') adds real value beyond the bare schema. It could go further on argument structure, but it is solid.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it calls a tool on a remote MCP server through the orchestrator. It also clarifies the exact name format, which helps distinguish it from generic tool use. However, it does not explicitly contrast itself with the sibling tool_search beyond implying direct remote invocation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies this tool is for direct invocation of a known remote tool, and gives the name format. It does not explicitly say when to prefer tool_search or when not to use this tool, so usage guidance is present but not fully developed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.2
    • First observedcall_remote_tool
    • First observedtool_search

TDQS

A3.9/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: one searches/disovers tools across servers, the other invokes a specific remote tool. There is no overlap or ambiguity between them.

Naming Consistency4/5

Both names are snake_case and descriptive, but 'tool_search' follows a noun_verb pattern while 'call_remote_tool' follows verb_remote_noun. This is a minor inconsistency; renaming to 'search_tools' would make it perfectly uniform.

Tool Count3/5

With only 2 tools, the server feels slightly thin for an orchestrator role. The tool count is borderline below the typical 3-15 range, but the two tools cover core orchestration functions (discovery and invocation).

Completeness3/5

The pair covers the main workflow: search for tools and call them. However, missing operations like listing registered servers, checking server health, or managing registrations mean the surface is not fully complete for a comprehensive orchestrator, though workable for basic flows.

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables centralized management and unified interface for multiple child MCP servers (filesystem, sqlite, etc.), allowing users to discover, launch, and execute tools across different MCP servers through a single gateway.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    A gateway that aggregates multiple MCP servers into a single endpoint, namespacing their tools and forwarding calls, so an agent connects to one MCP to access the entire stack.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Acts as an MCP gateway aggregating multiple child MCP servers into a single namespaced interface, with an optional memory layer that caches tool results to reduce redundant calls.
    15 npm
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI harnesses to connect to a single MCP endpoint that routes to multiple downstream MCP servers, discovering and executing capabilities on demand while keeping tool schemas out of context.
    4
    50 npm
    Apache 2.0