@brainbank/mcp 0.1.4 → 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 +11 -15
- package/dist/mcp-server.js +162 -185
- package/dist/mcp-server.js.map +1 -1
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -76,26 +76,22 @@ brainbank serve
|
|
|
76
76
|
| Variable | Description | Default |
|
|
77
77
|
|----------|-------------|---------|
|
|
78
78
|
| `BRAINBANK_REPO` | Default repo path (fallback if `repo` param not provided) | — |
|
|
79
|
-
| `BRAINBANK_EMBEDDING` | Embedding provider: `openai`
|
|
79
|
+
| `BRAINBANK_EMBEDDING` | Embedding provider: `local`, `openai`, `perplexity`, `perplexity-context` | `local` |
|
|
80
80
|
| `OPENAI_API_KEY` | Required when using `openai` embeddings | — |
|
|
81
|
+
| `PERPLEXITY_API_KEY` | Required when using `perplexity` or `perplexity-context` embeddings | — |
|
|
81
82
|
|
|
82
83
|
> The agent passes the `repo` parameter per tool call based on the active workspace — no hardcoded paths needed.
|
|
83
84
|
|
|
84
|
-
##
|
|
85
|
+
## Tools (6)
|
|
85
86
|
|
|
86
87
|
| Tool | Description |
|
|
87
88
|
|------|-------------|
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `brainbank_index` | Trigger incremental code/git indexing |
|
|
93
|
-
| `brainbank_stats` | Index statistics (files, commits, chunks) |
|
|
89
|
+
| `brainbank_search` | Unified search — `mode: hybrid` (default), `vector`, or `keyword` |
|
|
90
|
+
| `brainbank_context` | Formatted context block for a task (code + git + co-edits) |
|
|
91
|
+
| `brainbank_index` | Trigger incremental code/git/docs indexing |
|
|
92
|
+
| `brainbank_stats` | Index statistics (files, commits, chunks, collections) |
|
|
94
93
|
| `brainbank_history` | Git history for a specific file |
|
|
95
|
-
| `
|
|
96
|
-
| `brainbank_collection_add` | Add item to a KV collection |
|
|
97
|
-
| `brainbank_collection_search` | Semantic search within a collection |
|
|
98
|
-
| `brainbank_collection_trim` | Trim a collection to N most recent items |
|
|
94
|
+
| `brainbank_collection` | KV collection ops — `action: add`, `search`, or `trim` |
|
|
99
95
|
|
|
100
96
|
## Multi-Workspace
|
|
101
97
|
|
|
@@ -103,10 +99,10 @@ The MCP server maintains a pool of BrainBank instances — one per unique `repo`
|
|
|
103
99
|
|
|
104
100
|
```typescript
|
|
105
101
|
// Agent working in one workspace
|
|
106
|
-
|
|
102
|
+
brainbank_search({ query: "login form", repo: "/Users/you/project-a" })
|
|
107
103
|
|
|
108
104
|
// Agent switches to another project — new instance auto-created
|
|
109
|
-
|
|
105
|
+
brainbank_search({ query: "API routes", repo: "/Users/you/project-b" })
|
|
110
106
|
```
|
|
111
107
|
|
|
112
108
|
Instances are cached in memory after first initialization (~480ms), so subsequent queries to the same repo are fast.
|
|
@@ -117,7 +113,7 @@ Instances are cached in memory after first initialization (~480ms), so subsequen
|
|
|
117
113
|
AI Agent ←→ stdio ←→ @brainbank/mcp ←→ BrainBank core ←→ SQLite
|
|
118
114
|
```
|
|
119
115
|
|
|
120
|
-
1. Agent sends an MCP tool call (e.g., `
|
|
116
|
+
1. Agent sends an MCP tool call (e.g., `brainbank_search`)
|
|
121
117
|
2. Server routes to the correct BrainBank instance (by `repo` path)
|
|
122
118
|
3. BrainBank executes the search against its local SQLite database
|
|
123
119
|
4. Results returned as structured JSON to the agent
|
package/dist/mcp-server.js
CHANGED
|
@@ -29,9 +29,18 @@ async function createEmbeddingProvider() {
|
|
|
29
29
|
const { OpenAIEmbedding } = await import("brainbank");
|
|
30
30
|
return new OpenAIEmbedding();
|
|
31
31
|
}
|
|
32
|
+
if (embeddingEnv === "perplexity") {
|
|
33
|
+
const { PerplexityEmbedding } = await import("brainbank");
|
|
34
|
+
return new PerplexityEmbedding();
|
|
35
|
+
}
|
|
36
|
+
if (embeddingEnv === "perplexity-context") {
|
|
37
|
+
const { PerplexityContextEmbedding } = await import("brainbank");
|
|
38
|
+
return new PerplexityContextEmbedding();
|
|
39
|
+
}
|
|
32
40
|
return void 0;
|
|
33
41
|
}
|
|
34
42
|
__name(createEmbeddingProvider, "createEmbeddingProvider");
|
|
43
|
+
var MAX_POOL_SIZE = 10;
|
|
35
44
|
var _pool = /* @__PURE__ */ new Map();
|
|
36
45
|
var _sharedReranker = void 0;
|
|
37
46
|
var _sharedEmbedding = void 0;
|
|
@@ -51,50 +60,132 @@ async function getBrainBank(targetRepo) {
|
|
|
51
60
|
);
|
|
52
61
|
}
|
|
53
62
|
const resolved = rp.replace(/\/+$/, "");
|
|
54
|
-
if (_pool.has(resolved))
|
|
63
|
+
if (_pool.has(resolved)) {
|
|
64
|
+
const entry = _pool.get(resolved);
|
|
65
|
+
try {
|
|
66
|
+
const codeStats = entry.brain.indexer("code")?.stats?.();
|
|
67
|
+
if (codeStats && codeStats.hnswSize === 0) {
|
|
68
|
+
const dbPath = path.join(resolved, ".brainbank", "brainbank.db");
|
|
69
|
+
const dbSize = fs.existsSync(dbPath) ? fs.statSync(dbPath).size : 0;
|
|
70
|
+
if (dbSize > 1e5) {
|
|
71
|
+
evictPool(resolved);
|
|
72
|
+
} else {
|
|
73
|
+
entry.lastAccess = Date.now();
|
|
74
|
+
return entry.brain;
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
entry.lastAccess = Date.now();
|
|
78
|
+
return entry.brain;
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
entry.lastAccess = Date.now();
|
|
82
|
+
return entry.brain;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
55
85
|
await ensureShared();
|
|
86
|
+
if (_pool.size >= MAX_POOL_SIZE) {
|
|
87
|
+
let oldest;
|
|
88
|
+
let oldestTime = Infinity;
|
|
89
|
+
for (const [key, entry] of _pool) {
|
|
90
|
+
if (entry.lastAccess < oldestTime) {
|
|
91
|
+
oldestTime = entry.lastAccess;
|
|
92
|
+
oldest = key;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (oldest) evictPool(oldest);
|
|
96
|
+
}
|
|
97
|
+
const brain = await _createBrain(resolved);
|
|
98
|
+
_pool.set(resolved, { brain, lastAccess: Date.now() });
|
|
99
|
+
return brain;
|
|
100
|
+
}
|
|
101
|
+
__name(getBrainBank, "getBrainBank");
|
|
102
|
+
async function _createBrain(resolved) {
|
|
56
103
|
const opts = { repoPath: resolved, reranker: _sharedReranker };
|
|
57
104
|
if (_sharedEmbedding) {
|
|
58
105
|
opts.embeddingProvider = _sharedEmbedding;
|
|
59
106
|
opts.embeddingDims = _sharedEmbedding.dims;
|
|
60
107
|
}
|
|
61
108
|
const brain = new BrainBank(opts).use(code({ repoPath: resolved })).use(git({ repoPath: resolved })).use(docs());
|
|
62
|
-
|
|
63
|
-
|
|
109
|
+
try {
|
|
110
|
+
await brain.initialize();
|
|
111
|
+
} catch (err) {
|
|
112
|
+
if (err?.message?.includes("Invalid the given array length")) {
|
|
113
|
+
const dbPath = path.join(resolved, ".brainbank", "brainbank.db");
|
|
114
|
+
try {
|
|
115
|
+
fs.unlinkSync(dbPath);
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
fs.unlinkSync(dbPath + "-wal");
|
|
120
|
+
} catch {
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
fs.unlinkSync(dbPath + "-shm");
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
const fresh = new BrainBank(opts).use(code({ repoPath: resolved })).use(git({ repoPath: resolved })).use(docs());
|
|
127
|
+
await fresh.initialize();
|
|
128
|
+
return fresh;
|
|
129
|
+
}
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
64
132
|
return brain;
|
|
65
133
|
}
|
|
66
|
-
__name(
|
|
134
|
+
__name(_createBrain, "_createBrain");
|
|
135
|
+
function evictPool(resolved) {
|
|
136
|
+
const entry = _pool.get(resolved);
|
|
137
|
+
if (entry) {
|
|
138
|
+
try {
|
|
139
|
+
entry.brain.close();
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
_pool.delete(resolved);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
__name(evictPool, "evictPool");
|
|
67
146
|
var server = new McpServer({
|
|
68
147
|
name: "brainbank",
|
|
69
|
-
version: "0.
|
|
148
|
+
version: "0.2.0"
|
|
70
149
|
});
|
|
71
150
|
server.registerTool(
|
|
72
151
|
"brainbank_search",
|
|
73
152
|
{
|
|
74
153
|
title: "BrainBank Search",
|
|
75
|
-
description: "
|
|
154
|
+
description: "Search indexed code and git commits. Supports three modes:\n- hybrid (default): vector + BM25 fused with RRF \u2014 best quality\n- vector: semantic similarity only\n- keyword: instant BM25 for exact terms, function names, error messages",
|
|
76
155
|
inputSchema: z.object({
|
|
77
|
-
query: z.string().describe("
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
156
|
+
query: z.string().describe("Search query \u2014 works with both keywords and natural language"),
|
|
157
|
+
mode: z.enum(["hybrid", "vector", "keyword"]).optional().default("hybrid").describe("Search strategy"),
|
|
158
|
+
codeK: z.number().optional().default(8).describe("Max code results"),
|
|
159
|
+
gitK: z.number().optional().default(5).describe("Max git results"),
|
|
160
|
+
minScore: z.number().optional().default(0.25).describe("Minimum similarity score (0-1), only for vector mode"),
|
|
161
|
+
collections: z.record(z.string(), z.number()).optional().describe(
|
|
162
|
+
'Max results per source. Reserved: "code", "git", "docs". Any other key = KV collection.'
|
|
163
|
+
),
|
|
164
|
+
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
|
|
82
165
|
})
|
|
83
166
|
},
|
|
84
|
-
async ({ query, codeK, gitK, minScore, repo }) => {
|
|
167
|
+
async ({ query, mode, codeK, gitK, minScore, collections, repo }) => {
|
|
85
168
|
const brainbank = await getBrainBank(repo);
|
|
86
|
-
|
|
169
|
+
let results;
|
|
170
|
+
if (mode === "keyword") {
|
|
171
|
+
results = await brainbank.searchBM25(query, { codeK, gitK });
|
|
172
|
+
} else if (mode === "vector") {
|
|
173
|
+
results = await brainbank.search(query, { codeK, gitK, minScore });
|
|
174
|
+
} else {
|
|
175
|
+
results = await brainbank.hybridSearch(query, { codeK, gitK, collections });
|
|
176
|
+
}
|
|
87
177
|
if (results.length === 0) {
|
|
88
|
-
return { content: [{ type: "text", text: "No results found
|
|
178
|
+
return { content: [{ type: "text", text: "No results found." }] };
|
|
89
179
|
}
|
|
90
|
-
|
|
180
|
+
const modeLabel = mode === "keyword" ? "Keyword (BM25)" : mode === "vector" ? "Vector" : "Hybrid (Vector + BM25 \u2192 RRF)";
|
|
181
|
+
return { content: [{ type: "text", text: formatResults(results, modeLabel) }] };
|
|
91
182
|
}
|
|
92
183
|
);
|
|
93
184
|
server.registerTool(
|
|
94
185
|
"brainbank_context",
|
|
95
186
|
{
|
|
96
187
|
title: "BrainBank Context",
|
|
97
|
-
description: "Get a formatted knowledge context block for a task. Returns relevant code
|
|
188
|
+
description: "Get a formatted knowledge context block for a task. Returns relevant code, git history, and co-edit patterns as markdown.",
|
|
98
189
|
inputSchema: z.object({
|
|
99
190
|
task: z.string().describe("Description of the task you need context for"),
|
|
100
191
|
affectedFiles: z.array(z.string()).optional().default([]).describe("Files you plan to modify (improves co-edit suggestions)"),
|
|
@@ -117,13 +208,13 @@ server.registerTool(
|
|
|
117
208
|
"brainbank_index",
|
|
118
209
|
{
|
|
119
210
|
title: "BrainBank Index",
|
|
120
|
-
description: "Index (or re-index)
|
|
211
|
+
description: "Index (or re-index) code, git history, and docs. Incremental \u2014 only changed files are processed.",
|
|
121
212
|
inputSchema: z.object({
|
|
122
|
-
modules: z.array(z.enum(["code", "git", "docs"])).optional().describe("Which modules to index
|
|
123
|
-
docsPath: z.string().optional().describe("Path to a docs folder to register and index
|
|
124
|
-
forceReindex: z.boolean().optional().default(false).describe("Force re-index of all files
|
|
213
|
+
modules: z.array(z.enum(["code", "git", "docs"])).optional().describe("Which modules to index (default: all)"),
|
|
214
|
+
docsPath: z.string().optional().describe("Path to a docs folder to register and index"),
|
|
215
|
+
forceReindex: z.boolean().optional().default(false).describe("Force re-index of all files"),
|
|
125
216
|
gitDepth: z.number().optional().default(500).describe("Number of git commits to index"),
|
|
126
|
-
repo: z.string().optional().describe("Repository path
|
|
217
|
+
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
|
|
127
218
|
})
|
|
128
219
|
},
|
|
129
220
|
async ({ modules, docsPath, forceReindex, gitDepth, repo }) => {
|
|
@@ -132,7 +223,7 @@ server.registerTool(
|
|
|
132
223
|
const absPath = path.resolve(docsPath);
|
|
133
224
|
const collName = path.basename(absPath);
|
|
134
225
|
try {
|
|
135
|
-
brainbank.
|
|
226
|
+
brainbank.addCollection({
|
|
136
227
|
name: collName,
|
|
137
228
|
path: absPath,
|
|
138
229
|
pattern: "**/*.md",
|
|
@@ -163,7 +254,7 @@ server.registerTool(
|
|
|
163
254
|
"brainbank_stats",
|
|
164
255
|
{
|
|
165
256
|
title: "BrainBank Stats",
|
|
166
|
-
description: "Get statistics
|
|
257
|
+
description: "Get index statistics: file count, code chunks, git commits, HNSW sizes, KV collections.",
|
|
167
258
|
inputSchema: z.object({
|
|
168
259
|
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
|
|
169
260
|
})
|
|
@@ -171,35 +262,20 @@ server.registerTool(
|
|
|
171
262
|
async ({ repo }) => {
|
|
172
263
|
const brainbank = await getBrainBank(repo);
|
|
173
264
|
const s = brainbank.stats();
|
|
174
|
-
const lines = [
|
|
175
|
-
"## BrainBank Knowledge Base Stats",
|
|
176
|
-
""
|
|
177
|
-
];
|
|
265
|
+
const lines = ["## BrainBank Stats", ""];
|
|
178
266
|
if (s.code) {
|
|
179
|
-
lines.push(
|
|
180
|
-
lines.push(`- Files indexed: ${s.code.files}`);
|
|
181
|
-
lines.push(`- Code chunks: ${s.code.chunks}`);
|
|
182
|
-
lines.push(`- HNSW vectors: ${s.code.hnswSize}`);
|
|
183
|
-
lines.push("");
|
|
267
|
+
lines.push(`**Code**: ${s.code.files} files, ${s.code.chunks} chunks, ${s.code.hnswSize} vectors`);
|
|
184
268
|
}
|
|
185
269
|
if (s.git) {
|
|
186
|
-
lines.push(
|
|
187
|
-
lines.push(`- Commits indexed: ${s.git.commits}`);
|
|
188
|
-
lines.push(`- Files tracked: ${s.git.filesTracked}`);
|
|
189
|
-
lines.push(`- Co-edit pairs: ${s.git.coEdits}`);
|
|
190
|
-
lines.push(`- HNSW vectors: ${s.git.hnswSize}`);
|
|
191
|
-
lines.push("");
|
|
270
|
+
lines.push(`**Git**: ${s.git.commits} commits, ${s.git.filesTracked} files, ${s.git.coEdits} co-edit pairs`);
|
|
192
271
|
}
|
|
193
272
|
if (s.documents) {
|
|
194
|
-
lines.push(
|
|
195
|
-
lines.push(`- Collections: ${s.documents.collections}`);
|
|
196
|
-
lines.push(`- Documents: ${s.documents.documents}`);
|
|
197
|
-
lines.push(`- HNSW vectors: ${s.documents.hnswSize}`);
|
|
198
|
-
lines.push("");
|
|
273
|
+
lines.push(`**Docs**: ${s.documents.collections} collections, ${s.documents.documents} documents`);
|
|
199
274
|
}
|
|
200
275
|
const kvNames = brainbank.listCollectionNames();
|
|
201
276
|
if (kvNames.length > 0) {
|
|
202
|
-
lines.push("
|
|
277
|
+
lines.push("");
|
|
278
|
+
lines.push("**KV Collections**:");
|
|
203
279
|
for (const name of kvNames) {
|
|
204
280
|
const coll = brainbank.collection(name);
|
|
205
281
|
lines.push(`- ${name}: ${coll.count()} items`);
|
|
@@ -212,9 +288,9 @@ server.registerTool(
|
|
|
212
288
|
"brainbank_history",
|
|
213
289
|
{
|
|
214
290
|
title: "BrainBank File History",
|
|
215
|
-
description: "Get
|
|
291
|
+
description: "Get git commit history for a file. Shows changes, authors, and line counts.",
|
|
216
292
|
inputSchema: z.object({
|
|
217
|
-
filePath: z.string().describe('File path (relative or partial
|
|
293
|
+
filePath: z.string().describe('File path (relative or partial, e.g. "auth.ts")'),
|
|
218
294
|
limit: z.number().optional().default(20).describe("Max commits to return"),
|
|
219
295
|
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
|
|
220
296
|
})
|
|
@@ -233,155 +309,56 @@ server.registerTool(
|
|
|
233
309
|
}
|
|
234
310
|
);
|
|
235
311
|
server.registerTool(
|
|
236
|
-
"
|
|
237
|
-
{
|
|
238
|
-
title: "BrainBank Co-Edits",
|
|
239
|
-
description: "Find files that historically change together with a given file. Useful for understanding dependencies and ensuring you do not miss related changes.",
|
|
240
|
-
inputSchema: z.object({
|
|
241
|
-
filePath: z.string().describe("File path to check co-edit relationships for"),
|
|
242
|
-
limit: z.number().optional().default(5).describe("Max co-edit suggestions"),
|
|
243
|
-
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
|
|
244
|
-
})
|
|
245
|
-
},
|
|
246
|
-
async ({ filePath, limit, repo }) => {
|
|
247
|
-
const brainbank = await getBrainBank(repo);
|
|
248
|
-
const suggestions = brainbank.coEdits(filePath, limit);
|
|
249
|
-
if (suggestions.length === 0) {
|
|
250
|
-
return { content: [{ type: "text", text: `No co-edit patterns found for "${filePath}"` }] };
|
|
251
|
-
}
|
|
252
|
-
const lines = [`## Co-Edits for: ${filePath}`, ""];
|
|
253
|
-
lines.push("Files that frequently change together:");
|
|
254
|
-
for (const s of suggestions) {
|
|
255
|
-
lines.push(`- **${s.file}** (changed together ${s.count} times)`);
|
|
256
|
-
}
|
|
257
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
258
|
-
}
|
|
259
|
-
);
|
|
260
|
-
server.registerTool(
|
|
261
|
-
"brainbank_hybrid_search",
|
|
262
|
-
{
|
|
263
|
-
title: "BrainBank Hybrid Search",
|
|
264
|
-
description: "Best quality search: combines semantic vector search + BM25 keyword search using Reciprocal Rank Fusion. Catches both exact keyword matches AND conceptual similarities. Use this by default for all searches.",
|
|
265
|
-
inputSchema: z.object({
|
|
266
|
-
query: z.string().describe("Search query \u2014 works with both keywords and natural language"),
|
|
267
|
-
codeK: z.number().optional().default(8).describe("Max code results (shorthand for collections.code)"),
|
|
268
|
-
gitK: z.number().optional().default(5).describe("Max git results (shorthand for collections.git)"),
|
|
269
|
-
collections: z.record(z.string(), z.number()).optional().describe(
|
|
270
|
-
'Max results per source. Reserved keys: "code", "git", "docs" control built-in indexers. Any other key is a KV collection. Example: { "code": 8, "git": 5, "errors": 3, "slack": 2 }'
|
|
271
|
-
),
|
|
272
|
-
repo: z.string().optional().describe("Repository path to search (default: BRAINBANK_REPO)")
|
|
273
|
-
})
|
|
274
|
-
},
|
|
275
|
-
async ({ query, codeK, gitK, collections, repo }) => {
|
|
276
|
-
const t0 = performance.now();
|
|
277
|
-
const brainbank = await getBrainBank(repo);
|
|
278
|
-
const t1 = performance.now();
|
|
279
|
-
const results = await brainbank.hybridSearch(query, { codeK, gitK, collections });
|
|
280
|
-
const t2 = performance.now();
|
|
281
|
-
const timing = `
|
|
282
|
-
|
|
283
|
-
\u23F1 getBrainBank: ${(t1 - t0).toFixed(0)}ms | hybridSearch: ${(t2 - t1).toFixed(0)}ms | total: ${(t2 - t0).toFixed(0)}ms`;
|
|
284
|
-
if (results.length === 0) {
|
|
285
|
-
return { content: [{ type: "text", text: "No results found for this query." + timing }] };
|
|
286
|
-
}
|
|
287
|
-
return { content: [{ type: "text", text: formatResults(results, "Hybrid Search (Vector + BM25 \u2192 RRF)") + timing }] };
|
|
288
|
-
}
|
|
289
|
-
);
|
|
290
|
-
server.registerTool(
|
|
291
|
-
"brainbank_keyword_search",
|
|
292
|
-
{
|
|
293
|
-
title: "BrainBank Keyword Search",
|
|
294
|
-
description: "Instant BM25 keyword search (no embedding computation needed). Best for exact terms, function names, variable names, error messages, and specific identifiers.",
|
|
295
|
-
inputSchema: z.object({
|
|
296
|
-
query: z.string().describe("Keywords to search for"),
|
|
297
|
-
codeK: z.number().optional().default(8).describe("Max code results to return"),
|
|
298
|
-
gitK: z.number().optional().default(5).describe("Max git commit results to return"),
|
|
299
|
-
repo: z.string().optional().describe("Repository path to search (default: BRAINBANK_REPO)")
|
|
300
|
-
})
|
|
301
|
-
},
|
|
302
|
-
async ({ query, codeK, gitK, repo }) => {
|
|
303
|
-
const brainbank = await getBrainBank(repo);
|
|
304
|
-
const results = brainbank.searchBM25(query, { codeK, gitK });
|
|
305
|
-
if (results.length === 0) {
|
|
306
|
-
return { content: [{ type: "text", text: "No keyword matches found." }] };
|
|
307
|
-
}
|
|
308
|
-
return { content: [{ type: "text", text: formatResults(results, "Keyword Search (BM25)") }] };
|
|
309
|
-
}
|
|
310
|
-
);
|
|
311
|
-
server.registerTool(
|
|
312
|
-
"brainbank_collection_add",
|
|
312
|
+
"brainbank_collection",
|
|
313
313
|
{
|
|
314
|
-
title: "BrainBank Collection
|
|
315
|
-
description: "
|
|
314
|
+
title: "BrainBank Collection",
|
|
315
|
+
description: "Operate on KV collections (auto-created). Actions:\n- add: store content with optional metadata\n- search: hybrid vector + keyword search\n- trim: keep only N most recent items",
|
|
316
316
|
inputSchema: z.object({
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
317
|
+
action: z.enum(["add", "search", "trim"]).describe("Operation to perform"),
|
|
318
|
+
collection: z.string().describe('Collection name (e.g. "errors", "decisions")'),
|
|
319
|
+
content: z.string().optional().describe("Content to store (required for add)"),
|
|
320
|
+
query: z.string().optional().describe("Search query (required for search)"),
|
|
321
|
+
metadata: z.record(z.any()).optional().default({}).describe("Metadata for add"),
|
|
322
|
+
k: z.number().optional().default(5).describe("Max results for search"),
|
|
323
|
+
keep: z.number().optional().describe("Items to keep for trim"),
|
|
320
324
|
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
|
|
321
325
|
})
|
|
322
326
|
},
|
|
323
|
-
async ({ collection, content, metadata, repo }) => {
|
|
327
|
+
async ({ action, collection, content, query, metadata, k, keep, repo }) => {
|
|
324
328
|
const brainbank = await getBrainBank(repo);
|
|
325
329
|
const coll = brainbank.collection(collection);
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
server.registerTool(
|
|
333
|
-
"brainbank_collection_search",
|
|
334
|
-
{
|
|
335
|
-
title: "BrainBank Collection Search",
|
|
336
|
-
description: "Search a dynamic KV collection using hybrid vector + keyword search. Returns semantically similar items.",
|
|
337
|
-
inputSchema: z.object({
|
|
338
|
-
collection: z.string().describe("Collection name to search"),
|
|
339
|
-
query: z.string().describe("Search query"),
|
|
340
|
-
k: z.number().optional().default(5).describe("Max results"),
|
|
341
|
-
mode: z.enum(["hybrid", "vector", "keyword"]).optional().default("hybrid").describe("Search mode"),
|
|
342
|
-
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
|
|
343
|
-
})
|
|
344
|
-
},
|
|
345
|
-
async ({ collection, query, k, mode, repo }) => {
|
|
346
|
-
const brainbank = await getBrainBank(repo);
|
|
347
|
-
const coll = brainbank.collection(collection);
|
|
348
|
-
const results = await coll.search(query, { k, mode });
|
|
349
|
-
if (results.length === 0) {
|
|
350
|
-
return { content: [{ type: "text", text: `No results in '${collection}' for "${query}"` }] };
|
|
330
|
+
if (action === "add") {
|
|
331
|
+
if (!content) throw new Error("BrainBank: content is required for add action.");
|
|
332
|
+
const id = await coll.add(content, metadata);
|
|
333
|
+
return {
|
|
334
|
+
content: [{ type: "text", text: `\u2713 Item #${id} added to '${collection}' (${coll.count()} total)` }]
|
|
335
|
+
};
|
|
351
336
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
lines.push(` ${JSON.stringify(r.metadata)}`);
|
|
337
|
+
if (action === "search") {
|
|
338
|
+
if (!query) throw new Error("BrainBank: query is required for search action.");
|
|
339
|
+
const results = await coll.search(query, { k });
|
|
340
|
+
if (results.length === 0) {
|
|
341
|
+
return { content: [{ type: "text", text: `No results in '${collection}' for "${query}"` }] };
|
|
358
342
|
}
|
|
359
|
-
lines
|
|
343
|
+
const lines = [`## Collection: ${collection}`, ""];
|
|
344
|
+
for (const r of results) {
|
|
345
|
+
const score = Math.round((r.score ?? 0) * 100);
|
|
346
|
+
lines.push(`[${score}%] ${r.content}`);
|
|
347
|
+
if (Object.keys(r.metadata).length > 0) {
|
|
348
|
+
lines.push(` ${JSON.stringify(r.metadata)}`);
|
|
349
|
+
}
|
|
350
|
+
lines.push("");
|
|
351
|
+
}
|
|
352
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
360
353
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
inputSchema: z.object({
|
|
370
|
-
collection: z.string().describe("Collection name"),
|
|
371
|
-
keep: z.number().describe("Number of most recent items to keep"),
|
|
372
|
-
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
|
|
373
|
-
})
|
|
374
|
-
},
|
|
375
|
-
async ({ collection, keep, repo }) => {
|
|
376
|
-
const brainbank = await getBrainBank(repo);
|
|
377
|
-
const coll = brainbank.collection(collection);
|
|
378
|
-
const result = await coll.trim({ keep });
|
|
379
|
-
return {
|
|
380
|
-
content: [{
|
|
381
|
-
type: "text",
|
|
382
|
-
text: `\u2713 Trimmed ${result.removed} items from '${collection}' (kept ${keep})`
|
|
383
|
-
}]
|
|
384
|
-
};
|
|
354
|
+
if (action === "trim") {
|
|
355
|
+
if (keep == null) throw new Error("BrainBank: keep is required for trim action.");
|
|
356
|
+
const result = await coll.trim({ keep });
|
|
357
|
+
return {
|
|
358
|
+
content: [{ type: "text", text: `\u2713 Trimmed ${result.removed} items from '${collection}' (kept ${keep})` }]
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
throw new Error(`BrainBank: Unknown action "${action}".`);
|
|
385
362
|
}
|
|
386
363
|
);
|
|
387
364
|
function formatResults(results, mode) {
|
package/dist/mcp-server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp-server.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * BrainBank — MCP Server\n * \n * Exposes BrainBank as an MCP server via stdio transport.\n * Works with Google Antigravity, Claude Desktop, and any MCP-compatible client.\n * \n * Usage in Antigravity mcp_config.json:\n * {\n * \"mcpServers\": {\n * \"brainbank\": {\n * \"command\": \"npx\",\n * \"args\": [\"tsx\", \"/path/to/brainbank/src/integrations/mcp-server.ts\"],\n * \"env\": { \"BRAINBANK_REPO\": \"/path/to/your/repo\" }\n * }\n * }\n * }\n * \n * Tools exposed:\n * brainbank_search — Semantic search across code, commits\n * brainbank_hybrid_search — Best quality: vector + BM25 fused\n * brainbank_keyword_search — Instant BM25 full-text\n * brainbank_context — Get formatted context for a task\n * brainbank_index — Trigger code/git indexing\n * brainbank_stats — Get index statistics\n * brainbank_history — Git history for a specific file\n * brainbank_coedits — Files that frequently change together\n * brainbank_collection_add — Add item to a dynamic collection\n * brainbank_collection_search — Search a dynamic collection\n * brainbank_collection_trim — Trim a dynamic collection\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod/v3';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { BrainBank } from 'brainbank';\nimport { code } from 'brainbank/code';\nimport { git } from 'brainbank/git';\nimport { docs } from 'brainbank/docs';\n\n// ── Configuration from env ──────────────────────────\n\n/**\n * Detect repo root by walking up from startDir until we find `.git/`.\n * Returns startDir itself if no `.git/` is found (mono-repo or non-git project).\n */\nfunction findRepoRoot(startDir: string): string {\n let dir = path.resolve(startDir);\n while (true) {\n if (fs.existsSync(path.join(dir, '.git'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) break; // filesystem root\n dir = parent;\n }\n return path.resolve(startDir); // fallback: use startDir as-is\n}\n\nconst defaultRepoPath = process.env.BRAINBANK_REPO || undefined;\n\n// ── Reranker (default: qwen3, set BRAINBANK_RERANKER=none to disable) ──\n\nasync function createReranker() {\n const rerankerEnv = process.env.BRAINBANK_RERANKER ?? 'none';\n if (rerankerEnv === 'none') return undefined;\n if (rerankerEnv === 'qwen3') {\n const { Qwen3Reranker } = await import('@brainbank/reranker');\n return new Qwen3Reranker();\n }\n return undefined;\n}\n\n// ── Embedding Provider (default: local, set BRAINBANK_EMBEDDING=openai) ──\n\nasync function createEmbeddingProvider() {\n const embeddingEnv = process.env.BRAINBANK_EMBEDDING ?? 'local';\n if (embeddingEnv === 'openai') {\n const { OpenAIEmbedding } = await import('brainbank');\n return new OpenAIEmbedding();\n }\n return undefined; // BrainBank defaults to local WASM\n}\n\n// ── Multi-Workspace BrainBank Pool ─────────────────────\n// Reranker + embedding provider are shared; each repo gets its own DB.\n\nconst _pool = new Map<string, BrainBank>();\nlet _sharedReranker: any = undefined;\nlet _sharedEmbedding: any = undefined;\nlet _sharedReady = false;\n\nasync function ensureShared() {\n if (_sharedReady) return;\n _sharedReranker = await createReranker();\n _sharedEmbedding = await createEmbeddingProvider();\n _sharedReady = true;\n}\n\nasync function getBrainBank(targetRepo?: string): Promise<BrainBank> {\n const rp = targetRepo ?? defaultRepoPath;\n if (!rp) {\n throw new Error(\n 'No repository specified. Pass the `repo` parameter with the workspace path, ' +\n 'or set BRAINBANK_REPO environment variable.'\n );\n }\n const resolved = rp.replace(/\\/+$/, ''); // normalize\n\n if (_pool.has(resolved)) return _pool.get(resolved)!;\n\n await ensureShared();\n\n const opts: Record<string, any> = { repoPath: resolved, reranker: _sharedReranker };\n if (_sharedEmbedding) {\n opts.embeddingProvider = _sharedEmbedding;\n opts.embeddingDims = _sharedEmbedding.dims;\n }\n const brain = new BrainBank(opts)\n .use(code({ repoPath: resolved }))\n .use(git({ repoPath: resolved }))\n .use(docs());\n await brain.initialize();\n\n _pool.set(resolved, brain);\n return brain;\n}\n\n// ── MCP Server Setup ────────────────────────────────\n\nconst server = new McpServer({\n name: 'brainbank',\n version: '0.1.0',\n});\n\n// ── Tool: brainbank_search ─────────────────────────────\n\nserver.registerTool(\n 'brainbank_search',\n {\n title: 'BrainBank Search',\n description: 'Semantic search across indexed code and git commits. Returns the most relevant results sorted by similarity score.',\n inputSchema: z.object({\n query: z.string().describe('Natural language search query describing what you are looking for'),\n codeK: z.number().optional().default(6).describe('Max code results to return'),\n gitK: z.number().optional().default(5).describe('Max git commit results to return'),\n minScore: z.number().optional().default(0.25).describe('Minimum similarity score threshold (0-1)'),\n repo: z.string().optional().describe('Repository path to search (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ query, codeK, gitK, minScore, repo }) => {\n const brainbank = await getBrainBank(repo);\n const results = await brainbank.search(query, { codeK, gitK, minScore });\n\n if (results.length === 0) {\n return { content: [{ type: 'text', text: 'No results found for this query.' }] };\n }\n\n return { content: [{ type: 'text', text: formatResults(results, 'Semantic Search') }] };\n },\n);\n\n// ── Tool: brainbank_context ────────────────────────────\n\nserver.registerTool(\n 'brainbank_context',\n {\n title: 'BrainBank Context',\n description: 'Get a formatted knowledge context block for a task. Returns relevant code snippets, git history, co-edit patterns as markdown. Perfect for enriching your understanding before working on a task.',\n inputSchema: z.object({\n task: z.string().describe('Description of the task you need context for'),\n affectedFiles: z.array(z.string()).optional().default([]).describe('Files you plan to modify (improves co-edit suggestions)'),\n codeResults: z.number().optional().default(6).describe('Max code results'),\n gitResults: z.number().optional().default(5).describe('Max git commit results'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ task, affectedFiles, codeResults, gitResults, repo }) => {\n const brainbank = await getBrainBank(repo);\n const context = await brainbank.getContext(task, {\n affectedFiles,\n codeResults,\n gitResults,\n });\n\n return { content: [{ type: 'text', text: context }] };\n },\n);\n\n// ── Tool: brainbank_index ──────────────────────────────\n\nserver.registerTool(\n 'brainbank_index',\n {\n title: 'BrainBank Index',\n description: 'Index (or re-index) the repository code and git history. Run this before searching if the codebase has changed. Indexing is incremental — only changed files are processed.',\n inputSchema: z.object({\n modules: z.array(z.enum(['code', 'git', 'docs'])).optional().describe('Which modules to index. Default: all available (code, git, docs)'),\n docsPath: z.string().optional().describe('Path to a docs folder to register and index. Creates a doc collection named after the folder.'),\n forceReindex: z.boolean().optional().default(false).describe('Force re-index of all files, even unchanged ones'),\n gitDepth: z.number().optional().default(500).describe('Number of git commits to index'),\n repo: z.string().optional().describe('Repository path to index (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ modules, docsPath, forceReindex, gitDepth, repo }) => {\n const brainbank = await getBrainBank(repo);\n\n // Auto-register docs collection if docsPath provided\n if (docsPath) {\n const absPath = path.resolve(docsPath);\n const collName = path.basename(absPath);\n try {\n brainbank.module('docs').addCollection!({\n name: collName,\n path: absPath,\n pattern: '**/*.md',\n ignore: ['deprecated/**', 'node_modules/**'],\n });\n } catch {\n // docs module not loaded — ignore\n }\n }\n\n const result = await brainbank.index({ modules, forceReindex, gitDepth });\n\n const lines = [\n '## Indexing Complete',\n '',\n `**Code**: ${result.code?.indexed ?? 0} files indexed, ${result.code?.skipped ?? 0} skipped, ${result.code?.chunks ?? 0} chunks`,\n `**Git**: ${result.git?.indexed ?? 0} commits indexed, ${result.git?.skipped ?? 0} skipped`,\n ];\n\n if (result.docs) {\n for (const [name, stat] of Object.entries(result.docs)) {\n lines.push(`**Docs [${name}]**: ${stat.indexed} indexed, ${stat.skipped} skipped, ${stat.chunks} chunks`);\n }\n }\n\n const stats = brainbank.stats();\n lines.push('');\n lines.push(`**Totals**: ${stats.code?.chunks ?? 0} code chunks, ${stats.git?.commits ?? 0} commits, ${stats.documents?.documents ?? 0} docs`);\n\n return { content: [{ type: 'text', text: lines.join('\\n') }] };\n },\n);\n\n// ── Tool: brainbank_stats ──────────────────────────────\n\nserver.registerTool(\n 'brainbank_stats',\n {\n title: 'BrainBank Stats',\n description: 'Get statistics about the indexed knowledge base: file count, code chunks, git commits, HNSW index sizes, and KV collections.',\n inputSchema: z.object({\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ repo }) => {\n const brainbank = await getBrainBank(repo);\n const s = brainbank.stats();\n\n const lines = [\n '## BrainBank Knowledge Base Stats',\n '',\n ];\n\n if (s.code) {\n lines.push('### Code');\n lines.push(`- Files indexed: ${s.code.files}`);\n lines.push(`- Code chunks: ${s.code.chunks}`);\n lines.push(`- HNSW vectors: ${s.code.hnswSize}`);\n lines.push('');\n }\n\n if (s.git) {\n lines.push('### Git History');\n lines.push(`- Commits indexed: ${s.git.commits}`);\n lines.push(`- Files tracked: ${s.git.filesTracked}`);\n lines.push(`- Co-edit pairs: ${s.git.coEdits}`);\n lines.push(`- HNSW vectors: ${s.git.hnswSize}`);\n lines.push('');\n }\n\n if (s.documents) {\n lines.push('### Documents');\n lines.push(`- Collections: ${s.documents.collections}`);\n lines.push(`- Documents: ${s.documents.documents}`);\n lines.push(`- HNSW vectors: ${s.documents.hnswSize}`);\n lines.push('');\n }\n\n const kvNames = brainbank.listCollectionNames();\n if (kvNames.length > 0) {\n lines.push('### KV Collections');\n for (const name of kvNames) {\n const coll = brainbank.collection(name);\n lines.push(`- ${name}: ${coll.count()} items`);\n }\n }\n\n return { content: [{ type: 'text', text: lines.join('\\n') }] };\n },\n);\n\n// ── Tool: brainbank_history ────────────────────────────\n\nserver.registerTool(\n 'brainbank_history',\n {\n title: 'BrainBank File History',\n description: 'Get the git commit history for a specific file. Shows recent changes, authors, and line counts.',\n inputSchema: z.object({\n filePath: z.string().describe('File path (relative or partial match, e.g. \"auth.ts\" or \"src/core/agent.ts\")'),\n limit: z.number().optional().default(20).describe('Max commits to return'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ filePath, limit, repo }) => {\n const brainbank = await getBrainBank(repo);\n const history = await brainbank.fileHistory(filePath, limit);\n\n if (history.length === 0) {\n return { content: [{ type: 'text', text: `No git history found for \"${filePath}\"` }] };\n }\n\n const lines = [`## Git History: ${filePath}`, ''];\n for (const h of history as any[]) {\n lines.push(`**[${h.short_hash}]** ${h.message} *(${h.author}, +${h.additions}/-${h.deletions})*`);\n }\n\n return { content: [{ type: 'text', text: lines.join('\\n') }] };\n },\n);\n\n// ── Tool: brainbank_coedits ────────────────────────────\n\nserver.registerTool(\n 'brainbank_coedits',\n {\n title: 'BrainBank Co-Edits',\n description: 'Find files that historically change together with a given file. Useful for understanding dependencies and ensuring you do not miss related changes.',\n inputSchema: z.object({\n filePath: z.string().describe('File path to check co-edit relationships for'),\n limit: z.number().optional().default(5).describe('Max co-edit suggestions'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ filePath, limit, repo }) => {\n const brainbank = await getBrainBank(repo);\n const suggestions = brainbank.coEdits(filePath, limit);\n\n if (suggestions.length === 0) {\n return { content: [{ type: 'text', text: `No co-edit patterns found for \"${filePath}\"` }] };\n }\n\n const lines = [`## Co-Edits for: ${filePath}`, ''];\n lines.push('Files that frequently change together:');\n for (const s of suggestions) {\n lines.push(`- **${s.file}** (changed together ${s.count} times)`);\n }\n\n return { content: [{ type: 'text', text: lines.join('\\n') }] };\n },\n);\n\n// ── Tool: brainbank_hybrid_search ─────────────────────\n\nserver.registerTool(\n 'brainbank_hybrid_search',\n {\n title: 'BrainBank Hybrid Search',\n description: 'Best quality search: combines semantic vector search + BM25 keyword search using Reciprocal Rank Fusion. Catches both exact keyword matches AND conceptual similarities. Use this by default for all searches.',\n inputSchema: z.object({\n query: z.string().describe('Search query — works with both keywords and natural language'),\n codeK: z.number().optional().default(8).describe('Max code results (shorthand for collections.code)'),\n gitK: z.number().optional().default(5).describe('Max git results (shorthand for collections.git)'),\n collections: z.record(z.string(), z.number()).optional().describe(\n 'Max results per source. Reserved keys: \"code\", \"git\", \"docs\" control built-in indexers. ' +\n 'Any other key is a KV collection. Example: { \"code\": 8, \"git\": 5, \"errors\": 3, \"slack\": 2 }'\n ),\n repo: z.string().optional().describe('Repository path to search (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ query, codeK, gitK, collections, repo }) => {\n const t0 = performance.now();\n const brainbank = await getBrainBank(repo);\n const t1 = performance.now();\n const results = await brainbank.hybridSearch(query, { codeK, gitK, collections });\n const t2 = performance.now();\n\n const timing = `\\n\\n⏱ getBrainBank: ${(t1 - t0).toFixed(0)}ms | hybridSearch: ${(t2 - t1).toFixed(0)}ms | total: ${(t2 - t0).toFixed(0)}ms`;\n\n if (results.length === 0) {\n return { content: [{ type: 'text', text: 'No results found for this query.' + timing }] };\n }\n\n return { content: [{ type: 'text', text: formatResults(results, 'Hybrid Search (Vector + BM25 → RRF)') + timing }] };\n },\n);\n\n// ── Tool: brainbank_keyword_search ────────────────────\n\nserver.registerTool(\n 'brainbank_keyword_search',\n {\n title: 'BrainBank Keyword Search',\n description: 'Instant BM25 keyword search (no embedding computation needed). Best for exact terms, function names, variable names, error messages, and specific identifiers.',\n inputSchema: z.object({\n query: z.string().describe('Keywords to search for'),\n codeK: z.number().optional().default(8).describe('Max code results to return'),\n gitK: z.number().optional().default(5).describe('Max git commit results to return'),\n repo: z.string().optional().describe('Repository path to search (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ query, codeK, gitK, repo }) => {\n const brainbank = await getBrainBank(repo);\n const results = brainbank.searchBM25(query, { codeK, gitK });\n\n if (results.length === 0) {\n return { content: [{ type: 'text', text: 'No keyword matches found.' }] };\n }\n\n return { content: [{ type: 'text', text: formatResults(results, 'Keyword Search (BM25)') }] };\n },\n);\n\n// ── Tool: brainbank_collection_add ────────────────────\n\nserver.registerTool(\n 'brainbank_collection_add',\n {\n title: 'BrainBank Collection Add',\n description: 'Add an item to a dynamic KV collection. Collections are created automatically. Use this to store any structured data: errors, decisions, notes, context, etc.',\n inputSchema: z.object({\n collection: z.string().describe('Collection name (e.g. \"errors\", \"decisions\", \"context\")'),\n content: z.string().describe('Content to store'),\n metadata: z.record(z.any()).optional().default({}).describe('Optional metadata object'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ collection, content, metadata, repo }) => {\n const brainbank = await getBrainBank(repo);\n const coll = brainbank.collection(collection);\n const id = await coll.add(content, metadata);\n\n return {\n content: [{ type: 'text', text: `✓ Item #${id} added to '${collection}' (${coll.count()} total)` }],\n };\n },\n);\n\n// ── Tool: brainbank_collection_search ──────────────────\n\nserver.registerTool(\n 'brainbank_collection_search',\n {\n title: 'BrainBank Collection Search',\n description: 'Search a dynamic KV collection using hybrid vector + keyword search. Returns semantically similar items.',\n inputSchema: z.object({\n collection: z.string().describe('Collection name to search'),\n query: z.string().describe('Search query'),\n k: z.number().optional().default(5).describe('Max results'),\n mode: z.enum(['hybrid', 'vector', 'keyword']).optional().default('hybrid').describe('Search mode'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ collection, query, k, mode, repo }) => {\n const brainbank = await getBrainBank(repo);\n const coll = brainbank.collection(collection);\n const results = await coll.search(query, { k, mode: mode as any });\n\n if (results.length === 0) {\n return { content: [{ type: 'text', text: `No results in '${collection}' for \"${query}\"` }] };\n }\n\n const lines = [`## Collection: ${collection} — \"${query}\"`, ''];\n for (const r of results) {\n const score = Math.round((r.score ?? 0) * 100);\n lines.push(`[${score}%] ${r.content}`);\n if (Object.keys(r.metadata).length > 0) {\n lines.push(` ${JSON.stringify(r.metadata)}`);\n }\n lines.push('');\n }\n\n return { content: [{ type: 'text', text: lines.join('\\n') }] };\n },\n);\n\n// ── Tool: brainbank_collection_trim ────────────────────\n\nserver.registerTool(\n 'brainbank_collection_trim',\n {\n title: 'BrainBank Collection Trim',\n description: 'Trim a dynamic collection to keep only the N most recent items. Use this to prevent collections from growing unbounded.',\n inputSchema: z.object({\n collection: z.string().describe('Collection name'),\n keep: z.number().describe('Number of most recent items to keep'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ collection, keep, repo }) => {\n const brainbank = await getBrainBank(repo);\n const coll = brainbank.collection(collection);\n const result = await coll.trim({ keep });\n\n return {\n content: [{\n type: 'text',\n text: `✓ Trimmed ${result.removed} items from '${collection}' (kept ${keep})`,\n }],\n };\n },\n);\n\n// ── Shared result formatter ────────────────────────\n\nfunction formatResults(results: any[], mode: string): string {\n const lines: string[] = [`## ${mode}`, ''];\n for (const r of results) {\n const score = Math.round(r.score * 100);\n if (r.type === 'code') {\n const m = r.metadata;\n lines.push(`[CODE ${score}%] ${r.filePath} — ${m.name || m.chunkType} (L${m.startLine}-${m.endLine})`);\n lines.push(r.content);\n lines.push('');\n } else if (r.type === 'commit') {\n const m = r.metadata;\n lines.push(`[COMMIT ${score}%] ${m.shortHash} — ${r.content} (${m.author})`);\n if (m.files?.length) lines.push(` Files: ${m.files.join(', ')}`);\n lines.push('');\n } else if (r.type === 'document') {\n const ctx = r.context ? ` — ${r.context}` : '';\n lines.push(`[DOC ${score}%] ${r.filePath} [${r.metadata.collection}]${ctx}`);\n lines.push(r.content);\n lines.push('');\n } else if (r.type === 'collection') {\n const col = r.metadata?.collection ?? 'unknown';\n lines.push(`[COLLECTION ${score}%] [${col}]`);\n lines.push(r.content);\n lines.push('');\n }\n }\n return lines.join('\\n');\n}\n\n// ── Start Server ────────────────────────────────────\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch(err => {\n console.error(`BrainBank MCP Server Error: ${err.message}`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/mcp-server.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * BrainBank — MCP Server\n * \n * Exposes BrainBank as an MCP server via stdio transport.\n * Works with Google Antigravity, Claude Desktop, and any MCP-compatible client.\n * \n * Usage in Antigravity mcp_config.json:\n * {\n * \"mcpServers\": {\n * \"brainbank\": {\n * \"command\": \"npx\",\n * \"args\": [\"tsx\", \"/path/to/brainbank/packages/mcp/src/mcp-server.ts\"],\n * \"env\": { \"BRAINBANK_REPO\": \"/path/to/your/repo\" }\n * }\n * }\n * }\n * \n * Tools (6):\n * brainbank_search — Unified search (hybrid, vector, or keyword mode)\n * brainbank_context — Formatted knowledge context for a task\n * brainbank_index — Trigger code/git/docs indexing\n * brainbank_stats — Index statistics\n * brainbank_history — Git history for a specific file\n * brainbank_collection — KV collection operations (add, search, trim)\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod/v3';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { BrainBank } from 'brainbank';\nimport { code } from 'brainbank/code';\nimport { git } from 'brainbank/git';\nimport { docs } from 'brainbank/docs';\n\n// ── Configuration from env ──────────────────────────\n\n/**\n * Detect repo root by walking up from startDir until we find `.git/`.\n * Returns startDir itself if no `.git/` is found (mono-repo or non-git project).\n */\nfunction findRepoRoot(startDir: string): string {\n let dir = path.resolve(startDir);\n while (true) {\n if (fs.existsSync(path.join(dir, '.git'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) break; // filesystem root\n dir = parent;\n }\n return path.resolve(startDir); // fallback: use startDir as-is\n}\n\nconst defaultRepoPath = process.env.BRAINBANK_REPO || undefined;\n\n// ── Reranker (default: none, set BRAINBANK_RERANKER=qwen3 to enable) ──\n\nasync function createReranker() {\n const rerankerEnv = process.env.BRAINBANK_RERANKER ?? 'none';\n if (rerankerEnv === 'none') return undefined;\n if (rerankerEnv === 'qwen3') {\n const { Qwen3Reranker } = await import('@brainbank/reranker');\n return new Qwen3Reranker();\n }\n return undefined;\n}\n\n// ── Embedding Provider (default: local) ──\n\nasync function createEmbeddingProvider() {\n const embeddingEnv = process.env.BRAINBANK_EMBEDDING ?? 'local';\n if (embeddingEnv === 'openai') {\n const { OpenAIEmbedding } = await import('brainbank');\n return new OpenAIEmbedding();\n }\n if (embeddingEnv === 'perplexity') {\n const { PerplexityEmbedding } = await import('brainbank');\n return new PerplexityEmbedding();\n }\n if (embeddingEnv === 'perplexity-context') {\n const { PerplexityContextEmbedding } = await import('brainbank');\n return new PerplexityContextEmbedding();\n }\n return undefined; // BrainBank defaults to local WASM\n}\n\n// ── Multi-Workspace BrainBank Pool ─────────────────────\n\nconst MAX_POOL_SIZE = 10;\n\ninterface PoolEntry {\n brain: BrainBank;\n lastAccess: number;\n}\n\nconst _pool = new Map<string, PoolEntry>();\nlet _sharedReranker: any = undefined;\nlet _sharedEmbedding: any = undefined;\nlet _sharedReady = false;\n\nasync function ensureShared() {\n if (_sharedReady) return;\n _sharedReranker = await createReranker();\n _sharedEmbedding = await createEmbeddingProvider();\n _sharedReady = true;\n}\n\nasync function getBrainBank(targetRepo?: string): Promise<BrainBank> {\n const rp = targetRepo ?? defaultRepoPath;\n if (!rp) {\n throw new Error(\n 'No repository specified. Pass the `repo` parameter with the workspace path, ' +\n 'or set BRAINBANK_REPO environment variable.'\n );\n }\n const resolved = rp.replace(/\\/+$/, '');\n\n if (_pool.has(resolved)) {\n const entry = _pool.get(resolved)!;\n try {\n const codeStats = entry.brain.indexer('code')?.stats?.();\n if (codeStats && codeStats.hnswSize === 0) {\n const dbPath = path.join(resolved, '.brainbank', 'brainbank.db');\n const dbSize = fs.existsSync(dbPath) ? fs.statSync(dbPath).size : 0;\n if (dbSize > 100_000) {\n evictPool(resolved);\n } else {\n entry.lastAccess = Date.now();\n return entry.brain;\n }\n } else {\n entry.lastAccess = Date.now();\n return entry.brain;\n }\n } catch {\n entry.lastAccess = Date.now();\n return entry.brain;\n }\n }\n\n await ensureShared();\n\n if (_pool.size >= MAX_POOL_SIZE) {\n let oldest: string | undefined;\n let oldestTime = Infinity;\n for (const [key, entry] of _pool) {\n if (entry.lastAccess < oldestTime) {\n oldestTime = entry.lastAccess;\n oldest = key;\n }\n }\n if (oldest) evictPool(oldest);\n }\n\n const brain = await _createBrain(resolved);\n _pool.set(resolved, { brain, lastAccess: Date.now() });\n return brain;\n}\n\nasync function _createBrain(resolved: string): Promise<BrainBank> {\n const opts: Record<string, any> = { repoPath: resolved, reranker: _sharedReranker };\n if (_sharedEmbedding) {\n opts.embeddingProvider = _sharedEmbedding;\n opts.embeddingDims = _sharedEmbedding.dims;\n }\n const brain = new BrainBank(opts)\n .use(code({ repoPath: resolved }))\n .use(git({ repoPath: resolved }))\n .use(docs());\n\n try {\n await brain.initialize();\n } catch (err: any) {\n if (err?.message?.includes('Invalid the given array length')) {\n const dbPath = path.join(resolved, '.brainbank', 'brainbank.db');\n try { fs.unlinkSync(dbPath); } catch {}\n try { fs.unlinkSync(dbPath + '-wal'); } catch {}\n try { fs.unlinkSync(dbPath + '-shm'); } catch {}\n\n const fresh = new BrainBank(opts)\n .use(code({ repoPath: resolved }))\n .use(git({ repoPath: resolved }))\n .use(docs());\n await fresh.initialize();\n return fresh;\n }\n throw err;\n }\n\n return brain;\n}\n\nfunction evictPool(resolved: string) {\n const entry = _pool.get(resolved);\n if (entry) {\n try { entry.brain.close(); } catch {}\n _pool.delete(resolved);\n }\n}\n\n// ── MCP Server Setup ────────────────────────────────\n\nconst server = new McpServer({\n name: 'brainbank',\n version: '0.2.0',\n});\n\n// ── Tool: brainbank_search ──────────────────────────\n// Replaces: brainbank_search, brainbank_hybrid_search, brainbank_keyword_search\n\nserver.registerTool(\n 'brainbank_search',\n {\n title: 'BrainBank Search',\n description:\n 'Search indexed code and git commits. Supports three modes:\\n' +\n '- hybrid (default): vector + BM25 fused with RRF — best quality\\n' +\n '- vector: semantic similarity only\\n' +\n '- keyword: instant BM25 for exact terms, function names, error messages',\n inputSchema: z.object({\n query: z.string().describe('Search query — works with both keywords and natural language'),\n mode: z.enum(['hybrid', 'vector', 'keyword']).optional().default('hybrid').describe('Search strategy'),\n codeK: z.number().optional().default(8).describe('Max code results'),\n gitK: z.number().optional().default(5).describe('Max git results'),\n minScore: z.number().optional().default(0.25).describe('Minimum similarity score (0-1), only for vector mode'),\n collections: z.record(z.string(), z.number()).optional().describe(\n 'Max results per source. Reserved: \"code\", \"git\", \"docs\". Any other key = KV collection.'\n ),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ query, mode, codeK, gitK, minScore, collections, repo }) => {\n const brainbank = await getBrainBank(repo);\n\n let results;\n if (mode === 'keyword') {\n results = await brainbank.searchBM25(query, { codeK, gitK });\n } else if (mode === 'vector') {\n results = await brainbank.search(query, { codeK, gitK, minScore });\n } else {\n results = await brainbank.hybridSearch(query, { codeK, gitK, collections });\n }\n\n if (results.length === 0) {\n return { content: [{ type: 'text', text: 'No results found.' }] };\n }\n\n const modeLabel = mode === 'keyword' ? 'Keyword (BM25)' : mode === 'vector' ? 'Vector' : 'Hybrid (Vector + BM25 → RRF)';\n return { content: [{ type: 'text', text: formatResults(results, modeLabel) }] };\n },\n);\n\n// ── Tool: brainbank_context ─────────────────────────\n\nserver.registerTool(\n 'brainbank_context',\n {\n title: 'BrainBank Context',\n description: 'Get a formatted knowledge context block for a task. Returns relevant code, git history, and co-edit patterns as markdown.',\n inputSchema: z.object({\n task: z.string().describe('Description of the task you need context for'),\n affectedFiles: z.array(z.string()).optional().default([]).describe('Files you plan to modify (improves co-edit suggestions)'),\n codeResults: z.number().optional().default(6).describe('Max code results'),\n gitResults: z.number().optional().default(5).describe('Max git commit results'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ task, affectedFiles, codeResults, gitResults, repo }) => {\n const brainbank = await getBrainBank(repo);\n const context = await brainbank.getContext(task, {\n affectedFiles,\n codeResults,\n gitResults,\n });\n\n return { content: [{ type: 'text', text: context }] };\n },\n);\n\n// ── Tool: brainbank_index ───────────────────────────\n\nserver.registerTool(\n 'brainbank_index',\n {\n title: 'BrainBank Index',\n description: 'Index (or re-index) code, git history, and docs. Incremental — only changed files are processed.',\n inputSchema: z.object({\n modules: z.array(z.enum(['code', 'git', 'docs'])).optional().describe('Which modules to index (default: all)'),\n docsPath: z.string().optional().describe('Path to a docs folder to register and index'),\n forceReindex: z.boolean().optional().default(false).describe('Force re-index of all files'),\n gitDepth: z.number().optional().default(500).describe('Number of git commits to index'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ modules, docsPath, forceReindex, gitDepth, repo }) => {\n const brainbank = await getBrainBank(repo);\n\n if (docsPath) {\n const absPath = path.resolve(docsPath);\n const collName = path.basename(absPath);\n try {\n brainbank.addCollection({\n name: collName,\n path: absPath,\n pattern: '**/*.md',\n ignore: ['deprecated/**', 'node_modules/**'],\n });\n } catch {\n // docs module not loaded\n }\n }\n\n const result = await brainbank.index({ modules, forceReindex, gitDepth });\n\n const lines = [\n '## Indexing Complete',\n '',\n `**Code**: ${result.code?.indexed ?? 0} files indexed, ${result.code?.skipped ?? 0} skipped, ${result.code?.chunks ?? 0} chunks`,\n `**Git**: ${result.git?.indexed ?? 0} commits indexed, ${result.git?.skipped ?? 0} skipped`,\n ];\n\n if (result.docs) {\n for (const [name, stat] of Object.entries(result.docs)) {\n lines.push(`**Docs [${name}]**: ${stat.indexed} indexed, ${stat.skipped} skipped, ${stat.chunks} chunks`);\n }\n }\n\n const stats = brainbank.stats();\n lines.push('');\n lines.push(`**Totals**: ${stats.code?.chunks ?? 0} code chunks, ${stats.git?.commits ?? 0} commits, ${stats.documents?.documents ?? 0} docs`);\n\n return { content: [{ type: 'text', text: lines.join('\\n') }] };\n },\n);\n\n// ── Tool: brainbank_stats ───────────────────────────\n\nserver.registerTool(\n 'brainbank_stats',\n {\n title: 'BrainBank Stats',\n description: 'Get index statistics: file count, code chunks, git commits, HNSW sizes, KV collections.',\n inputSchema: z.object({\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ repo }) => {\n const brainbank = await getBrainBank(repo);\n const s = brainbank.stats();\n\n const lines = ['## BrainBank Stats', ''];\n\n if (s.code) {\n lines.push(`**Code**: ${s.code.files} files, ${s.code.chunks} chunks, ${s.code.hnswSize} vectors`);\n }\n if (s.git) {\n lines.push(`**Git**: ${s.git.commits} commits, ${s.git.filesTracked} files, ${s.git.coEdits} co-edit pairs`);\n }\n if (s.documents) {\n lines.push(`**Docs**: ${s.documents.collections} collections, ${s.documents.documents} documents`);\n }\n\n const kvNames = brainbank.listCollectionNames();\n if (kvNames.length > 0) {\n lines.push('');\n lines.push('**KV Collections**:');\n for (const name of kvNames) {\n const coll = brainbank.collection(name);\n lines.push(`- ${name}: ${coll.count()} items`);\n }\n }\n\n return { content: [{ type: 'text', text: lines.join('\\n') }] };\n },\n);\n\n// ── Tool: brainbank_history ─────────────────────────\n\nserver.registerTool(\n 'brainbank_history',\n {\n title: 'BrainBank File History',\n description: 'Get git commit history for a file. Shows changes, authors, and line counts.',\n inputSchema: z.object({\n filePath: z.string().describe('File path (relative or partial, e.g. \"auth.ts\")'),\n limit: z.number().optional().default(20).describe('Max commits to return'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ filePath, limit, repo }) => {\n const brainbank = await getBrainBank(repo);\n const history = await brainbank.fileHistory(filePath, limit);\n\n if (history.length === 0) {\n return { content: [{ type: 'text', text: `No git history found for \"${filePath}\"` }] };\n }\n\n const lines = [`## Git History: ${filePath}`, ''];\n for (const h of history as any[]) {\n lines.push(`**[${h.short_hash}]** ${h.message} *(${h.author}, +${h.additions}/-${h.deletions})*`);\n }\n\n return { content: [{ type: 'text', text: lines.join('\\n') }] };\n },\n);\n\n// ── Tool: brainbank_collection ──────────────────────\n// Replaces: brainbank_collection_add, brainbank_collection_search, brainbank_collection_trim\n\nserver.registerTool(\n 'brainbank_collection',\n {\n title: 'BrainBank Collection',\n description:\n 'Operate on KV collections (auto-created). Actions:\\n' +\n '- add: store content with optional metadata\\n' +\n '- search: hybrid vector + keyword search\\n' +\n '- trim: keep only N most recent items',\n inputSchema: z.object({\n action: z.enum(['add', 'search', 'trim']).describe('Operation to perform'),\n collection: z.string().describe('Collection name (e.g. \"errors\", \"decisions\")'),\n content: z.string().optional().describe('Content to store (required for add)'),\n query: z.string().optional().describe('Search query (required for search)'),\n metadata: z.record(z.any()).optional().default({}).describe('Metadata for add'),\n k: z.number().optional().default(5).describe('Max results for search'),\n keep: z.number().optional().describe('Items to keep for trim'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ action, collection, content, query, metadata, k, keep, repo }) => {\n const brainbank = await getBrainBank(repo);\n const coll = brainbank.collection(collection);\n\n if (action === 'add') {\n if (!content) throw new Error('BrainBank: content is required for add action.');\n const id = await coll.add(content, metadata);\n return {\n content: [{ type: 'text', text: `✓ Item #${id} added to '${collection}' (${coll.count()} total)` }],\n };\n }\n\n if (action === 'search') {\n if (!query) throw new Error('BrainBank: query is required for search action.');\n const results = await coll.search(query, { k });\n\n if (results.length === 0) {\n return { content: [{ type: 'text', text: `No results in '${collection}' for \"${query}\"` }] };\n }\n\n const lines = [`## Collection: ${collection}`, ''];\n for (const r of results) {\n const score = Math.round((r.score ?? 0) * 100);\n lines.push(`[${score}%] ${r.content}`);\n if (Object.keys(r.metadata).length > 0) {\n lines.push(` ${JSON.stringify(r.metadata)}`);\n }\n lines.push('');\n }\n return { content: [{ type: 'text', text: lines.join('\\n') }] };\n }\n\n if (action === 'trim') {\n if (keep == null) throw new Error('BrainBank: keep is required for trim action.');\n const result = await coll.trim({ keep });\n return {\n content: [{ type: 'text', text: `✓ Trimmed ${result.removed} items from '${collection}' (kept ${keep})` }],\n };\n }\n\n throw new Error(`BrainBank: Unknown action \"${action}\".`);\n },\n);\n\n// ── Shared result formatter ─────────────────────────\n\nfunction formatResults(results: any[], mode: string): string {\n const lines: string[] = [`## ${mode}`, ''];\n for (const r of results) {\n const score = Math.round(r.score * 100);\n if (r.type === 'code') {\n const m = r.metadata;\n lines.push(`[CODE ${score}%] ${r.filePath} — ${m.name || m.chunkType} (L${m.startLine}-${m.endLine})`);\n lines.push(r.content);\n lines.push('');\n } else if (r.type === 'commit') {\n const m = r.metadata;\n lines.push(`[COMMIT ${score}%] ${m.shortHash} — ${r.content} (${m.author})`);\n if (m.files?.length) lines.push(` Files: ${m.files.join(', ')}`);\n lines.push('');\n } else if (r.type === 'document') {\n const ctx = r.context ? ` — ${r.context}` : '';\n lines.push(`[DOC ${score}%] ${r.filePath} [${r.metadata.collection}]${ctx}`);\n lines.push(r.content);\n lines.push('');\n } else if (r.type === 'collection') {\n const col = r.metadata?.collection ?? 'unknown';\n lines.push(`[COLLECTION ${score}%] [${col}]`);\n lines.push(r.content);\n lines.push('');\n }\n }\n return lines.join('\\n');\n}\n\n// ── Start Server ────────────────────────────────────\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch(err => {\n console.error(`BrainBank MCP Server Error: ${err.message}`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainbank/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server for BrainBank",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/mcp-server.js",
|
|
7
7
|
"bin": "dist/mcp-server.js",
|
|
8
|
-
"files": [
|
|
9
|
-
|
|
8
|
+
"files": [
|
|
9
|
+
"dist/"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup"
|
|
13
|
+
},
|
|
10
14
|
"dependencies": {
|
|
11
|
-
"brainbank": ">=0.
|
|
15
|
+
"brainbank": ">=0.2.2",
|
|
12
16
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
13
17
|
"zod": "^4.3.6"
|
|
14
18
|
},
|