Skip to main content
Glama
amp-rh
by amp-rh

MCP Router

A production MCP router that aggregates multiple backend servers into a single endpoint with intelligent routing, health checking, and automatic failover. Route requests across backends using path-based, capability-based, or fallback strategies — with circuit breakers and namespace prefixing to keep everything isolated.

Install Python Tests FastMCP

Quick Install: pip install git+https://github.com/amp-rh/mcp.git

Features

  • Multi-Backend Aggregation: Route requests across multiple MCP servers

  • Three Routing Strategies:

    • Path-based routing (glob patterns)

    • Capability-based routing (query backend capabilities)

    • Priority/fallback chains

  • Namespace Prefixing: Prevent naming conflicts with backend.tool_name syntax

  • Health Checking: Active probes + passive monitoring with circuit breaker pattern

  • Retry Logic: Exponential backoff for transient failures

  • Router Management Tools: Monitor backend health and routing decisions

  • YAML Configuration: Simple backend definitions with environment variable overrides

  • FastMCP: High-level Python framework for MCP servers

  • HTTP/SSE Transport: Ready for web deployment

  • Container Support: UBI9 rootless container for enterprise deployments

  • Modern Python Tooling: uv, pytest, ruff

Quick Start

For Claude Code Users

šŸš€ Install directly from GitHub (no clone needed):

claude mcp add --transport stdio mcp-server \
  -- uv run --with "git+https://github.com/amp-rh/mcp.git" mcp-server

Or clone for local development:

git clone https://github.com/amp-rh/mcp.git && cd mcp && ./install-to-claude.sh

Verify and use:

# Check installation
claude mcp list

# In Claude Code, test the tools
/mcp

Next steps:

  • Type /mcp in Claude Code to see available tools

  • For customization, clone the repo and edit src/mcp_server/tools/

  • See CLAUDE_CODE_SETUP.md for detailed installation options

For Claude Desktop Users

  1. Add to Claude Desktop configuration:

{
  "mcpServers": {
    "mcp-router": {
      "command": "uv",
      "args": [
        "run",
        "--with",
        "git+https://github.com/amp-rh/mcp.git",
        "mcp-router"
      ]
    }
  }
}
  1. Restart Claude Desktop

  2. Done! The router tools are now available in Claude Desktop

For Local Development

# Install uv if not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh

# Clone and setup
git clone https://github.com/amp-rh/mcp.git
cd mcp
uv sync --all-extras

# Copy example configuration
cp config/backends.yaml.example config/backends.yaml

# Run tests (50+ tests covering all routing functionality)
make test

# Run the router server locally
make run

The router will be available at http://localhost:8000 and expose all backend tools with namespace prefixes.

Installation

pip install git+https://github.com/amp-rh/mcp.git

After installation, you can run:

# Template mode (simple MCP server)
mcp-server

# Router mode (multi-backend aggregation)
mcp-router

Method 2: Install with uv

uv pip install git+https://github.com/amp-rh/mcp.git

Method 3: Development Installation

git clone https://github.com/amp-rh/mcp.git
cd mcp
uv sync --all-extras

Claude Desktop Integration

To use this MCP router with Claude Desktop, add to your MCP settings:

Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%/Claude/claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "mcp-router": {
      "command": "uv",
      "args": [
        "run",
        "--with",
        "git+https://github.com/amp-rh/mcp.git",
        "mcp-router"
      ],
      "env": {
        "MCP_BACKENDS_CONFIG": "/path/to/your/config/backends.yaml"
      }
    }
  }
}

Option 2: Using Installed Package

If you've installed via pip:

{
  "mcpServers": {
    "mcp-router": {
      "command": "mcp-router",
      "env": {
        "MCP_BACKENDS_CONFIG": "/path/to/your/config/backends.yaml"
      }
    }
  }
}

Option 3: Using FastMCP CLI

# Install the router
pip install git+https://github.com/amp-rh/mcp.git

# Configure for Claude Desktop
fastmcp install claude-desktop mcp_server.server:mcp --name mcp-router

Option 4: Template Mode (No Routing)

For simple template mode without routing:

{
  "mcpServers": {
    "mcp-template": {
      "command": "mcp-server"
    }
  }
}

Note: After updating configuration, restart Claude Desktop for changes to take effect.

Configuration for Claude Desktop

Step 1: Create Backend Configuration

Create or edit config/backends.yaml:

backends:
  - name: my-backend
    url: http://localhost:8001
    namespace: backend
    priority: 10
    routes:
      - pattern: "*"
        strategy: capability
    health_check:
      enabled: true
      interval_seconds: 30
    circuit_breaker:
      failure_threshold: 5
      timeout_seconds: 60

Step 2: Set Environment Variable

Point Claude Desktop to your config file:

{
  "mcpServers": {
    "mcp-router": {
      "command": "mcp-router",
      "env": {
        "MCP_BACKENDS_CONFIG": "/absolute/path/to/config/backends.yaml"
      }
    }
  }
}

Step 3: Restart Claude Desktop

Restart Claude Desktop to load the MCP server.

Troubleshooting

Server not appearing:

  • Check Claude Desktop logs at ~/Library/Logs/Claude/mcp*.log (macOS)

  • Verify backend config file path is absolute

  • Ensure backend MCP servers are running

Connection errors:

  • Verify backend URLs are correct

  • Check backend servers are accessible

  • Review health check settings in config

Tool conflicts:

  • Enable namespace prefixing to avoid naming conflicts

  • Use unique namespaces for each backend

Routing Configuration

Step 1: Create Backend Configuration

Create config/backends.yaml from the example:

backends:
  - name: database
    url: http://localhost:8001
    namespace: db
    priority: 10
    routes:
      - pattern: "*_user"
        strategy: path
    health_check:
      enabled: true
      interval_seconds: 30
    circuit_breaker:
      failure_threshold: 5
      timeout_seconds: 60

Step 2: Run Backend MCP Servers

# Terminal 1: Backend 1
uv run fastmcp run path/to/backend1:mcp --transport sse --port 8001

# Terminal 2: Backend 2
uv run fastmcp run path/to/backend2:mcp --transport sse --port 8002

Step 3: Start the Router

make run
# Router starts on http://localhost:8000

Routing Strategies

The router supports three routing strategies per backend:

Path-Based Routing

routes:
  - pattern: "fetch_*"
    strategy: path
  - pattern: "*_user"
    strategy: path

Routes tools based on glob patterns matching the tool name.

Capability-Based Routing

routes:
  - pattern: "*"
    strategy: capability

Queries backend capabilities and routes to backends that have the tool.

Fallback Chains

routes:
  - pattern: "analyze_*"
    strategy: fallback
    fallback_to: analytics-secondary

Tries primary backend first, falls back to secondary if circuit is open.

Namespace Prefixing

When namespace prefixing is enabled (default), tools from different backends are exposed with prefixes:

Tool on 'db' backend:       fetch_user  →  db.fetch_user
Tool on 'api' backend:      fetch_user  →  api.fetch_user
Tool on 'analytics' backend: analyze    →  analytics.analyze

This prevents naming conflicts when aggregating tools from multiple backends.

Health Checking & Circuit Breaker

The router monitors backend health with:

  • Active Probing: Periodic health checks to /health endpoint

  • Passive Monitoring: Tracks errors from actual requests

  • Circuit Breaker States:

    • CLOSED: Normal operation (requests flow through)

    • OPEN: Backend unhealthy (requests rejected immediately)

    • HALF_OPEN: Testing recovery (allows limited requests)

Configure per backend:

circuit_breaker:
  failure_threshold: 5        # Failures before opening circuit
  timeout_seconds: 60         # Wait time before HALF_OPEN test
  half_open_attempts: 3       # Attempts in HALF_OPEN state

Router Management Tools

The router exposes three management tools:

list_backends()

Lists all configured backends with health status:

{
  "name": "database",
  "url": "http://localhost:8001",
  "namespace": "db",
  "priority": 10,
  "healthy": true,
  "circuit_state": "CLOSED",
  "error_count": 0
}

get_backend_health(backend_name)

Gets detailed health information for a specific backend:

{
  "name": "database",
  "healthy": true,
  "circuit_state": "CLOSED",
  "error_count": 0,
  "last_error": null
}

Environment Variables

For dynamic configuration override:

Variable

Default

Description

MCP_SERVER_NAME

mcp-router

Router server name

MCP_HOST

0.0.0.0

Host to bind to

MCP_PORT

8000

Port to listen on

MCP_BACKENDS_CONFIG

config/backends.yaml

Backend config file path

MCP_DEFAULT_STRATEGY

capability

Default routing strategy

MCP_ENABLE_NAMESPACES

true

Enable namespace prefixing

MCP_CACHE_TTL

300

Capability cache TTL (seconds)

MCP_REQUEST_TIMEOUT

30

Backend request timeout (seconds)

MCP_HEALTH_CHECK_INTERVAL

30

Health check interval (seconds)

MCP_HEALTH_CHECK_TIMEOUT

5

Health check timeout (seconds)

MCP_MAX_RETRIES

3

Max retry attempts

MCP_RETRY_BACKOFF

2.0

Exponential backoff multiplier

MCP_MAX_BACKOFF

10

Max backoff time (seconds)

Project Structure

ā”œā”€ā”€ config/
│   ā”œā”€ā”€ backends.yaml           # Router backend configuration
│   └── backends.yaml.example   # Example configuration
ā”œā”€ā”€ src/mcp_server/
│   ā”œā”€ā”€ server.py               # Server factory & router
│   ā”œā”€ā”€ config.py               # Server & router config
│   ā”œā”€ā”€ routing/                # Routing module
│   │   ā”œā”€ā”€ client.py           # HTTP client for backends
│   │   ā”œā”€ā”€ backends.py         # Backend manager
│   │   ā”œā”€ā”€ engine.py           # Routing engine (strategies)
│   │   ā”œā”€ā”€ health.py           # Health checker & circuit breaker
│   │   ā”œā”€ā”€ models.py           # Data models
│   │   ā”œā”€ā”€ config_loader.py    # YAML config parsing
│   │   └── exceptions.py       # Routing exceptions
│   ā”œā”€ā”€ tools/                  # Tool implementations
│   ā”œā”€ā”€ resources/              # Resource implementations
│   └── prompts/                # Prompt implementations
ā”œā”€ā”€ tests/
│   ā”œā”€ā”€ test_routing/           # Routing tests (50+)
│   │   ā”œā”€ā”€ test_models.py      # Data model tests
│   │   ā”œā”€ā”€ test_config_loader.py # Config parsing tests
│   │   ā”œā”€ā”€ test_client.py      # HTTP client tests
│   │   ā”œā”€ā”€ test_engine.py      # Routing strategy tests
│   │   └── test_health.py      # Circuit breaker tests
│   └── fixtures/
│       └── test_backends.yaml  # Test configuration
ā”œā”€ā”€ Containerfile               # UBI9 container (podman)
ā”œā”€ā”€ Makefile                    # Build targets
ā”œā”€ā”€ pyproject.toml              # Project configuration
└── README.md                   # This file

Running the Router

Local Development

# Run with auto-reload
make run-dev

# Run production mode
make run

Container Deployment

# Build container
make build

# Run container
make run-container

# Or manually:
podman build -t mcp-router:latest .
podman run --rm -p 8000:8000 -v $(pwd)/config:/app/config mcp-router:latest

Testing

The project includes comprehensive tests covering all routing functionality:

# Run all tests
make test

# Run with coverage report
make test-cov

# Run specific test file
uv run pytest tests/test_routing/test_engine.py -v

# Run routing tests only
uv run pytest tests/test_routing/ -v

Test Coverage:

  • 50+ tests across all routing components

  • Config parsing (valid/invalid scenarios)

  • Circuit breaker state transitions

  • Routing strategies (path, capability, fallback)

  • Retry logic and exponential backoff

  • Error handling and edge cases

  • Health checking and recovery

  • Backend management and capability discovery

Make Targets

make help          # Show all targets
make install       # Install dependencies
make dev           # Install with dev dependencies
make test          # Run tests
make test-cov      # Run tests with coverage
make lint          # Run linting
make format        # Format code
make run           # Run server locally
make run-dev       # Run with auto-reload
make build         # Build container image
make run-container # Run container
make clean         # Clean build artifacts

For AI Agents

This project uses AGENTS.md files as indexes. Before making changes:

  1. Read the AGENTS.md in the directory you're working in

  2. Follow linked documentation in .agents/docs/

  3. Update docs when patterns are learned or decisions made

See AGENTS.md for the root index.

Testing

# Run all tests
make test

# Run with coverage
make test-cov

# Run specific test file
uv run pytest tests/test_tools.py -v

Contributing

See .agents/docs/workflows/contributing.md for guidelines.

License

Apache License 2.0

Available Tools

3 tools
meta.testing.calculate_sumB

Calculate the sum of a list of numbers.

ParametersJSON Schema
NameRequiredDescriptionDefault
numbersYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.3/5.0
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It only states what the tool does ('calculate the sum') without adding context such as error handling for empty lists, performance limits, or output format details. This leaves significant gaps in understanding the tool's behavior beyond its basic function.

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

Conciseness5/5

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

The description is a single, clear sentence that directly states the tool's purpose without any wasted words. It is appropriately sized and front-loaded, making it easy for an agent to parse quickly and understand the core functionality.

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

Completeness4/5

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

Given the tool's low complexity (one parameter, simple arithmetic operation) and the presence of an output schema (which handles return values), the description is reasonably complete. It covers the basic purpose and parameter intent, though it lacks behavioral details like error cases or usage context, which slightly limits completeness.

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?

The description adds meaning by specifying that the input is 'a list of numbers,' which clarifies the semantics of the 'numbers' parameter beyond the schema's type definition (array of integers). With 0% schema description coverage and only one parameter, this compensation is effective, though it doesn't detail constraints like minimum list length or number ranges.

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 clearly states the tool's purpose with a specific verb ('calculate') and resource ('sum of a list of numbers'), making it easy to understand what the tool does. However, it doesn't explicitly differentiate from sibling tools like 'greet' or 'reverse_string', which are unrelated mathematical operations, so it doesn't reach the highest score.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention any context, prerequisites, or exclusions, leaving the agent to infer usage based solely on the purpose. This lack of explicit guidelines reduces its helpfulness in tool selection.

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

meta.testing.greetB

Generate a greeting for the given name.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.2/5.0
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the tool generates a greeting but doesn't mention any behavioral traits such as output format, potential side effects, or performance characteristics. This leaves significant gaps in understanding how the tool behaves beyond its basic function.

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

Conciseness5/5

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

The description is a single, clear sentence that directly states the tool's function without any unnecessary words. It is front-loaded and efficiently communicates the core purpose, making it easy for an agent to parse and understand quickly.

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

Completeness4/5

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

Given the tool's low complexity (one required parameter) and the presence of an output schema, the description is reasonably complete for its purpose. It covers the basic function, and the output schema can handle return value details, reducing the need for extensive description. However, it could benefit from more behavioral context, especially with no annotations.

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

Parameters3/5

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

The schema description coverage is 0%, meaning the input schema provides no descriptions for parameters. The description mentions 'given name', which adds some semantic context for the 'name' parameter, but it doesn't fully compensate for the lack of schema details (e.g., format constraints or examples). With 0% coverage, a baseline of 3 is appropriate as the description provides minimal but not comprehensive parameter meaning.

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 clearly states the tool's purpose with a specific verb ('Generate') and resource ('greeting'), making it easy to understand what it does. However, it doesn't explicitly differentiate from sibling tools like 'calculate_sum' or 'reverse_string', which would require a 5.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives like 'calculate_sum' or 'reverse_string'. It lacks context about appropriate scenarios or exclusions, leaving the agent to infer usage based solely on the tool name and description.

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

meta.testing.reverse_stringB

Reverse the given text string.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.3/5.0
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the action ('reverse') but doesn't explain traits like whether it handles edge cases (e.g., empty strings, special characters), performance characteristics, or error handling. For a mutation tool with zero annotation coverage, this is a significant gap in transparency.

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

Conciseness5/5

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

The description is a single, efficient sentence that directly states the tool's function without any waste. It is front-loaded with the core action ('reverse'), making it easy to parse quickly. Every word earns its place, adhering to best practices for concise tool descriptions.

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

Completeness4/5

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

Given the tool's low complexity (1 parameter, no nested objects) and the presence of an output schema (which handles return values), the description is reasonably complete. It covers the basic operation but lacks details on behavioral traits and usage context. With annotations absent, it could benefit from more disclosure, but the output schema reduces the need for extensive return value explanation.

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?

The input schema has 1 parameter with 0% description coverage, so the description must compensate. It adds meaning by specifying that the parameter 'text' is a 'given text string' to be reversed, clarifying the input's purpose beyond the schema's basic type. However, it doesn't detail constraints like string length or allowed characters, keeping it from a perfect score.

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 clearly states the tool's purpose with a specific verb ('reverse') and resource ('given text string'), making it immediately understandable. However, it doesn't differentiate from sibling tools like 'calculate_sum' or 'greet', which would require explicit comparison. The description avoids tautology by not just restating the name 'reverse_string'.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives like 'calculate_sum' or 'greet'. It lacks context about scenarios where string reversal is appropriate, such as for data transformation or debugging, and offers no exclusions or prerequisites. This leaves the agent to infer usage based solely on the tool name.

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. 6 tool updatesv1.0.0
    • Removedcalculate_sum
    • Removedgreet
    • Addedmeta.testing.calculate_sum
    • Addedmeta.testing.greet
    • Addedmeta.testing.reverse_string
    • Removedreverse_string
  2. 3 tool updates
    • First observedcalculate_sum
    • First observedgreet
    • First observedreverse_string

TDQS

B3.4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a clearly distinct purpose with no overlap: calculate_sum performs arithmetic on numbers, greet generates a greeting for a name, and reverse_string manipulates text strings. An agent can easily tell these tools apart based on their specific functions.

Naming Consistency5/5

All tool names follow a consistent snake_case pattern with a clear verb_noun structure (e.g., calculate_sum, greet, reverse_string). There are no deviations in naming conventions, making the set predictable and readable.

Tool Count3/5

With only 3 tools, the count feels thin for a general-purpose 'MCP Server Template', as it lacks coverage for common operations like data manipulation or file handling. However, for a minimal testing or example server, it is borderline acceptable.

Completeness2/5

The tool surface is severely incomplete for a server template, as it only includes trivial utility functions (sum, greeting, string reversal) without covering basic CRUD operations, data processing, or other essential domains. This will likely cause agent failures in practical use.

Related MCP Connectors