mcp-mux

mcp-mux

A dynamic MCP server orchestrator and multiplexer that routes requests to multiple remote and local sub-MCP servers, with hot-reload config, SSE/Streamable HTTP support, and a summary endpoint to reduce context token usage.

Category
访问服务器

README

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

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

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选