mcpdeck
Intelligent MCP (Model Context Protocol) router that selects tools via embeddings, semantic search, and RAG over a Qdrant vector database. Python · FastAPI · Qdrant · Docker.
Documentation
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 `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
mcpdeckRunning `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(primary strategy + fallback)"]
Vector["Vector search router"]
LLM["LLM router"]
RAG["RAG router"]
Pipeline["RAG pipeline(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(Claude Desktop / Claude Code)"] -->|"MCP over stdio(mcpdeck serve)"| Engine
Vector --> Qdrant[("Qdranttool + doc embeddings")]
Pipeline --> Qdrant
Emb -->|primary| LMS["LM Studioembeddings + local LLM"]
Emb -.->|fallback| ST["sentence-transformers(local model)"]
LLM --> LMS
Pipeline --> LMS
Manager --> C1["Child MCP server(e.g. filesystem)"]
Manager --> C2["Child MCP server(e.g. github)"]
Manager --> C3["Child MCP server(...)"]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: 6333Validate any config file before relying on it:
uv run mcpdeck validate-config path/to/mcpdeck.yamlCommands
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-modelsDocker
`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/collectionsThe 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 DEBUGTests 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-dockerUpgrading 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 --forceNo 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.
Frequently asked questions
What is mcpdeck?
mcpdeck is Intelligent MCP (Model Context Protocol) router that selects tools via embeddings, semantic search, and RAG over a Qdrant vector database. Python · FastAPI · Qdrant · Docker.
How do I install mcpdeck?
Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.
Is mcpdeck open source?
Yes — it is hosted on GitHub at https://github.com/anirudhlath/mcpdeck and has 2 stars.
Related MCP tools
Open-source coding agent memory. Records issues, attempts, fixes and decisions, then warns your agent before it repeats an approach that already failed. Native MCP server for Claude Code, Cursor, Antigravity and Codex. 100% local, no cloud, no telemetry. MIT.
An AI Gateway, registry, and proxy that sits in front of any MCP, A2A, or REST/gRPC APIs, exposing a unified endpoint with centralized discovery, guardrails and management. Optimizes Agent & Tool calling, and supports plugins.
AI-powered OSINT agent with interactive REPL, MCP server, and CLI. 19 tools. Works with Claude, GPT-4, or local models. For authorized security research only.
Build effective agents using Model Context Protocol and simple workflow patterns Python-based implementation. Trusted by 7600+ developers.
Fast and Accurate Code Search for Agents. Uses 99% fewer tokens than grep+read
Cut AI token costs 95%+ on code exploration. The leading MCP server for precise, symbol-level GitHub code retrieval via tree-sitter AST. Works with Claude Code, Cursor & any MCP client. 313B+ tokens saved.
Run your own MCP server? See who uses it and what to fix.
Measure it with TrackMCP