Meta MCP Server

एक MCP सर्वर जो बुद्धिमान उपकरण रूटिंग के लिए है, जिसमें Qdrant वेक्टर डेटाबेस और एम्बेडिंग के लिए LM Studio का उपयोग किया जाता है।

दस्तावेज़

MCP Deck

CI

Your command deck for MCP servers. MCP Deck is an intelligent MCP (Model Context Protocol) router: it spawns your child MCP servers, embeds their tools, and exposes them to an MCP client through a single connection — either proxying every tool directly (namespaced) or, via the find_tools meta-tool, letting the client ask "what tool should I use for X?" and get back the most relevant ones instead of the whole list.

There are two ways to run it:

  • mcpdeck serve — an MCP server over stdio for Claude Desktop / Claude Code (or any MCP client). This is the integration most people want.
  • mcpdeck start — a standalone dashboard/router process with a Gradio web UI, useful for development, debugging tool selection, and inspecting child-server health outside of an MCP client.

Install via uvx/uv tool install from git as shown below, or once a tagged release is published to PyPI, uv tool install mcpdeck / uvx mcpdeck.

Prerequisites

  • Python 3.11+
  • uv — provides the uvx and uv commands used throughout this README. If you only have pipx, run pipx install uv to get uvx.
  • Docker or Apple Container (macOS Apple Silicon) — needed to run Qdrant, which backs vector-based tool selection. Optional if you only ever use --no-setup against an already-running Qdrant, or don't need tool-selection routing at all.
  • LM Studio (optional) — for local embeddings and LLM-based tool selection. Without it, MCP Deck falls back to a bundled sentence-transformers model automatically.

Use with Claude Desktop / Claude Code

This is the mcpdeck serve path: an MCP server over stdio that exposes every child tool as {server}__{tool} plus a find_tools meta-tool. stdout is reserved for the JSON-RPC protocol — all logs and human-readable output go to stderr, so this is safe to run under any MCP client's process supervisor.

Add to your MCP client config (Claude Desktop's claude_desktop_config.json, or Claude Code's .mcp.json):

{
  "mcpServers": {
    "mcpdeck": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/anirudhlath/mcpdeck",
        "mcpdeck",
        "serve",
        "--mcp-servers-json",
        "/absolute/path/to/mcp-servers.json"
      ]
    }
  }
}

mcp-servers.json uses the same mcpServers shape Claude Desktop itself uses, so you can point --mcp-servers-json at your existing Claude Desktop config to re-expose the same child servers through MCP Deck's tool-selection layer:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"]
    }
  }
}

Working from a local checkout instead of git+https (e.g. while developing)? Point uv run --project at it instead of uvx:

{
  "mcpServers": {
    "mcpdeck": {
      "command": "uv",
      "args": [
        "run",
        "--project",
        "/path/to/mcpdeck",
        "mcpdeck",
        "serve",
        "--mcp-servers-json",
        "/absolute/path/to/mcp-servers.json"
      ]
    }
  }
}

serve supports --setup (default --no-setup) if you want it to also detect/start a container runtime and Qdrant before serving — see mcpdeck serve --help. Restart Claude Desktop / Claude Code after editing the config.

The Gradio web UI is disabled on the serve path even if your config sets web_ui.enabled: true — Gradio's launch() prints to stdout, which would corrupt the JSON-RPC channel. Use mcpdeck start when you want the dashboard.

find_tools and tool namespacing

Every child tool is published under {server_name}__{tool_name} (dots aren't legal in MCP tool names, so server.tool becomes server__tool; any other disallowed character is replaced with -, and the name is truncated to the MCP-mandated 64 characters). Call these directly like any other MCP tool.

find_tools is a built-in meta-tool, always listed first, that runs MCP Deck's intelligent selection (vector / LLM / RAG, depending on config and what initialized successfully) against a natural-language query:

{"name": "find_tools", "arguments": {"query": "read a file from disk", "max_results": 5}}

It returns a JSON list of {"name": ..., "description": ..., "server": ...} for the most relevant tools, which you then call directly by their namespaced name. This is the main point of MCP Deck: instead of a client seeing every tool from every child server at once, it can ask for just the ones relevant to the current task.

Quick Start (dashboard mode)

Run the dashboard/router (start) straight from this repository with uvx:

# Automatic setup: detects Docker/Apple Container, starts Qdrant, opens the
# web UI on http://localhost:8080
uvx --from git+https://github.com/anirudhlath/mcpdeck mcpdeck

# With explicit config
uvx --from git+https://github.com/anirudhlath/mcpdeck mcpdeck \
    --config my-config.yaml --mcp-servers-json my-servers.json

# Or install it as a persistent CLI tool
uv tool install git+https://github.com/anirudhlath/mcpdeck
mcpdeck

Running mcpdeck with no arguments (or with top-level flags like --config/--web-ui, with no subcommand) runs start. On startup it will:

  • Detect and set up a container runtime (Docker or Apple Container Framework)
  • Start the Qdrant vector database (unless --no-setup)
  • Auto-detect an existing mcp-servers.json or Claude Desktop config in standard locations (read-only — it does not write or modify your Claude Desktop config)
  • Start the MCP Deck server with the web UI at http://localhost:8080

Architecture

flowchart TD
    subgraph Server["MCP Deck server"]
        Engine["Routing engine<br/>(primary strategy + fallback)"]
        Vector["Vector search router"]
        LLM["LLM router"]
        RAG["RAG router"]
        Pipeline["RAG pipeline<br/>(doc chunking + retrieval)"]
        Emb["Embedding service"]
        Manager["Child server manager"]
        Engine --> Vector
        Engine --> LLM
        Engine --> RAG
        RAG --> Pipeline
        Vector --> Emb
        Pipeline --> Emb
        Engine -->|selected tools / proxied calls| Manager
    end

    Client["MCP client<br/>(Claude Desktop / Claude Code)"] -->|"MCP over stdio<br/>(mcpdeck serve)"| Engine

    Vector --> Qdrant[("Qdrant<br/>tool + doc embeddings")]
    Pipeline --> Qdrant
    Emb -->|primary| LMS["LM Studio<br/>embeddings + local LLM"]
    Emb -.->|fallback| ST["sentence-transformers<br/>(local model)"]
    LLM --> LMS
    Pipeline --> LMS

    Manager --> C1["Child MCP server<br/>(e.g. filesystem)"]
    Manager --> C2["Child MCP server<br/>(e.g. github)"]
    Manager --> C3["Child MCP server<br/>(...)"]

Main components (all under src/mcpdeck/):

  • North-bound MCP server (server/mcp_stdio.py): the mcpdeck serve entry point — wraps MetaMCPServer in the MCP stdio protocol, publishes {server}__{tool} names, and provides find_tools
  • Server core (server/meta_server.py): initializes and owns every other component; resilient startup means a failed embedding/vector-store/LLM/RAG component is logged as a warning and left None rather than crashing — child tools are still exposed even with no Qdrant/LM Studio running
  • Routing strategies (routing/): vector search (vector_router.py), LLM selection (llm_router.py), and RAG-based selection (rag_router.py)
  • RAG pipeline (rag/pipeline.py): chunks and indexes child-server documentation, retrieves relevant context, and augments selection queries
  • Embedding service (embeddings/service.py): LM Studio embeddings when available, with automatic sentence-transformers fallback and local caching
  • Vector store (vector_store/qdrant_client.py): Qdrant-based storage and similarity search for tool and documentation embeddings
  • Child server manager (child_servers/): spawns and manages the lifecycle of downstream MCP servers and proxies tool calls to them
  • Web interface (web_ui/): Gradio-based real-time monitoring and configuration dashboard (start only; not used by serve)
  • Health / auto-setup (health/): infrastructure detection, health checks, and automatic Docker/Apple Container + Qdrant setup

Features

Intelligent Tool Selection

  • Vector Search (default): fast semantic similarity using embeddings
  • LLM Selection: AI-powered tool selection using a local LLM (LM Studio)
  • RAG-Based Selection: context-augmented selection using retrieved child-server documentation

Automatic Setup (start / --setup)

  • Container runtime detection: Apple Container Framework on Apple Silicon macOS, or Docker elsewhere
  • Starts Qdrant automatically
  • Auto-detects an existing mcp-servers.json or Claude Desktop config

Web Dashboard (start only)

  • Real-time server monitoring and logs
  • Interactive configuration editor
  • Tool usage analytics and metrics
  • Child server status monitoring
  • Optional HTTP basic auth (web_ui.auth_enabled + username/password; fails closed — the UI refuses to start if enabled without both credentials)

Configuration

Auto-Detection

mcpdeck start (and bare mcpdeck) looks for configuration files in these locations when --config/--mcp-servers-json aren't given:

Main Config (mcpdeck.yaml):

  • ./config/mcpdeck.yaml
  • ./mcpdeck.yaml
  • ~/.mcpdeck/config.yaml
  • ./config/meta-server.yaml (legacy, pre-rename)
  • ./meta-server.yaml (legacy, pre-rename)
  • ~/.meta-mcp/config.yaml (legacy, pre-rename)
  • /etc/meta-mcp/config.yaml (legacy, pre-rename)

MCP Servers Config (JSON), read-only — never written to:

  • ./mcp-servers.json
  • ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
  • ~/.config/claude/claude_desktop_config.json (Linux/Windows)
  • ~/.claude/claude_desktop_config.json

mcpdeck serve does not auto-detect a Claude Desktop mcp-servers.json (pass --mcp-servers-json explicitly — see the Claude Desktop/Code section above), but when --config is omitted it still searches the same main-config locations as start, in the order listed above (falling back to built-in defaults if none exist).

Creating Custom Config

mcp-servers.json (Claude Desktop format):

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"]
    },
    "fetch": {
      "command": "uvx",
      "args": ["mcp-server-fetch"]
    }
  }
}

mcpdeck.yaml (every field is real and validated — unknown fields are rejected; see examples/simple-config.yaml and examples/advanced-config.yaml for complete, working examples):

strategy:
  primary: "vector"      # vector, llm, or rag
  fallback: "vector"     # fallback strategy
  vector_threshold: 0.4  # similarity threshold
  max_tools: 10          # max tools to return

web_ui:
  enabled: true
  port: 8080
  auth_enabled: false    # set true + username/password for basic auth

embeddings:
  # Primary: LM Studio (optional). Canonical endpoint form ends in /v1 —
  # /v1/ and /v1/embeddings are also accepted and normalized.
  lm_studio_endpoint: "http://localhost:1234/v1"
  lm_studio_model: "nomic-embed-text-v1.5"

  # Fallback: local sentence-transformers model (automatic)
  fallback_model: "all-MiniLM-L6-v2"

vector_store:
  type: "qdrant"
  host: "localhost"
  port: 6333

Validate any config file before relying on it:

uv run mcpdeck validate-config path/to/mcpdeck.yaml

Commands

mcpdeck [OPTIONS] COMMAND [ARGS]...

Running mcpdeck with no subcommand, or with a top-level flag (e.g. mcpdeck --config x.yaml --web-ui), routes to start.

CommandPurpose
serveRun the MCP server over stdio for Claude Desktop/Code (see above)
startDashboard/full-stack mode with auto-setup + web UI (default command)
runStart the server without auto-setup or config auto-detection
validate-config FILEValidate a configuration file
list-strategiesList available tool-selection strategies
debug-vectorRun a test query against the vector search index
regenerate-embeddingsRecompute tool embeddings (--force to clear and rebuild)
init-configWrite a default mcpdeck.yaml
healthCheck system health and dependencies

Every command supports --help for its exact flags, e.g. mcpdeck serve --help. When running via uvx, prefix these with uvx --from git+https://github.com/anirudhlath/mcpdeck.

health

uv run mcpdeck health                    # text output, exits non-zero on issues
uv run mcpdeck health --output-format json
uv run mcpdeck health --fix --setup-docker --download-models

Docker

docker-compose.yml runs Qdrant plus the mcpdeck dashboard service (built from the repo Dockerfile, using config/docker.yaml which binds the web UI to 0.0.0.0:8080 and points vector_store.host at the qdrant service):

docker-compose up -d
# Web UI: http://localhost:8080
# Qdrant: http://localhost:6333/collections

The container's CMD is mcpdeck start --no-setup --config /app/config/docker.yaml (Qdrant is provided by compose, so setup is skipped); its HEALTHCHECK curls http://localhost:8080/ (the Gradio dashboard root — there is no /health HTTP endpoint).

For running Qdrant via Apple's container framework instead of Docker, see docs/apple-container-setup.md.

Development

git clone https://github.com/anirudhlath/mcpdeck.git
cd mcpdeck

uv sync --extra dev
uv run pre-commit install

uv run pytest
uv run ruff check src/ tests/
uv run ruff format src/ tests/
uv run mypy src/
# or all at once:
./scripts/check-all.sh

# Run the stdio server against a local checkout:
uv run mcpdeck serve --no-setup --mcp-servers-json path/to/mcp-servers.json --log-level DEBUG

# Run dashboard mode against a local checkout:
uv run mcpdeck start --log-level DEBUG

Tests are marked unit, integration (may spawn real subprocesses; no Docker/Qdrant required — resilient init is exercised directly), and slow.

Troubleshooting

Qdrant connection failed

curl http://localhost:6333/collections
uv run mcpdeck health --setup-docker

Upgrading from before v0.2.0: vector-store point IDs and embedding cache keys changed (the old scheme used a per-process salted hash that produced duplicate points on every restart). Run this once after upgrading:

uv run mcpdeck regenerate-embeddings --force

No MCP servers found: create an mcp-servers.json file, or point --mcp-servers-json at an existing Claude Desktop config.

Web UI not accessible: check the port isn't already in use (lsof -i :8080) or pick another with --port.

LM Studio not being used: confirm the endpoint responds at http://localhost:1234/v1/models, and that lm_studio_endpoint is set (it's null/unset by default — the fallback sentence-transformers model is used unless you configure it explicitly).

Logs: stderr in serve mode; ./logs/mcpdeck.log and the web UI's log viewer in start/run mode (path from logging.file in your config).

Security Considerations

  • Run child servers with minimal privileges
  • Use environment variables for sensitive configuration (${VAR} expansion in child-server env blocks)
  • Review child server configurations before use
  • Enable web_ui.auth_enabled (+ username/password) if the dashboard is reachable beyond localhost

Contributing

  1. Fork the repository and clone your fork
  2. uv sync --extra dev && uv run pre-commit install
  3. Create a feature branch, make your changes with tests (pre-commit runs Ruff format/lint and mypy on commit)
  4. ./scripts/check-all.sh before opening a PR

License

MIT License - see LICENSE file for details.