RepoGraph
by meethjaswani
README.md
# RepoGraph
A local-first code intelligence graph for Python repos, exposed over MCP so agents like
Claude Code can query it instead of re-reading the whole codebase every session.
Coding agents don't remember your repo's structure between sessions. Ask one to change a
function and it either greps around or reads a pile of files just to figure out what
calls what. RepoGraph parses your repo once with tree-sitter, builds a typed graph of
functions/classes/modules and their calls/imports/inherits/tests relationships, and
keeps it updated incrementally from git diffs. Agents then query it directly: "what
calls this?", "what breaks if I change this?" — without touching the rest of the repo.

## What it gives you
Three MCP tools:
- **`get_subgraph(entity, depth)`** — the neighborhood around a function/class/module
- **`find_callers(fn)`** — who calls this, directly
- **`find_impact(fn)`** — the full blast radius: everyone who transitively calls or tests it

Plus two resources (`repograph://schema`, `repograph://stats`), a reviewer agent that
pulls just the blast radius of a diff before asking Claude to review it, and a
graph-maintainer agent that flags orphaned code and modules with too many dependents.
## How it's built
```
git repo
│ tree-sitter parse (full) / git diff (incremental)
▼
graph builder — nodes: module/class/function, edges: imports/inherits/calls/tests
▼
SQLite + NetworkX (local, no server, easy to inspect)
▼
FastMCP server — get_subgraph / find_callers / find_impact
│ │
▼ ▼
reviewer agent graph-maintainer agent
```
Python 3.11+, single language for now (the parser/graph-builder split is where a second
tree-sitter grammar would plug in). Full stack: tree-sitter, NetworkX, SQLite, FastMCP,
GitPython, the Claude API for the two agents, pytest for everything else.
## Setup
```bash
python3.11 -m venv .venv
.venv/bin/pip install -e ".[dev]"
```
Build a graph and run the server:
```bash
.venv/bin/repograph-build /path/to/some/repo --db repograph.db
.venv/bin/repograph-mcp repograph.db
```
`--incremental` re-runs against the last indexed commit instead of parsing everything again.
## Adding it to Claude Code
```bash
claude mcp add repograph -- /absolute/path/to/repograph/.venv/bin/repograph-mcp /absolute/path/to/repograph.db
```
or drop this into a project's `.mcp.json` (see `.mcp.json.example`):
```json
{
"mcpServers": {
"repograph": {
"command": "/absolute/path/to/repograph/.venv/bin/repograph-mcp",
"args": ["/absolute/path/to/repograph.db"]
}
}
}
```
Then just ask it to check `find_impact` before touching something.
## Does it actually work? (the evaluation harnesses)
Most "code graph" tools ship a headline number with nothing backing it up. Every claim
here is a test, not a paragraph:
| Harness | File | Checks |
|---|---|---|
| Context reduction | `tests/test_context_reduction.py` | subgraph context is smaller than full-file context, with a real table below |
| Graph correctness | `tests/test_graph_correctness.py` | exact match on hand-labeled edges, plus precision/recall against an independent `ast`-based extractor |
| Staleness/drift | `tests/test_staleness_drift.py` | 50 simulated commits — incremental updates always converge to a full rebuild |
| MCP context budget | `tests/test_mcp_context_budget.py` | tool schemas stay under a fixed token budget |
| Impact-query accuracy | `tests/test_impact_query.py` | `find_impact` precision/recall against a hand-labeled blast-radius set |
```bash
.venv/bin/pytest -q
```
### Context reduction on the bundled fixture repo
<!-- BENCHMARK:START -->
| Target function | Full-file tokens (est.) | Subgraph tokens (est.) | Reduction |
|---|---:|---:|---:|
| `main.build_shapes` | 386 | 311 | 19.4% |
| `main.main` | 386 | 273 | 29.3% |
| `shapes.base.Shape.area` | 386 | 57 | 85.2% |
| `shapes.base.Shape.describe` | 386 | 57 | 85.2% |
| `shapes.circle.Circle.__init__` | 386 | 28 | 92.7% |
| `shapes.circle.Circle.area` | 386 | 27 | 93.0% |
| `shapes.rectangle.Rectangle.__init__` | 386 | 39 | 89.9% |
| `shapes.rectangle.Rectangle.area` | 386 | 29 | 92.5% |
| `shapes.utils.compute_total_area` | 386 | 296 | 23.3% |
| `shapes.utils.summarize` | 386 | 261 | 32.4% |
<!-- BENCHMARK:END -->
This is a ~10-function fixture repo, not a real production codebase, so treat the exact
percentages as illustrative. Point the benchmark at any real repo to regenerate it:
```bash
.venv/bin/python scripts/benchmark.py --repo /path/to/some/repo --out README.md
```
CI does this automatically on every push (`.github/workflows/ci.yml`).
## Where it falls short
Symbol resolution is a static heuristic, not real type inference, and it's tuned to
favor precision over recall:
- **Dynamic dispatch isn't resolved.** `s.area()` where `s` could be any subclass
produces no edge rather than a guess. That's deliberate — see the fixture's
`compute_total_area`, which is the one call the correctness harness expects to miss.
- **`self.method()` resolves to whatever's defined on the enclosing class**, not to
whichever override would actually run.
- **Decorator arguments aren't parsed for calls** — `@app.route("/x")` won't create an
edge to `app.route`.
- **Only same-repo imports get nodes.** Calls into stdlib/third-party code are correctly
left unresolved instead of invented.
The graph-maintainer's orphan detection inherits this: a method whose only real caller
is dynamic dispatch will look "orphaned" even though it isn't. It's documented in
`tests/test_maintainer_agent.py`, not hidden.
## Layout
```
src/repograph/
parser.py tree-sitter extraction, one file at a time
graph_builder.py cross-file symbol resolution -> graph
store.py SQLite persistence
git_integration.py incremental updates from git diffs
queries.py get_subgraph / find_callers / find_impact
benchmark.py context-reduction measurement
mcp_server.py FastMCP server
cli.py repograph-build
agents/reviewer.py, maintainer.py
tests/
fixtures/ hand-crafted sample repo + golden JSON
ast_reference.py independent ast-based ground truth
test_*.py one file per harness, plus MCP/agent/CLI tests
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues