Skip to main content
Glama

repo-graph

repo-graph MCP server

Structural graph memory for AI coding assistants. Map your codebase. Navigate by structure. Read only what matters.

repo-graph gives LLMs a map of your codebase — entities, relationships, and flows — so they can navigate to the right files without reading everything first.

Instead of flooding an LLM's context window with your entire codebase (or hoping it guesses right), repo-graph builds a lightweight graph of what exists, how things connect, and where the entry points are. The LLM queries the graph, finds the minimal set of files it needs, and reads only those.

It pays off most where that's hardest to do by hand: large repos, monorepos that span several languages, and multi-service systems where a feature's path crosses files, stacks, and service boundaries. On a small single-language project a model can just read the files — see Where it fits best for the honest sweet spot.

Install in one click:

Install in VS Code Install in VS Code Insiders Add to Cursor

Or one command in your terminal wires up every agent you have: uvx mcp-repo-graph install (see Install).

Demo

https://github.com/user-attachments/assets/a1e4171b-b225-40d4-9210-39453e14b76a

https://github.com/user-attachments/assets/fc3191e5-fc35-4bd7-8372-72af55995883

Same bug, same model, same prompt — the only difference is whether repo-graph is installed.

The task: fix a reversed comparison operator in a Go + Angular monorepo (566 nodes, 620 edges).

Without repo-graph

With repo-graph

Tokens used

75,308

29,838

Time to fix

4m 36s

~30s

Files explored

~15 (grep, read, grep, read...)

2 (trace lookup + handler file)

Outcome

Found and fixed the bug

Found and fixed the bug

2.5x fewer tokens. ~9x faster. Same correct fix.

How the test was run

Both runs used identical conditions to keep the comparison fair:

  • Same model: Claude Opus, 100% (no Haiku routing)

  • Same prompt: "Groups that were created recently are showing as closed, and old groups show as open. This is backwards — new groups should be open for members to join. Find and fix the bug."

  • Fresh context: each run started from /clear with no prior conversation

  • No other tools: CLAUDE.md, plugins, hooks, and all other MCP servers were removed for both runs — the only variable was whether repo-graph was installed

  • No hints: the prompt describes the symptom, not the location — Claude has to find group_controller.go:57 on its own

Without repo-graph, Claude greps for keywords, reads files, greps again, reads more files, and eventually narrows down to the bug. With repo-graph, Claude calls trace("groups"), gets back the exact handler function and file, reads it, and fixes it.

Browse pre-generated examples for FastAPI, Gin, Hono, and NestJS — real graph output you can inspect without installing anything.

Related MCP server: Serena

The problem

LLMs working on code waste most of their context on orientation:

  • Reading files that turn out to be irrelevant

  • Missing connections between components in different languages

  • Not knowing where a feature starts or what it touches

  • Loading 50 files when 5 would do

This is expensive, slow, and gets worse as codebases grow.

How repo-graph solves it

repo-graph scans your codebase once and builds a graph of:

  • Entities: modules, packages, classes, functions, routes, services, components

  • Relationships: imports, calls, handles, defines, contains, cross-stack HTTP

  • Flows: end-to-end paths from entry point to data layer

Then it exposes 6 MCP tools that let the LLM:

  1. Orient — "What languages are in this repo? What are the main features? Where is the graph blind?"

  2. Navigate — "Trace the login flow from route to database" / "What's the shortest path between UserService and the payments API?"

  3. Scope — "Which nodes matter for this bug?" / "Give me just the files I need for this fix"

  4. Assess — "What's the blast radius of changing this function?" / "What here is dead code?"

The LLM gets structural context in a few hundred tokens instead of reading thousands of lines.

Where it fits best

repo-graph earns its keep when a codebase is bigger or more tangled than the model can hold in its head at once. The payoff scales with three things:

  • Size — enough files that reading the relevant ones blows the context budget.

  • Complexity — rules, indirection, and layers, so "just read it" stops working.

  • Cross-boundary reach — the answer spans files, languages, or services that a text search can't link.

Strong fits:

  • Monorepos — a frontend calling a backend across a language boundary. repo-graph links the HTTP call to the route it hits and the handler behind it — the one thing grep structurally can't do. Point --repo at the monorepo root and a single graph spans every project. (The demo above is exactly this: Go + Angular in one repo.)

  • Multi-service / polyrepo systems — drop the services under one directory and point --repo at it; the graph traces a feature across service boundaries in one call.

  • Large single codebases — thousands of files where orientation itself is the cost.

  • Unfamiliar or legacy code — where you don't yet know what touches what.

Where it doesn't pull its weight: a small, single-language repo with a clear task. The model can just read the files — grep wins and the graph is overhead. Don't reach for it to shave tokens, either: the MCP layer is a fixed per-turn cost, so on easy tasks it can cost more. The token win shows up only when it heads off a grep-read-grep spiral (like the demo above). What it reliably buys you is correct, complete, cross-boundary answers in a few calls on code too big or too interconnected to fit in context — yours or the model's. (Don't want the MCP layer at all? Skip it and call the engine directly.)

Use it without MCP

The MCP server is the zero-config path, but the graph isn't tied to it. The engine ships as a plain Python wheel — pip install repo-graph-py — so you can build the graph and call the same answer primitives directly, from a script or your own tooling, with none of the per-turn MCP cost:

import repo_graph_py as rg

g = rg.generate(".")                            # or rg.load_from_gmap(rg.default_gmap_dir("."))
print(g.blast_radius("checkout", "both"))       # ranked, located, live-filtered — JSON
print(g.cross_stack_trace("notifications"))     # feature path across the stack, mechanism-labelled
print(g.resolve(open("error.log").read()))      # stacktrace / test / diff → the nodes that matter
print(g.coverage())                             # where extraction is partial (grep those)

Same graph, same answers — just without the tool schemas in your context. It's the same Rust engine (glia) the MCP server wraps; repo-graph-py is its published wheel. Good for CI checks, batch analysis, or wiring the graph into your own agent.

Supported languages

Language

Detection

What it extracts

Go

go.mod

Packages, functions, HTTP routes (gin/echo/chi/stdlib), imports

Rust

Cargo.toml

Crates, modules, structs, traits, functions, routes (Actix/Rocket/Axum)

TypeScript

tsconfig.json / package.json

Modules, classes, functions, import relationships

React

react in package.json

Components, hooks, context providers, React Router routes, fetch/axios calls, flows

Angular

@angular/core in package.json

Components, services, guards, DI injection, HTTP calls, feature flows

Vue

vue in package.json

SFCs, composables, Vue Router routes, fetch/axios calls

Python

pyproject.toml / setup.py / requirements.txt

Packages, modules, classes, functions, routes (Flask/FastAPI/Django)

Java/Kotlin

pom.xml / build.gradle

Packages, classes, routes (Spring/JAX-RS/Ktor/WebFlux/Micronaut)

Scala

build.sbt

Packages, objects/classes/traits, routes (Play/Akka HTTP/http4s)

Clojure

project.clj / deps.edn

Namespaces, defn/defprotocol/defrecord, routes (Compojure/Reitit)

C#/.NET

.csproj / .sln

Namespaces, classes, routes (ASP.NET/Minimal API)

Ruby

Gemfile / .gemspec

Files, classes, modules, Rails routes

PHP

composer.json

Namespaces, classes, interfaces, routes (Laravel/Symfony)

Swift

Package.swift / .xcodeproj

Files, types (class/struct/enum/protocol/actor), Vapor routes

C/C++

CMakeLists.txt / Makefile / meson.build

Sources, headers, classes, structs, enums, namespaces, includes

Dart/Flutter

pubspec.yaml

Modules, classes, widgets, go_router/shelf routes

Elixir/Phoenix

mix.exs

Modules, functions, Phoenix router scopes + routes

Solidity

.sol files / foundry.toml / hardhat.config.*

Contracts, interfaces, libraries, events, inheritance

Terraform

.tf files

Modules, resources, variables, outputs, module sources

SCSS

.scss files present

File-level bloat analysis

Cross-cutting extractors (work across all languages):

  • Data sources — DB/cache/queue/blob/search/email client detection

  • CLI entrypoints — Python click, JS commander/yargs, Go cobra, Rust clap

  • gRPC — service/method definitions from .proto files

  • Queue consumers — Celery, Dramatiq, BullMQ, Sidekiq, Oban, NATS

  • Cross-stack HTTP — frontend fetch/axios calls linked to backend routes

Multiple languages can match one repo (e.g., Go backend + Angular frontend + SCSS). Each contributes its nodes and edges into a single unified graph.

Install

One command

uvx mcp-repo-graph install

This detects the AI coding agents you have installed (Claude Code, Claude Desktop, Cursor, Windsurf, VS Code, Codex, Gemini CLI, opencode, Kiro), writes each one's MCP config, and adds a short usage block to its instructions file so the agent reaches for the graph before it greps. Where the agent supports it, it also grants auto-allow so repo-graph tools don't prompt on every call.

It's safe to re-run, and uvx mcp-repo-graph uninstall reverses everything (config, instructions, permissions) while leaving your graph data in place.

uvx mcp-repo-graph install --agents all          # every supported agent, not just detected
uvx mcp-repo-graph install --scope user          # your global config, not this project
uvx mcp-repo-graph install --dry-run             # show what it would write, change nothing
uvx mcp-repo-graph install --yes                 # no prompt (scripts and CI)
uvx mcp-repo-graph install --print-config cursor # print one agent's config, write nothing

Manual, per client

If you'd rather wire it up yourself, the package name is the run command. uvx mcp-repo-graph just works. No prior pip install, nothing to keep on PATH. This is the same command VS Code, Cursor, and the MCP registry use under the hood.

Requirements: Python 3.11+, and uv if you use the uvx path. Prebuilt wheels ship for the Rust engine on Linux (x86_64, aarch64), macOS (Intel + Apple Silicon), and Windows (x86_64) — no Rust toolchain needed.

Claude Code

claude mcp add repo-graph -- uvx mcp-repo-graph --repo .

(--repo . points the graph at the current project; use an absolute path to pin it.)

VS Code

One command — adds the server to your user config:

code --add-mcp '{"name":"repo-graph","command":"uvx","args":["mcp-repo-graph","--repo","${workspaceFolder}"]}'

Or click Install on the MCP gallery entry, or add it to .vscode/mcp.json manually (see below).

Cursor / any MCP client — manual config

Add this to your client's MCP config (.mcp.json, .cursor/mcp.json, .vscode/mcp.json, or ~/.claude.json):

{
  "mcpServers": {
    "repo-graph": {
      "command": "uvx",
      "args": ["mcp-repo-graph", "--repo", "/path/to/your/project"]
    }
  }
}

Prefer a persistent install? pip install mcp-repo-graph (or uv tool install mcp-repo-graph) puts a mcp-repo-graph / repo-graph command on your PATH; then use "command": "mcp-repo-graph" in the config above.

--repo also accepts a git URL. Point it at any public repo without cloning first — it shallow-clones and maps it (requires git):

uvx mcp-repo-graph --repo https://github.com/org/repo

Quick start

1. Initialise the target repo (optional)

uvx --from mcp-repo-graph repo-graph-init --repo /path/to/your/project
# or, if installed:  repo-graph-init --repo /path/to/your/project

This generates the graph, writes .mcp.json and CLAUDE.md instructions, and gets your AI assistant ready to use repo-graph. If you used the one-liners above, you can skip this — the server builds the graph on first connect.

2. Use it

The AI assistant now has access to all 6 tools. Example queries it can answer:

  • "What does this codebase do?"orient tool

  • "Trace the checkout flow"trace tool

  • "What would break if I change UserService?"impact tool

  • "Which nodes are relevant to this bug?" / "Here's a stacktrace — where do I look?"find tool

  • "Show me that function's source"read tool

  • "Give me the full graph context cheaply"orient full=true

  • "Rebuild after a big refactor"refresh tool

3. Freshness (automatic)

The graph stays current on its own. While the server is running it watches the repo and does an incremental rebuild a moment after you save, so a structural question right after an edit reflects the change with no manual refresh. On top of that, the graph refreshes on cold start whenever the source tree changed since the cached .gmap was written, so it's never stale when your assistant connects.

The watcher is on by default. Set REPO_GRAPH_WATCH=0 to disable it (the cold-start refresh still applies). It needs the watchdog package, which ships as a dependency.

Want the cache pre-built and committed so teammates and CI get it too? Add the pre-commit hook automatically:

uvx mcp-repo-graph install --agents none --git-hook

That installs a marker-fenced pre-commit hook that refreshes the graph and stages .ai/repo-graph/ on every commit. uvx mcp-repo-graph uninstall removes it again.

Tip: If you don't want graph data in version control, add .ai/repo-graph/ to .gitignore and skip the hook — the watcher and cold-start refresh keep it fresh locally.

MCP tools reference

repo-graph exposes 6 tools — one natural verb each, backed by a Rust engine primitive.

Tool

Parameters

Description

orient

seed (optional), full, budget

The first call on a repo: node/edge counts, detected kinds, entry points, and a blind-spots note flagging which languages/edges are under-linked (so you grep those deliberately). seed=<node> → scoped map; full=true → whole-repo dense map

find

query, expand, kind, top_k, budget

Turn any text into the ranked nodes that matter — a symbol/keyword, or a pasted stacktrace / failing-test id / diff (resolved to the code it implicates). expand=true fans out to the surrounding neighbourhood. Every row carries path:line

impact

nodes (comma-separated), direction, depth, live_only, top_k, budget

Blast radius: what a change affects (forward) or depends on / is used by (backward), as a ranked, located closure — each row with the edge via reason and a when the engine finds it unreachable (likely dead). Pass several nodes for a whole-diff radius

trace

from_node, to_node (optional), depth, budget

One arg: a feature end-to-end across the stack, each hop labelled with its mechanism (call / HTTP / queue / event) and cross-service hops marked. Two args: the shortest path between two nodes

read

node (comma-separated), context_lines, budget

A node's exact source, sliced from its file by the graph's line span, plus a context: footer (HTTP method, cross-stack callers, covering tests, governing docs). Comma-separate to batch-read a ranked set

refresh

repo_path (optional), full

Rebuild the graph (incremental by default — only changed files re-parse). repo_path retargets a different path or git URL; full=true forces a clean reparse. Routine edits are auto-picked-up by the file watcher

Most tools also take a budget (max chars) so a result fits a small-model context window.

These 6 collapsed from an earlier 13 once the engine (v0.4.18) grew answer-shaped primitives — blast_radius, cross_stack_trace, resolve, coverage — that return complete, ranked, located, live-filtered results in one call. Fewer tools = less fixed per-turn overhead and less agent confusion.

How it works

mcp-repo-graph is a thin Python MCP server that wraps glia, a Rust engine.

  1. Parse — per-language tree-sitter parsers extract raw nodes and unresolved references

  2. Extract — cross-cutting extractors layer on HTTP routes, data sources, CLI entrypoints, gRPC services, queue consumers

  3. Resolve — graph builder resolves intra-repo references; cross-graph resolvers link stacks (frontend HTTP calls → backend routes, etc.)

  4. Store — merged graph lands in .ai/repo-graph/ as a zero-copy .gmap (rkyv + mmap) plus JSON projections for portability

  5. Serve — the MCP server loads the graph into memory and exposes the 6 tools

The Rust engine lives in its own glia repo; mcp-repo-graph is the MCP-facing thin wrapper.

Config (optional escape hatch)

If auto-detection misses a weird layout, drop .ai/repo-graph/config.yaml in the target repo:

skip:
  - legacy       # directory basenames excluded from the walk
  - scratch

roots:           # explicit roots heuristics miss — added on top of auto-detection
  - path: apps/weird-layout
    kind: python
  - path: services/custom
    kind: go

kind values: go, rust, python, typescript, react, vue, angular, java, scala, clojure, csharp, ruby, php, swift, c_cpp, dart, elixir, solidity, terraform. config.json works too if you prefer.

Graph data format

Generated files live in .ai/repo-graph/ inside the target repo:

  • nodes.json[{id, type, name, file_path, confidence, ...}, ...]

  • edges.json[{from, to, type}, ...]

  • flows/*.yaml — named feature flows with ordered step sequences and kind (http/page/cli/grpc/queue)

  • state.md — human-readable snapshot for quick orientation

Common edge types: imports, defines, contains, uses, calls, handles, handled_by, exports, includes, tests, cross-stack HTTP links.

Privacy Policy

repo-graph runs on your machine and is built to keep your code there. Full text: PRIVACY.md.

  • Telemetry / analytics: None. No tracking, no update checks, no phone-home.

  • Data collection & sharing: None. Your source code and graph data are never sent to repo-graph, its author, or any third party.

  • Local processing & storage: Scanning and graph-building happen locally; the graph is cached in your project's .ai/repo-graph/ directory and stays on your device.

  • Network access — only two cases, both user-initiated:

    1. Installationuvx/pip downloads the package and its prebuilt engine wheel from PyPI.

    2. Git-URL targets — if you pass a git URL to --repo, repo-graph runs git clone against the URL you specified; nothing is sent to repo-graph or its author. A local --repo path (the default) makes zero network calls.

  • Data retention: The local cache persists until you delete it — fully under your control.

  • Contact: GitHub issues

License

MIT

Support

If repo-graph saved you time, consider buying me a coffee.

Available Tools

6 tools
findA
Read-only

Turn any text into the ranked nodes that matter — the on-ramp to the graph. A symbol or keyword returns matching nodes; a pasted stacktrace / failing-test id / diff is resolved to the code it implicates and ranked by relevance. Set expand=true to fan out to the surrounding neighbourhood (spreading activation). Every row carries path:line, so read the top hits directly — no grep.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNoForce the signal type: 'symbol', 'stacktrace', 'test', 'diff', or 'auto' (sniff the shape).auto
queryYesWhat to locate: a symbol/keyword (e.g. `User`, `checkout`), OR a failure signal — paste a raw stacktrace, a failing-test id (path::test), or a unified diff / changed-file list.
top_kNoMax results. Default 20.
budgetNoMax chars in the result. 0 = no cap.
expandNoReturn the relevant neighbourhood (Personalized-PageRank ranked) around the matches, not just the matches themselves. Use to discover what surrounds a seed.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior4/5

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

Annotations only provide readOnlyHint=true, while the description adds rich behavioral detail: ranking by relevance, resolving different input types (stacktrace, diff), neighborhood expansion via spreading activation, and output format (path:line). This goes well beyond the minimal annotation.

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 description is concise (4-5 sentences) yet packs substantial information. It front-loads the core purpose and covers key variations. Every sentence contributes value, avoiding redundancy or filler.

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?

Given the tool's complexity (5 parameters, output schema exists), the description covers the primary use cases and output characteristics (path:line). It does not need to restate the output schema, but provides enough context for effective use.

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?

All 5 parameters are described in the schema (100% coverage), but the description adds meaningful context: examples for 'query' (paste raw stacktrace, test id), explanation of 'expand' (Personalized-PageRank), and 'kind' auto-detect behavior. This enhances understanding beyond schema defaults.

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?

The description clearly states the tool transforms text into ranked graph nodes, serving as an on-ramp to the graph. It specifies that it handles symbols, keywords, stacktraces, test IDs, and diffs, but does not explicitly differentiate from sibling tools like 'trace' or 'orient', though it mentions 'read' for subsequent steps.

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

Usage Guidelines3/5

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

The description implies usage context: use for initial search or failure resolution, with 'expand=true' for neighborhood exploration. It suggests reading top hits via 'read', but lacks explicit when-not-to-use guidelines or comparisons with sibling tools like 'impact' or 'trace'.

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

impactA
Read-only

Blast radius in one call: fan out from one or more nodes to everything they affect (forward) or depend on / are used by (backward), returned as a complete, deduped, Personalized-PageRank-ranked, located closure. Each row carries path:line, the edge via reason it's in scope, and a when the engine finds it unreachable from any entry point (likely dead). Structural import/containment fan-out is excluded — no noise. Depth-1 in both directions is a node's immediate neighbours. Pass several comma-separated nodes to assess a whole diff at once.

ParametersJSON Schema
NameRequiredDescriptionDefault
depthNoHow many hops to fan out. Default 4.
nodesYesOne or more node names/qnames, comma-separated. A diff touching N symbols is one call.
top_kNoCap the ranked result. 0 = no cap.
budgetNoMax chars in the result. 0 = no cap.
directionNo'forward' (what it affects), 'backward' (what it depends on / who uses it), or 'both'. (Aliases: downstream=forward, upstream=backward.)both
live_onlyNoDrop nodes not reachable from any entry point (likely-dead code). Default False = show all, marking dead ones ⊘.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

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

Beyond the readOnlyHint annotation, the description reveals key behaviors: ranking algorithm (Personalized PageRank), result fields (path:line, via, dead marker), exclusion of structural edges, depth meaning, and handling of multiple nodes. No contradictions.

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?

The description is a single dense paragraph that front-loads the core functionality. Every sentence contributes meaningful detail, with no wasted words. It efficiently communicates complex behavior.

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 the tool's complexity and rich parameters/output, the description fully covers result structure, filtering, ranking, and exclusive behavior. It matches the needed detail without relying on output schema.

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?

With 100% schema coverage, the baseline is 3. The description adds value by explaining node usage for whole diffs, aliases for direction, and the meaning of depth-1. This enriches the schema descriptions.

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 clearly states the tool's purpose: computing blast radius/impact from nodes in forward or backward directions. It distinguishes itself from siblings like trace and find by emphasizing its comprehensive, deduped, ranked closure and exclusion of structural edges.

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?

The description implies when to use this tool (assessing impact of changes, whole diff) and even hints at what it excludes (structural fan-out). However, it doesn't explicitly compare to alternatives or state when not to use it.

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

orientA
Read-only

Get the lay of the land — ALWAYS the first call on a codebase. With no arguments: a counts + entry-points overview plus a blind spots note flagging which (language, edge-kind) extractions are partial so you know where to fall back to grep. With seed=<node>: the dense structural map scoped to that node's neighbourhood. With full=true: the whole-repo dense map (the full context dump). Orient first, then find to jump to nodes, impact for blast radius, trace for flows.

ParametersJSON Schema
NameRequiredDescriptionDefault
fullNoWith no seed: return the whole-repo dense structural map instead of the counts overview. Ignored when seed is given.
seedNoOptional node/qname to scope the map around (its activated neighbourhood). Blank = repo overview.
budgetNoMax chars in the result. 0 = default cap (~50k for the full map, uncapped otherwise).

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, so the description doesn't need to repeat that. It meaningfully adds context about output types (counts, entry-points, blind spots, structural maps) and the budget parameter, which are not covered by annotations.

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?

The description is concise and well-structured. The critical instruction 'ALWAYS the first call on a codebase' is front-loaded, and each sentence serves a purpose: explaining modes, blind spots, and sibling tool ordering.

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 the tool's complexity (three modes, multiple output types, sibling tools), the description is remarkably complete. It covers each mode's output, mentions limitations (blind spots), and provides a recommended workflow. The presence of an output schema reduces the need to detail return values.

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?

With 100% schema coverage, the baseline is 3. The description adds value by explaining the behavioral differences of each parameter combination (no args vs. seed vs. full) and clarifying the budget default behavior, going beyond what the schema alone provides.

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 clearly specifies the tool's purpose: getting an overview of a codebase. It details three distinct modes (no args, seed, full) and explicitly contrasts with sibling tools (find, impact, trace), making it easy for an agent to select the right tool.

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?

The description provides explicit usage guidance: 'ALWAYS the first call on a codebase' and explains when to use each variant. It also tells when to fall back to grep for blind spots and names alternative tools for subsequent steps.

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

readA
Read-only

Return the source code for one or more nodes, sliced from their files by the graph's line spans. Use after find/impact to read the exact code without grepping — comma-separate several node names to read the whole ranked set in a single call. Each node is a code block headed by its qname and path:start-end, plus a context: footer with structural facts the source alone doesn't show: HTTP method, cross-stack callers, covering tests, and intent/decision/constraint cells when present.

ParametersJSON Schema
NameRequiredDescriptionDefault
nodeYesNode name or qname to read. Comma-separate several (e.g. the top-ranked nodes from `find`/`impact`) to slice them all in one call.
budgetNoMax chars in the result (shared across all nodes when several are given). 0 = no cap.
context_linesNoLines of padding above and below the node's span. Default 0.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already indicate readOnlyHint=true, and description adds that it slices from files and returns structured blocks with qname, path, and a context footer. No contradiction with annotations.

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?

Single paragraph with good front-loading of purpose. It is information-dense but not overly verbose. Could potentially be broken into shorter sentences for readability, but no wasted words.

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 the read-only nature and existing annotations, the description fully explains what the tool does, when to use it, how to use parameters, and what the output contains (including structural facts). No gaps.

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

Parameters5/5

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

Schema coverage is 100%, but description adds valuable context: comma-separating nodes, budget shared across nodes, context_lines as padding. Also ties parameters to use case (after find/impact).

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?

Clearly describes returning source code for nodes, sliced by line spans. Distinguishes itself from sibling tools like find/impact by stating it should be used after those to read code, not to find it.

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?

Explicitly says to use after find/impact, and gives usage example like comma-separating node names. Could be more explicit about when not to use (e.g., for non-code files), but the context is clear enough.

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

refreshA

(Re)build the structural graph with tree-sitter AST parsing across 20 languages, running the cross-stack resolvers (HTTP, gRPC, GraphQL, WebSocket, queues, events, CLI). Incremental by default — only changed files re-parse — so it's cheap to call after edits; set full=true to force a clean reparse. Accepts a local path or a git URL (cloned on demand). Call after a major refactor; routine edits are picked up automatically by the file watcher.

ParametersJSON Schema
NameRequiredDescriptionDefault
fullNoForce a full reparse instead of reusing the per-file parse cache. Default False (incremental — only changed files re-parse, so this is cheap after edits).
repo_pathNoPath or git URL to (re)scan. Blank = the repo the server is serving. A different path/URL retargets the server at it.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior4/5

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

Beyond annotations (readOnlyHint=false, openWorldHint=true), the description adds behavioral details: incremental re-parsing by default, cheap after edits, full=true for clean reparse, and support for local path or git URL with on-demand cloning. No contradictions.

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?

The description is concise (under 100 words), front-loaded with the tool's primary purpose, and each sentence adds value. Well-structured for quick understanding.

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?

Given the tool's complexity (graph rebuilding, 20 languages, multiple resolvers) and that an output schema exists, the description adequately covers core behavior, parameters, and usage context. Could be more detailed about output, but output schema fills that gap.

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%, so baseline is 3. The description rephrases schema info for both parameters (full and repo_path) but does not add significant new meaning beyond what the schema already provides.

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 clearly states the tool rebuilds a structural graph using tree-sitter AST parsing across 20 languages and runs cross-stack resolvers. It distinguishes from siblings like find and trace by focusing on graph regeneration after major refactors.

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?

The description explicitly advises calling after a major refactor and notes that routine edits are handled automatically by the file watcher. It does not explicitly mention when not to use or list alternative sibling tools, but provides clear usage context.

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

traceA
Read-only

Follow the code across boundaries. One argument: trace a feature end-to-end — the ordered path from entry through the stack, each hop labelled with its mechanism (call / HTTP / queue / event / data), crossing service boundaries (frontend→backend). Two arguments: the shortest path between two specific nodes, hop by hop. This is where the graph beats reading many files — it knows the cross-stack links grep can't see.

ParametersJSON Schema
NameRequiredDescriptionDefault
depthNoMax hops for the cross-stack feature trace. Default 6.
budgetNoMax chars in the result. 0 = no cap.
to_nodeNoOptional target node. Given → shortest path from_node→to_node. Blank → trace `from_node` as a feature across the stack.
from_nodeYesA feature/keyword to trace end-to-end (one arg), OR the start node when tracing a path to `to_node`.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

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

Annotations declare readOnlyHint=true, so the agent knows it's a read operation. The description adds behavioral detail: the tool traverses service boundaries, labels mechanisms (call/HTTP/queue/event/data), and behaves differently with one vs two arguments. No contradictions.

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?

The description is concise: four sentences, front-loaded with the purpose, each sentence adding value (action, mode distinction, justification). No wasted words.

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?

Given 4 parameters, an output schema, and moderate complexity, the description covers the core behavior and use cases. It does not detail node types or mechanism labels, but provides enough for an agent to select the tool appropriately.

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 description coverage is 100%, baseline 3. The description adds meaning by explaining the dual behavior of from_node (feature or start node) and to_node (target node for shortest path), which complements the schema descriptions.

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 clearly states the tool traces code across boundaries, explains one-argument (feature end-to-end) and two-argument (shortest path) modes, and distinguishes from siblings like find and impact by emphasizing cross-stack links that grep cannot see.

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?

The description explains that the tool is used for tracing features end-to-end or finding paths between nodes, and contrasts with reading files/grep. It does not explicitly state when not to use it versus specific siblings, but the context is clear enough.

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. 15 tool updatesv0.4.20
    • Removedactivate
    • Removeddense_text
    • Changedfind4 fields changed
      • addedInput schema / properties / expand
        Added value: +{
        +  "default": false,
        +  "description": "Return the relevant neighbourhood (Personalized-PageRank ranked) around the matches, not just the matches themselves. Use to discover what surrounds a seed.",
        +  "title": "Expand",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / kind
        Added value: +{
        +  "default": "auto",
        +  "description": "Force the signal type: 'symbol', 'stacktrace', 'test', 'diff', or 'auto' (sniff the shape).",
        +  "title": "Kind",
        +  "type": "string"
        +}
      • changedInput schema / properties / query / description
        Previous value: -"Node name or qname pattern to search for. Supports partial matching."New value: +"What to locate: a symbol/keyword (e.g. `User`, `checkout`), OR a failure signal — paste a raw stacktrace, a failing-test id (path::test), or a unified diff / changed-file list."
      • addedInput schema / properties / top_k
        Added value: +{
        +  "default": 20,
        +  "description": "Max results. Default 20.",
        +  "maximum": 100,
        +  "minimum": 1,
        +  "title": "Top K",
        +  "type": "integer"
        +}
    • Removedflow
    • Removedgenerate
    • Removedgraph_view
    • Changedimpact8 fields changed
      • changedInput schema / properties / depth / default
        Previous value: -3New value: +4
      • changedInput schema / properties / depth / description
        Previous value: -"How many hops to traverse. Default 3."New value: +"How many hops to fan out. Default 4."
      • changedInput schema / properties / direction / default
        Previous value: -"downstream"New value: +"both"
      • changedInput schema / properties / direction / description
        Previous value: -"'downstream' (what it affects) or 'upstream' (what it depends on)."New value: +"'forward' (what it affects), 'backward' (what it depends on / who uses it), or 'both'. (Aliases: downstream=forward, upstream=backward.)"
      • addedInput schema / properties / live_only
        Added value: +{
        +  "default": false,
        +  "description": "Drop nodes not reachable from any entry point (likely-dead code). Default False = show all, marking dead ones ⊘.",
        +  "title": "Live Only",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / mode
        Removed value: -{
        -  "default": "table",
        -  "description": "'table' (tiered list) or 'prose' (primed prose for LLM context).",
        -  "title": "Mode",
        -  "type": "string"
        -}
      • changedInput schema / properties / nodes / description
        Previous value: -"One or more node names/qnames, comma-separated. A diff touching N files is one call."New value: +"One or more node names/qnames, comma-separated. A diff touching N symbols is one call."
      • addedInput schema / properties / top_k
        Added value: +{
        +  "default": 0,
        +  "description": "Cap the ranked result. 0 = no cap.",
        +  "maximum": 200,
        +  "minimum": 0,
        +  "title": "Top K",
        +  "type": "integer"
        +}
    • Removedlocate
    • Removedneighbours
    • Addedorient
    • Changedread2 fields changed
      • changedInput schema / properties / budget / description
        Previous value: -"Max chars in the result. 0 = no cap."New value: +"Max chars in the result (shared across all nodes when several are given). 0 = no cap."
      • changedInput schema / properties / node / description
        Previous value: -"Node name or qname to read the source for."New value: +"Node name or qname to read. Comma-separate several (e.g. the top-ranked nodes from `find`/`impact`) to slice them all in one call."
    • Addedrefresh
    • Removedreload
    • Removedstatus
    • Changedtrace6 fields changed
      • addedInput schema / properties / budget
        Added value: +{
        +  "default": 0,
        +  "description": "Max chars in the result. 0 = no cap.",
        +  "minimum": 0,
        +  "title": "Budget",
        +  "type": "integer"
        +}
      • addedInput schema / properties / depth
        Added value: +{
        +  "default": 6,
        +  "description": "Max hops for the cross-stack feature trace. Default 6.",
        +  "maximum": 12,
        +  "minimum": 1,
        +  "title": "Depth",
        +  "type": "integer"
        +}
      • changedInput schema / properties / from_node / description
        Previous value: -"Starting node name or qname pattern."New value: +"A feature/keyword to trace end-to-end (one arg), OR the start node when tracing a path to `to_node`."
      • addedInput schema / properties / to_node / default
        Added value: +""
      • changedInput schema / properties / to_node / description
        Previous value: -"Target node name or qname pattern."New value: +"Optional target node. Given → shortest path from_node→to_node. Blank → trace `from_node` as a feature across the stack."
      • changedInput schema / required
        Previous value: -[
        -  "from_node",
        -  "to_node"
        -]New value: +[
        +  "from_node"
        +]
  2. 10 tool updatesv0.4.19
    • Changedactivate3 fields changed
      • addedInput schema / properties / budget
        Added value: +{
        +  "default": 0,
        +  "description": "Max chars in the result. 0 = no cap.",
        +  "minimum": 0,
        +  "title": "Budget",
        +  "type": "integer"
        +}
      • addedInput schema / properties / mode
        Added value: +{
        +  "default": "table",
        +  "description": "'table' (ranked list) or 'prose' (primed prose for LLM context).",
        +  "title": "Mode",
        +  "type": "string"
        +}
      • addedInput schema / properties / profile
        Added value: +{
        +  "default": "default",
        +  "description": "Edge-weight preset: 'default', 'repair' (up-weights call/data), 'review', or 'onboard' (up-weights entry/module).",
        +  "title": "Profile",
        +  "type": "string"
        +}
    • Changeddense_text2 fields changed
      • addedInput schema / properties / budget
        Added value: +{
        +  "default": 0,
        +  "description": "Max chars in the result. 0 = default cap (~50k, or REPO_GRAPH_DENSE_MAX_CHARS).",
        +  "minimum": 0,
        +  "title": "Budget",
        +  "type": "integer"
        +}
      • addedInput schema / properties / seed
        Added value: +{
        +  "default": "",
        +  "description": "Optional node/qname to scope the map around (its activated neighbourhood). Blank = whole graph.",
        +  "title": "Seed",
        +  "type": "string"
        +}
    • Changedfind1 field changed
      • addedInput schema / properties / budget
        Added value: +{
        +  "default": 0,
        +  "description": "Max chars in the result. 0 = no cap.",
        +  "minimum": 0,
        +  "title": "Budget",
        +  "type": "integer"
        +}
    • Changedflow1 field changed
      • addedInput schema / properties / budget
        Added value: +{
        +  "default": 0,
        +  "description": "Max chars in the result. 0 = no cap.",
        +  "minimum": 0,
        +  "title": "Budget",
        +  "type": "integer"
        +}
    • Changedgenerate1 field changed
      • addedInput schema / properties / incremental
        Added value: +{
        +  "default": true,
        +  "description": "Reuse the per-file parse cache so unchanged files skip re-parsing (default True). Set False to force a full reparse.",
        +  "title": "Incremental",
        +  "type": "boolean"
        +}
    • Changedimpact6 fields changed
      • addedInput schema / properties / budget
        Added value: +{
        +  "default": 0,
        +  "description": "Max chars in the result. 0 = no cap.",
        +  "minimum": 0,
        +  "title": "Budget",
        +  "type": "integer"
        +}
      • changedInput schema / properties / direction / description
        Previous value: -"'downstream' or 'upstream'."New value: +"'downstream' (what it affects) or 'upstream' (what it depends on)."
      • addedInput schema / properties / mode
        Added value: +{
        +  "default": "table",
        +  "description": "'table' (tiered list) or 'prose' (primed prose for LLM context).",
        +  "title": "Mode",
        +  "type": "string"
        +}
      • removedInput schema / properties / node
        Removed value: -{
        -  "description": "Node name or qname pattern to analyze.",
        -  "title": "Node",
        -  "type": "string"
        -}
      • addedInput schema / properties / nodes
        Added value: +{
        +  "description": "One or more node names/qnames, comma-separated. A diff touching N files is one call.",
        +  "title": "Nodes",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "node"
        -]New value: +[
        +  "nodes"
        +]
    • Addedlocate
    • Changedneighbours1 field changed
      • addedInput schema / properties / budget
        Added value: +{
        +  "default": 0,
        +  "description": "Max chars in the result. 0 = no cap.",
        +  "minimum": 0,
        +  "title": "Budget",
        +  "type": "integer"
        +}
    • Addedread
    • Changedreload1 field changed
      • addedInput schema / properties / incremental
        Added value: +{
        +  "default": true,
        +  "description": "Reuse the per-file parse cache so unchanged files skip re-parsing (default True). Set False to force a full reparse.",
        +  "title": "Incremental",
        +  "type": "boolean"
        +}
  3. 14 tool updatesv0.1.3
    • Addedactivate
    • Removedbloat_report
    • Removedcost
    • Addeddense_text
    • Addedfind
    • Changedflow1 field changed
      • addedInput schema / properties / feature / description
        Added value: +"Feature name or keyword to match against entry points. Case-insensitive, supports partial matching."
    • Changedgenerate1 field changed
      • addedInput schema / properties / repo_path / description
        Added value: +"Absolute path to the repository to scan. Defaults to the repo the server was started with."
    • Changedgraph_view5 fields changed
      • addedInput schema / properties / depth / description
        Added value: +"Tree depth. Default 2."
      • addedInput schema / properties / depth / maximum
        Added value: +5
      • addedInput schema / properties / depth / minimum
        Added value: +1
      • removedInput schema / properties / feature
        Removed value: -{
        -  "default": "",
        -  "title": "Feature",
        -  "type": "string"
        -}
      • addedInput schema / properties / node / description
        Added value: +"Node name or qname to render as a tree. Leave blank for full overview."
    • Removedhotspots
    • Changedimpact5 fields changed
      • addedInput schema / properties / depth / description
        Added value: +"How many hops to traverse. Default 3."
      • addedInput schema / properties / depth / maximum
        Added value: +10
      • addedInput schema / properties / depth / minimum
        Added value: +1
      • addedInput schema / properties / direction / description
        Added value: +"'downstream' or 'upstream'."
      • addedInput schema / properties / node / description
        Added value: +"Node name or qname pattern to analyze."
    • Removedminimal_read
    • Changedneighbours1 field changed
      • addedInput schema / properties / node / description
        Added value: +"Node name or qname pattern to inspect."
    • Removedsplit_plan
    • Changedtrace2 fields changed
      • addedInput schema / properties / from_node / description
        Added value: +"Starting node name or qname pattern."
      • addedInput schema / properties / to_node / description
        Added value: +"Target node name or qname pattern."
  4. 13 tool updatesv0.1.2
    • First observedbloat_report
    • First observedcost
    • First observedflow
    • First observedgenerate
    • First observedgraph_view
    • First observedhotspots
    • First observedimpact
    • First observedminimal_read
    • First observedneighbours
    • First observedreload
    • First observedsplit_plan
    • First observedstatus
    • First observedtrace

TDQS

A4.4/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a distinct purpose: find resolves text to nodes, impact shows dependency blast radius, orient provides overviews, read retrieves source code, refresh rebuilds the graph, and trace follows cross-boundary flows. There is no overlap in functionality.

Naming Consistency5/5

All tool names are single-word, lowercase imperatives (find, impact, orient, read, refresh, trace) following a consistent verb pattern without any mixing of conventions.

Tool Count5/5

With 6 tools, the server covers all necessary operations for code graph analysis—build, explore, search, read, and trace—without being excessive or insufficient.

Completeness5/5

The tool set covers the full lifecycle: building the graph (refresh), getting oriented (orient), finding nodes (find), reading code (read), analyzing impact (impact), and tracing flows (trace). There are no obvious gaps.

Maintenance

ActivityStale
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    A fully featured coding agent that uses symbolic operations (enabled by language servers) and works well even in large code bases. Essentially a free to use alternative to Cursor and Windsurf Agents, Cline, Roo Code and others.
    29
    42,914 PyPI
    29,226
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that transforms codebases into knowledge graphs using Neo4J, enabling AI assistants to understand code structure, relationships, and metrics for more context-aware assistance.
    28
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to understand and navigate codebases through structural analysis. Provides code mapping, symbol search, and impact analysis using ast-grep for accurate parsing of Python, JavaScript, TypeScript, and Go projects.
    4
    52
    MIT