An MCP server for searching and analyzing Rust codebases. Combines hybrid BM25 + vector search with a HIR-driven workspace hypergraph built on rust-analyzer, exposing 45+ tools for symbol navigation, call-graph traversal, structural audits, and semantic neighborhood queries.
Links:Website · — come share how you're using it; we want to hear about people's workflows.
mcp server for rust code semantic hybrid search bm25 + embeddings with tree-sitter, gpu accelerated indexing and merkle tree updates
Pluggable embedding models - local GPU (Qwen3 via Candle/CUDA), local CPU (BGE via ONNX), or API-backed (OpenRouter); new API models are added through a config file with no recompile
Symbol navigation - rust-analyzer–backed find_definition / find_references / rename_symbol (rename returns a preview; no files are modified)
Persisted hypergraph - HIR-driven workspace snapshot (LMDB) with cross-crate imports, exports, re-exports, call edges, attributes, signatures, statics, and unsafe blocks
The skills/ directory ships 26 Claude Code skills that compose these MCP tools into ready-made audit recipes. Each skill is a self-contained SKILL.md with prerequisites, step-by-step prompts, and hand-offs to related skills — invoke them in Claude Code as /<skill-name> once installed.
Scope
Skill
Purpose
Workspace
rmc-workspace-overview
First-look audit of a Rust workspace
Workspace
rmc-architecture-rules
Enforce crate-edge rules
Workspace
rmc-dependency-metric
Rank crates by Robert Martin instability / abstractness
Workspace
rmc-imports-exports
Cross-crate imports and exports audit
Workspace
rmc-type-overlaps
Name collisions and module shadows
Workspace
rmc-semantic-overlaps
Duplicate logic detection by embedding similarity
Workspace
rmc-snapshot-diff
Compare two workspace snapshots (e.g. across branches)
Crate
rmc-crate-audit
Audit one crate end-to-end
Crate
rmc-api-surface
Audit a crate's public API
Crate
rmc-module-audit
Audit a single module
Symbol
rmc-find-symbol
Resolve a symbol's qualified name
Symbol
rmc-symbol-forensics
Deep dive on one symbol (refs, callers, attrs)
Symbol
rmc-method-api
Audit a type's methods
Symbol
rmc-trait-audit
Audit a trait and its impls
Symbol
rmc-enum-variants
Inspect an enum's variants
Symbol
rmc-rename-symbol
Preview a rename — exact reference set & refactor probe
Then in Claude Code, type /rmc- to discover them, or /rmc-workspace-overview to kick off a tour of a new repo.
Installation
1. Build the binary
git clone https://github.com/molaco/rust-code-mcp.git
cd rust-code-mcp
cargo build --release
The binary is at target/release/rust-code-mcp.
Build prerequisite: the default embedding backend (Qwen3) runs on Candle
with CUDA, so the build needs the CUDA toolkit (nvcc) on PATH. The
simplest way to get a correct toolchain is the Nix dev shell (see Nix)
— nix develop provides nightly Rust plus the CUDA toolkit, cuDNN, and
cuBLAS. See GPU & CUDA for details.
Once Claude Code starts, the server is running. Use the index_codebase tool to index your project:
> index my codebase at /absolute/path/to/my-rust-project
Or call the tool directly with the directory parameter set to your project root. Pass an optional embedding_profile to choose the embedding model (see Embedding Models); the default is a local GPU model. Indexing is incremental — subsequent runs only process changed files (via Merkle tree change detection). A background sync also re-indexes every 5 minutes automatically, using the profile each index was built with.
4. Start using it
All tools accept a directory parameter pointing to your project root. Examples:
Search code: search with a query like "error handling in parser"
Find definitions: find_definition for a symbol name
Find references: find_references to see all usages of a symbol
Preview a rename: rename_symbol returns the full edit set without touching files
Call graph: get_call_graph or who_calls / calls_from to trace function relationships
Skeleton facade: crate_skeleton writes a stripped mirrored source tree under .skeleton/
Similar code: get_similar_code for semantic similarity search
For Rust-specific workspace analysis, first call build_hypergraph once (reuses a fingerprinted snapshot on subsequent calls), then run audits like unsafe_audit, dead_pub_report, overlaps, crate_dependency_metric, or semantic_overlaps. The codemap tool (build_codemap) produces a Mermaid-renderable subgraph seeded by symbols of interest. The skeleton tool (crate_skeleton) writes generated facade files to <workspace>/.skeleton/, mirroring source-relative paths.
Index data is stored in ~/Library/Application Support/dev.rust-code-mcp.search/ (macOS) or ~/.local/share/search/ (Linux), keyed by a hash of the project path and the active embedding profile — so different profiles get independent indexes. It does not write index/cache data to your project directory; crate_skeleton is the explicit exception and writes generated files under .skeleton/. The persisted hypergraph lives alongside the index data (under graph/<workspace_hash>/, in LMDB). clear_cache with include_hypergraph=true wipes both.
Shared daemon (one server per project)
The stdio transport is 1:1 with its client by construction: every editor window or
agent session spawns its own server process — and with it another copy of the loaded
rust-analyzer context (~2 GB per workspace) plus another ONNX/GPU context. Measured on
one developer machine before this landed: six live servers, 8.9 GB, all analyzing the
same repository.
The runtime state was already shareable (RuntimeState is a bundle of Arcs, semantic
contexts are cached per project path, and locking is per workspace), so the only missing
piece was a transport that accepts more than one client:
running the binary with no arguments makes it a client of a per-project daemon,
spawning that daemon on first use — no change to .mcp.json is needed;
the daemon serves every connection from one shared RuntimeState and exits after
30 minutes with no clients (--idle-secs / RMC_DAEMON_IDLE_SECS, 0 = never);
if the daemon cannot be reached or started, the client serves the session in-process,
exactly as before — the daemon is a memory optimisation, not a new point of failure;
a session whose daemon dies mid-flight ends with exit code 1 and a message on stderr,
instead of continuing quietly in a second server: stdin is already partly consumed, so
a server started afterwards would answer a truncated stream;
a bad RMC_EMBEDDING_PROFILE exits 2 in every mode that serves a session, the client
included, while --help and --print-socket answer normally and exit 0, because they
serve nothing and are how a host reads back the value it got wrong.
The socket key covers the workspace root — the walk upwards takes the nearest
Cargo.toml that declares a [workspace] and stops at a .git or .jj boundary, so two
sessions started in different subdirectories of one repository share a daemon while a stray
outer manifest cannot merge two repositories into one — the binary's size and mtime, the
resolved index root (data_dir()), because it decides where the index lives and is itself
resolved from XDG_DATA_HOME and then HOME, LD_LIBRARY_PATH, because it decides which
CUDA and ONNX libraries the daemon links at start, and every RMC_* and RUST_CODE_MCP_*
variable plus OPENROUTER_API_KEY. The four transport knobs (RMC_DAEMON,
RMC_DAEMON_DIR, RMC_DAEMON_LOG_DIR, RMC_DAEMON_IDLE_SECS) are excluded, because they
choose which daemon a client talks to, or where it logs, rather than what it answers. A rebuilt binary, or a
differently configured one, therefore gets its own daemon instead of silently attaching to
one that answers differently.
The daemon runs in the project root it is keyed on, so every client of one daemon resolves
a relative directory argument the same way, whichever subdirectory its session started
in. A client whose own working directory is a subdirectory therefore sees "." mean the
project root through the daemon and that subdirectory with RMC_DAEMON=0, so prefer
absolute paths in tool arguments.
rust-code-mcp --print-socket # which socket this project resolves to
rust-code-mcp --in-process # previous behaviour (same as RMC_DAEMON=0)
rust-code-mcp --daemon # run the daemon in the foreground
Sockets and locks live in $XDG_RUNTIME_DIR/rust-code-mcp/ (override with
RMC_DAEMON_DIR). Daemon logs live on disk in $XDG_STATE_HOME/rust-code-mcp/logs/,
normally ~/.local/state/rust-code-mcp/logs/ (override with RMC_DAEMON_LOG_DIR), named
after the socket plus a short hash of its full path, with a .log extension, so two
user-named sockets that share a name do not share a log — never in the runtime directory,
which is a small tmpfs meant for sockets. The log is appended rather than replaced, so the crash of an earlier daemon stays readable
after a new one starts, and the daemon truncates it in place before any tracing event that
would take it past 32 MiB, so a long-lived daemon cannot fill a disk however verbose its
RUST_LOG. The bound is read from the file, not counted: panic output, which reaches the
same file through the daemon's raw stderr, counts at the next event. A write the log
cannot take is dropped, never reported through stderr. Either
directory, when it already exists, is used as it is and never re-permissioned; only a
directory the daemon creates gets owner-only access. A symlink, a path that is not a
directory, and a directory owned by another user are refused. Unix only; on other
platforms the server stays in-process.
Embedding Models
Semantic search and the embedding-backed audits (get_similar_code, similar_to_item, semantic_overlaps) run on a configurable embedding profile. Built-in profiles:
Profile
Model
Runtime
Dim
local-cpu-small(default)
BGE-small-en-v1.5
local ONNX/CPU
384
local-gpu-small
Qwen3-Embedding-0.6B
local Candle/CUDA
1024
local-qwen3-4b
Qwen3-Embedding-4B
local Candle/CUDA
2560
local-qwen3-8b
Qwen3-Embedding-8B
local Candle/CUDA
4096
openrouter-qwen3-8b
Qwen3-Embedding-8B
OpenRouter API
4096
The default when embedding_profile is omitted is local-cpu-small. Select another profile by passing embedding_profile to index_codebase and the search tools. Each profile gets its own independent index, and search must use the profile its index was built with.
Set the default with RMC_EMBEDDING_PROFILE — this environment variable names the profile used whenever a call omits embedding_profile, and changing it needs no rebuild:
Built-in profiles only: the canonical names above, their legacy aliases, and any letter case. A profile from embedding_profiles.toml needs the embedding_profile argument instead. Unset or blank keeps local-cpu-small, and whitespace is trimmed. The value is read once at startup, and an unusable name — including a local CUDA profile in a build without CUDA support — stops the server with one stderr line and exit code 2 rather than a silent fall back, because the profile is part of the index identity. Background sync is unchanged: it keeps syncing the indexes already on disk, and only a new local-CUDA index stays out of it.
API models (OpenRouter) require an API key in the environment — keys are never read from config files:
export OPENROUTER_API_KEY=sk-or-...
Adding an API model is a config change, no recompile — drop an embedding_profiles.toml in your project root:
[[profile]]
name = "openrouter-text-embedding-3-small"
model_id = "openai/text-embedding-3-small"
dim = 1536
max_len = 8191
Local models (Candle/ONNX) are code-bound and ship as built-ins. See .docs/configure-models-guide.md for the full TOML schema, OpenRouter tuning knobs, and the trade-offs between models.
Nix
A Nix flake is provided for easy setup:
# Enter dev shell with all dependencies
nix develop github:molaco/rust-code-mcp
# Build the binary
nix build github:molaco/rust-code-mcp
The dev shell includes nightly Rust, ONNX Runtime, and the full CUDA toolchain (toolkit, cuDNN, cuBLAS) needed when you opt into the GPU embedding path.
GPU & CUDA
The default embedding profile (local-cpu-small, BGE) runs on ONNX/CPU and needs no GPU. Local Qwen3 profiles (local-gpu-small, local-qwen3-4b, local-qwen3-8b) run on Candle with CUDA; OpenRouter profiles offload embedding to the API and need no local GPU.
Requirements
NVIDIA GPU — ≥8 GB VRAM is comfortable for the default 0.6B model; the local 8B profile needs ~16 GB
CUDA toolkit (nvcc) at build time — Candle's CUDA backend (cudarc) requires it during compilation
CUDA runtime + cuDNN + cuBLAS libraries at run time
Setup
The Nix dev shell provides the full CUDA build and runtime environment — this is the supported path:
nix develop # nightly Rust + CUDA toolkit + cuDNN/cuBLAS
cargo build --release --features cuda
When using a local CUDA profile and the MCP server is spawned by Claude Code (rather than launched from the Nix shell), its process still needs the CUDA runtime libraries on LD_LIBRARY_PATH. Set them in your MCP client config:
A GPU is not required. The default build and default embedding profile are CPU-only:
cargo build --release
Index and search with the omitted/default profile or embedding_profile = "local-cpu-small" (BGE on ONNX/CPU). OpenRouter profiles also avoid local GPU use, but require OPENROUTER_API_KEY.
Performance
Indexing throughput measured on this repository (~2,280 chunks) with an RTX 3090:
Profile
Indexing throughput
Notes
local-gpu-small (Qwen3-0.6B)
~55 chunks/sec
local GPU; private, deterministic
openrouter-qwen3-8b (Qwen3-8B)
~45–50 chunks/sec
API; per-request latency varies run-to-run
openrouter text-embedding-3-small
~220 chunks/sec
API; fastest, general-purpose (not code-tuned)
Embedding is ~95%+ of indexing time. Larger / higher-quality models embed more slowly; text-embedding-3-small is fastest but not code-tuned. Vector search is exact brute-force KNN — fast at workspace scale regardless of dimension. Pick a profile for your quality / speed / privacy trade-off; see the config guide.
คัดลอกคอนฟิก stdio สำหรับ Cursor, Claude Desktop, Claude Code หรือ VS Code ได้ในหน้านี้ คอนฟิกนี้ใช้การติดตั้งของผู้เผยแพร่ ไม่ใช่พร็อกซีของ PlugMCP
Rust Code ต้องใช้ API key ไหม?
ระเบียนนี้ไม่ได้ระบุตัวแปรสภาพแวดล้อมที่จำเป็น
Rust Code ใช้ทรานสปอร์ตแบบใด?
Rust Code ทำงานแบบโลคัลผ่าน stdio
Rust Code ถูกตรวจสอบล่าสุดเมื่อใด?
ตรวจสอบล่าสุดเมื่อ 2026-10-28 mcp server for rust code semantic hybrid search bm25 + embeddings with tree-sitter, gpu accelerated indexing and merkle tree updates