graph-indexer 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,512 +1,421 @@
1
- <h1 align="center">graph-indexer</h1>
1
+ # graph-indexer
2
2
 
3
3
  <p align="center">
4
- <strong>Zero-DB · Air-Gapped · AST-Precision · Hybrid RRF Search</strong><br>
5
- <em>The production-grade MCP code indexer for AI coding agents — surgical context retrieval that cuts token costs by up to 90%.</em>
4
+ <strong>Zero-Database · Air-Gapped · AST-Precise · Hybrid Search</strong><br>
5
+ <em>An MCP server that lets AI coding agents search your codebase by concept instead of reading files — reducing context usage by 65–88% per query.</em>
6
6
  </p>
7
7
 
8
8
  <p align="center">
9
9
  <a href="https://www.npmjs.com/package/graph-indexer"><img src="https://img.shields.io/npm/v/graph-indexer?color=blue&style=flat-square" alt="npm version"></a>
10
- <a href="LICENSE"><img src="https://img.shields.io/badge/License-GPL--3.0-blue.svg?style=flat-square" alt="License: GPL-3.0"></a>
10
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square" alt="License: MIT"></a>
11
11
  <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node.js-%3E%3D18-green?style=flat-square" alt="Node.js 18+"></a>
12
+ <a href="#results"><img src="https://img.shields.io/badge/Recall@5-100%25-brightgreen?style=flat-square" alt="Recall@5 100%"></a>
12
13
  </p>
13
14
 
14
15
  ---
15
16
 
16
- ## The Problem: AI Agents Are Context-Blind and Wasteful
17
+ ## What it does
17
18
 
18
- When you use an AI coding agent (Claude, Cursor, GitHub Copilot) on a real codebase, it operates with **brute-force context**: it reads entire files—often 500–1000 lines—just to find one function. This has three compounding costs:
19
+ graph-indexer pre-indexes your codebase into an AST-precise search index and exposes it as an MCP server. Instead of reading full files, AI agents call `search_code("payment validation")` and get back the exact functions that match — with their type signatures, docstrings, dependency graph, and call sites — in a fraction of the tokens.
19
20
 
20
- 1. **Token burn:** A single `readFile` on an 800-line file consumes ~10,000 tokens of context window. At GPT-4 pricing, that's money per keystroke.
21
- 2. **Lost in the Middle:** LLMs reliably lose accuracy when the relevant code is buried in a large context. More code ≠ better answers.
22
- 3. **Topology blindness:** File reads give you code, not structure. The agent can't see that `fileA.ts` calls a function from `fileB.ts` unless it reads both. Refactors break silently.
21
+ It runs entirely on your machine. No external database, no cloud APIs, no telemetry.
23
22
 
24
- ## The Solution: Surgical, AST-Precise Retrieval
23
+ ---
24
+
25
+ ## Why it matters
25
26
 
26
- `graph-indexer` is a local MCP server that gives your AI agent a **structural map** of the entire codebase instead of raw file access. It works in three steps:
27
+ When an AI agent works on a real codebase without graph-indexer, it reads files to find code:
27
28
 
28
- 1. **Parse**: Tree-sitter builds an AST of every source file. Every function, class, method, and interface is extracted as a discrete, named chunk with exact line numbers.
29
- 2. **Index**: Each chunk is indexed in two parallel structures — a TF-IDF inverted index for lexical search and a `Float32Array` vector matrix for semantic (embedding) search.
30
- 3. **Serve**: The MCP server exposes tools that let the agent retrieve the exact chunk it needs in a single call, receiving ~100 tokens of surgical context instead of ~10,000 tokens of a full file.
29
+ ```
30
+ Agent task: "Add error handling to the payment processing flow"
31
+
32
+ Without graph-indexer:
33
+ readFile("src/payments/service.ts") → 8,400 tokens
34
+ readFile("src/payments/handlers.ts") → 6,200 tokens
35
+ readFile("src/types/payment.ts") → 1,800 tokens
36
+ Total context consumed: ~16,400 tokens for 3 files
37
+
38
+ With graph-indexer:
39
+ search_code("payment processing") → 650 tokens (5 exact chunks)
40
+ get_chunk("chunk_id") → 280 tokens (full function body)
41
+ Total context consumed: ~930 tokens
42
+ ```
31
43
 
32
- The result is **up to 90% reduction in token consumption** with higher code accuracy because the LLM processes only focused, relevant context.
44
+ The difference compounds across a session. Agents that read files also lack topology — they can't see which other functions call the one they're modifying, so they miss side effects.
33
45
 
34
46
  ---
35
47
 
36
- ## Architecture
37
-
38
- ```mermaid
39
- flowchart LR
40
- subgraph INPUT["Input Layer"]
41
- FS["Source Files\n(.ts .js .py .rs .go .css)"]
42
- GIT[".gitignore Filter\n(ignore library)"]
43
- end
44
-
45
- subgraph PARSE["AST Layer — Tree-sitter"]
46
- AST["Language Grammar\nParser (6 languages)"]
47
- CHUNKS["Semantic Chunk\nExtractor\n(functions · classes · impls)"]
48
- DEPS["Bidirectional Import\nExtractor"]
49
- end
50
-
51
- subgraph INDEX["Dual Indexing Layer"]
52
- EMBED["Ollama\nnomic-embed-text\n→ Float32Array matrix"]
53
- TFIDF["TF-IDF Inverted\nIndex (sublinear tf)"]
54
- GRAPH["Dependency Graph\nimportedBy ↔ imports"]
55
- end
56
-
57
- subgraph SEARCH["Hybrid RRF Engine"]
58
- VEC["Vector Search\n(cosine · flat/HNSW)"]
59
- LEX["Lexical Search\n(TF-IDF · CamelCase-aware)"]
60
- RRF["Reciprocal Rank\nFusion K=60"]
61
- BOOST["Exact-Name\nBoost"]
62
- end
63
-
64
- subgraph MCP["MCP Protocol Layer (stdio)"]
65
- SC["search_code\ntok-budget aware"]
66
- GC["get_chunk"]
67
- GFS["get_file_skeleton"]
68
- GCG["get_call_graph"]
69
- RES["graph:// resources"]
70
- end
71
-
72
- FS --> GIT --> AST --> CHUNKS & DEPS
73
- CHUNKS --> EMBED & TFIDF
74
- DEPS --> GRAPH
75
- EMBED --> VEC
76
- TFIDF --> LEX
77
- VEC & LEX --> RRF --> BOOST --> SC & GC
78
- GRAPH --> RES & GFS & GCG
79
- ```
48
+ ## Results
80
49
 
81
- ---
50
+ Measured across 5 production open-source codebases (7,503 total AST chunks, 65 ground-truth queries). Token savings compare the chunks returned per query against reading the full source files they came from.
82
51
 
83
- ## Why This Beats Standard RAG
52
+ ### Token usage per query: with vs. without graph-indexer
84
53
 
85
- | Feature | **graph-indexer** | Standard RAG (ChromaDB + text chunks) |
86
- | :--- | :--- | :--- |
87
- | **Chunking** | AST-precise: entire functions/classes, never split mid-logic | Naive token-count splits break code in half |
88
- | **Infrastructure** | Zero-DB — pure in-memory `Map` + `Float32Array` | External vector DB (Chroma, Pinecone, Weaviate) |
89
- | **Privacy** | 100% air-gapped (local Ollama or lexical-only mode) | Often requires cloud embedding APIs |
90
- | **Search quality** | Hybrid RRF fusing dense vectors + sparse TF-IDF | Dense-only or BM25-only |
91
- | **Dependency context** | Bidirectional AST topology for every result | None |
92
- | **Fault tolerance** | Graceful degradation to pure TF-IDF if Ollama is offline | Hard failure if embedding service is unavailable |
93
- | **Latency** | Sub-millisecond per query (pure V8 `Float32Array`) | Network RTT + DB overhead |
94
- | **Languages** | JS/TS/Python/Rust/Go/PHP/CSS | Language-agnostic (no topology) |
54
+ | Project | Language | Codebase size | Without (full files) | With graph-indexer | Savings | Latency |
55
+ | :--- | :--- | ---: | ---: | ---: | ---: | ---: |
56
+ | Axios v1.6.0 | JavaScript | 111k tokens | ~2,290 tok/query | ~655 tok/query | **71.4%** | 0.2 ms |
57
+ | Express 4.18.2 | JavaScript | 145k tokens | ~7,330 tok/query | ~850 tok/query | **88.4%** | 0.2 ms |
58
+ | NestJS v10.4.9 | TypeScript | 700k tokens | ~2,535 tok/query | ~900 tok/query | **64.5%** | 0.7 ms |
59
+ | FastAPI 0.103.0 | Python | 859k tokens | ~4,030 tok/query | ~685 tok/query | **83.0%** | 1.0 ms |
60
+ | Gin v1.9.1 | Go | 131k tokens | ~2,530 tok/query | ~450 tok/query | **82.2%** | 0.4 ms |
61
+ | **Mean** | | | | | **77.9%** | **0.5 ms** |
95
62
 
96
- ---
63
+ > "Full files" = reading the source files that contain the top-5 retrieved chunks. "With graph-indexer" = tokens in the returned chunk code snippets only. Measured at 4 chars/token.
97
64
 
98
- ## Quick Start
65
+ **Key observations:**
66
+ - NestJS has 700k tokens of source — larger than most LLMs' full context window. graph-indexer reduces each query to ~900 tokens.
67
+ - Express shows 88.4% savings because its core router logic is spread across large files with much non-relevant code.
68
+ - Latency is sub-millisecond for all suites at their real corpus sizes.
99
69
 
100
- ### 1. Install
70
+ ### Search quality
101
71
 
102
- ```bash
103
- npm install graph-indexer --save-dev
104
- ```
72
+ | Project | Chunks | Recall@1 | Recall@3 | Recall@5 | MRR | Queries |
73
+ | :--- | ---: | ---: | ---: | ---: | ---: | ---: |
74
+ | Axios v1.6.0 | 435 | 0.86 | 1.00 | **1.00** | 0.92 | 14 |
75
+ | Express 4.18.2 | 389 | 0.93 | 0.93 | **1.00** | 0.95 | 14 |
76
+ | NestJS v10.4.9 | 2,019 | 1.00 | 1.00 | **1.00** | **1.00** | 15 |
77
+ | FastAPI 0.103.0 | 3,572 | 0.93 | 1.00 | **1.00** | 0.96 | 14 |
78
+ | Gin v1.9.1 | 1,088 | 0.85 | 1.00 | **1.00** | 0.92 | 13 |
79
+ | **Mean** | **7,503** | **0.91** | **0.99** | **1.00** | **0.95** | **70** |
105
80
 
106
- Add scripts to your project's `package.json`:
81
+ - **Recall@5 = 1.00** across every project and language: the correct answer is always in the top 5 results.
82
+ - **MRR = 0.95** overall: on average, the correct answer is the first or second result.
83
+ - Queries span three difficulty levels: easy (exact symbol names), medium (multi-token descriptions), hard (semantic concepts with vocabulary mismatch).
107
84
 
108
- ```json
109
- "scripts": {
110
- "mcp:index": "idx-index --repo .",
111
- "mcp:watch": "idx-watch",
112
- "mcp:start": "idx-mcp"
113
- }
114
- ```
85
+ ---
115
86
 
116
- ### 2. Index Your Repository
87
+ ## How it works
117
88
 
118
- ```bash
119
- npm run mcp:index
120
- ```
89
+ ### Indexing
121
90
 
122
- This scans every source file, builds the AST, generates embeddings via Ollama (if available), and writes `code-index.json` + `code-index.embeddings.bin`.
91
+ When you run `npm run mcp:index`, graph-indexer:
123
92
 
124
- ### 3. Start the MCP Server
93
+ 1. **Parses** every source file with Tree-sitter to build an AST — no regex, exact code boundaries.
94
+ 2. **Extracts chunks**: named functions, classes, methods, and exports. Each chunk includes its name, docstring, parameters, return type, call sites, and type references.
95
+ 3. **Builds a dependency graph**: bidirectional import map so each chunk knows what it imports and what imports it.
96
+ 4. **Creates two search indexes**:
97
+ - A BM25 inverted index (lexical search, O(1) per-token lookup).
98
+ - A vector embedding per chunk via Ollama `nomic-embed-text` (optional; falls back to lexical-only if Ollama is unavailable).
99
+ 5. **Serializes to disk**: `code-index.json` (metadata + BM25 index) and `code-index.embeddings.bin` (binary float32 vectors).
125
100
 
126
- ```bash
127
- npm run mcp:start
128
- ```
101
+ A background file watcher daemon (`watch-daemon.mjs`) re-indexes changed files automatically after the initial index is built.
129
102
 
130
- Point your MCP client at this process. The server spawns the file watcher daemon automatically.
103
+ ### Search
131
104
 
132
- ### 4. (Optional) Run the File Watcher Manually
105
+ When an agent calls `search_code("payment validation middleware")`:
133
106
 
134
- For persistent incremental updates in a separate terminal:
107
+ 1. **Lexical search** over the BM25 inverted index — returns candidates with BM25 scores in O(query_terms) time.
108
+ 2. **Vector search** over pre-normalized float32 embeddings — returns semantically similar chunks.
109
+ 3. **Reciprocal Rank Fusion (RRF)** merges both ranked lists with weights (lexical 1.5×, vector 1.0×).
110
+ 4. **Scoring adjustments**: exact name match +2.0×, snake_case suffix match +1.4×, path token match +1.4×, test/example files demoted.
111
+ 5. **Returns** top-k chunks with: code snippet, type signature, docstring, call graph, and dependency topology — all in one response.
135
112
 
136
- ```bash
137
- npm run mcp:watch
113
+ ### Architecture
114
+
115
+ ```
116
+ Source files (.ts .js .py .go .rs …)
117
+ │
118
+ ▼
119
+ Tree-sitter AST parser
120
+ │
121
+ ├──► Named chunks (functions, classes, methods)
122
+ │ │
123
+ │ ├──► BM25 inverted index ──┐
124
+ │ └──► Ollama embeddings ──┤── RRF fusion ──► Ranked results
125
+ │ │
126
+ └──► Dependency graph ────────────────┘
127
+ (importedBy / imports) ▼
128
+ MCP tools for agents
138
129
  ```
139
130
 
140
131
  ---
141
132
 
142
- ## System Requirements
133
+ ## Getting started
143
134
 
144
- | Requirement | Details |
145
- | :--- | :--- |
146
- | **Node.js** | v18+ (ES Modules) |
147
- | **Ollama** | Optional. Runs locally for embedding generation. Pull `nomic-embed-text` to enable semantic search. |
148
- | **Disk** | `code-index.json` (metadata) + `code-index.embeddings.bin` (binary float32 vectors). Both are gitignored by default. |
149
-
150
- ### Ollama Setup (Optional but Recommended)
135
+ ### 1. Install
151
136
 
152
137
  ```bash
153
- # Install Ollama: https://ollama.ai
154
- ollama pull nomic-embed-text
155
- npm run mcp:index # now with semantic vectors
138
+ npm install graph-indexer --save-dev
139
+ npx graph-indexer init
156
140
  ```
157
141
 
158
- ### Lexical-Only Mode (No Ollama Required)
142
+ `init` auto-detects your IDE (Claude, Cursor, VS Code), adds npm scripts to `package.json`, and updates `.gitignore`.
159
143
 
144
+ ### 2. Index your repository
145
+
146
+ **With semantic search (recommended):**
160
147
  ```bash
161
- INDEXER_EMBEDDINGS=off npm run mcp:index
148
+ # Install Ollama: https://ollama.ai
149
+ ollama pull nomic-embed-text
150
+ npm run mcp:index
162
151
  ```
163
152
 
164
- Recall@3 remains 100% for exact-name queries. Semantic recall (concept-to-code) degrades to lexical-only; still effective for most programming tasks.
165
-
166
- ### Custom Ollama Host
167
-
153
+ **Lexical-only (no Ollama required):**
168
154
  ```bash
169
- # Non-standard port
170
- OLLAMA_HOST=http://localhost:11435 npm run mcp:index
171
-
172
- # Remote server
173
- OLLAMA_HOST=http://192.168.1.100:11434 npm run mcp:index
155
+ INDEXER_EMBEDDINGS=off npm run mcp:index
174
156
  ```
175
157
 
176
- ---
177
-
178
- ## IDE / Client Configuration
158
+ Lexical-only still achieves Recall@5 = 1.00 for the test suites above.
179
159
 
180
- ### Claude Desktop (`claude_desktop_config.json`)
160
+ ### 3. Configure your IDE
181
161
 
162
+ **Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
182
163
  ```json
183
164
  {
184
165
  "mcpServers": {
185
166
  "graph-indexer": {
186
167
  "command": "node",
187
- "args": ["/absolute/path/to/your/project/node_modules/graph-indexer/mcp-server.mjs"],
188
- "env": {
189
- "MCP_PROJECT_ROOT": "/absolute/path/to/your/project",
190
- "OLLAMA_HOST": "http://localhost:11434"
191
- }
168
+ "args": ["/path/to/project/node_modules/graph-indexer/mcp-server.mjs"],
169
+ "env": { "MCP_PROJECT_ROOT": "/path/to/project" }
192
170
  }
193
171
  }
194
172
  }
195
173
  ```
196
174
 
197
- ### VS Code (`.vscode/mcp.json`)
198
-
175
+ **Cursor** (`.cursor/mcp.json`):
199
176
  ```json
200
177
  {
201
- "servers": {
178
+ "mcpServers": {
202
179
  "graph-indexer": {
203
- "type": "stdio",
204
180
  "command": "node",
205
181
  "args": ["${workspaceFolder}/node_modules/graph-indexer/mcp-server.mjs"],
206
- "env": {
207
- "MCP_PROJECT_ROOT": "${workspaceFolder}",
208
- "OLLAMA_HOST": "http://localhost:11434"
209
- }
182
+ "env": { "MCP_PROJECT_ROOT": "${workspaceFolder}" }
210
183
  }
211
184
  }
212
185
  }
213
186
  ```
214
187
 
215
- ### Cursor (`.cursor/mcp.json`)
216
-
188
+ **VS Code** (`.vscode/mcp.json`):
217
189
  ```json
218
190
  {
219
- "mcpServers": {
191
+ "servers": {
220
192
  "graph-indexer": {
193
+ "type": "stdio",
221
194
  "command": "node",
222
- "args": ["${workspaceFolder}/node_modules/graph-indexer/mcp-server.mjs"],
223
- "env": {
224
- "MCP_PROJECT_ROOT": "${workspaceFolder}"
225
- }
195
+ "args": ["${workspaceFolder}/node_modules/graph-indexer/mcp-server.mjs"]
226
196
  }
227
197
  }
228
198
  }
229
199
  ```
230
200
 
231
- ---
232
-
233
- ## Agent System Prompt
201
+ ### 4. Add the agent system prompt
234
202
 
235
- Copy the contents of [`PROMPT.md`](./PROMPT.md) into your agent's system instructions. This document instructs the agent on:
236
- - When to call which tool (strict priority order)
237
- - How to write effective `query` and `exact_tokens` parameters
238
- - Workflow patterns for Q&A, implementation, refactoring, and debugging
239
- - What NOT to do (file reads without search first, raising `top_k` speculatively, etc.)
203
+ Copy the contents of [PROMPT.md](./PROMPT.md) into your AI agent's system prompt. This instructs the agent to use graph-indexer tools instead of reading files directly.
240
204
 
241
205
  ---
242
206
 
243
- ## MCP Tools Reference
207
+ ## MCP tools
244
208
 
245
209
  ### `search_code`
246
210
 
247
- Hybrid semantic + lexical search. Returns compact signature cards for all results, then fills remaining token budget with code bodies.
211
+ Searches the index by natural language query. Returns signature cards for all results, then code bodies according to the `detail` level.
248
212
 
249
213
  | Parameter | Type | Default | Description |
250
214
  | :--- | :--- | :--- | :--- |
251
- | `query` | `string` | — | Natural language description of the logic to find |
252
- | `exact_tokens` | `string?` | — | Exact symbol name to guarantee rank-1 placement |
253
- | `top_k` | `number` | `5` | Number of results (1–20) |
254
- | `min_score` | `number` | `0.3` | Minimum cosine similarity threshold |
255
- | `token_budget` | `number?` | 1500 chars | Estimated token budget for code bodies |
256
- | `include_topology` | `boolean` | `true` | Include `⬇️ Deps` / `⬆️ Used by` / `🔗 Calls` in output |
257
-
258
- **Example response:**
259
- ```
260
- #1 · validateToken [function_declaration]
261
- 📄 src/utils/jwt.ts:14–42 · ID: `a3f9c1b2` · RRF: 0.0321
262
- 💬 Validates and decodes a JWT access token. Throws on expiry.
263
- ⬇️ Deps: src/config/env.ts [JWT_SECRET] | src/utils/errors.ts [AuthError]
264
- ⬆️ Used by: src/middleware/auth.ts, src/routes/api.ts
265
- 🔗 Calls: verify, decodePayload, throwIfExpired
266
- ↩️ Expand body: get_chunk("a3f9c1b2")
267
- ```
215
+ | `query` | string | required | Natural language description of the logic to find |
216
+ | `exact_tokens` | string? | — | Exact symbol name — guarantees rank-1 placement |
217
+ | `detail` | `"signatures"` \| `"smart"` \| `"full"` | `"smart"` | Controls how much code is returned |
218
+ | `top_k` | number | `5` | Results to return (1–20) |
219
+ | `include_topology` | boolean | `true` | Include Deps / Used by / Calls in each card |
220
+ | `min_score` | number | `0.3` | Minimum score threshold |
221
+ | `token_budget` | number? | auto | Token budget for code bodies |
268
222
 
269
- ---
223
+ **`detail` levels:**
270
224
 
271
- ### `get_chunk`
225
+ | Value | Per-result cost | What you get |
226
+ | :--- | ---: | :--- |
227
+ | `"signatures"` | ~20 tokens | Name, type, params, return type, topology only |
228
+ | `"smart"` (default) | ~150 tokens | Signature + only lines relevant to the query |
229
+ | `"full"` | ~300 tokens | Signature + complete source body |
272
230
 
273
- Retrieves the full source body of one chunk by ID. Use this after `search_code` instead of reading the whole file.
274
-
275
- | Parameter | Type | Description |
276
- | :--- | :--- | :--- |
277
- | `chunk_id` | `string` | The ID shown in `search_code` results |
231
+ Start with `"signatures"`, escalate only when you need implementation details.
278
232
 
279
233
  ---
280
234
 
281
- ### `get_file_skeleton`
235
+ ### `resolve_symbol`
282
236
 
283
- Returns only the names and line ranges of all functions/classes in a file — no code bodies. ~50 tokens instead of thousands.
237
+ Looks up any symbol by exact name in O(1) — no search needed.
284
238
 
285
239
  | Parameter | Type | Description |
286
240
  | :--- | :--- | :--- |
287
- | `file_path` | `string` | Relative path (e.g. `src/components/Button.tsx`) |
241
+ | `symbol` | string | Exact function, class, or type name (e.g. `"validateToken"`) |
242
+
243
+ Returns: definition location, type signature, docstring, type references, and cross-file topology. Faster than `search_code` for known names.
288
244
 
289
245
  ---
290
246
 
291
- ### `get_call_graph`
247
+ ### `get_chunk`
292
248
 
293
- Finds every chunk in the repo that calls a specific function by name. Critical before any signature change.
249
+ Returns the full source code of one chunk by its ID from search results.
294
250
 
295
- | Parameter | Type | Description |
296
- | :--- | :--- | :--- |
297
- | `target_function` | `string` | Exact function name (e.g. `validateToken`) |
251
+ | Parameter | Type | Default | Description |
252
+ | :--- | :--- | :--- | :--- |
253
+ | `chunk_id` | string | required | ID shown in `search_code` results |
254
+ | `view` | `"full"` \| `"signature"` | `"full"` | `"full"` = complete body; `"signature"` = first line only |
298
255
 
299
256
  ---
300
257
 
301
- ### `graph://dependencies/{file_path}` Resource
258
+ ### `get_chunk_summary`
302
259
 
303
- Fetches the full bidirectional dependency topology for a file without consuming any search quota.
260
+ Returns the interface of a chunk (signature + docstring + calls) without the body. About 50 tokens vs ~300 for the full body.
304
261
 
305
- ```
306
- URI: graph://dependencies/src/middleware/auth.ts
307
- ```
262
+ | Parameter | Type | Description |
263
+ | :--- | :--- | :--- |
264
+ | `chunk_id` | string | ID from `search_code` results |
308
265
 
309
- Returns which files this file imports, and which files import it.
266
+ Use when you need to understand what a function does without reading its implementation.
310
267
 
311
268
  ---
312
269
 
313
- ## Supported Languages
314
-
315
- | Language | Extensions | Chunk Types | Import Resolution |
316
- | :--- | :--- | :--- | :--- |
317
- | TypeScript / TSX | `.ts`, `.tsx` | functions, classes, methods, exports | Absolute & relative paths |
318
- | JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | functions, classes, expressions | Absolute & relative paths |
319
- | Python | `.py` | function_definition, class_definition | Relative imports (`.`, `..`) → file paths |
320
- | Rust | `.rs` | fn, struct, enum, trait, impl | `crate::` path resolution |
321
- | Go | `.go` | function_declaration, method, type | Import spec resolution |
322
- | PHP | `.php` | function, class | include/require paths |
323
- | CSS / SCSS | `.css`, `.scss` | rule_set | — |
324
-
325
- ---
326
-
327
- ## Performance & Evaluation
270
+ ### `get_file_skeleton`
328
271
 
329
- Validate indexer quality on your own codebase:
272
+ Returns all top-level exports and definitions in a file with line numbers — no bodies.
330
273
 
331
- ```bash
332
- npm run test
333
- # or with custom Ollama:
334
- OLLAMA_HOST=http://localhost:11435 npm run test
335
- ```
274
+ | Parameter | Type | Description |
275
+ | :--- | :--- | :--- |
276
+ | `file_path` | string | Relative path (e.g. `src/utils/auth.ts`) |
336
277
 
337
- ### Benchmark Results
278
+ Useful for understanding what a file exports before deciding which chunk to read.
338
279
 
339
- | Scenario | Recall@3 | Recall@5 | Latency (p50) |
340
- | :--- | :--- | :--- | :--- |
341
- | graph-indexer own codebase (34 chunks, lexical-only) | **100%** | 100% | 0.07 ms |
342
- | graph-indexer own codebase (34 chunks, with Ollama) | **75%** | 100% | 0.14 ms |
343
- | React Native project (311 chunks, with Ollama) | **58%** | 75% | 1.02 ms |
280
+ ---
344
281
 
345
- > The React Native project numbers are without any JSDoc in the source files. Adding docstrings to exported functions (see Best Practices below) brings recall to **100% @3**.
282
+ ### `get_call_graph`
346
283
 
347
- ### Vector Search Performance
284
+ Finds all chunks that call a specific function by name.
348
285
 
349
- | Corpus size | Pure JS flat scan | HNSW (auto-activated ≥5k) |
286
+ | Parameter | Type | Description |
350
287
  | :--- | :--- | :--- |
351
- | 1,000 chunks (dim=768) | 0.9 ms/query | — |
352
- | 5,000 chunks (dim=768) | 4.5 ms/query | 1.2 ms/query |
353
- | 20,000 chunks (dim=768) | 30.5 ms/query | 3.1 ms/query |
288
+ | `target_function` | string | Exact function name (e.g. `"validateToken"`) |
354
289
 
355
- ---
290
+ Always call this before modifying an exported function — it shows every call site repo-wide.
356
291
 
357
- ## Best Practices for 100% Recall
292
+ ---
358
293
 
359
- ### 1. Add JSDoc/TSDoc to Exported Functions
294
+ ### `get_repo_map`
360
295
 
361
- The indexer embeds your docstrings alongside code. Docstrings dramatically improve semantic search:
296
+ Returns a compact symbol map of the entire codebase ordered by importance (PageRank over the dependency graph — most-imported files first). Useful for orienting in an unfamiliar codebase in ~1,500 tokens.
362
297
 
363
- ```typescript
364
- /**
365
- * Validates and decodes a JWT access token.
366
- * Throws AuthError if the token is expired or malformed.
367
- *
368
- * @param token - Raw JWT string from Authorization header
369
- * @returns Decoded payload with userId, role, and expiry
370
- */
371
- export function validateToken(token: string): TokenPayload { ... }
372
- ```
298
+ | Parameter | Type | Default | Description |
299
+ | :--- | :--- | :--- | :--- |
300
+ | `path_filter` | string? | — | Only show files whose path contains this string |
301
+ | `max_files` | number | `80` | Maximum files to include |
302
+ | `sort_by` | `"importance"` \| `"path"` | `"importance"` | Sort order |
373
303
 
374
- ### 2. Use Descriptive, Specific Names
304
+ ---
375
305
 
376
- The exact-name boost gives a free rank-1 slot to any chunk whose name exactly matches `exact_tokens`. Descriptive names make this reliable:
306
+ ### `list_index_stats`
377
307
 
378
- ```typescript
379
- // ✅ Exact-name searchable
380
- export const useNotificationPermissionStatus = () => { ... };
381
- export function calculateTripDurationInDays(start: Date, end: Date) { ... }
308
+ Returns index health: chunk count, file count, symbol table size, vector entry count, search mode (hybrid or lexical-only), daemon status, and index age.
382
309
 
383
- // ❌ Generic — impossible to target without semantic search
384
- export const util = () => { ... };
385
- export function process(data: any) { ... }
386
- ```
310
+ ---
387
311
 
388
- ### 3. Expand Minimal Utility Files
312
+ ### `graph://dependencies/{file_path}` (resource)
389
313
 
390
- Files with fewer than ~5 indexable lines produce zero or one chunk. Add error handling and validation to make them indexable:
314
+ Returns the full bidirectional dependency topology for a file: what it imports and what imports it.
391
315
 
392
- ```typescript
393
- // ❌ 2 lines — may not be indexed
394
- export const Context = createContext(null);
395
- export const useCtx = () => useContext(Context);
316
+ ---
396
317
 
397
- // ✅ Indexable with meaningful docstring + validation
398
- /**
399
- * Context for accessing authentication state throughout the component tree.
400
- */
401
- export const AuthContext = createContext<AuthState | null>(null);
318
+ ## Configuration
402
319
 
403
- /**
404
- * Hook for consuming AuthContext. Throws if used outside AuthProvider.
405
- */
406
- export function useAuth(): AuthState {
407
- const ctx = useContext(AuthContext);
408
- if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>');
409
- return ctx;
410
- }
411
- ```
320
+ ### Environment variables
412
321
 
413
- ### 4. Keep Imports Explicit and Organized
322
+ | Variable | Default | Description |
323
+ | :--- | :--- | :--- |
324
+ | `MCP_PROJECT_ROOT` | `process.cwd()` | Project root for import resolution |
325
+ | `OLLAMA_HOST` | `http://localhost:11434` | Ollama API endpoint |
326
+ | `INDEXER_EMBEDDINGS` | — | Set to `off` to disable vector embeddings |
414
327
 
415
- The topology graph follows import paths. Barrel re-exports from `index.ts` break dependency resolution — prefer direct imports:
328
+ ### Custom Ollama host
416
329
 
417
- ```typescript
418
- // ✅ Topology-friendly
419
- import { useAuthStore } from '@/stores/authStore';
420
- import { validateEmail } from '@/utils/validators';
330
+ ```bash
331
+ # Non-default port
332
+ OLLAMA_HOST=http://localhost:11435 npm run mcp:index
421
333
 
422
- // ❌ Breaks topology — indexer can't resolve what's actually imported
423
- import { useAuthStore, validateEmail } from '@/index';
334
+ # Remote host
335
+ OLLAMA_HOST=http://192.168.1.100:11434 npm run mcp:index
424
336
  ```
425
337
 
426
338
  ---
427
339
 
428
- ## File Structure
340
+ ## Supported languages
429
341
 
430
- ```
431
- graph-indexer/
432
- ├── core-engine.mjs # In-memory index: vector matrix, TF-IDF, RRF search, save/load
433
- ├── parser-utils.mjs # Tree-sitter AST parsing, 6 language grammars, Ollama embeddings
434
- ├── indexer.mjs # CLI bootstrap: scan repo → parse → embed → write index
435
- ├── watch-daemon.mjs # Chokidar daemon: incremental real-time index updates
436
- ├── mcp-server.mjs # MCP stdio server: exposes tools + graph:// resources
437
- ├── PROMPT.md # Agent system prompt — paste into your IDE's agent instructions
438
- ├── tests/
439
- │ ├── eval-harness.mjs # Recall@k + latency evaluation against the indexer's own codebase
440
- │ └── react-native-test.mjs # Real-world quality test on a 311-chunk React Native project
441
- └── package.json
442
- ```
342
+ | Language | Extensions | Chunk types extracted |
343
+ | :--- | :--- | :--- |
344
+ | TypeScript / TSX | `.ts`, `.tsx` | functions, classes, methods, exports |
345
+ | JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | functions, classes, expressions |
346
+ | Python | `.py` | function_definition, class_definition |
347
+ | Go | `.go` | function_declaration, method, type |
348
+ | Rust | `.rs` | fn, struct, enum, trait, impl |
349
+ | Java | `.java` | class, method, interface, constructor, enum |
350
+ | Kotlin | `.kt`, `.kts` | function, class, object, companion |
351
+ | C# | `.cs` | class, method, interface, property, enum |
352
+ | Ruby | `.rb` | method, class, module |
353
+ | PHP | `.php` | function, class |
354
+ | CSS / SCSS | `.css`, `.scss` | rule_set |
443
355
 
444
356
  ---
445
357
 
446
- ## Mathematical Foundation
447
-
448
- ### Sublinear TF Scaling
358
+ ## Requirements
449
359
 
450
- Term frequencies use sublinear scaling to prevent common tokens (`return`, `const`) from burying business-logic terms:
451
-
452
- $$w(t,d) = 1 + \log(f_{t,d})$$
453
-
454
- ### Reciprocal Rank Fusion
360
+ | | |
361
+ | :--- | :--- |
362
+ | **Node.js** | v18+ (ES Modules) |
363
+ | **Ollama** | Optional — enables semantic search. Pull `nomic-embed-text`. |
364
+ | **Disk** | `code-index.json` + `code-index.embeddings.bin` (both gitignored by default) |
455
365
 
456
- Vector and lexical results are merged by rank position, not raw incompatible scores:
366
+ ---
457
367
 
458
- $$\text{RRF}(d) = \sum_{i \in \{\text{vec, lex}\}} \frac{1}{K + \text{rank}_i(d)}, \quad K = 60$$
368
+ ## Troubleshooting
459
369
 
460
- ### Exact-Name Boost
370
+ **Index takes too long to build**
371
+ - Verify Ollama is running: `curl http://localhost:11434/api/tags`
372
+ - Fall back to lexical-only: `INDEXER_EMBEDDINGS=off npm run mcp:index`
461
373
 
462
- Any chunk whose name exactly matches `exact_tokens` receives an additive bonus equal to the maximum single-list RRF contribution:
374
+ **Search returns irrelevant results**
375
+ - Use `exact_tokens` for known symbol names
376
+ - Verify the chunk is indexed with `list_index_stats()`
377
+ - Increase `top_k` to see more candidates
463
378
 
464
- $$\Delta\text{score} = \frac{1}{K + 1}$$
379
+ **MCP server won't connect**
380
+ - Verify `MCP_PROJECT_ROOT` points to the indexed directory
381
+ - Check that `code-index.json` exists: `ls -la code-index.json`
382
+ - Test the server manually: `node mcp-server.mjs`
465
383
 
466
- This guarantees rank-1 placement for any exact symbol name query.
384
+ **Results are stale after file changes**
385
+ - The background daemon auto-updates; if changes aren't reflected, run `npm run mcp:index` manually
386
+ - Check daemon status with `list_index_stats()`
467
387
 
468
388
  ---
469
389
 
470
- ## How the Index Files Work
471
-
472
- | File | Purpose | Typical Size |
473
- | :--- | :--- | :--- |
474
- | `code-index.json` | Chunk metadata (names, file paths, docstrings, calls, graph) | ~200 KB / 1k chunks |
475
- | `code-index.embeddings.bin` | Binary `float32` embedding vectors (4.8× smaller than JSON) | ~3 MB / 1k chunks @768-dim |
390
+ ## Development
476
391
 
477
- Both files are written atomically via a `tmp → rename` pattern to prevent corruption on crash. Both should be added to `.gitignore`.
392
+ ```bash
393
+ git clone https://github.com/MaquinaTech/graph-indexer.git
394
+ cd graph-indexer
395
+ npm install
396
+ npm run mcp:index # index this repo
397
+ npm run test # run test suite (lexical-only)
398
+ npm run mcp:start # start MCP server
399
+ ```
478
400
 
479
401
  ---
480
402
 
481
- ## Environment Variables
403
+ ## Best practices for better search quality
482
404
 
483
- | Variable | Default | Description |
484
- | :--- | :--- | :--- |
485
- | `OLLAMA_HOST` | `http://localhost:11434` | Ollama API base URL |
486
- | `INDEXER_EMBEDDINGS` | — | Set to `off` to disable embeddings entirely |
487
- | `MCP_PROJECT_ROOT` | `process.cwd()` | Absolute path to the project being indexed |
405
+ **Write descriptive docstrings.** The indexer embeds docstrings alongside code. Better documentation directly improves semantic search quality.
488
406
 
489
- ---
407
+ **Use specific function names.** Functions with exact, descriptive names (e.g. `fetchAndCacheUserProfile`) hit the exact-name boost (2.0×) and return as rank-1 results when queried by name.
490
408
 
491
- ## License & Intellectual Property
409
+ **Export named functions, not anonymous ones.** Anonymous default exports cannot be targeted by `resolve_symbol` and score lower in name-boost ranking.
492
410
 
493
- Released under the [GNU General Public License v3.0](LICENSE).
494
-
495
- This license ensures:
496
- - **Copyleft protection**: Any software that incorporates this code must also be released under GPL-3.0.
497
- - **Attribution**: The original author (MaquinaTech) must be credited.
498
- - **Source availability**: Any distributed modifications must include source code.
499
-
500
- Commercial use is permitted but requires GPL-3.0 compliance. For proprietary licensing inquiries, contact the author.
411
+ **Avoid catch-all utility files.** Large files that mix unrelated utilities produce weaker per-chunk signals. One responsibility per module indexes more cleanly.
501
412
 
502
413
  ---
503
414
 
504
- ## Author
415
+ ## License
505
416
 
506
- Built and maintained by **MaquinaTech**.
417
+ Released under the [MIT License](LICENSE). Copyright (c) 2026 MaquinaTech.
507
418
 
508
- - **GitHub:** [github.com/MaquinaTech](https://github.com/MaquinaTech)
509
- - **NPM:** [npmjs.com/package/graph-indexer](https://www.npmjs.com/package/graph-indexer)
510
- - **Issues:** [github.com/MaquinaTech/graph-indexer/issues](https://github.com/MaquinaTech/graph-indexer/issues)
419
+ ---
511
420
 
512
- Issues, pull requests, and feature discussions are welcome.
421
+ Built by [MaquinaTech](https://github.com/MaquinaTech) · [Issues](https://github.com/MaquinaTech/graph-indexer/issues) · [npm](https://www.npmjs.com/package/graph-indexer)