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/LICENSE +21 -677
- package/PROMPT.md +160 -133
- package/README.md +244 -335
- package/core-engine.mjs +396 -121
- package/indexer.mjs +19 -19
- package/init.mjs +241 -0
- package/mcp-server.mjs +441 -117
- package/package.json +16 -4
- package/parser-utils.mjs +357 -17
- package/watch-daemon.mjs +25 -16
package/README.md
CHANGED
|
@@ -1,512 +1,421 @@
|
|
|
1
|
-
|
|
1
|
+
# graph-indexer
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<strong>Zero-
|
|
5
|
-
<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-
|
|
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
|
-
##
|
|
17
|
+
## What it does
|
|
17
18
|
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Why it matters
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
When an AI agent works on a real codebase without graph-indexer, it reads files to find code:
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
52
|
+
### Token usage per query: with vs. without graph-indexer
|
|
84
53
|
|
|
85
|
-
|
|
|
86
|
-
| :--- | :--- |
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
| **
|
|
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
|
-
|
|
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
|
-
###
|
|
70
|
+
### Search quality
|
|
101
71
|
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
"scripts": {
|
|
110
|
-
"mcp:index": "idx-index --repo .",
|
|
111
|
-
"mcp:watch": "idx-watch",
|
|
112
|
-
"mcp:start": "idx-mcp"
|
|
113
|
-
}
|
|
114
|
-
```
|
|
85
|
+
---
|
|
115
86
|
|
|
116
|
-
|
|
87
|
+
## How it works
|
|
117
88
|
|
|
118
|
-
|
|
119
|
-
npm run mcp:index
|
|
120
|
-
```
|
|
89
|
+
### Indexing
|
|
121
90
|
|
|
122
|
-
|
|
91
|
+
When you run `npm run mcp:index`, graph-indexer:
|
|
123
92
|
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
### Search
|
|
131
104
|
|
|
132
|
-
|
|
105
|
+
When an agent calls `search_code("payment validation middleware")`:
|
|
133
106
|
|
|
134
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
##
|
|
133
|
+
## Getting started
|
|
143
134
|
|
|
144
|
-
|
|
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
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
-
|
|
148
|
+
# Install Ollama: https://ollama.ai
|
|
149
|
+
ollama pull nomic-embed-text
|
|
150
|
+
npm run mcp:index
|
|
162
151
|
```
|
|
163
152
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
### Custom Ollama Host
|
|
167
|
-
|
|
153
|
+
**Lexical-only (no Ollama required):**
|
|
168
154
|
```bash
|
|
169
|
-
|
|
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
|
-
###
|
|
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": ["/
|
|
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
|
-
|
|
198
|
-
|
|
175
|
+
**Cursor** (`.cursor/mcp.json`):
|
|
199
176
|
```json
|
|
200
177
|
{
|
|
201
|
-
"
|
|
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
|
-
|
|
216
|
-
|
|
188
|
+
**VS Code** (`.vscode/mcp.json`):
|
|
217
189
|
```json
|
|
218
190
|
{
|
|
219
|
-
"
|
|
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 [
|
|
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
|
|
207
|
+
## MCP tools
|
|
244
208
|
|
|
245
209
|
### `search_code`
|
|
246
210
|
|
|
247
|
-
|
|
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` |
|
|
252
|
-
| `exact_tokens` |
|
|
253
|
-
| `
|
|
254
|
-
| `
|
|
255
|
-
| `
|
|
256
|
-
| `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
### `
|
|
235
|
+
### `resolve_symbol`
|
|
282
236
|
|
|
283
|
-
|
|
237
|
+
Looks up any symbol by exact name in O(1) — no search needed.
|
|
284
238
|
|
|
285
239
|
| Parameter | Type | Description |
|
|
286
240
|
| :--- | :--- | :--- |
|
|
287
|
-
| `
|
|
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
|
-
### `
|
|
247
|
+
### `get_chunk`
|
|
292
248
|
|
|
293
|
-
|
|
249
|
+
Returns the full source code of one chunk by its ID from search results.
|
|
294
250
|
|
|
295
|
-
| Parameter | Type | Description |
|
|
296
|
-
| :--- | :--- | :--- |
|
|
297
|
-
| `
|
|
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
|
-
### `
|
|
258
|
+
### `get_chunk_summary`
|
|
302
259
|
|
|
303
|
-
|
|
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
|
-
|
|
307
|
-
|
|
262
|
+
| Parameter | Type | Description |
|
|
263
|
+
| :--- | :--- | :--- |
|
|
264
|
+
| `chunk_id` | string | ID from `search_code` results |
|
|
308
265
|
|
|
309
|
-
|
|
266
|
+
Use when you need to understand what a function does without reading its implementation.
|
|
310
267
|
|
|
311
268
|
---
|
|
312
269
|
|
|
313
|
-
|
|
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
|
-
|
|
272
|
+
Returns all top-level exports and definitions in a file with line numbers — no bodies.
|
|
330
273
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
278
|
+
Useful for understanding what a file exports before deciding which chunk to read.
|
|
338
279
|
|
|
339
|
-
|
|
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
|
-
|
|
282
|
+
### `get_call_graph`
|
|
346
283
|
|
|
347
|
-
|
|
284
|
+
Finds all chunks that call a specific function by name.
|
|
348
285
|
|
|
349
|
-
|
|
|
286
|
+
| Parameter | Type | Description |
|
|
350
287
|
| :--- | :--- | :--- |
|
|
351
|
-
|
|
|
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
|
-
|
|
292
|
+
---
|
|
358
293
|
|
|
359
|
-
###
|
|
294
|
+
### `get_repo_map`
|
|
360
295
|
|
|
361
|
-
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
|
|
304
|
+
---
|
|
375
305
|
|
|
376
|
-
|
|
306
|
+
### `list_index_stats`
|
|
377
307
|
|
|
378
|
-
|
|
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
|
-
|
|
384
|
-
export const util = () => { ... };
|
|
385
|
-
export function process(data: any) { ... }
|
|
386
|
-
```
|
|
310
|
+
---
|
|
387
311
|
|
|
388
|
-
###
|
|
312
|
+
### `graph://dependencies/{file_path}` (resource)
|
|
389
313
|
|
|
390
|
-
|
|
314
|
+
Returns the full bidirectional dependency topology for a file: what it imports and what imports it.
|
|
391
315
|
|
|
392
|
-
|
|
393
|
-
// ❌ 2 lines — may not be indexed
|
|
394
|
-
export const Context = createContext(null);
|
|
395
|
-
export const useCtx = () => useContext(Context);
|
|
316
|
+
---
|
|
396
317
|
|
|
397
|
-
|
|
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
|
-
|
|
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
|
-
|
|
328
|
+
### Custom Ollama host
|
|
416
329
|
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
423
|
-
|
|
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
|
-
##
|
|
340
|
+
## Supported languages
|
|
429
341
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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
|
-
##
|
|
447
|
-
|
|
448
|
-
### Sublinear TF Scaling
|
|
358
|
+
## Requirements
|
|
449
359
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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
|
-
|
|
366
|
+
---
|
|
457
367
|
|
|
458
|
-
|
|
368
|
+
## Troubleshooting
|
|
459
369
|
|
|
460
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
##
|
|
403
|
+
## Best practices for better search quality
|
|
482
404
|
|
|
483
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
415
|
+
## License
|
|
505
416
|
|
|
506
|
-
|
|
417
|
+
Released under the [MIT License](LICENSE). Copyright (c) 2026 MaquinaTech.
|
|
507
418
|
|
|
508
|
-
|
|
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
|
-
|
|
421
|
+
Built by [MaquinaTech](https://github.com/MaquinaTech) · [Issues](https://github.com/MaquinaTech/graph-indexer/issues) · [npm](https://www.npmjs.com/package/graph-indexer)
|