Skip to main content
Glama

Nexus-MCP

PyPI version PyPI downloads PyPI downloads (pypistats) Python 3.10–3.12 License: PolyForm Noncommercial 1.0.0 CI Glama MCP server

Hybrid search + code graph + semantic memory in a single local MCP server. About 90 MB idle and 460 MB with the embedding model loaded (measurements).

Nexus-MCP is a local code-intelligence MCP server for Claude Code, Codex, Cursor and any Model Context Protocol client. It gives AI agents precise, token-efficient answers about your codebase without cloud dependencies: no API keys, no data egress, no subscriptions.

Not sure yet? Measure it on your own code first. It is free, runs offline, calls no LLM and takes a few minutes:

uvx nexus-mcp-ci eval            # the current folder
uvx nexus-mcp-ci eval --example  # or the Nexus-MCP source, if you have no project at hand

You get a verdict ("Strong fit", "Likely fit" or "Weak fit") and the tokens an agent needs to reach the right file, with and without Nexus-MCP. Nothing leaves your machine. See Evaluate on your project.

Ready?

pip install nexus-mcp-ci
claude mcp add nexus-mcp -- nexus-mcp-ci

Results at a glance

Project

Cost: Grep and Read → Nexus-MCP

Turns

Correct answers

Private Python project (460 files)

$0.571 → $0.446 (-22%)

55 → 35

36 → 35 of 36

Django (3,000 files)

$0.894 → $0.863 (-3%)

93 → 71

34 → 36 of 36

Live runs: Claude Code, Claude Sonnet, headless (claude -p), 12 questions per project, 3 runs each, version 2.1.0 (2026-10-28). The same code can vary by about 20% in cost with 3 runs per question (measured 2026-10-28). Search hit@1 on the four retrieval suites: jobscout 0.81, nexus_mcp 0.80, shop_repo 0.50, flask 0.64.

nexus-mcp-ci eval (the free offline check, no LLM) on 3 projects:

Project

Verdict

Median tokens to the right file

Right file first

Chromium base/ (3,638 files)

Strong fit

17,998 → 950

40% → 70%

pallets/click (91 files)

Strong fit

24,774 → 1,301

27% → 97%

Nexus-MCP source (59 files)

Likely fit

5,385 → 1,234

70% → 93%

All numbers: benchmarks page · JSON · CSV.


The Problem It Solves

An agent that explores a codebase with Grep and Read pays for every call: each call is one more turn, and each turn reads the whole conversation again. Nexus-MCP answers a question about code in fewer calls. search returns the whole code of its top results, find_symbol returns the code of several names in one call, and graph returns every text reference beside the call edges.

The north star of the project is lower token use with the same accuracy. It is measured, not estimated: see Token efficiency and benchmarks/.


Related MCP server: embecode

Evaluate on your project

Decide with your own numbers. The quick check is free, offline and calls no LLM:

uvx nexus-mcp-ci eval                                   # the current folder
uvx nexus-mcp-ci eval path/to/project                   # another folder
uvx nexus-mcp-ci eval https://github.com/pallets/flask  # a public repo (shallow clone)
uvx nexus-mcp-ci eval --example                         # the Nexus-MCP source, offline

It asks about 30 questions made from your own code and compares Grep and Read with one Nexus-MCP search. The right answer is the file that defines the symbol, from the parser, never from the Nexus-MCP ranking. On this repo's source (59 files) it gave "Likely fit": 1,234 median tokens to reach the right file against 5,385, and the right file first in 93% of questions against 70%. On Chromium's base/ (3,638 files of C++, Java and Python) it gave "Strong fit": 950 median tokens against 17,998 (95% fewer), and the right file first in 70% of questions against 40%. On pallets/click (91 files, from its URL) it gave "Strong fit": 1,301 median tokens against 24,774 (95% fewer), and the right file first in 97% of questions against 27%. On a tiny project Grep and Read win, and the report says so.

Want the real agent cost? The agent tier runs your own Claude Code or Codex login on tasks from your code, with and without Nexus-MCP. It shows the cost first and asks:

nexus-mcp-ci eval --agent claude                       # Claude Sonnet
nexus-mcp-ci eval --preset efficient --agent claude:haiku,claude:sonnet

Results are saved in ~/.nexus-mcp/evals/ (eval list, eval show <id>). eval share <id> posts the numbers (no code, no paths) to the community table, only after you say yes. Full guide: docs/EVALUATE.md. In Claude Code, the plugin adds /nexus-mcp:evaluate.


Privacy and Data

Your code stays on your machine. The server makes no network calls and has no telemetry.

  • In: the package from PyPI, and the embedding model from Hugging Face once (set NEXUS_OFFLINE=1 after that). eval <url> also runs a git clone.

  • Out: nothing, unless you run eval --agent (prompts and code go to Anthropic or OpenAI through your own CLI) or eval share (numbers only). Both ask first.

  • Stored: the index, memories and graph in <project>/.nexus/ (it holds code: add it to .gitignore), the model in ~/.cache/huggingface/, eval results in ~/.nexus-mcp/evals/.

nexus-mcp-ci data where              # every location and its size
nexus-mcp-ci data clear --all        # dry run; add --yes to delete

Every location, the shell commands that do the same, and the uninstall steps: docs/PRIVACY.md.


Quickstart (60 seconds)

# 1. Install
pip install nexus-mcp-ci

# 2. Register with Claude Code
claude mcp add nexus-mcp -- nexus-mcp-ci

# 3. Verify: start Claude Code in your project and run /mcp
#    nexus-mcp should be listed as connected

The server sends its own usage instructions when it connects, so Claude knows which tool answers which question without any extra setup. Ask something like "where do we retry failed payments?" and it will call search (and index once, the first time in a project).

The plugin registers the server and adds a short routing skill that teaches Claude when to reach for each tool. Install the Python package first, then:

/plugin marketplace add jaggernaut007/Nexus-MCP
/plugin install nexus-mcp@nexus-mcp

Do not register the server twice. If you use the plugin, skip the claude mcp add line. Tool names differ by route: mcp__nexus-mcp__search after claude mcp add nexus-mcp, mcp__plugin_nexus-mcp_nexus-mcp__search through the plugin. The rest of this README writes bare names (search) and you do not need the prefix in prompts.

Optional: project instructions

If you want the rules in your own project file, a short block is enough:

## Code Navigation

The nexus-mcp server is connected. For questions about how this codebase works:
- No setup call is needed; run `index` only if a tool says the project is not indexed.
- Use `search` to find code, `find_symbol` for symbols, `map` for structure.
- Use `graph` with `transitive=true` before you change a shared function. The call graph
  is static, so also use `search` for dynamic call sites.
- Use plain grep or read when you already know the file or the exact string.

The full routing guide is in docs/AGENT_ROUTING.md.


How It Works

Indexing Pipeline (8 steps)

Source files
    │
    ├─ Step 1: Discover ──────── walk tree, filter by ext/size/.gitignore
    │
    ├─ Step 2: Parse symbols ─── tree-sitter (parallel ThreadPool)
    │           extracts: functions, classes, methods
    │           captures: name, signature, docstring, line_start/end, language
    │
    ├─ Step 3: Parse graph ────── ast-grep (sequential for consistency)
    │           extracts: containment edges, import edges, callee names of each function
    │                     (indexing/call_resolver.py makes CALLS edges after the last batch;
    │                      no inheritance edges yet)
    │           output: UniversalGraph — nodes: modules, functions, classes;
    │                                    edges: CONTAINS, IMPORTS, CALLS
    │
    ├─ Step 4: Transfer graph ── populate rustworkx PyDiGraph
    │           O(1) node lookup by name, Rust-backed traversal
    │
    ├─ Step 5: Chunk ──────────── Symbol → CodeChunk
    │           deterministic IDs: SHA256(file_path + symbol_name + line)
    │           avoids duplicate inserts on incremental reindex
    │
    ├─ Step 6: Embed ──────────── bge-small-en: 384-dim
    │           lazy-loaded, unloaded after indexing (try/finally)
    │           GPU/MPS auto-detected; falls back to CPU
    │
    ├─ Step 7: Store ──────────── write to LanceDB `chunks` table (12-col PyArrow schema)
    │           rebuild native FTS (Tantivy) index after write
    │
    └─ Step 8: Cleanup ────────── unload model, persist metadata (mtimes for incremental)
                                  save rustworkx graph to SQLite (warm-start recovery)

Incremental reindex: mtime-based — only changed files are re-processed. Corrupt index detection triggers automatic full rebuild.

Search Pipeline

search("how does auth work")
         │
         ├─► vector_engine.search(query, n=30)  ← cosine similarity on 384-dim (bge-small-en) embeddings
         │                                         "auth" finds "verify_credentials", "token_check"
         │
         ├─► bm25_engine.search(query, n=30)    ← Tantivy FTS on same LanceDB table
         │                                         fast exact-keyword matching
         │
         ├─► graph_engine.boost(query, n=30)    ← structural relevance score
         │                                         hub symbols (high in/out degree) boosted
         │
         └─► fusion.merge(v_results, b_results)
                  │
                  │  Reciprocal Rank Fusion: score = Σ weight_i / (k + rank_i)
                  │  default weights: vector=0.5, bm25=0.3
                  │
                  └─► token_budget.truncate()   ← summary / detailed / full
                           │
                           └─► Top-N chunks, scored, formatted

Technology Stack

Layer

Technology

Decision Rationale

Vector store

LanceDB

mmap disk-backed → ~20–50 MB overhead vs ChromaDB's in-memory model. Native Tantivy FTS means one store for both vector and BM25. (ADR-002)

Embeddings

bge-small-en

Lightweight (384-dim, no trust_remote_code). Lazy-load/unload keeps RAM flat after indexing. (ADR-003)

Graph engine

rustworkx PyDiGraph

Rust-backed, O(1) node lookup, PageRank + centrality algorithms. Thread-safe with RLock. (ADR-006)

Symbol parser

tree-sitter 0.21.3

25+ languages, incremental parsing, AST-level symbol extraction with metadata. Parallel via ThreadPool. (ADR-005)

Graph parser

ast-grep

Structural matching for containment and import edges (call edges are resolved after indexing; inheritance edges are not extracted yet). Sequential run for graph consistency. (ADR-005)

Chunking

Symbol-based

One chunk per function/class. Deterministic SHA256 IDs prevent duplicate inserts. (ADR-008)

Persistence

SQLite + LanceDB

Graph in SQLite (warm-start recovery), vectors+FTS in LanceDB, mtimes in JSON. Zero-config.

MCP framework

FastMCP 2.0

Stdio transport, automatic tool registration, schema generation.


Token Efficiency

Two live runs, 12 questions each, with Claude Sonnet, 3 runs for each question and condition (72 runs each) and the prompt cache warm for both conditions. The result depends on the project.

Latest (commit a67e5b4, find_symbol shows an outline of a long class): the numbers are in Results at a glance, generated from benchmarks/published/results.json.

Only the nexus condition ran again for this commit; the Grep and Read numbers are the stored runs below. On django nexus is cheaper in total but still dearer on 8 of 12 questions (the short lookups). The one wrong answer on the private project came from a run that did not call find_symbol. With 3 runs for each question the same code can vary by about 20% in cost (measured 2026-10-28), so the change against the runs before the outline is inside the noise; all three recent nexus runs on the private project cost less than Grep and Read ($0.446 to $0.531 against $0.571), and on django nexus is about equal ($0.863 to $0.973 against $0.894). Details: docs/EVALS.md. The sections below are the runs before the outline.

Private 460-file Python project (2026-10-08, commit 91a338e):

Grep and Read

Nexus-MCP

Turns (sum of the median of each question)

55

34 (-38%)

Output tokens

11,917

8,881 (-25%)

Cost

$0.571

$0.459 (-20%)

Correct answers

36 of 36

36 of 36

Prompt on the first turn

12,700 tokens

16,300 tokens

Nexus is cheaper on 7 questions, equal on 3 and dearer on 2 (the two impact questions, by $0.005 and $0.004). The saving comes from fewer turns and from smaller tool results than the whole-file reads of Grep and Read (see the cost split below). Per question numbers are in docs/EVALS.md. The same questions on 2026-10-07 (before exact names, code in find_symbol, the smaller tool list and the warm-up call) gave nexus 6% more cost than Grep and Read.

Django, a public repository (3,000 files; 2026-10-27, commit 3fa8810):

Grep and Read

Nexus-MCP

Turns (sum of the median of each question)

93

78 (-16%)

Output tokens

32,291

27,513 (-15%)

Cost

$0.894

$0.973 (+8.9%)

Correct answers

34 of 36

35 of 36

On django nexus uses fewer turns but costs more. It is cheaper on the questions where Grep and Read need 18 to 24 turns (SQLCompiler.as_sql -21%, URLResolver.resolve -10%) and dearer on short lookups that take 2 or 3 turns. Django is a repository that the model knows from training, so part of this measures the model. One ground truth was corrected after the run (a question asked for a value, and the scorer also required a file name); details in docs/EVALS.md.

Why: the cost split of the traces. The parts below add up to the reported cost of each run.

Change, nexus against Grep and Read

django

jobscout

Cache read (the prompt, on each turn)

+$0.011

-$0.002

Cache write (tool results and the first message)

+$0.111

-$0.078

Output

-$0.042

-$0.033

Total

+$0.079

-$0.112

  • The 3,600 extra prompt tokens on each turn cost little: a cache read is $0.20 per million.

  • Tool results are written to the cache at $4 per million, 20 times the read price. So the size of a result matters more than the size of the prompt.

  • On jobscout the agent does not know the code and reads whole files (10,000 characters for each Read). A find_symbol result of 9,700 characters replaces a Read and saves a turn.

  • On django the agent knows the code and reads short line ranges (3,400 characters for each Read). A find_symbol result of 9,200 characters is larger than the read that it replaces. A class lookup sends up to 6,000 characters of the class body: CharField cost $0.023 with nexus and $0.013 with Grep.

This led to the outline for a class in find_symbol (methods and lines, not the body). With it, nexus on django went from $0.973 to $0.863 (see "Latest" above). Details and the other candidates are in docs/EVALS.md.

The saving is large on a project that the model does not know and small on one that it knows. Run benchmarks/ on your own project before you rely on it.


The 8 Tools

New in v2.2.0 (no breaking change): nexus-mcp-ci eval measures Nexus-MCP on your own code (Evaluate), and nexus-mcp-ci data shows and clears every stored file (Privacy). The MCP tools do not change.

v2.1.0 breaking change: explain and health are removed from the MCP tool list, and several rarely used parameters are removed from the schemas (see CHANGELOG). find_symbol now returns the code with callers and callees.

v2.0.0 breaking change: find_callers/find_callees/impact merged into graph, overview/architecture merged into map, and remember/recall/forget merged into memory — see CHANGELOG for the old→new mapping and ADR-017 for why. Fewer, richer tools route better under MCP Tool Search than many thin ones.

Discovery & Indexing

Tool

Use When

index(path)

First action in any session. Supports comma-separated multi-folder paths. Incremental by default, reports progress as it runs, and starts a debounced auto-reindex watcher (NEXUS_AUTO_WATCH) when it finishes.

status()

Check index health: symbol count, chunk count, memory usage, engine availability, and a stale/staleness_warning pair if files changed since the last index.

map(detail)

Replaces ls + manual browsing. detail="summary" (files/languages/quality/top-modules, was overview()), "architecture" (layers/dependencies/classes/entry points/hub symbols, was architecture()), or "full" for both.

Tool

Use When

search(query, limit)

Primary code discovery. A query can hold exact names (parse_config RetryPolicy): their definitions come first. The top 3 results carry the whole symbol; the others show the signature. Falls back to live grep if results are sparse. Returns a warning if the index looked stale (a background reindex starts).

Graph Analysis

Tool

Use When

find_symbol(symbol_name, exact)

Look up one or more symbols by name (several names in one call): the code of each, with its callers and callees by name. exact=False for fuzzy matching.

graph(symbol_name, direction, transitive)

direction="callers" (who calls this, was find_callers) or "callees" (what this calls, was find_callees). transitive=True (was impact()) gives the transitive change blast radius before a refactor (a lower bound: static edges only). It requires direction="callers"; direction="callees" with transitive=True returns an error. Call edges are static and name-based, so use search for call sites the graph cannot see (see Known Limitations).

analyze(path)

Code quality: cyclomatic complexity, cognitive complexity, code smells, dependency metrics.

Memory

Tool

Use When

memory(action, content, query, tags, memory_id)

action="store" to persist a decision or note across sessions; "search" for semantic retrieval; "delete" to remove by ID or tag. With NEXUS_PERMISSION_LEVEL=read only search is allowed.


Install

pip install nexus-mcp-ci

# GPU (CUDA) support — adds ONNX CUDA execution provider
pip install nexus-mcp-ci[gpu]

From Source

git clone https://github.com/jaggernaut007/Nexus-MCP.git
cd Nexus-MCP
./setup.sh           # creates venv, installs, verifies
# or
pip install -e ".[dev]"

Python 3.10–3.12 supported. Python 3.13+ is not yet supported by the current dependency stack, and the packaged Glama/Docker build uses Python 3.12 for compatibility. Optional: rg (ripgrep) for 100% search coverage fallback on unindexed files.

The deprecated jina-code model requires ONNX Runtime. If you see ONNX/Optimum errors:

pip install "sentence-transformers[onnx]" "optimum[onnxruntime]>=1.19.0"

The default bge-small-en model needs neither ONNX nor trust_remote_code.


MCP Client Setup

Claude Code

# Minimal
claude mcp add nexus-mcp -- nexus-mcp-ci

# GPU embeddings
claude mcp add nexus-mcp -e NEXUS_EMBEDDING_DEVICE=cuda -- nexus-mcp-ci

# Virtualenv install — pass the full binary path
claude mcp add nexus-mcp -- /path/to/.venv/bin/nexus-mcp-ci

Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json (macOS):

{
  "mcpServers": {
    "nexus-mcp": {
      "command": "nexus-mcp-ci",
      "args": [],
      "env": {
        "NEXUS_EMBEDDING_DEVICE": "cpu"
      }
    }
  }
}

Cursor / Windsurf / Cline / Any MCP Client

{
  "nexus-mcp": {
    "command": "nexus-mcp-ci",
    "transport": "stdio"
  }
}

Agent Integration Patterns

CLAUDE.md boilerplate (drop into project root)

## Code Intelligence — nexus-mcp

1. **Session start**: no setup call. Run `index` only if a tool says "No codebase indexed"
2. **To find code you cannot name**: `search`, before reading files one by one
3. **To see symbols you can name**: `find_symbol`, with all the names in one call
4. **Before refactoring**: `graph` with `transitive=true` to assess blast radius. The call graph is static (no dynamic dispatch), so also use `search` to find call sites.
5. **For project orientation**: `map` with `detail="summary"` or `detail="architecture"`
6. **When you know the exact file or string**: plain grep or read is fine

Typical agent tool-call sequence

# Session start
status()               → "indexed: True, 8,412 chunks, 1,203 symbols, 87 MB"

# Code discovery
search("JWT token validation", limit=10)
  → auth/jwt.py:42  validate_token()         score=0.94
  → auth/middleware.py:18  require_auth()    score=0.87
  → tests/test_auth.py:91  test_valid_jwt()  score=0.81

# Symbols you can name
find_symbol("validate_token, require_auth")
  → the code of each, file and lines
  → callers: [post_order, ...], callees: [...]

# Pre-refactor safety check
graph("validate_token", direction="callers", transitive=True)
  → total_impacted: N          # a lower bound: dynamic calls are not visible

# Locate call sites with search instead
search("validate_token(", mode="bm25")
  → auth/middleware.py:18  require_auth()  ...
  → auth/login.py:57  login_required()     ...

Multi-folder monorepo indexing

# Index multiple roots in one call — processed sequentially, shared engines
index(path="packages/api/src,packages/shared/src,packages/cli/src")

# Or use the paths parameter for additional roots
index(path="packages/api/src", paths="packages/shared/src,packages/cli/src")

Configuration

All settings via NEXUS_ environment variables:

Variable

Default

Description

NEXUS_EMBEDDING_MODEL

bge-small-en

bge-small-en (384-dim). jina-code is deprecated

NEXUS_EMBEDDING_DEVICE

auto

auto (CUDA → MPS → CPU), cuda, mps, cpu

NEXUS_STORAGE_DIR

.nexus

Index storage directory

NEXUS_AUTO_WATCH

true

Auto-reindex on file change via a debounced watcher, started after index()

NEXUS_AUTO_RESTORE

true

A new server process reattaches to the stored index, so status is already indexed: true

NEXUS_WARM_START

true

Attach the index and load the embedding model in the background while the server starts, so the first search does not wait about 7 s. The model then uses its memory (about 370 MB) from the start of each session

NEXUS_STALENESS_CHECK_INTERVAL

15

Seconds between status()/search() staleness checks (throttled, not per-call)

NEXUS_MAX_FILE_SIZE_MB

10

Skip files larger than this

NEXUS_CHUNK_MAX_CHARS

4000

Max chars per code chunk

NEXUS_EMBEDDING_BATCH_SIZE

32

Chunks per embedding batch. Lower it to cut peak RAM while indexing

NEXUS_INDEX_FILE_BATCH_SIZE

50

Files per streaming indexing batch

NEXUS_GRAPH_MAX_DEPTH

10

Max depth for transitive graph queries

NEXUS_MAX_MEMORY_MB

350

Memory budget target

NEXUS_SEARCH_MODE

hybrid

hybrid, vector, or bm25

NEXUS_FUSION_WEIGHT_VECTOR

0.5

Vector score weight in RRF

NEXUS_FUSION_WEIGHT_BM25

0.3

BM25 score weight in RRF

NEXUS_PERMISSION_LEVEL

full

full, read, or restricted

NEXUS_RATE_LIMIT_ENABLED

false

Enable per-tool token-bucket rate limiting

NEXUS_AUDIT_ENABLED

true

Structured audit logging with correlation IDs

NEXUS_TRUST_REMOTE_CODE

true

Needed only by the deprecated jina-code; set false with bge-small-en

NEXUS_LOG_LEVEL

INFO

Logging level

NEXUS_LOG_FORMAT

text

text or json

The Python package defaults to bge-small-en. The Dockerfile, smithery.yaml and glama.json use the same default. jina-code still loads, with a warning, so an existing index keeps working. It is deprecated and will be removed in a future major release.

Embedding Models

Model

Key

Dims

Max Seq

Backend

trust_remote_code

BGE Small EN v1.5 (default)

bge-small-en

384

512

PyTorch

No

Jina Embeddings v2 Code (deprecated)

jina-code

768

8,192

ONNX

Yes

After changing model, re-index. Embeddings from different models are incompatible.


Comparison

vs. other code-intelligence MCP servers

Features only, from each project's own documentation. We make no performance claims about other tools: run nexus-mcp-ci eval on your code instead.

Feature

Nexus-MCP

Serena

claude-context

How it finds code

hybrid: vectors + BM25

language servers (LSP)

hybrid: vectors + BM25

Where the index lives

local LanceDB

none (live LSP)

a vector database (Milvus or Zilliz Cloud)

Embeddings

local model, no API key

none

an embedding provider (OpenAI by default; Ollama possible)

Call graph

static, 6 languages

LSP references

no

Project memory

yes

yes

no

Edits code

no (read only)

yes

no

Measure it on your repo

nexus-mcp-ci eval

—

—

Other servers in this space include codebase-memory-mcp, code-graph-rag, code-index-mcp and ast-grep-mcp. Check each project's docs. Corrections to this table are welcome.

vs. Other MCP Servers

Feature

Nexus-MCP

Sourcegraph MCP

Greptile MCP

GitHub MCP

tree-sitter MCP

Fully local / private

✅

❌ infra required

❌ cloud

❌ cloud

✅

Semantic (vector) search

✅

❌ keyword only

✅ LLM-based

❌

❌

Keyword (BM25) search

✅

✅

—

✅

❌

Hybrid fusion (RRF)

✅

❌

❌

❌

❌

Code graph (call/import)

static: containment, imports, calls

✅ SCIP

❌

❌

❌

Semantic memory (persistent)

✅ 6 types

❌

❌

❌

❌

Change impact analysis

static callers only

partial

❌

❌

❌

Token-budgeted responses

✅ 3 levels

❌

❌

❌

❌

Languages

25+

30+

many

many

many

Cost

Free noncommercial; paid commercial license

$$$

$40/mo

$10–39/mo

Free

API keys required

No

Yes

Yes

Yes

No

vs. AI Code Tools

Capability

Nexus-MCP

Cursor

Copilot @workspace

Cody

Continue.dev

Aider

IDE-agnostic

✅

❌

❌

❌

❌

✅

MCP-native

✅

partial

❌

❌

✅ client

❌

Fully local

✅

partial

❌

partial

✅

✅

Hybrid search

✅

unknown

unknown

keyword

yes

❌

Code graph

partial (containment + imports)

unknown

unknown

✅ SCIP

basic

❌

Semantic memory

✅ persistent

❌

❌

❌

❌

❌

Token-budgeted output

✅

—

—

—

—

—

Source available

✅ PolyForm Noncommercial

❌

❌

partial

✅

✅

Cost

Free noncommercial; paid commercial license

$20–40/mo

$10–39/mo

$0–49/mo

Free

Free


Development

git clone https://github.com/jaggernaut007/Nexus-MCP.git
cd Nexus-MCP
pip install -e ".[dev]"

pytest -v                    # all tests (-m "not slow" skips the performance ones)
pytest -m "not slow"         # skip performance benchmarks
pytest tests/test_hybrid_search.py  # single module
ruff check .                 # lint

Project Structure

src/nexus_mcp/
├── server.py              # FastMCP entrypoint — 10 thin tool wrappers (permission guard, rate limit, audit), graceful shutdown
├── core_api.py            # Transport-agnostic tool logic + input validation; importable without MCP
├── config.py              # Settings (NEXUS_ env prefix)
├── state.py               # Global singleton SessionState
├── core/
│   ├── models.py          # Symbol, ParsedFile, CodebaseIndex, Memory
│   ├── graph_models.py    # UniversalNode, Relationship
│   ├── interfaces.py      # IParser, IEngine protocols
│   └── exceptions.py      # NexusException hierarchy
├── parsing/
│   ├── treesitter_parser.py   # Symbol extraction (parallel)
│   ├── astgrep_parser.py      # Structural graph extraction (sequential)
│   ├── language_registry.py   # 25+ language definitions
│   └── file_watcher.py        # Debounced watchdog for live reindex
├── engines/
│   ├── vector_engine.py   # LanceDB cosine similarity search
│   ├── bm25_engine.py     # LanceDB native FTS (Tantivy)
│   ├── graph_engine.py    # rustworkx PyDiGraph with RLock
│   ├── live_grep.py       # rg/grep fallback for unindexed or sparse results
│   ├── fusion.py          # Reciprocal Rank Fusion
├── indexing/
│   ├── pipeline.py        # 8-step indexing pipeline
│   ├── embedding_service.py   # ONNX Runtime, GPU/MPS auto-detect
│   ├── parallel_indexer.py    # ThreadPool over files
│   └── chunker.py         # Symbol → CodeChunk with deterministic IDs
├── memory/
│   └── memory_store.py    # LanceDB-backed memory, TTL, 6 types
├── analysis/
│   └── code_analyzer.py   # Cyclomatic/cognitive complexity, smells
├── formatting/
│   ├── token_budget.py    # Token counting and truncation
│   └── response_builder.py  # Structured responses
├── persistence/
│   └── store.py           # SQLite graph persistence
├── security/
│   ├── permissions.py     # READ/MUTATE/WRITE tool categories
│   └── rate_limiter.py    # Token-bucket, per-tool, thread-safe
└── middleware/
    └── audit.py           # Structured audit logs, correlation IDs, field redaction
benchmarks/                # Token-efficiency benchmark harness (ADR-018)
self_test/                 # End-to-end demo of the 8 tools and the Python API
plugin/                    # Claude Code plugin: registers the server + routing skill

Adding a New Tool

  1. Add the logic as a plain function in core_api.py

  2. Validate input there with the validate_* helpers (validate_path, validate_symbol_name, validate_query)

  3. Add a thin wrapper in server.py decorated with @mcp.tool() and @_audited, and call _guard("<tool>") first

  4. Add the permission category to security/permissions.py

  5. Write tests in tests/

  6. Update self_test/demo_mcp.py to exercise the tool

Adding a New Language

  1. Add entry to parsing/language_registry.py with the tree-sitter grammar

  2. Add structural patterns to parsing/astgrep_parser.py for call/import extraction

  3. Add test fixtures in tests/fixtures/


Self-Test

Verify your installation exercises the 8 tools and the Python API end-to-end:

python self_test/demo_mcp.py                   # built-in sample project
python self_test/demo_mcp.py /path/to/project  # your own codebase

Expected output: the 8 tools and the Python API exercised with pass/fail per tool and a summary.


Known Limitations

  • Sequential graph parsing: ast-grep runs sequentially (not parallel) to keep the call graph consistent. This is the main indexing bottleneck on large codebases.

  • bge-small-en uses PyTorch: The default model runs on PyTorch, not ONNX.

  • No incremental graph updates: Graph is rebuilt in full on incremental reindex (only vector/BM25 are incremental at the chunk level).

  • No SSE transport: Only stdio transport is currently supported.

  • Language coverage: 25+ languages get tree-sitter symbol extraction. ast-grep structure (functions, classes, imports) covers Python, JavaScript, TypeScript, Go, Java, and Rust; other languages have no graph structure.

  • Call edges are static and name-based. graph() and the callers/callees fields of find_symbol() come from parsing, not runtime tracing. Edges exist for Python, JavaScript, TypeScript, Go, Java and Rust. A call resolves only when the callee is in the same file, in an imported file, or has a unique name in the index. A name shared by several definitions gets no edge, so results are a lower bound. Dynamic dispatch, monkey-patching, callbacks and reflection do not show up, so treat graph(transitive=True) as a lower bound on blast radius.

  • Complexity is approximate: cyclomatic complexity counts branch points per function. Only Python counts and/or, and only Python functions get docstrings on graph nodes.

  • Auto-reindex has a detection lag: with the file watcher enabled (default), edits are picked up after a short debounce, and status()/search() run a throttled staleness check as a backstop — not an instant, per-call guarantee of freshness.


Architecture Decision Records

Key decisions are documented in docs/adr/:

ADR

Decision

ADR-001

Merge two MCP servers into one

ADR-002

LanceDB over ChromaDB

ADR-003

ONNX Runtime over PyTorch for embeddings

ADR-004

bge-small-en as default embedding model

ADR-005

Dual parser: tree-sitter + ast-grep

ADR-006

rustworkx for graph algorithms

ADR-007

12-column PyArrow schema for LanceDB

ADR-008

Symbol-based chunking with deterministic IDs

ADR-009

8-step indexing pipeline

ADR-010

Graph tools API: serialization, ambiguity handling

ADR-011

Graceful shutdown, corruption recovery, JSON logging

ADR-012

READ/MUTATE/WRITE permission categories

ADR-013

Pydantic v2 I/O schemas — superseded by ADR-016 (never wired in, deleted)

ADR-014

Token-bucket rate limiting (off by default)

ADR-015

Auto-watch + throttled staleness detection

ADR-016

Removal of unused Pydantic schemas (supersedes ADR-013)

ADR-017

Tool consolidation 15→10, action-aware permission categories

ADR-018

Token-efficiency benchmark harness (benchmarks/)


Documentation

Start at the docs index.


Acknowledgments

Nexus-MCP consolidates two earlier open-source projects:

  • CodeGrok MCP by rdondeti (Ravitez Dondeti, MIT) — Contributed the symbol extraction pipeline, embedding service, parallel indexer, core data models, and memory retrieval system.

  • code-graph-mcp by entrepeneur4lyf — Contributed the ast-grep structural parser, rustworkx graph engine, complexity analysis, and relationship extraction.

Source files retain "Ported from" attribution in their module docstrings. See ADR-001 for the consolidation rationale.


License

PolyForm Noncommercial License 1.0.0. Free to use, copy, modify, and distribute for any noncommercial purpose. Commercial use requires a separate license — contact Shreyas Jagannath to inquire.

Versions published before 2.0.1 (0.1.0, 0.1.1, 2.0.0) remain available under their original MIT terms for anyone who obtained them under that license.

Available Tools

8 tools
analyzeAnalyze code qualityA
Read-onlyIdempotent

Review the quality of the indexed project, or of one path: the most complex functions, long functions, large classes, dead code (functions with no static caller), module dependencies and an overall quality score. Use it for "what is the most complex code", "review this directory", "is there dead code". Pass path (relative) to limit it to a directory or file. Read-only. Complexity is cyclomatic and approximate, and dead-code entries have no static caller, so check them before you delete anything.

NameRequiredDescriptionDefault
pathNoOptional relative path to filter analysis (subdirectory or file)

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, and the description earns credit for adding caveats the annotations cannot: complexity is cyclomatic and therefore approximate, and dead-code entries merely lack a static caller, so they must be verified before deletion. 'Read-only' restates the annotation but the accuracy and deletion-safety warnings are real added value.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose, then use cases, then the parameter, then the caveats; each sentence carries a distinct payload. The only slack is the redundant 'Read-only' clause already covered by annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be described, and the description covers scope, filtering, what analyses are produced and their accuracy limits. For a zero-required-parameter, read-only analysis tool this is everything an agent needs before calling it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single `path` parameter is already documented as an optional relative path filtering a subdirectory or file. The description's 'Pass `path` (relative) to limit it to a directory or file' largely restates that, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('review the quality of the indexed project, or of one path') and enumerates the concrete outputs: complex/long functions, large classes, dead code, module dependencies, overall score. It does not name any sibling tool, so the agent has to infer the boundary with `graph` or `map` on its own, which keeps it just short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete trigger phrases ('what is the most complex code', 'review this directory', 'is there dead code') and tells the caller to pass `path` to narrow to a directory or file. No exclusion or 'use X instead' guidance is given, so nothing prevents overlap confusion with `graph` or `search`, but the positive usage context is strong.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_symbolFind a symbolA
Read-onlyIdempotent

Look up functions, classes, methods or constants by name and get the whole code of each, with file, line range and direct callers and callees. Use it when you know the name ("show me create_order", "where is TokenBudget defined"), and in place of a file read or a grep for a definition. Pass several names in one call. A class longer than 2,000 characters gives an outline (its head and one line per method); ask for "Class.method" for a method's code. Other code longer than 6,000 characters is cut and marked (truncated). Set exact=false to match part of a name when you are unsure of the spelling. For every use of a name, use graph.

NameRequiredDescriptionDefault
exactNoTrue for exact match, False for fuzzy substring
symbol_nameYesOne name, or several with commas (e.g. 'create_server, TokenBudget, MAX_RETRIES')

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, so safety is covered; the description instead discloses non-obvious behaviors: classes over 2,000 chars return an outline, other code over 6,000 chars is cut and marked `(truncated)`, and several names can be batched in one call. It doesn't mention ordering or how missing symbols are reported, which is a minor gap given an output schema exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the verb and the returned artifact, then usage examples, then caveats. Dense but nearly every clause carries information; the truncation thresholds are the only slightly over-specified part, though they change how the agent interprets output.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter read tool with a full output schema, the description covers purpose, alternatives, batching, naming patterns and output truncation. Nothing an agent needs in order to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% (both parameters documented), so the baseline is 3. The description adds real meaning on top: comma-separated multi-name batching, the "Class.method" addressing form for method bodies, and the intent behind exact=false (match part of a name when unsure of spelling).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource (look up functions, classes, methods or constants by name) plus what comes back (code, file, line range, callers and callees). It explicitly separates itself from file reads/greps and points to `graph` for a different question, so an agent can distinguish it from siblings without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete when-to-use triggers ("show me create_order", "where is TokenBudget defined") and a when-not-to-use rule: use `graph` for every use of a name, and don't use a file read or grep for a definition. Alternatives are named explicitly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

graphCall graphA
Read-onlyIdempotent

Trace the call graph: who calls a function (direction='callers') or what it calls (direction='callees'). With transitive=true it lists everything that depends on the symbol, directly or indirectly; use that before you change a signature or rename, move or delete a shared function ("what breaks if I change X"). A callers result also has references: every use of the name as a whole word (source lines with their code, test files as counts), so you do not need a grep to check it. Edges are static, so calls through callbacks, reflection or dynamic dispatch are missing from the caller list; references still shows those lines.

NameRequiredDescriptionDefault
directionNo'callers' (who calls this) or 'callees' (what this calls)callers
transitiveNoTrue = everything that depends on the symbol, directly or indirectly. Use before changing a shared symbol. Only valid with direction='callers'.
symbol_nameYesName of the function/symbol to trace

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, and the description goes further with the key limitation: edges are static, so callbacks, reflection and dynamic dispatch are absent from caller lists, while `references` still surfaces those lines. It also pre-announces the shape of a callers result (whole-word source lines, test files as counts), which is exactly the context an agent needs before trusting the output.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core verb and both directions, then the risk-analysis use case, then the caveat — a sensible ordering. The parentheticals ('what breaks if I change X', the grep aside) are dense but each carries information, so no sentence is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-param read-only analysis tool with an output schema, this covers everything: what it returns for each direction, the extra `references` channel, and the static-analysis blind spot. Nothing an agent needs to call it correctly or interpret results is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline is 3, but the description adds real meaning: it spells out the consequence of transitive=true ('everything that depends on the symbol, directly or indirectly') and the purpose of the references payload. It stops short of adding anything the schema lacks for direction/symbol_name.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource ('Trace the call graph') and immediately disambiguates the two modes via direction='callers'/'callees'. An agent can tell this apart from search/find_symbol without consulting the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit use case with a condition: use transitive before changing a signature or renaming/moving/deleting a shared function ('what breaks if I change X'). It also names grep as the alternative it replaces, but never mentions sibling tools like find_symbol or search, so routing between MCP tools is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

indexIndex a projectA
Idempotent

Build or refresh the code index for a project. Call it once per project when status says indexed is false; search, find_symbol, graph, map and analyze all need it. Pass an absolute path, or several comma-separated paths for a monorepo. It is incremental when an index exists, and a file watcher then keeps the index fresh, so you rarely need to call it again. Large repositories take a while; progress is reported.

NameRequiredDescriptionDefault
pathYesAbsolute path to the codebase directory (or comma-separated paths)
pathsNoAdditional comma-separated paths to index

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds behavior beyond annotations: indexing is incremental when an index exists, a file watcher maintains freshness afterward, large repositories take time, and progress is reported. Annotations already cover the mutation/idempotency profile, but the description usefully clarifies runtime behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core action, then immediately explains when to call it and what depends on it. Every sentence adds operational value, including the monorepo path note and watcher behavior, with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given annotations, full schema coverage, and an output schema, the description provides everything needed to decide when to call it and how to pass paths. It also covers incremental behavior, watcher freshness, runtime expectations, and progress reporting.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, and the schema already documents both path parameters as absolute and comma-separated. The description repeats the monorepo comma-separated usage but adds no parameter detail beyond the schema, so baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: build or refresh the code index for a project. It clearly distinguishes this from siblings such as status, search, find_symbol, graph, map, and analyze by naming them as dependent tools rather than equivalents.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit invocation guidance: call once per project when status says indexed is false, and explains that search, find_symbol, graph, map, and analyze all need the index. It also says the file watcher keeps it fresh, so repeat calls are rarely needed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

mapProject mapA
Read-onlyIdempotent

Get an overview of the project: "give me an overview", "how is this structured", "where do I start", "what are the main modules". 'summary' lists files, languages, top modules and quality. 'architecture' gives layers, module dependencies, entry points and the most connected symbols. 'full' gives both. Use it at the start of work in an unfamiliar repository, before you list directories or open files one by one.

NameRequiredDescriptionDefault
detailNo'summary' (files/languages/quality/top-modules), 'architecture' (layers/dependencies/classes/entry points/hub symbols), or 'full' (both)summary

Output Schema

NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so the safety profile is fully covered structurally. The description adds what each mode surfaces (files/languages/quality vs layers/dependencies/entry points), but says nothing about output size, cost, or whether 'full' is expensive on large repositories — the main behavioral risk for an overview tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose, then intent examples, then mode semantics, then usage timing — a sensible order with little waste. There is mild redundancy between the description's mode list and the enum's own descriptions, but nothing is padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description need not explain return values, and the single optional parameter is fully covered; annotations cover safety. The only meaningful gap is guidance on when the richer 'architecture'/'full' modes are worth the extra output versus defaulting to 'summary'.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single enum is already documented in the schema; the description's mode explanations largely restate that text ('full' gives both appears in both places). Baseline 3 applies since the schema does the work and the description adds only minor rephrasing.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get an overview of the project') and reinforces it with real user phrasings ('how is this structured', 'where do I start') plus a breakdown of the three detail modes. It does not explicitly name or contrast the siblings (search, find_symbol, graph, status), so an agent must infer that this is the orientation tool rather than a lookup tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear timing guidance: 'Use it at the start of work in an unfamiliar repository, before you list directories or open files one by one.' That establishes when to use it and steers away from manual exploration, but it names no alternative sibling (e.g. status or index) and gives no when-not condition, such as skipping it in a repo you already know.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memoryProject memoryA
Destructive

Keep and recall project knowledge across sessions: decisions, preferences, conventions, status. Use action='store' when the user says "remember that..." or a decision is made, action='search' for "what did we decide about...", and action='delete' to remove outdated notes by ID or tags. Notes are stored with the index of this project (the .nexus folder) and found by meaning, not by exact words.

NameRequiredDescriptionDefault
tagsNoComma-separated tags (all actions)
queryNoWhat to look for (action='search')
actionYes'store' saves a note, 'search' finds notes by meaning, 'delete' removes notes
contentNoThe note to store (action='store')
memory_idNoID of the note to delete (action='delete')

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and readOnlyHint=false, so the safety profile is covered. The description adds genuinely useful context beyond that: notes are persisted to the project's .nexus index and retrieval is semantic rather than keyword-based. It stops short of stating whether deletes are recoverable, so it is not a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core purpose is front-loaded and each sentence carries actionable content (purpose, per-action triggers, storage/semantics). It runs slightly long across multiple lines, but no sentence is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and 100% schema description coverage, the description needn't explain return values and correctly focuses on usage. What remains unstated is deletion recovery behavior, a minor gap given the destructive annotation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description goes beyond it by tying parameters to actions in prose, notably that delete can target by ID or tags and that search matches by meaning — clarifications that help disambiguate the tags parameter's role across actions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb-and-resource pair ('keep and recall project knowledge') and enumerates the three concrete actions (store/search/delete). It differentiates from the sibling 'search' tool by clarifying notes are 'found by meaning, not by exact words' and scoped to the project's .nexus index.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It names explicit trigger phrases for each action: 'remember that...' or a decision for store, 'what did we decide about...' for search, and removal of outdated notes by ID or tags for delete. The agent knows exactly when to pick each branch without inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

statusIndex statusA
Read-onlyIdempotent

Check whether this project is indexed and whether the index is fresh. You do not need it before other nexus tools: an index saved in an earlier session is attached when the server starts. Use it when a tool says "No codebase indexed" or to see index size. Returns indexed (true/false), file and symbol counts, which engines are ready, memory use, and a stale warning if files changed since the last index (a background reindex starts by itself). If indexed is false, call index.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), so the description is free to add real behavioral context: a prior session's index is auto-attached at startup, and a stale index triggers an automatic background reindex. That auto-reindex disclosure is valuable because it explains why an agent should not panic over staleness. Points withheld because the enumerated return fields duplicate the existing output schema rather than adding new behavioral information.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in the first sentence, followed by the when-to-use guidance and then return contents. Mostly tight, but the middle list of return fields (counts, engines, memory) is a partial restatement of the output schema and could be trimmed without loss.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only status tool with rich annotations and an output schema, the description supplies everything an agent needs: what it reports, when to call it, that it is not a prerequisite, and what to do on a false result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is no parameter semantics to explain and the baseline of 4 applies. The description correctly does not waste space inventing inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: checks whether the project is indexed and whether the index is fresh. It is clearly distinguishable from siblings like `index`, `search`, and `map`, and an agent knows immediately this is a health/state check rather than a query.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when NOT to call it ('You do not need it before other nexus tools: an index saved in an earlier session is attached when the server starts'), names the triggering condition ('Use it when a tool says "No codebase indexed"'), and routes to the alternative ('If `indexed` is false, call `index`'). This is the strongest form of routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 7 tool updatesv2.1.0
    • Removedexplain
    • Changedfind_symbol3 fields changed
      • removedInput schema / properties / name
        Removed value: -{
        -  "description": "Symbol name (e.g. 'create_server', 'TokenBudget')",
        -  "type": "string"
        -}
      • addedInput schema / properties / symbol_name
        Added value: +{
        +  "description": "One name, or several with commas (e.g. 'create_server, TokenBudget, MAX_RETRIES')",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "name"
        -]New value: +[
        +  "symbol_name"
        +]
    • Changedgraph3 fields changed
      • addedInput schema / properties / direction / enum
        Added value: +[
        +  "callers",
        +  "callees"
        +]
      • removedInput schema / properties / max_depth
        Removed value: -{
        -  "default": 10,
        -  "description": "Max traversal depth when transitive=True (default 10)",
        -  "type": "integer"
        -}
      • changedInput schema / properties / transitive / description
        Previous value: -"True = full transitive closure for change-impact analysis (MUST use before refactoring a shared symbol). Only valid with direction='callers'."New value: +"True = everything that depends on the symbol, directly or indirectly. Use before changing a shared symbol. Only valid with direction='callers'."
    • Removedhealth
    • Changedmap1 field changed
      • addedInput schema / properties / detail / enum
        Added value: +[
        +  "summary",
        +  "architecture",
        +  "full"
        +]
    • Changedmemory9 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"'store' (was remember), 'search' (was recall), or 'delete' (was forget)"New value: +"'store' saves a note, 'search' finds notes by meaning, 'delete' removes notes"
      • addedInput schema / properties / action / enum
        Added value: +[
        +  "store",
        +  "search",
        +  "delete"
        +]
      • changedInput schema / properties / content / description
        Previous value: -"Memory content to store (action='store')"New value: +"The note to store (action='store')"
      • removedInput schema / properties / limit
        Removed value: -{
        -  "default": 5,
        -  "description": "Max results (action='search', default 5)",
        -  "type": "integer"
        -}
      • changedInput schema / properties / memory_id / description
        Previous value: -"Specific memory ID to delete (action='delete')"New value: +"ID of the note to delete (action='delete')"
      • removedInput schema / properties / memory_type
        Removed value: -{
        -  "default": "",
        -  "description": "Type/filter, e.g. 'note', 'decision' (store: type; search/delete: filter)",
        -  "type": "string"
        -}
      • removedInput schema / properties / project
        Removed value: -{
        -  "default": "default",
        -  "description": "Project name for scoping (action='store')",
        -  "type": "string"
        -}
      • changedInput schema / properties / query / description
        Previous value: -"Natural language search query (action='search')"New value: +"What to look for (action='search')"
      • removedInput schema / properties / ttl
        Removed value: -{
        -  "default": "permanent",
        -  "description": "Time-to-live for action='store': 'permanent', 'month', 'week', 'day', 'session'",
        -  "type": "string"
        -}
    • Changedsearch7 fields changed
      • removedInput schema / properties / language
        Removed value: -{
        -  "default": "",
        -  "description": "Filter by language (e.g. 'python')",
        -  "type": "string"
        -}
      • changedInput schema / properties / limit / description
        Previous value: -"Max results (default 10, max 100)"New value: +"Max results (default 10)"
      • removedInput schema / properties / live_grep
        Removed value: -{
        -  "default": false,
        -  "description": "Force live-grep fallback (rg/grep)",
        -  "type": "boolean"
        -}
      • removedInput schema / properties / mode
        Removed value: -{
        -  "default": "hybrid",
        -  "description": "Search mode: 'hybrid', 'vector', or 'bm25'",
        -  "type": "string"
        -}
      • changedInput schema / properties / query / description
        Previous value: -"Natural language or code query (e.g. 'retry logic')"New value: +"What to find, in plain words or code terms. Exact names (parse_config) first"
      • removedInput schema / properties / rerank
        Removed value: -{
        -  "default": true,
        -  "description": "FlashRank reranking (default True)",
        -  "type": "boolean"
        -}
      • removedInput schema / properties / symbol_type
        Removed value: -{
        -  "default": "",
        -  "description": "Filter by type (e.g. 'function', 'class')",
        -  "type": "string"
        -}
  2. 3 tool updates
    • Addedgraph
    • Addedsearch
    • Addedstatus
  3. 7 tool updatesv1.0.4
    • First observedanalyze
    • First observedexplain
    • First observedfind_symbol
    • First observedhealth
    • First observedindex
    • First observedmap
    • First observedmemory

TDQS

A4.3/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a distinct facet of code intelligence: status vs index (state vs build), search vs find_symbol (semantic vs name lookup), graph (call relations), analyze (quality), map (overview), memory (notes). Descriptions explicitly cross-reference each other to steer selection, leaving no real overlap.

Naming Consistency4/5

Names are consistently short, lowercase single tokens, but they mix noun-style (status, graph, map, memory) with verb-style (index, search, analyze) and one snake_case outlier (find_symbol). Mostly predictable, minor deviations only.

Tool Count5/5

Eight tools is well-scoped for a code-index server, and each earns its place with a clear, non-redundant role spanning indexing, search, navigation, analysis and memory.

Completeness5/5

Covers the full lifecycle: index status/build, semantic and name search, call-graph tracing, quality analysis, architectural overview, and persistent project memory. Literal grep is delegated to built-ins, which is a reasonable boundary, leaving no notable gaps.

Maintenance

ActivityActive
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers