Skip to main content
Glama
fvanevski

mcp-mux

by fvanevski

๐Ÿ”€ MCP Mux

Dynamic Multi-Endpoint Python Model Context Protocol (MCP) Router & Orchestrator

mcp-mux is a dynamic MCP server orchestrator and multiplexer. It acts as a central proxy to multiple remote and local sub-MCP servers, monitoring a config.yaml file to hot-reload endpoints live without server restarts. It supports Server-Sent Events (SSE) and Streamable HTTP backend transports, including a local SSE bridge for clients that need an SSE endpoint while the upstream server speaks Streamable HTTP. It also provides a lightweight /summary endpoint to minimize context token flooding for AI agents.


๐ŸŒŸ Key Features

  • ๐Ÿ”„ Dynamic Hot-Reloading: Reloads endpoint configuration in place and uses one shared filesystem observer to invalidate only managed backends whose watched source trees changed.

  • ๐Ÿš€ Flexible Sub-Server Modes:

    • Remote: Seamless proxying to external HTTP MCP endpoints.

    • Managed CLI: Native spawning of command-line tools (e.g., via npx or uvx).

  • ๐Ÿงฉ Configurable Request Headers: Adds endpoint-specific upstream headers, including tokens loaded from environment variables.

  • ๐ŸŒ‰ Optional Local SSE-to-Streamable-HTTP Bridge: Streamable HTTP endpoints are proxied as Streamable HTTP by default. Endpoints can opt into a legacy SSE compatibility bridge when traditional SSE clients need a local event: endpoint flow.

  • โšก Automatic Transport Auto-Detection: Dynamically detects the backend transport mode (streamable-http vs sse) based on URL paths. Sub-servers with /mcp or /mcp/ in their URL automatically default to streamable-http.

  • ๐Ÿ›ก๏ธ Session Propagation & Isolation: For opt-in legacy bridge sessions, tracks local sessions per endpoint, maps upstream Mcp-Session-Id values to the correct local session, and rejects cross-endpoint session reuse.

  • ๐Ÿค Streamable HTTP Client Compatibility: Normalizes upstream Accept headers for Streamable HTTP POST/DELETE requests and fills in missing JSON-RPC "jsonrpc": "2.0" fields for request bodies that otherwise look like JSON-RPC messages.

  • ๐Ÿงผ Decoded Response Header Safety: Strips stale Content-Encoding and upstream Content-Length headers when the router reads and rebuilds JSON responses.

  • ๐Ÿ“Š Token-Saving Metadata Endpoint: Registers a custom /summary route returning only namespaces and descriptions, shielding AI clients from schema bloat.

  • ๐Ÿงน Clean Subprocess Lifecycle: The manager isolates background subprocesses inside unique Unix process groups (os.setsid) to guarantee no zombie processes are left behind on teardown.


Related MCP server: MCP Hub

๐Ÿ“ Architecture

graph TD
    A[main.py - Uvicorn/Starlette Server] --> B[config_loader.py - ConfigWatcher]
    B -->|Watches & Parses| C[config.yaml]
    A --> J[source_watcher.py - Shared Observer]
    J -->|Debounce and invalidate| E
    A --> D[process_manager.py - ProcessManager]
    D -->|Spawns / Cleans up| E[Managed Subprocesses: uvx / npx]
    A --> F[server.py - MCPRouter]
    F -->|Proxy SSE| G[Remote SSE Servers]
    F -->|Proxy Streamable HTTP & map sessions| H[Streamable HTTP Servers]
    A --> I[summary route]

โš™๏ธ Configuration (config.yaml)

Define your endpoints in mcp_router/config.yaml. Here is an example layout:

endpoints:
  - path: "web-search"
    mode: "remote"
    url: "https://mcp.garion.us/mcp"
    summary: "Google Search and content extraction tool"
    # transport: "streamable-http"  (Automatically detected due to /mcp path suffix)
    allowed_tools:
      - "google_search"
      - "batch_extract_urls"

  - path: "firecrawl"
    mode: "managed_cli"
    command: "export NVM_DIR=$HOME/.config/nvm && [ -s $NVM_DIR/nvm.sh ] && . $NVM_DIR/nvm.sh && HTTP_STREAMABLE_SERVER=true PORT=3033 HOST=localhost FIRECRAWL_API_URL=http://garion.us:3002 npx --yes firecrawl-mcp"
    url: "http://localhost:3033/mcp"
    summary: "Firecrawl Web Content Extraction Tool"
    timeout: 300  # Automatically shuts down after 300 seconds of inactivity
    watch:
      paths:
        - "/absolute/path/to/firecrawl-mcp/src"
      debounce_ms: 400
    allowed_tools:
      - "firecrawl_search"
      - "firecrawl_scrape"

  - path: "huggingface"
    mode: "remote"
    url: "https://huggingface.co/mcp"
    summary: "HuggingFace MCP Server โ€” model search, hub browsing, model download"
    headers:
      Authorization: "Bearer ${HF_TOKEN:-}"

Environment Variable Expansion

Configuration values support shell-style environment references before validation:

  • ${NAME} expands to the required environment variable NAME and fails config loading if it is missing.

  • ${NAME:-fallback} expands to NAME when set, otherwise to fallback.

  • Empty Authorization: "Bearer ${HF_TOKEN:-}" values are omitted, allowing endpoints such as Hugging Face to fall back to anonymous access.

  • If HF_TOKEN is accidentally set to Bearer hf_..., the loader normalizes Bearer Bearer hf_... to Bearer hf_....

Configuration Parameters

Parameter

Type

Required

Description

path

String

Yes

Unique namespace/route for the sub-server.

mode

String

Yes

Spawning mode. remote and managed_cli are currently handled by the router.

url

String

Yes (for remote/managed)

Target endpoint URL.

command

String

Yes (for managed)

Command string to spawn the local server process.

summary

String

Yes

Brief description of the sub-server, returned by /summary.

timeout

Integer

No

Inactivity timeout in seconds for CLI mode (defaults to 300).

transport

String

No

Transport mode (sse or streamable-http). Automatically detected if omitted.

legacy_sse_bridge

Boolean

No

For streamable-http endpoints only. Defaults to false; set to true to expose the local legacy SSE bridge instead of preserving upstream GET+SSE behavior.

headers

Mapping

No

Extra request headers forwarded upstream after environment expansion.

allowed_tools

List of Strings

No

Allowlist of tool names. Only these tools are exposed.

denied_tools

List of Strings

No

Denylist of tool names. These tools are excluded. (Ignored if allowed_tools is set).

watch.paths

List of Strings

No

Absolute, existing source directories that invalidate this managed endpoint when files change. Managed CLI endpoints only.

watch.debounce_ms

Integer

No

Per-endpoint change coalescing window from 50 through 60,000 milliseconds. Defaults to 400.

watch.ignore_patterns

List of Strings

No

Filename or relative-path glob patterns ignored by the watcher. Defaults to Python bytecode, editor swap/temporary files, and backup files.

Managed Backend Source Invalidation

watch provides selective development-time reload behavior without restarting the mux or unrelated backends:

endpoints:
  - path: "gh"
    mode: "managed_cli"
    command: >-
      cd /workspace/mcp_servers/gh_mcp &&
      MCP_GH_TRANSPORT=streamable-http
      MCP_GH_HTTP_PORT=8768
      uv run mcp-gh
    url: "http://localhost:8768/mcp"
    summary: "GitHub CLI MCP server"
    timeout: 300
    transport: "streamable-http"
    watch:
      paths:
        - "/workspace/mcp_servers/gh_mcp/src"
      debounce_ms: 400

The mux creates one watchdog Observer for all configured endpoints and schedules each unique source directory only once. A file create, delete, modification, or atomic move is mapped back to every endpoint that watches that path. Changes under version-control metadata, virtual environments, dependency directories, and common tool caches are always ignored. ignore_patterns provides additional endpoint-specific exclusions.

Events are transferred from watchdog's thread to the mux event loop and coalesced per endpoint. When the debounce window expires, the mux:

  1. acquires the endpoint's existing startup lock;

  2. verifies that the current configuration still watches the endpoint;

  3. discards its local bridge sessions and inactivity timestamp;

  4. terminates its complete managed process group; and

  5. leaves the endpoint stopped.

The next request follows the normal on-demand activation path and starts the backend from its revised source. An endpoint that was already stopped is not started merely because a file changed. Shared source directories may invalidate multiple explicitly configured endpoints, while unrelated managed and remote endpoints remain untouched.

Watch paths must be absolute, existing directories. Filesystem roots are rejected to prevent accidentally monitoring an entire host. To include dependency manifests or other files outside src, watch a suitably narrow common directory and add ignore_patterns for generated content. Do not watch .venv, node_modules, build output, logs, or other high-churn trees.

Invalidation intentionally interrupts active calls to the affected backend. Clients must retry or reinitialize according to the backend transport; stateless Streamable HTTP backends provide the cleanest behavior. This mechanism reloads backend code but does not force external clients such as ChatGPT to rediscover a cached MCP tool schema.

Streamable HTTP Bridge Behavior

For streamable-http endpoints, the mux preserves the original transport by default:

  • POST /<path> forwards JSON-RPC messages to the upstream MCP endpoint.

  • GET /<path> with Accept: text/event-stream forwards to the upstream MCP endpoint and preserves the upstream response status and stream.

To expose the legacy local SSE bridge, set legacy_sse_bridge: true on that endpoint. SSE clients can then connect with:

curl -N -H 'Accept: text/event-stream' http://127.0.0.1:8012/huggingface

The first SSE event contains a local POST endpoint such as:

event: endpoint
data: /huggingface?session_id=<local-session-id>

Client POSTs to that local URL are forwarded upstream. If the upstream returns Mcp-Session-Id, the router stores it on the local bridge session and forwards it on later POSTs for the same endpoint. Sessions are removed when the local SSE stream closes or when their endpoint is removed or changed during config reload.


๐Ÿš€ Getting Started

Prerequisites

Make sure you have uv installed.

1. Installation & Setup

Clone the repository and install all dependencies:

# Activate virtual environment
source .venv/bin/activate

# Install & sync dependencies
uv sync

2. Running the Orchestrator

Start the main router server (default port is 8012):

export HF_TOKEN=hf_xxx  # optional; omit for anonymous Hugging Face access
uv run python main.py --port 8012

3. Querying Endpoint Summary

You can check active routes and summaries by visiting:

curl http://127.0.0.1:8012/summary

๐Ÿงช Testing

The project is fully tested using pytest and pytest-asyncio. To execute unit tests:

uv run pytest

Current verification state: 35 passed.

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

โ€“Maintainers
โ€“Response time
โ€“Release cycle
โ€“Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    -
    quality
    D
    maintenance
    A powerful orchestration layer for Model Context Protocol (MCP) servers that enables AI assistants to dynamically discover, inspect, and interact with multiple MCP servers through a unified interface.
    Last updated
    MIT
  • F
    license
    -
    quality
    -
    maintenance
    MCP Hub aggregates and proxies multiple Model Context Protocol servers into a unified Streamable HTTP interface. It allows users to combine diverse stdio, SSE, and HTTP-based servers while providing tool namespacing, health monitoring, and secure authentication.
    Last updated
    748

View all related MCP servers

Related MCP Connectors

  • Hosted MCP server for LLM cost estimation, model comparison, and budget-aware routing.

  • AI Reasoning Cache & Consensus Layer with 11 MCP tools via Streamable HTTP.

  • Real-time chat hub for AI agents โ€” Claude Code, Cursor, Cline, Codex over MCP or REST.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/fvanevski/mcp-mux'

If you have feedback or need assistance with the MCP directory API, please join our Discord server