Skip to main content
Glama

graphdb

tokens vs grep: 3.0x fewer

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.

graphdb demo

The animation above shows the CLI. The same eight tools are exposed to Claude Code via MCP — see 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

Related MCP server: sonde

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

# 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):

{
  "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.

Available Tools

8 tools
associations_ofA

Rails-specific: all ActiveRecord associations on a model.

Returns outgoing has_many/has_one/belongs_to/HABTM plus reverse-references (other models that point at this one).

Inputs: model (required). Returns: result

find_callersA

Functions that call name, transitively up to depth hops.

Inputs: name (required), depth. Returns: result

find_definitionB

Find all definitions matching a simple name (functions/classes/methods).

Inputs: name (required). Returns: result

find_referencesA

All inbound references to name (calls, imports, inheritance).

Inputs: name (required). Returns: result

graph_statsB

Counts of nodes/edges by kind — quick health check.

Inputs: no named parameters. Returns: result

impact_ofB

Transitive callers — predict what may break if name changes.

Inputs: name (required), max_depth. Returns: result

module_overviewA

Summary of a single module: exports, imports, most-called internal symbols.

path is the file path relative to the indexed root.

Inputs: path (required). Returns: result

routes_forA

Rails-specific: HTTP routes that hit a controller (or controller#action).

Accepts a bare class ("UsersController"), fully qualified ("Api::V1::UsersController"), or qualified action ("Api::V1::UsersController#create"). Returns verb/url/handler.

Inputs: controller_or_action (required). Returns: result

Tool Schema Changelog

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

Loading tool schema changelog…

    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
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      C
      maintenance
      Provides Claude Code with a repository symbol graph via tree-sitter, enabling instant symbol lookup, file skeletons, call-site analysis, and persistent notes, reducing token usage by reading signatures instead of whole files.
      12
      1
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Indexes a codebase into a symbol-level graph and exposes tools for finding symbols, querying relationships, and assessing impact, letting AI coding agents answer structural questions in a single call within a token budget.
      83 npm
      4
      Apache 2.0
    • A
      license
      Not graded
      quality
      A
      maintenance
      Turns a codebase into a persistent knowledge graph so AI coding agents can answer structural questions about functions, call chains, routes, and cross-service links through graph queries instead of reading files one by one.
      MIT
    • A
      license
      Not graded
      quality
      Not graded
      maintenance
      Enables AI agents to explore a repository by querying a pre-built structural knowledge graph of symbols and their relationships — definitions, callers, callees, blast radius, and file-level impact — instead of grepping and reading files. This cuts the tokens spent on code exploration by roughly 96–99% while running entirely on local SQLite with tree-sitter parsing.
      1
      MIT