@esankhan3/code-search-mcp 0.0.2 → 0.2.0
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 +343 -248
- package/dist/__tests__/config-resolver.test.js +153 -0
- package/dist/__tests__/daemon-rpc.test.js +100 -0
- package/dist/__tests__/observability.test.js +69 -0
- package/dist/__tests__/projects.test.js +141 -0
- package/dist/backends/daemon-client.js +87 -0
- package/dist/backends/in-process.js +104 -0
- package/dist/backends/index.js +29 -0
- package/dist/cli/index.js +231 -0
- package/dist/core/config.js +362 -49
- package/dist/core/env-config.js +45 -43
- package/dist/daemon/index.js +119 -0
- package/dist/daemon/rpc-server.js +99 -0
- package/dist/daemon/watcher.js +105 -0
- package/dist/index.js +7 -0
- package/dist/observability/logger.js +66 -0
- package/dist/observability/metrics.js +138 -0
- package/dist/projects/registry.js +146 -0
- package/dist/resources/resources.js +3 -3
- package/dist/server.js +6 -2
- package/dist/tools/graph.js +1 -1
- package/dist/tools/index-tools.js +1 -1
- package/dist/tools/profile.js +2 -2
- package/dist/tools/search.js +1 -1
- package/dist/transports/http-transport.js +38 -0
- package/package.json +31 -12
- package/scripts/smoke/run-smoke.sh +167 -0
- package/dist/core/ast-graph-builder.js +0 -800
- package/dist/core/chunker.js +0 -368
- package/dist/core/claude-runner.js +0 -173
- package/dist/core/cross-repo-detector.js +0 -887
- package/dist/core/embedder.js +0 -334
- package/dist/core/file-walker.js +0 -186
- package/dist/core/git-diff.js +0 -160
- package/dist/core/graph-metrics.js +0 -233
- package/dist/core/indexer.js +0 -552
- package/dist/core/project-graph-builder.js +0 -202
- package/dist/core/query-classifier.js +0 -144
- package/dist/core/query-router.js +0 -210
- package/dist/core/rag-evaluator.js +0 -213
- package/dist/core/repo-profiler.js +0 -383
- package/dist/core/reranker.js +0 -218
- package/dist/core/retriever.js +0 -242
- package/dist/core/semantic-edge-detector.js +0 -147
- package/dist/core/service-mesh-inferrer.js +0 -241
- package/dist/core/structural-hasher.js +0 -316
- package/dist/core/tree-sitter-parser.js +0 -539
- package/dist/core/vector-store.js +0 -259
- package/dist/core/workspace-detector.js +0 -481
- /package/dist/{core → backends}/types.js +0 -0
package/README.md
CHANGED
|
@@ -1,333 +1,428 @@
|
|
|
1
|
-
|
|
1
|
+
# @esankhan3/code-search-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Your whole codebase, in any MCP client.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
[](https://modelcontextprotocol.io)
|
|
11
|
-
[]()
|
|
12
|
-
[]()
|
|
13
|
-
|
|
14
|
-
[Quick Start](#quick-start) · [Tools](#tools) · [Deploy](#deployment) · [Configuration](#configuration)
|
|
15
|
-
|
|
16
|
-
</div>
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## What You Get
|
|
21
|
-
|
|
22
|
-
Ask questions in natural language. The server searches, traces, and analyzes across all your repos.
|
|
23
|
-
|
|
24
|
-
| Query | Tool | Result |
|
|
25
|
-
|:------|:-----|:-------|
|
|
26
|
-
| "How does authentication work across services?" | `search_code` | 8 results from 3 repos, ranked by relevance |
|
|
27
|
-
| "What calls handlePayment?" | `find_callers` | 12 call sites traced across api-gateway and billing-service |
|
|
28
|
-
| "If I change user.ts, what breaks?" | `impact_analysis` | 4 files in 2 repos identified as affected |
|
|
29
|
-
|
|
30
|
-
Works with Claude Code, Claude Desktop, Cursor, and any MCP client.
|
|
5
|
+
A Model Context Protocol server that wraps `@anvil/knowledge-core`
|
|
6
|
+
behind an MCP-compliant tool surface. Point Claude Code, Claude
|
|
7
|
+
Desktop, or Cursor at it and your agent gains hybrid search,
|
|
8
|
+
caller tracing, and impact analysis across every repo you've
|
|
9
|
+
indexed — local or remote.
|
|
31
10
|
|
|
32
11
|
---
|
|
33
12
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
```mermaid
|
|
37
|
-
graph LR
|
|
38
|
-
A[Your AI Client] -- stdio / http --> B[Code Search MCP Server]
|
|
39
|
-
B --> C[LanceDB vectors]
|
|
40
|
-
B --> D[AST Graph]
|
|
41
|
-
B --> E[Git Repos]
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
---
|
|
13
|
+
## Why "code search" needs more than grep
|
|
45
14
|
|
|
46
|
-
|
|
15
|
+
MCP gave us a clean way to expose tools to LLM agents. What's
|
|
16
|
+
missing is a *good* tool to expose. `grep` over your repo answers
|
|
17
|
+
"where is this string." Vector search over your repo answers
|
|
18
|
+
"what's semantically near this." Neither answers "who calls this
|
|
19
|
+
function and which other repos depend on it."
|
|
47
20
|
|
|
48
|
-
|
|
49
|
-
|
|
21
|
+
**code-search-mcp gives agents the answer.** Same hybrid retriever
|
|
22
|
+
that powers Anvil's pipelines, exposed through MCP. Agents get
|
|
23
|
+
AST-aware chunks, cross-repo graph traversal, and a whole-project
|
|
24
|
+
view — without you writing a single tool.
|
|
50
25
|
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
For GitHub orgs:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
claude mcp add code-search -- npx @esankhan3/code-search-mcp --local github:your-org --token ghp_xxx
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
</details>
|
|
62
|
-
|
|
63
|
-
<details>
|
|
64
|
-
<summary><strong>Connect to a team server</strong></summary>
|
|
65
|
-
|
|
66
|
-
**Claude Code:**
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
claude mcp add code-search \
|
|
70
|
-
-e CODE_SEARCH_SERVER=https://your-server:3100 \
|
|
71
|
-
-e CODE_SEARCH_API_KEY=your-api-key \
|
|
72
|
-
-- npx @esankhan3/code-search-mcp
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
**Claude Desktop / Cursor (JSON config):**
|
|
76
|
-
|
|
77
|
-
```json
|
|
26
|
+
```jsonc
|
|
27
|
+
// claude_desktop_config.json
|
|
78
28
|
{
|
|
79
29
|
"mcpServers": {
|
|
80
30
|
"code-search": {
|
|
81
31
|
"command": "code-search-mcp",
|
|
82
|
-
"
|
|
83
|
-
"CODE_SEARCH_SERVER": "https://your-server:3100",
|
|
84
|
-
"CODE_SEARCH_API_KEY": "your-api-key"
|
|
85
|
-
}
|
|
32
|
+
"args": ["--local", "/path/to/your/projects"]
|
|
86
33
|
}
|
|
87
34
|
}
|
|
88
35
|
}
|
|
89
36
|
```
|
|
90
37
|
|
|
91
|
-
|
|
38
|
+
That's the whole setup. Claude Desktop now knows your codebase.
|
|
92
39
|
|
|
93
|
-
|
|
94
|
-
<summary><strong>Deploy the server</strong> (Docker)</summary>
|
|
40
|
+
---
|
|
95
41
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
42
|
+
## Three bin entries (one install)
|
|
43
|
+
|
|
44
|
+
The npm package ships three binaries. Pick the one that matches the
|
|
45
|
+
job — none of them need a separate install.
|
|
46
|
+
|
|
47
|
+
| Bin | Use case |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `code-search-mcp` | The MCP-server entry. Three operating modes below. |
|
|
50
|
+
| `code-search` | Standalone CLI: `index` / `query` / `status` / `reset` / `daemon` / `serve` / `mcp`. |
|
|
51
|
+
| `code-search-daemon` | Long-running indexer with file-watcher and JSON-RPC socket. |
|
|
52
|
+
|
|
53
|
+
### Standalone CLI
|
|
54
|
+
```sh
|
|
55
|
+
code-search index . # index current dir
|
|
56
|
+
code-search query "where do we verify JWTs" # hybrid by default
|
|
57
|
+
code-search query "createPet" --mode bm25 --top-k 5 # exact-identifier mode
|
|
58
|
+
code-search status --project petshop # JSON report
|
|
59
|
+
code-search reset --project petshop # drop the index
|
|
60
|
+
code-search --print-config # resolved config (redacted)
|
|
99
61
|
```
|
|
62
|
+
Every `CodeSearchConfig` leaf is settable as `--<dotted-path>` —
|
|
63
|
+
e.g. `--embedding.provider codestral`, `--retrieval.max-chunks 12`,
|
|
64
|
+
`--no-indexing.auto-index`.
|
|
100
65
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
curl -X POST http://localhost:3100/index \
|
|
105
|
-
-H 'Content-Type: application/json' \
|
|
106
|
-
-d '{"path": "/repos/my-service", "project": "my-project"}'
|
|
66
|
+
### Daemon mode (long-running indexer)
|
|
67
|
+
```sh
|
|
68
|
+
code-search-daemon --workspace /path/to/repos
|
|
107
69
|
```
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
70
|
+
Builds the index once, then watches the workspace and debounce-
|
|
71
|
+
reindexes on file events. Listens on a UDS socket at
|
|
72
|
+
`<dataDir>/daemon/<project>.sock` (named pipe on Windows). The CLI
|
|
73
|
+
and MCP server automatically prefer the daemon when it's alive,
|
|
74
|
+
falling back to in-process when it isn't.
|
|
75
|
+
|
|
76
|
+
### MCP — three modes (`code-search-mcp` argv)
|
|
77
|
+
|
|
78
|
+
**Remote proxy (default)**
|
|
79
|
+
```sh
|
|
80
|
+
code-search-mcp # default — proxies to remote
|
|
81
|
+
code-search-mcp --remote URL
|
|
113
82
|
```
|
|
83
|
+
Spawns a stdio MCP server that forwards to a remote HTTP server.
|
|
84
|
+
Zero local setup, zero local index — perfect for hosted
|
|
85
|
+
deployments where the index lives in the cloud and dev machines
|
|
86
|
+
stay light. Auth via `CODE_SEARCH_API_KEY`.
|
|
87
|
+
|
|
88
|
+
**Local**
|
|
89
|
+
```sh
|
|
90
|
+
code-search-mcp --local /path/to/repos
|
|
91
|
+
code-search-mcp --local github:my-org/my-pattern
|
|
92
|
+
```
|
|
93
|
+
Discovers every repo under a path (or clones a GitHub org), builds
|
|
94
|
+
the knowledge base, and serves over stdio. Works fully offline if
|
|
95
|
+
your embedder + reranker are local (Ollama).
|
|
114
96
|
|
|
115
|
-
|
|
97
|
+
**Serve**
|
|
98
|
+
```sh
|
|
99
|
+
code-search-mcp --serve --port 4000 --auth api-key
|
|
100
|
+
```
|
|
101
|
+
Boots an HTTP server (Streamable HTTP transport, SSE optional)
|
|
102
|
+
with `/mcp`, `/health`, `/ready`, `/version`, `/status`,
|
|
103
|
+
`/metrics` (Prometheus), `/admin/api/status`, and an admin
|
|
104
|
+
`POST /index`. Use this to host one index for a whole team — every
|
|
105
|
+
dev points their client at the same URL.
|
|
116
106
|
|
|
117
107
|
---
|
|
118
108
|
|
|
119
|
-
##
|
|
109
|
+
## Tool surface
|
|
110
|
+
|
|
111
|
+
Eleven tools across four categories. Every one of them maps to a
|
|
112
|
+
function in `@anvil/knowledge-core` — you're getting the same
|
|
113
|
+
retrieval pipeline that powers the Anvil dashboard.
|
|
114
|
+
|
|
115
|
+
### Search
|
|
116
|
+
| Tool | What it does |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `search_code` | Hybrid retrieval — vector + BM25 + graph + rerank |
|
|
119
|
+
| `search_semantic` | Vector-only (paraphrases, intent) |
|
|
120
|
+
| `search_exact` | BM25-only (identifiers, error codes) |
|
|
121
|
+
|
|
122
|
+
### Graph
|
|
123
|
+
| Tool | What it does |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `get_repo_graph` | Single-repo AST graph |
|
|
126
|
+
| `get_cross_repo_edges` | Inter-repo edges (Kafka, HTTP, gRPC, shared types, …) |
|
|
127
|
+
| `find_callers` | Who calls this function |
|
|
128
|
+
| `find_dependencies` | What this function calls |
|
|
129
|
+
| `impact_analysis` | What breaks if this changes |
|
|
130
|
+
|
|
131
|
+
### Profiles
|
|
132
|
+
| Tool | What it does |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `list_repos` | All indexed repos with profiles |
|
|
135
|
+
| `get_repo_profile` | Single repo's tech stack, structure, key entry points |
|
|
136
|
+
|
|
137
|
+
### Index
|
|
138
|
+
| Tool | What it does |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `index_status` | Last indexed SHA, chunk count, embedding provider |
|
|
141
|
+
|
|
142
|
+
Plus four MCP resources via `code-search://`:
|
|
143
|
+
`repos`, `system-graph`, `repo/{name}/profile`, `repo/{name}/graph`.
|
|
120
144
|
|
|
121
|
-
|
|
122
|
-
Hybrid vector+BM25, semantic-only, and keyword search across all indexed repos.
|
|
123
|
-
|
|
124
|
-
**Graph** -- `get_repo_graph` · `find_callers` · `find_dependencies` · `impact_analysis` · `get_cross_repo_edges`
|
|
125
|
-
AST knowledge graph, caller tracing, dependency lookup, change impact, and cross-repo connections.
|
|
145
|
+
---
|
|
126
146
|
|
|
127
|
-
|
|
128
|
-
Indexed repo listing and LLM-generated repo profiles with role, stack, and endpoints.
|
|
147
|
+
## What makes the search good
|
|
129
148
|
|
|
130
|
-
|
|
131
|
-
Chunk count, embedding provider, repo list, and last-indexed timestamp.
|
|
149
|
+
This isn't a thin wrapper. The retrieval pipeline runs:
|
|
132
150
|
|
|
133
|
-
|
|
151
|
+
1. **Vector ⫽ BM25 in parallel** — semantic + lexical recall.
|
|
152
|
+
2. **Reciprocal Rank Fusion** — combine without one dominating.
|
|
153
|
+
3. **AST tripartite expansion** — pull in callers, callees, type
|
|
154
|
+
refs via the project graph.
|
|
155
|
+
4. **Cross-encoder rerank** — Qwen3-Reranker by default; Cohere /
|
|
156
|
+
Voyage / OpenAI-compatible swappable.
|
|
134
157
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
| `GET /health` | Server status, active sessions, index readiness |
|
|
139
|
-
| `GET /status` | Live indexing progress, phase, percent, history |
|
|
158
|
+
A query classifier picks adaptive weights — identifier queries
|
|
159
|
+
lean BM25, natural-language leans vector, error codes lean both.
|
|
160
|
+
You don't tune anything; the retriever does it per query.
|
|
140
161
|
|
|
141
162
|
---
|
|
142
163
|
|
|
143
|
-
##
|
|
164
|
+
## Multi-repo by design
|
|
165
|
+
|
|
166
|
+
Unlike single-repo code-search MCPs, this one is built for the
|
|
167
|
+
real world: a team has *many* repos, and the interesting
|
|
168
|
+
questions cross them. Where does this Kafka topic get consumed?
|
|
169
|
+
Which services depend on this proto? What service-mesh edges
|
|
170
|
+
exist between web and api?
|
|
144
171
|
|
|
145
|
-
|
|
146
|
-
|
|
172
|
+
Fourteen cross-repo edge strategies cover shared types, Kafka,
|
|
173
|
+
HTTP, gRPC, databases, env vars, npm/workspace deps, k8s,
|
|
174
|
+
docker-compose, proto, Redis, S3, and shared constants. Plus an
|
|
175
|
+
LLM-inferred semantic edge layer. `find_callers` works *across
|
|
176
|
+
repos*. So does `impact_analysis`.
|
|
147
177
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
| 2. Git diff via Merkle DAG | File | Only processes added/modified/deleted files from `git diff` |
|
|
152
|
-
| 3. Content hash SHA-256 | File | Fallback dedup when git diff is unavailable |
|
|
153
|
-
| 4. Embedding diff against LanceDB | Chunk | Only new/changed chunks sent to the embedding provider |
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Observability
|
|
154
181
|
|
|
155
|
-
|
|
156
|
-
|
|
182
|
+
`code-search-mcp --serve` exposes these endpoints out of the box —
|
|
183
|
+
no scrape config or APM agent required.
|
|
157
184
|
|
|
158
|
-
|
|
185
|
+
| Endpoint | Returns |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `GET /health` | Liveness ping with project + uptime |
|
|
188
|
+
| `GET /ready` | `200` only after first index completes |
|
|
189
|
+
| `GET /version` | Package + Node + platform |
|
|
190
|
+
| `GET /status` | Current indexing phase + last-success history |
|
|
191
|
+
| `GET /metrics` | Prometheus text format (queries, latency histogram, chunks, embedding/LLM calls, errors) |
|
|
192
|
+
| `GET /admin/api/status` | JSON status dashboard for the admin UI |
|
|
159
193
|
|
|
160
|
-
|
|
194
|
+
Set `CODE_SEARCH_STRUCTURED_LOGS=1` for one-JSON-line-per-event
|
|
195
|
+
stderr logs, and `CODE_SEARCH_TELEMETRY_RECORD_QUERIES=1` to opt
|
|
196
|
+
into recording query text in spans (off by default — queries are
|
|
197
|
+
PII-shaped).
|
|
161
198
|
|
|
162
|
-
|
|
163
|
-
<summary>Embedding Providers</summary>
|
|
199
|
+
---
|
|
164
200
|
|
|
165
|
-
|
|
166
|
-
|:---------|:--------|:------|
|
|
167
|
-
| Ollama (local, free) | `OLLAMA_HOST` | `bge-m3` |
|
|
168
|
-
| Mistral / Codestral | `MISTRAL_API_KEY` | `codestral-embed-2505` |
|
|
169
|
-
| OpenAI | `OPENAI_API_KEY` | `text-embedding-3-large` |
|
|
170
|
-
| Voyage AI | `VOYAGE_API_KEY` | `voyage-code-3` |
|
|
171
|
-
| Gemini | OAuth via `~/.gemini/` | `text-embedding-004` |
|
|
172
|
-
| Any OpenAI-compatible | `CODE_SEARCH_EMBEDDING_BASE_URL` | Custom |
|
|
201
|
+
## Multi-project / multi-tenant
|
|
173
202
|
|
|
174
|
-
|
|
203
|
+
Drop project entries under `<dataDir>/projects/<name>/project.yaml`:
|
|
175
204
|
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
205
|
+
```yaml
|
|
206
|
+
workspace: /Users/me/repos/petshop
|
|
207
|
+
scopes: [team-shop]
|
|
208
|
+
quotas:
|
|
209
|
+
max_queries_per_minute: 60
|
|
210
|
+
max_embedding_cost_usd: 5
|
|
181
211
|
```
|
|
182
212
|
|
|
183
|
-
|
|
213
|
+
`ProjectRegistry` reads them at boot. `projectAccessAllowed`
|
|
214
|
+
gates by scope (`*` is admin). `checkProjectQuota` enforces a
|
|
215
|
+
sliding 1-minute window per `(identity, project)`.
|
|
184
216
|
|
|
185
|
-
|
|
186
|
-
<summary>LLM Configuration -- repo profiling and service mesh inference</summary>
|
|
217
|
+
---
|
|
187
218
|
|
|
188
|
-
|
|
219
|
+
## Server features
|
|
189
220
|
|
|
190
|
-
|
|
191
|
-
-
|
|
192
|
-
-
|
|
221
|
+
### Auth, three flavors
|
|
222
|
+
`none` (local-only, default 127.0.0.1 binding), `api-key`
|
|
223
|
+
(`Authorization: Bearer ...`, timing-safe compare), `jwt` (HS256,
|
|
224
|
+
signature + `exp` + `iss` validated). Public binding without auth
|
|
225
|
+
logs a warning, because that's what should happen.
|
|
193
226
|
|
|
194
|
-
|
|
227
|
+
### Rate limiting
|
|
228
|
+
In-memory sliding 1-minute window keyed by identity subject. Tune
|
|
229
|
+
per-deployment.
|
|
195
230
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
231
|
+
### Auto-reindex
|
|
232
|
+
`CODE_SEARCH_REINDEX_INTERVAL=30m` (or `1h`, `6h`, `0` to disable)
|
|
233
|
+
runs an incremental re-index in the background. Skips itself if a
|
|
234
|
+
manual index is already running. The timer is `unref`'d so it
|
|
235
|
+
doesn't keep the process alive.
|
|
201
236
|
|
|
202
|
-
|
|
237
|
+
### Status tracking
|
|
238
|
+
Every index call lands in a 50-entry FIFO history with start
|
|
239
|
+
timestamp, success / error, last duration. Available at
|
|
240
|
+
`GET /status`.
|
|
203
241
|
|
|
204
|
-
|
|
205
|
-
|
|
242
|
+
### Streamable HTTP sessions
|
|
243
|
+
Per-session `StreamableHTTPServerTransport`, max 100 concurrent
|
|
244
|
+
sessions, 30 min TTL. The remote proxy captures `mcp-session-id`
|
|
245
|
+
for continuity. SSE transport available as a fallback.
|
|
206
246
|
|
|
207
|
-
|
|
247
|
+
### Admin index endpoint
|
|
248
|
+
`POST /index` with `{ project, dirPath?, opts? }` triggers a
|
|
249
|
+
fresh index. Gated against concurrent runs. Auth-required when
|
|
250
|
+
auth is on.
|
|
208
251
|
|
|
209
|
-
|
|
252
|
+
---
|
|
210
253
|
|
|
211
|
-
|
|
212
|
-
<summary>Supported Languages</summary>
|
|
254
|
+
## How it fits with the rest of Anvil
|
|
213
255
|
|
|
214
|
-
|
|
256
|
+
```
|
|
257
|
+
┌─────────────────────────┐
|
|
258
|
+
│ MCP client │
|
|
259
|
+
│ (Claude Code, │
|
|
260
|
+
│ Claude Desktop, │
|
|
261
|
+
│ Cursor, …) │
|
|
262
|
+
└────────────┬────────────┘
|
|
263
|
+
│ stdio / HTTP
|
|
264
|
+
▼
|
|
265
|
+
┌─────────────────────────┐
|
|
266
|
+
│ code-search-mcp │
|
|
267
|
+
│ (this package) │
|
|
268
|
+
│ tools + resources │
|
|
269
|
+
└────────────┬────────────┘
|
|
270
|
+
│ wraps
|
|
271
|
+
▼
|
|
272
|
+
┌─────────────────────────┐
|
|
273
|
+
│ @anvil/knowledge-core │
|
|
274
|
+
│ AST chunks + │
|
|
275
|
+
│ project graph + │
|
|
276
|
+
│ hybrid retriever │
|
|
277
|
+
└────────────┬────────────┘
|
|
278
|
+
│ on disk
|
|
279
|
+
▼
|
|
280
|
+
~/.anvil/knowledge-base/<project>/
|
|
281
|
+
```
|
|
215
282
|
|
|
216
|
-
|
|
283
|
+
Three different fronts, one knowledge stack:
|
|
217
284
|
|
|
218
|
-
|
|
285
|
+
- **The CLI** indexes via `anvil index` and retrieves during
|
|
286
|
+
pipelines.
|
|
287
|
+
- **The dashboard** browses the project graph and surfaces
|
|
288
|
+
retrieval results in pipeline UI.
|
|
289
|
+
- **code-search-mcp** exposes the same retriever to any MCP
|
|
290
|
+
client.
|
|
219
291
|
|
|
220
|
-
|
|
221
|
-
|
|
292
|
+
If you've already indexed a project with `anvil index`, this
|
|
293
|
+
server picks it up — same `~/.anvil/knowledge-base/` path, same
|
|
294
|
+
LanceDB store, same graph files.
|
|
222
295
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Configuration — defaults → file → env → CLI flags
|
|
299
|
+
|
|
300
|
+
Single resolver in `src/core/config.ts`. Five precedence layers
|
|
301
|
+
(lowest → highest):
|
|
302
|
+
|
|
303
|
+
1. compiled-in `DEFAULTS`
|
|
304
|
+
2. `~/.code-search/config.yaml` (user-global)
|
|
305
|
+
3. `<workspaceDir>/.code-search.yaml` (per-workspace overlay)
|
|
306
|
+
4. `CODE_SEARCH_*` env vars
|
|
307
|
+
5. CLI flags via `--<dotted-path>` (e.g. `--embedding.provider codestral`)
|
|
308
|
+
|
|
309
|
+
Run `code-search --print-config` or `code-search-mcp --print-config`
|
|
310
|
+
any time to see the resolved struct (secrets redacted).
|
|
311
|
+
|
|
312
|
+
| Var (`CODE_SEARCH_*`) | Effect |
|
|
313
|
+
|---|---|
|
|
314
|
+
| `SERVER` | Remote URL (proxy mode) |
|
|
315
|
+
| `API_KEY` | API key for proxy or serve modes |
|
|
316
|
+
| `DATA_DIR` | Override the index storage root |
|
|
317
|
+
| `PORT` / `HOST` / `TRANSPORT` | HTTP server bind |
|
|
318
|
+
| `AUTH_MODE` / `AUTH_API_KEYS` / `AUTH_JWT_SECRET` | Server auth |
|
|
319
|
+
| `REINDEX_INTERVAL` | `30m` / `1h` / `6h` / `0` |
|
|
320
|
+
| `EMBEDDING_PROVIDER` | `auto` / `codestral` / `voyage` / `openai` / `ollama` / `openai-compatible` / `custom` / `gemini-oauth` |
|
|
321
|
+
| `EMBEDDING_MODEL` / `EMBEDDING_DIMENSIONS` | Model + dims |
|
|
322
|
+
| `EMBEDDING_API_KEY` / `EMBEDDING_BASE_URL` | Credentials |
|
|
323
|
+
| `OLLAMA_HOST` | Default `http://localhost:11434` |
|
|
324
|
+
| `RERANKER_PROVIDER` | `ollama` (default) / `cohere` / `voyage` / `openai-compatible` / `custom` / `none` |
|
|
325
|
+
| `RERANKER_MODEL` / `RERANKER_API_KEY` / `RERANKER_BASE_URL` | Reranker config |
|
|
326
|
+
| `RETRIEVAL_MAX_CHUNKS` / `RETRIEVAL_MAX_TOKENS` | Retrieval tuning |
|
|
327
|
+
| `AUTO_INDEX` | `false` to disable startup auto-index |
|
|
328
|
+
| `DAEMON_DISABLED` / `DAEMON_AUTO_SPAWN` | Daemon backend control |
|
|
329
|
+
| `LLM_MODE` / `LLM_PROVIDER` / `LLM_MODEL` / `LLM_API_KEY` / `LLM_BASE_URL` | Profiling + service-mesh LLM. Pre-promoted to `ANVIL_LLM_*` so the agent-core integration is silent. |
|
|
330
|
+
| `INDEXING_DEBOUNCE_MS` / `CHUNKING_MAX_TOKENS` | Indexer knobs |
|
|
331
|
+
| `METRICS_ENABLED` / `STRUCTURED_LOGS` / `TELEMETRY_RECORD_QUERIES` | Observability |
|
|
332
|
+
|
|
333
|
+
### Per-workspace config
|
|
334
|
+
|
|
335
|
+
Drop a `.code-search.yaml` at the root of your workspace:
|
|
336
|
+
|
|
337
|
+
```yaml
|
|
338
|
+
embedding:
|
|
339
|
+
provider: codestral
|
|
340
|
+
model: codestral-embed-2505
|
|
341
|
+
dimensions: 1024
|
|
342
|
+
reranker:
|
|
343
|
+
provider: ollama
|
|
344
|
+
model: qwen2.5-coder:7b # the default that actually answers
|
|
345
|
+
retrieval:
|
|
346
|
+
max_chunks: 12
|
|
347
|
+
hybrid_weights: { vector: 0.55, bm25: 0.3, graph: 0.15 }
|
|
348
|
+
indexing:
|
|
349
|
+
auto_index: true
|
|
350
|
+
debounce_ms: 500
|
|
239
351
|
```
|
|
240
352
|
|
|
241
|
-
|
|
353
|
+
CLI flags override env, env overrides this file, this file overrides
|
|
354
|
+
the user-global `~/.code-search/config.yaml`, that overrides
|
|
355
|
+
`DEFAULTS`.
|
|
242
356
|
|
|
243
357
|
---
|
|
244
358
|
|
|
245
|
-
##
|
|
359
|
+
## Philosophy
|
|
246
360
|
|
|
247
|
-
|
|
361
|
+
**Multi-repo or nothing.** Real codebases span repos. Code search
|
|
362
|
+
that doesn't is a toy.
|
|
248
363
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
| `CODE_SEARCH_API_KEY` | Client | API key for remote server |
|
|
253
|
-
| `CODE_SEARCH_EMBEDDING_PROVIDER` | Server | `auto` \| `codestral` \| `openai` \| `voyage` \| `ollama` \| `custom` |
|
|
254
|
-
| `CODE_SEARCH_LLM_API_KEY` | Server | API key for LLM provider (repo profiling, service mesh) |
|
|
255
|
-
| `CODE_SEARCH_AUTH_MODE` | Server | `none` \| `api-key` \| `jwt` |
|
|
364
|
+
**No vendor LLM SDK.** Anything LLM-driven (repo profiling,
|
|
365
|
+
semantic edges) routes through `@anvil/agent-core`'s single-shot —
|
|
366
|
+
the same router, retries, and cost ledger as the rest of Anvil.
|
|
256
367
|
|
|
257
|
-
|
|
258
|
-
|
|
368
|
+
**Three modes, one binary.** Remote proxy for hosted, local for
|
|
369
|
+
solo, serve for teams. The dispatcher is `src/index.ts:argv`.
|
|
259
370
|
|
|
260
|
-
**
|
|
371
|
+
**Stateless sessions.** HTTP sessions are in-memory by design.
|
|
372
|
+
Restart drops them; clients re-init on first request. No persistent
|
|
373
|
+
session store to maintain.
|
|
261
374
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
--transport <mode> streamable-http | sse
|
|
267
|
-
|
|
268
|
-
code-search-mcp --local [source] [options]
|
|
269
|
-
--project <name> Project name (default: derived from source)
|
|
270
|
-
--token <token> GitHub token (or GITHUB_TOKEN env)
|
|
271
|
-
--force Force full re-index
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
**All environment variables:**
|
|
275
|
-
|
|
276
|
-
| Variable | Description | Default |
|
|
277
|
-
|:---------|:------------|:--------|
|
|
278
|
-
| `CODE_SEARCH_TRANSPORT` | Transport mode | `streamable-http` |
|
|
279
|
-
| `CODE_SEARCH_PORT` | HTTP port | `3100` |
|
|
280
|
-
| `CODE_SEARCH_HOST` | Bind address | `0.0.0.0` |
|
|
281
|
-
| `CODE_SEARCH_AUTH_MODE` | Auth mode | `none` |
|
|
282
|
-
| `CODE_SEARCH_AUTH_API_KEYS` | Comma-separated API keys | -- |
|
|
283
|
-
| `CODE_SEARCH_AUTH_JWT_SECRET` | JWT signing secret | -- |
|
|
284
|
-
| `CODE_SEARCH_EMBEDDING_PROVIDER` | Embedding provider | `auto` |
|
|
285
|
-
| `CODE_SEARCH_EMBEDDING_API_KEY` | Unified embedding API key | -- |
|
|
286
|
-
| `CODE_SEARCH_EMBEDDING_BASE_URL` | Custom embedding endpoint | -- |
|
|
287
|
-
| `CODE_SEARCH_EMBEDDING_MODEL` | Custom embedding model | -- |
|
|
288
|
-
| `CODE_SEARCH_RERANKER_PROVIDER` | `ollama` \| `cohere` \| `voyage` \| `custom` \| `none` | `ollama` |
|
|
289
|
-
| `CODE_SEARCH_RERANKER_BASE_URL` | Custom reranker endpoint | -- |
|
|
290
|
-
| `CODE_SEARCH_RERANKER_MODEL` | Custom reranker model | -- |
|
|
291
|
-
| `CODE_SEARCH_DATA_DIR` | Data directory override | -- |
|
|
292
|
-
| `CODE_SEARCH_REINDEX_INTERVAL` | Auto-reindex schedule (`30m`, `1h`, `6h`, `0`) | `0` |
|
|
293
|
-
| `CODE_SEARCH_RATE_LIMIT_PER_MINUTE` | Rate limit per identity | `100` |
|
|
294
|
-
| `CODE_SEARCH_LLM_MODE` | `cli` \| `api` \| `none` | `cli` |
|
|
295
|
-
| `CODE_SEARCH_LLM_PROVIDER` | `anthropic` \| `openai` \| `ollama` \| `custom` | `anthropic` |
|
|
296
|
-
| `CODE_SEARCH_LLM_API_KEY` | API key for LLM provider | -- |
|
|
297
|
-
| `CODE_SEARCH_LLM_MODEL` | Model for profiling + service mesh | `claude-sonnet-4-6` |
|
|
298
|
-
| `CODE_SEARCH_LLM_BASE_URL` | Custom LLM endpoint | -- |
|
|
299
|
-
|
|
300
|
-
</details>
|
|
375
|
+
**Security defaults that don't bite.** Default bind is `127.0.0.1`
|
|
376
|
+
when auth is `none`. API keys compared timing-safe. JWT
|
|
377
|
+
signature + exp + iss validated. Public binding without auth
|
|
378
|
+
logs a warning instead of pretending it's fine.
|
|
301
379
|
|
|
302
380
|
---
|
|
303
381
|
|
|
304
|
-
##
|
|
382
|
+
## End-to-end smoke test (T1–T8)
|
|
383
|
+
|
|
384
|
+
The eight-case smoke that pins the standalone product contract lives
|
|
385
|
+
at `scripts/smoke/run-smoke.sh`. It spins up a tiny `pet-shop` git
|
|
386
|
+
fixture under `/tmp/cs-smoke/`, then runs:
|
|
305
387
|
|
|
306
|
-
|
|
307
|
-
|
|
388
|
+
| # | Verifies |
|
|
389
|
+
|---|---|
|
|
390
|
+
| **T1** | `--print-config` env + CLI flag layering, redaction |
|
|
391
|
+
| **T2** | `code-search index` produces a real Ollama-backed index; no stale legacy-env warning |
|
|
392
|
+
| **T3** | `code-search query --mode vector` and `--mode bm25` both return the right file |
|
|
393
|
+
| **T4** | `code-search status` reports provider + chunk count |
|
|
394
|
+
| **T5** | Issue-#6 reproduction: `CODE_SEARCH_EMBEDDING_PROVIDER=openai` actually constructs the OpenAI embedder (skipped when `OPENAI_API_KEY` is unset) |
|
|
395
|
+
| **T6** | Vector-space mismatch guard hard-errors on cross-provider query |
|
|
396
|
+
| **T7** | Daemon round-trip: spawn, UDS socket, query through RPC, file-watcher debounce-reindex |
|
|
397
|
+
| **T8** | HTTP serve: `/health` `/ready` `/version` `/metrics` `/admin/api/status` all 200; Prom text format |
|
|
308
398
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
399
|
+
Run it locally:
|
|
400
|
+
|
|
401
|
+
```sh
|
|
402
|
+
npm -w @esankhan3/code-search-mcp run build # required first
|
|
403
|
+
npm -w @esankhan3/code-search-mcp run smoke # T1–T8
|
|
312
404
|
```
|
|
313
405
|
|
|
314
|
-
|
|
406
|
+
Requirements: Ollama on `$OLLAMA_HOST` (default `localhost:11434`)
|
|
407
|
+
with `bge-m3` pulled. Pass `OPENAI_API_KEY=...` to exercise the T5
|
|
408
|
+
provider-switch path against the real OpenAI endpoint.
|
|
315
409
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
410
|
+
The smoke runs **outside CI** — it requires Ollama + real models —
|
|
411
|
+
so it's a hand-rolled local check, not a release gate. The release
|
|
412
|
+
workflow does enforce a static guard that every bin entry
|
|
413
|
+
(`code-search-mcp` / `code-search` / `code-search-daemon`) is in the
|
|
414
|
+
built tarball before publishing.
|
|
321
415
|
|
|
322
416
|
---
|
|
323
417
|
|
|
324
|
-
##
|
|
325
|
-
|
|
326
|
-
MIT
|
|
418
|
+
## Status
|
|
327
419
|
|
|
328
|
-
|
|
329
|
-
|
|
420
|
+
Stable. The retrieval and graph layers move with
|
|
421
|
+
`@anvil/knowledge-core`; the MCP surface is locked. New tools
|
|
422
|
+
land additively. P0–P8 of the standalone-product plan
|
|
423
|
+
(`docs/CODE-SEARCH-MCP-STANDALONE-PLAN.md`) have landed; F1–F4
|
|
424
|
+
follow-up fixes verified by `scripts/smoke/run-smoke.sh`.
|
|
330
425
|
|
|
331
|
-
|
|
426
|
+
---
|
|
332
427
|
|
|
333
|
-
|
|
428
|
+
## Part of [Anvil](../../) — the AI development pipeline.
|