contextplus-rs
High-performance MCP server for semantic code analysis, written in Rust. Drop-in replacement for the original Context+ TypeScript implementation with 5-20x faster warm queries.
Why Rust?
Server Latency (~9.7K-file monorepo)
Measured on a ~9.7K-file TypeScript/Go monorepo with a warm embedding cache, release build, Intel Core Ultra 7 265 (20 CPUs), Linux. Cold start = first call from a fresh process (three runs, median reported). Warm = median of 20 consecutive calls to the same running server.
Cold start (process boot + cache load included):
Second-call speedup from the running server: explore 551 ms → 7 ms (79×); impact dead 1,814 ms → 43 ms (42×).
Warm per-call latency (subsequent calls, same process):
Memory stays flat across repeated calls: 20 back-to-back outline directory calls hold at 700–750 MiB of process memory (RSS + swap), where the previous glibc-malloc build climbed from 835 to 1,002 MiB.
Historical Comparison (cold-process methodology)
The rows below were collected with a different methodology: each call spawns a fresh process. They are retained as a rough Rust-vs-TypeScript baseline.
* Grep is fast but returns raw matches — see token comparison below.
Token Efficiency (MCP vs Grep)
MCP tools return ranked, structured results. Grep returns raw lines.
20-Search Session Cost
Internal Bottleneck Comparison
Rust eliminates all overhead via:
- Zero-copy cache with
rkyv+memmap2(no deserialization) - SIMD cosine similarity via
simsimd(AVX-512/AVX2 auto-dispatch) - Native tree-sitter (compiled in, no WASM VM) — 16 languages + regex fallback
- Disk-persistent embedding cache with content-hash staleness detection
- Adaptive embedding retry with exponential backoff and cancellation tokens
- Automatic chunking for oversized embedding inputs (chunk → embed → merge)
- Process lifecycle management — idle timeout, parent PID orphan detection, SIGTERM/SIGHUP handling
- jemalloc allocator with background purge via
tikv-jemallocator
Install
Build from source
git clone https://github.com/mrsufgi/contextplus-rs.git
cd contextplus-rs
cargo build --release
The binary is at target/release/contextplus-rs.
Default Ollama setup
The default configuration uses Ollama locally:
ollama pull snowflake-arctic-embed2 # embeddings
ollama pull qwen3.5:9b # chat (for cluster labeling)
Configuration
Providers
Ollama remains the default for both embeddings and cluster-label chat. The binary reads provider credentials only from its process environment; it does not fetch from or integrate with a secrets manager.
OpenAI-compatible embeddings (including vLLM, LiteLLM, and compatible local/proxy servers):
export CONTEXTPLUS_EMBED_PROVIDER=openai
export CONTEXTPLUS_OPENAI_BASE_URL=https://api.openai.com/v1
export CONTEXTPLUS_OPENAI_API_KEY=your-key
export CONTEXTPLUS_OPENAI_EMBED_MODEL=text-embedding-3-small
contextplus-rs --root-dir /path/to/project
Groq chat with embeddings configured independently:
export CONTEXTPLUS_CHAT_PROVIDER=openai
export CONTEXTPLUS_CHAT_BASE_URL=https://api.groq.com/openai/v1
export GROQ_API_KEY=your-groq-key
export CONTEXTPLUS_OPENAI_CHAT_MODEL=your-groq-model
contextplus-rs --root-dir /path/to/project
claude runs one non-interactive Claude Code process per label prompt using the existing Claude Code login. anthropic calls the Messages API directly. All chat providers retain the 90-second timeout and fall back to non-LLM labels on errors.
The Claude adapter uses print/JSON mode, safe mode, an empty strict MCP configuration, no tools or skills, empty settings sources, no session persistence or permission prompts, and a temporary working directory. The installed CLI must support these flags; unsupported flags produce a fallback label. Claude Code's admin-managed policies still apply. --bare is deliberately not used because it disables OAuth login. The child inherits the entire environment without credential inspection and is killed on timeout; its stderr is discarded.
Embedding batching, adaptive context-length retries, chunk-and-merge, prefixes, cancellation, and query caching are shared by both providers. Existing Ollama cache names are unchanged. OpenAI cache names include the provider and a model/base-URL fingerprint so different endpoints or model aliases do not share vectors. Provider errors omit remote response bodies and request URLs to prevent credential disclosure.
Ollama / embedding
Search / indexing
Warmup
Tracker (file-watcher)
Process lifecycle
Usage
As an MCP server (stdio)
contextplus-rs --root-dir /path/to/project
Claude Code integration
Add to your MCP config (~/.claude/mcp.json or project .mcp.json):
{
"mcpServers": {
"contextplus": {
"command": "/path/to/contextplus-rs",
"args": ["--root-dir", "/path/to/project"],
"env": {
"OLLAMA_EMBED_MODEL": "snowflake-arctic-embed2",
"OLLAMA_CHAT_MODEL": "qwen3.5:9b",
"OLLAMA_HOST": "http://127.0.0.1:11434",
"CONTEXTPLUS_EMBED_BATCH_SIZE": "256",
"CONTEXTPLUS_EMBED_TRACKER": "eager"
}
}
}
}
Note:
think: falseis sent automatically to the chat model to avoid slow thinking-mode responses. Models likeqwen3.5:9bproduce cluster labels in <1s with thinking disabled vs 45s+ with thinking enabled.
Git worktrees
One --root-dir (the primary checkout) serves every worktree of the repo. A tool call runs
against the worktree the host process is in (Claude Code's EnterWorktree, Codex started
inside a worktree) or the one an absolute path argument points into; relative paths resolve
from that worktree's root. A worktree is attached on first use, forking the primary's
embedding cache, so no attach_worktree call is needed. The host's cwd is read from
/proc/<parent pid>/cwd, so the automatic part is Linux-only; absolute paths route everywhere.
Each worktree's tracker also watches its gitdir and the shared refs/heads: when HEAD moves
(pull, merge, rebase, checkout, reset) the files that differ between the old and new commit are
re-embedded for that worktree. The contextplus-rs hooks install sentinels remain an optional
fast path; nothing depends on them, which matters when another tool (lefthook, husky) owns
.git/hooks.
CLI subcommands
# Generate MCP config for your editor (claude, cursor, vscode, windsurf, opencode)
contextplus-rs init claude
contextplus-rs init cursor
# Print file skeleton
contextplus-rs skeleton src/main.rs
# Print context tree
contextplus-rs tree --max-tokens 5000
MCP Resources
Tools
Five tools, named for what an agent is doing. Each description says when to use it, so no separate instructions are needed.
The pre-facade names in the last column still dispatch for one release but are not listed.
Architecture
See ARCHITECTURE.md for detailed internals (data flow, caching strategy, performance architecture, and how to add new tools).
src/
main.rs # CLI + MCP server entry point
server_adapters.rs # rmcp ServerHandler impl + tool dispatch
server_definitions.rs # Tool definitions (names, descriptions, JSON schemas)
server_helpers.rs # Shared handler utilities
config.rs # Environment variable configuration
error.rs # ContextPlusError enum (thiserror)
core/
embeddings.rs # OllamaClient + adaptive retry + chunking + cancellation
tree_sitter.rs # Native multi-lang parser (16 languages)
parser.rs # Code symbol extraction + regex fallback for unsupported langs
walker.rs # gitignore-aware file walker (ignore crate)
embedding_tracker.rs # File watcher with lazy/eager/off modes
clustering.rs # Spectral clustering (nalgebra)
hub.rs # Wikilink parser
process_lifecycle.rs # Idle timeout + parent PID orphan detection
safe_path.rs # Path traversal prevention
utils.rs # Shared utilities
tools/ # One file per tool (context_tree supports depth_limit filtering)
git/shadow.rs # Restore points (file-based backup)
cache/rkyv_store.rs # Zero-copy rkyv+mmap VectorStore
Supported Languages (tree-sitter)
TypeScript, TSX, JavaScript, Python, Rust, Go, Java, C, C++, Bash, Ruby, PHP, C#, Kotlin, HTML, CSS
Unsupported file types fall back to regex-based symbol extraction.
Key Crates
Benchmarks (cargo bench)
Nine Criterion benchmark suites cover the critical hot paths — no Ollama dependency, fully reproducible.
Cache Load (rkyv + mmap)
How fast the embedding cache loads from disk. This was the #1 bottleneck in TS (1,109ms raw, 115ms with VectorStore optimization).
At 5K vectors (typical project size), mmap + store build totals ~1.7ms. mmap beats read by 57% at 30K.
Cosine Similarity (simsimd SIMD vs scalar)
Single pair:
Full corpus scan (1024-dim vectors):
30K-vector scan in 4.6ms — well under the 20ms target.
Tree-sitter Parse (native, 16 languages)
Bench covers 10 of the 16 supported languages; the remaining 6 (Ruby, PHP, C#, Kotlin, HTML, CSS) are not benchmarked.
10 languages parsed sequentially in 1.2ms — vs 50-200ms for WASM in TS.
Warm Search Pipeline
End-to-end: disk load → SearchIndex build → search() hybrid scorer (brute-force for ≤50K files, which is the production path for every normal-sized repo).
Warm search on 30K files: 6.4ms. Hash-check (no-op refresh): <1ms.
Blast Radius
Import reachability scan across the symbol-to-file dependency graph.
Context inclusion adds negligible overhead. 2K-file scan in ~2.8ms.
Context Tree
Directory tree with optional symbol resolution and depth limiting.
Depth limiting cuts 5K-file time from 2.9ms to 919µs (3.1x).
Spectral Clustering
K-means over the eigenvector embedding of the similarity graph (nalgebra eigendecomposition).
Identifier Score
Ranking identifiers by semantic relevance against a query vector.
114K-identifier scan (large monorepo) in ~5-6ms.
Walker
gitignore-aware directory traversal (ignore crate).
Development
cargo test # 1835+ tests
cargo bench # 9 Criterion benchmark suites
cargo clippy --all-targets # Lint
cargo fmt --check # Format check
Credits
This is a Rust rewrite of Context+ (TypeScript), originally created by the Context+ community. The Rust port was built from the fix/merge-cache-on-save branch which includes performance optimizations (hash-based cache invalidation, VectorStore extraction, parallel call-site ranking, tree-sitter dedup).
License
MIT