Meta MCP Server
एक MCP सर्वर जो बुद्धिमान उपकरण रूटिंग के लिए है, जिसमें Qdrant वेक्टर डेटाबेस और एम्बेडिंग के लिए LM Studio का उपयोग किया जाता है।
दस्तावेज़
MCP Deck
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
uvxanduvcommands used throughout this README. If you only havepipx, runpipx install uvto getuvx. - Docker or Apple Container (macOS
Apple Silicon) — needed to run Qdrant, which backs vector-based tool
selection. Optional if you only ever use
--no-setupagainst 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-transformersmodel 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.jsonor 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): themcpdeck serveentry point — wrapsMetaMCPServerin the MCP stdio protocol, publishes{server}__{tool}names, and providesfind_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 leftNonerather 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 (startonly; not used byserve) - 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.jsonor 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.
| Command | Purpose |
|---|---|
serve | Run the MCP server over stdio for Claude Desktop/Code (see above) |
start | Dashboard/full-stack mode with auto-setup + web UI (default command) |
run | Start the server without auto-setup or config auto-detection |
validate-config FILE | Validate a configuration file |
list-strategies | List available tool-selection strategies |
debug-vector | Run a test query against the vector search index |
regenerate-embeddings | Recompute tool embeddings (--force to clear and rebuild) |
init-config | Write a default mcpdeck.yaml |
health | Check 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-serverenvblocks) - Review child server configurations before use
- Enable
web_ui.auth_enabled(+username/password) if the dashboard is reachable beyond localhost
Contributing
- Fork the repository and clone your fork
uv sync --extra dev && uv run pre-commit install- Create a feature branch, make your changes with tests (pre-commit runs Ruff format/lint and mypy on commit)
./scripts/check-all.shbefore opening a PR
License
MIT License - see LICENSE file for details.