graphdb
by Samagra001
README.md
# graphdb
[](./benchmarks/BENCHMARK.md)
A Kùzu-backed code-graph MCP plugin for Claude Code. Indexes Ruby on Rails
codebases into a queryable graph of associations, routes, callbacks, jobs, and
mailers — a token-efficient alternative to grep for navigating Rails code.

> The animation above shows the CLI. The same eight tools are exposed to Claude
> Code via MCP — see [docs/PROMPTS.md](docs/PROMPTS.md) for natural-language
> prompts you can try directly in a Claude Code session.
## Why
Rails encodes most of its architecture in **DSL declarations** (`has_many`,
`before_action`, `resources`, `include`) rather than direct method calls. Grep
matches the strings but cannot turn them into relationships:
| Question | Grep | graphdb |
|---|---|---|
| "What models reference User?" | 200+ matches across `_id` columns, validators, params | `query associations User` — 4 outgoing assocs + 16 reverse-refs |
| "What routes hit `Api::V1::CampaignsController`?" | Scan 218-line `routes.rb` mentally | `query routes Api::V1::CampaignsController` — 5 records |
| "Who delivers `UserInviteMailer`?" | `grep -rn UserInviteMailer` then read each callsite | `query callers UserInviteMailer --depth 2` — 4 invokers |
| "What jobs do `ScheduledReports::*` services enqueue?" | Multi-step grep + read | `query impact ScheduleExecutionJob` |
## Status
Proof of concept, validated against a real Rails 8 / Ruby 3.3 codebase
(`smart-hub-backend`, 494 indexed `.rb` files):
```
nodes: 2913 edges: 4731 index time: ~0.8s
60 models 63 controllers 25 concerns 121 services 67 serializers
7 mailers 6 jobs 109 migrations 230 routes 421 actions
```
Validation against grep ground truth on smart-hub-backend:
| Pattern | Grep | Graph | |
|---|---:|---:|---|
| `has_many` | 52 | 52 | exact |
| `belongs_to` | 79 | 79 | exact |
| `has_one` | 15 | 15 | exact |
| `scope` | 66 | 66 | exact |
| `deliver_later` + `deliver_now` | 11 | 11 | exact |
| `validates` | 48 lines | 58 edges | each call's rules expand to one edge per rule |
| `before_action` | 46 lines | 377 edges | each filter × N actions in `only:`/all |
## Quickstart
```bash
# Set up a venv with uv (sidesteps Homebrew's broken ensurepip on macOS)
uv venv .venv --python 3.13
uv pip install --python .venv/bin/python -e .
# Index a Rails project (writes to ~/.cache/graphdb/<repo>-<hash>.kuzu/;
# nothing is written to the target repo)
.venv/bin/graphdb --root /path/to/rails_app index /path/to/rails_app
# CLI queries
.venv/bin/graphdb --root /path/to/rails_app query def User
.venv/bin/graphdb --root /path/to/rails_app query associations User
.venv/bin/graphdb --root /path/to/rails_app query routes Api::V1::UsersController
.venv/bin/graphdb --root /path/to/rails_app query callers UserInviteMailer --depth 2
.venv/bin/graphdb --root /path/to/rails_app query impact ScheduleExecutionJob
.venv/bin/graphdb --root /path/to/rails_app query refs Account
.venv/bin/graphdb --root /path/to/rails_app stats
```
## MCP integration with Claude Code
Add to your Claude Code MCP config (`~/.claude.json` or project-level):
```json
{
"mcpServers": {
"graphdb": {
"command": "/path/to/Graph_DB/.venv/bin/graphdb-mcp",
"env": { "GRAPHDB_ROOT": "/path/to/your/rails_app" }
}
}
}
```
Tools exposed to Claude:
| Tool | Answers |
|---|---|
| `find_definition(name)` | Where is `name` defined? (model, controller, action, …) |
| `find_callers(name, depth)` | Who calls/enqueues/delivers `name`? (CALLS ∪ ENQUEUES ∪ DELIVERS) |
| `find_references(name)` | All inbound edges to `name` — associations, includes, calls, etc. |
| `impact_of(name, max_depth)` | Transitive callers — predicted blast radius |
| `module_overview(path)` | File-level summary: exports, imports, hotspots |
| `routes_for(controller_or_action)` | HTTP routes that hit a controller / action |
| `associations_of(model)` | All `has_many`/`has_one`/`belongs_to`/HABTM on a model + reverse |
| `graph_stats()` | Counts of nodes/edges by kind |
## Graph schema
```
NODE KINDS
file, module, class, method, class_method,
model, controller, action, concern, service, serializer,
job, mailer, validator, helper, channel, error_class,
migration, route, external
EDGE KINDS
Generic Ruby: CONTAINS, CALLS, INHERITS, INCLUDES
Model DSL: HAS_MANY, BELONGS_TO, HAS_ONE, HABTM, VALIDATES, SCOPES
Controllers: BEFORE_ACTION, AFTER_ACTION, SKIP_BEFORE, RENDERS
Routes: HANDLES, MOUNTS
Async: ENQUEUES, DELIVERS
```
## How it works
1. **`indexer.py`** parses every `.rb` under `app/`, `lib/`, `config/`,
`db/migrate/` with tree-sitter-ruby. Two passes:
- Pass 1: register every class/module/method as a node, build a qualname
index used for resolution.
- Pass 2: walk again, emit `INHERITS`, `INCLUDES`, `CALLS`, and the
Rails-specific edges (`HAS_MANY`, `BEFORE_ACTION`, `ENQUEUES`, etc.)
- Pass 3: a separate walker handles `config/routes.rb` (DSL outside any
class), producing `Route` nodes with `HANDLES` edges to controller actions.
2. **`graph.py`** buffers nodes/edges in memory and bulk-flushes via Cypher
`UNWIND` at end of indexing. Drops full-repo indexing from ~8 minutes (individual
inserts) to ~0.8 seconds.
3. **`queries.py`** issues Cypher patterns like
`MATCH (caller)-[:CALLS|ENQUEUES|DELIVERS*1..3]->(target)` — the database
does the BFS.
4. **`mcp_server.py`** exposes the queries as MCP tools for Claude Code.
## Known PoC limitations
- **Static parsing only.** Doesn't run Rails — no type inference, no resolution
of `define_method` / `method_missing` / dynamic `class_eval`.
- **Relative imports / autoload constants:** resolved by unique-name fallback;
ambiguous names are skipped silently.
- **Custom DSLs (acts_as_paranoid, paper_trail's `has_paper_trail`, amoeba):**
not yet specialized — they emit generic `CALLS` edges at class scope.
- **Routes:** `scope`, member/collection custom routes, `constraints`,
`concerns` (route concerns), nested resources past 1 level — partial or
skipped. The 7 standard REST actions, `namespace`, `mount`, `devise_for`,
and `get/post/...` with `to:` work fully.
- **Reindex is one-shot.** Edit a file, rerun `graphdb index`. No file watcher.
## Roadmap
- Specialize the major Gemfile DSLs in smart-hub-backend (paper_trail, discard,
amoeba, flipper).
- Member / collection / nested resource routes.
- Incremental reindex via file-watcher.
- AI-generated per-method summaries stored on nodes (read summary first, file second).
- Benchmark harness: grep-only vs graph-only Claude sessions on identical tasks.
TDQS
A3.8/5.0
Scored across 8 tools
Disambiguation5/5
Each tool targets a distinct aspect of code analysis: definitions, references, callers, impact, module overview, and Rails-specific associations/routes. No overlap in purpose.
Naming Consistency5/5
All tool names follow a consistent snake_case pattern with a clear verb_noun or noun_of structure, making them predictable.
Tool Count5/5
8 tools is well-scoped for a code analysis server, covering both general and Rails-specific functionality without bloat.
Completeness4/5
The set covers key analysis operations (definitions, references, callers, impact, module overview, routes, associations). A minor gap is the lack of a tool to list all modules without a specific path.
Maintenance
ActivityInactive
ResponsivenessNo issues