🧬 adaptive-codegraph
Language-agnostic code graph indexer, search engine, and MCP server
Add a new language by dropping a .toml + .scm file — no Rust code changes needed.
How It Works · Why Not grep? · Quick Start · Adding a Language · MCP Tools · CLI · Architecture · Full Docs
💡 Why?
A similar implementation to mie-codegraph that replaces hardcoded per-language extractors with tree-sitter query files. The result:
📊 Status
� How It Works Under the Hood
adaptive-codegraph builds a structural understanding of your codebase, not just a text index. Here's what happens when you run adaptive-codegraph index:
1. Parse — Tree-sitter turns source code into syntax trees
Every source file is parsed with tree-sitter, producing a concrete syntax tree (CST). This is a full structural parse — it knows the difference between a function definition, a function call, a variable, and a string literal.
2. Extract — Query files pull out symbols and relationships
Tree-sitter S-expression queries (.scm files) are run against the syntax tree to extract:
- Symbols — function definitions, classes, structs, enums, modules, traits, interfaces, etc.
- Edges — function calls, imports, inheritance, trait implementations, etc.
Each symbol gets a stable ID computed via BLAKE3 hashing of its language, kind, fully-qualified name, and file path. This ID survives edits that don't rename or move the symbol.
3. Build — A directed graph of your entire codebase
All symbols become nodes and all relationships become directed edges in a petgraph graph, with side-table indexes for O(1) lookup by ID, file path, and name. This gives you:
- "Who calls this function?" → walk incoming edges
- "What does this function call?" → walk outgoing edges
- "Show me everything connected to this symbol within 3 hops" → BFS traversal
4. Index — Full-text search with BM25 ranking
Every symbol is indexed into a Tantivy search engine across multiple fields (name, fully-qualified name, file path, signature). Queries are ranked using BM25 — the same algorithm used by Elasticsearch and Lucene — so a function named parse_config ranks higher than one that merely contains those words in a long path.
5. Embed — Optional vector search for semantic matching
Symbol names are embedded into vector space for similarity search:
- Default: BLAKE3 hash-based embeddings (32-dim, fast, deterministic)
- With
fastembed: BGE-small-en-v1.5 transformer (384-dim, semantic understanding)
With transformer embeddings, a search for "authenticate user" can match login, verify_credentials, and check_password even though the words don't overlap.
6. Persist — Everything is saved for instant reloads
The graph, search index, and vectors are serialized to .adaptive-codegraph/ in the project root. Subsequent runs load the index in milliseconds. Incremental reindexing uses git diff to detect changed files and only re-processes those.
🆚 Why Not Just grep?
grep searches text. adaptive-codegraph understands code structure.
When grep is still the right tool
- Searching for arbitrary text (log messages, string literals, comments)
- One-off searches where you don't need an index
- Searching non-code files (docs, configs, data files)
When adaptive-codegraph is better
- Understanding code structure — what calls what, what depends on what
- Navigating large codebases where grep returns too many irrelevant results
- Powering AI assistants (via MCP) that need structural context, not just text matches
- Finding all callers of a function across the entire project
- Exploring the dependency graph around a symbol
- Semantic search — finding code by meaning, not exact text
📖 Full documentation → — CLI reference, MCP tool schemas, config options, data model, plugin system, and more.
🚀 Quick Start
Prerequisites
- Rust 1.75+ (
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh)
Install
git clone https://github.com/Fanaperana/adaptive-codegraph.git
cd adaptive-codegraph
./install.sh
Use in Any Project
cd /path/to/your/project
# One-time setup: creates .adaptive-codegraph/ with languages, MCP config, and .gitignore entry
adaptive-codegraph init
# Build the index
adaptive-codegraph index
# Search
adaptive-codegraph search "handle_request"
Everything lives inside .adaptive-codegraph/ — just add it to .gitignore (done automatically by init).
Add a Custom Language
adaptive-codegraph add-language my_lang.toml --symbol-query my_lang.scm --edge-query my_lang_edges.scm
🏗️ Architecture
┌─────────────────────────────────────────────────────────────┐
│ adaptive-codegraph │
├─────────┬─────────┬───────────┬────────────────────────────┤
│ CLI │ MCP │ Daemon │ (future: LSP, web UI) │
├─────────┴─────────┴───────────┴────────────────────────────┤
│ Core Library │
│ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Extract │ │ Store │ │ Search │ │ Embed │ │
│ │ (TS+SCM) │ │ (Graph) │ │ (Tantivy) │ │(HNSW/Hash) │ │
│ └──────────┘ └──────────┘ └────────────┘ └────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Config │ │ Index │ │Incremental │ │ Query │ │
│ │(auto-det)│ │(pipeline)│ │ (git-aware)│ │ (BFS etc) │ │
│ └──────────┘ └──────────┘ └────────────┘ └────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Language Definitions │
│ languages/*.toml + languages/queries/*.scm │
│ (C, JavaScript, Rust, Python, TypeScript, Go, ...) │
└─────────────────────────────────────────────────────────────┘
Key Design Decisions
🌐 Adding a New Language
Three files. Zero Rust changes.
1. Language definition — languages/<lang>.toml
id = "ruby"
name = "Ruby"
extensions = ["rb"]
grammar = "builtin"
2. Symbol extraction — languages/queries/<lang>.scm
;; Methods
(method name: (identifier) @symbol.name) @symbol.def
;; Classes
(class name: (constant) @symbol.name) @symbol.def
3. Edge extraction — languages/queries/<lang>_edges.scm
;; Function calls
(call method: (identifier) @call.name)
;; Imports
(call method: (identifier) @import.path
(#eq? @import.path "require"))
Built-in Languages
🔌 Custom Edge Patterns (Plugins)
For domain-specific edges that tree-sitter queries can't capture (framework routing, layout rendering, etc.):
use adaptive_codegraph_core::extract::plugin::RegexEdgePattern;
let pattern = RegexEdgePattern {
name: "django_url".to_string(),
pattern: regex_lite::Regex::new(r#"path\("([^"]+)",\s*(\w+)"#).unwrap(),
edge_kind: "endpoint".to_string(),
from_group: 2,
to_group: 1,
};
🛠️ MCP Tools
The MCP server exposes these tools over JSON-RPC 2.0 (stdio):
VS Code / Copilot Configuration
Add a .vscode/mcp.json to your project:
{
"servers": {
"adaptive-codegraph": {
"type": "stdio",
"command": "adaptive-codegraph-mcp",
"args": ["--base", "${workspaceFolder}"]
}
}
}
💻 CLI Usage
# cd into any project, then:
# Full index
adaptive-codegraph index
# Search symbols
adaptive-codegraph search "parse_config"
# Find callers of a function
adaptive-codegraph callers "handle_request"
# Find callees
adaptive-codegraph callees "main"
# BFS neighborhood (depth 3)
adaptive-codegraph neighborhood "main" --depth 3
# List detected languages
adaptive-codegraph languages
# Check index status
adaptive-codegraph status
You can also specify a different project with --base:
adaptive-codegraph --base /path/to/other/project index
⚙️ Configuration
Place .adaptive-codegraph.toml in your project root. If absent, languages are auto-detected.
Minimal
roots = ["src"]
Full
roots = ["src", "lib"]
index_dir = ".adaptive-codegraph"
exclude = [
"**/.git/**",
"**/node_modules/**",
"**/target/**",
"**/build/**",
"**/*.min.js",
]
[[languages]]
id = "c"
extensions = ["c", "h"]
[[languages]]
id = "javascript"
extensions = ["js"]
Config Reference
Language Auto-Detection
When languages is omitted, the tool scans for marker files:
📁 Project Layout
adaptive-codegraph/
├── Cargo.toml # Workspace root
├── crates/
│ ├── core/ # Core library (extract, store, search, embed)
│ ├── cli/ # Command-line interface (clap)
│ ├── mcp/ # MCP server (JSON-RPC 2.0 over stdio)
│ └── daemon/ # File-watching daemon (notify-rs)
└── languages/
├── c.toml # Language definitions
├── javascript.toml
├── rust.toml
├── python.toml
├── typescript.toml
├── go.toml
└── queries/
├── c.scm # Symbol extraction queries
├── c_edges.scm # Edge extraction queries
├── javascript.scm
├── javascript_edges.scm
├── rust.scm
├── rust_edges.scm
└── ...