@brainbank/mcp 0.3.0 → 0.3.1
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/dist/mcp-server.js +388 -0
- package/dist/mcp-server.js.map +1 -0
- package/package.json +6 -4
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
4
|
+
|
|
5
|
+
// src/mcp-server.ts
|
|
6
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
import { z } from "zod/v3";
|
|
9
|
+
import { existsSync as existsSync2 } from "fs";
|
|
10
|
+
|
|
11
|
+
// src/workspace-pool.ts
|
|
12
|
+
var DEFAULT_MAX_MEMORY_MB = 2048;
|
|
13
|
+
var DEFAULT_TTL_MINUTES = 30;
|
|
14
|
+
var EVICTION_INTERVAL_MS = 6e4;
|
|
15
|
+
function formatAgo(ms) {
|
|
16
|
+
if (ms < 6e4) return `${Math.round(ms / 1e3)}s ago`;
|
|
17
|
+
if (ms < 36e5) return `${Math.round(ms / 6e4)}m ago`;
|
|
18
|
+
return `${Math.round(ms / 36e5)}h ago`;
|
|
19
|
+
}
|
|
20
|
+
__name(formatAgo, "formatAgo");
|
|
21
|
+
var WorkspacePool = class {
|
|
22
|
+
static {
|
|
23
|
+
__name(this, "WorkspacePool");
|
|
24
|
+
}
|
|
25
|
+
_pool = /* @__PURE__ */ new Map();
|
|
26
|
+
_timer = null;
|
|
27
|
+
_maxMemoryBytes;
|
|
28
|
+
_ttlMs;
|
|
29
|
+
_factory;
|
|
30
|
+
_onEvict;
|
|
31
|
+
_onError;
|
|
32
|
+
constructor(options) {
|
|
33
|
+
this._maxMemoryBytes = (options.maxMemoryMB ?? DEFAULT_MAX_MEMORY_MB) * 1024 * 1024;
|
|
34
|
+
this._ttlMs = (options.ttlMinutes ?? DEFAULT_TTL_MINUTES) * 60 * 1e3;
|
|
35
|
+
this._factory = options.factory;
|
|
36
|
+
this._onEvict = options.onEvict;
|
|
37
|
+
this._onError = options.onError;
|
|
38
|
+
this._timer = setInterval(() => this._evictStale(), EVICTION_INTERVAL_MS);
|
|
39
|
+
if (this._timer.unref) this._timer.unref();
|
|
40
|
+
}
|
|
41
|
+
/** Number of cached workspaces. */
|
|
42
|
+
get size() {
|
|
43
|
+
return this._pool.size;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get a BrainBank for the given repo path.
|
|
47
|
+
* Returns a cached instance (with hot-reload) or creates a new one.
|
|
48
|
+
*/
|
|
49
|
+
async get(repoPath) {
|
|
50
|
+
const key = repoPath.replace(/\/+$/, "");
|
|
51
|
+
const existing = this._pool.get(key);
|
|
52
|
+
if (existing) {
|
|
53
|
+
existing.lastAccess = Date.now();
|
|
54
|
+
try {
|
|
55
|
+
await existing.brain.ensureFresh();
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
return existing.brain;
|
|
59
|
+
}
|
|
60
|
+
this._evictByMemoryPressure();
|
|
61
|
+
const brain = await this._factory(key);
|
|
62
|
+
this._pool.set(key, {
|
|
63
|
+
brain,
|
|
64
|
+
repoPath: key,
|
|
65
|
+
lastAccess: Date.now(),
|
|
66
|
+
createdAt: Date.now(),
|
|
67
|
+
activeOps: 0
|
|
68
|
+
});
|
|
69
|
+
return brain;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Execute an operation with active-op tracking.
|
|
73
|
+
* Prevents the workspace from being evicted while the operation runs.
|
|
74
|
+
*/
|
|
75
|
+
async withBrain(repoPath, fn) {
|
|
76
|
+
const brain = await this.get(repoPath);
|
|
77
|
+
const key = repoPath.replace(/\/+$/, "");
|
|
78
|
+
const entry = this._pool.get(key);
|
|
79
|
+
if (entry) entry.activeOps++;
|
|
80
|
+
try {
|
|
81
|
+
return await fn(brain);
|
|
82
|
+
} finally {
|
|
83
|
+
if (entry) {
|
|
84
|
+
entry.activeOps--;
|
|
85
|
+
entry.lastAccess = Date.now();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Manually evict a specific workspace. */
|
|
90
|
+
evict(repoPath) {
|
|
91
|
+
const key = repoPath.replace(/\/+$/, "");
|
|
92
|
+
this._evictEntry(key);
|
|
93
|
+
}
|
|
94
|
+
/** Get pool statistics. */
|
|
95
|
+
stats() {
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
let totalMemory = 0;
|
|
98
|
+
const entries = [];
|
|
99
|
+
for (const entry of this._pool.values()) {
|
|
100
|
+
const memBytes = entry.brain.memoryHint();
|
|
101
|
+
const memMB = Math.round(memBytes / 1024 / 1024 * 100) / 100;
|
|
102
|
+
totalMemory += memBytes;
|
|
103
|
+
entries.push({
|
|
104
|
+
repoPath: entry.repoPath,
|
|
105
|
+
lastAccessAgo: formatAgo(now - entry.lastAccess),
|
|
106
|
+
memoryMB: memMB,
|
|
107
|
+
activeOps: entry.activeOps
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
size: this._pool.size,
|
|
112
|
+
totalMemoryMB: Math.round(totalMemory / 1024 / 1024 * 100) / 100,
|
|
113
|
+
entries
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** Close all entries and stop the eviction timer. */
|
|
117
|
+
close() {
|
|
118
|
+
if (this._timer) {
|
|
119
|
+
clearInterval(this._timer);
|
|
120
|
+
this._timer = null;
|
|
121
|
+
}
|
|
122
|
+
for (const key of [...this._pool.keys()]) {
|
|
123
|
+
this._evictEntry(key);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Evict workspaces that haven't been accessed within the TTL. */
|
|
127
|
+
_evictStale() {
|
|
128
|
+
const cutoff = Date.now() - this._ttlMs;
|
|
129
|
+
for (const [key, entry] of this._pool) {
|
|
130
|
+
if (entry.lastAccess < cutoff && entry.activeOps === 0) {
|
|
131
|
+
this._evictEntry(key);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/** Evict oldest idle entries until total memory is under the limit. */
|
|
136
|
+
_evictByMemoryPressure() {
|
|
137
|
+
let totalMemory = 0;
|
|
138
|
+
for (const entry of this._pool.values()) {
|
|
139
|
+
totalMemory += entry.brain.memoryHint();
|
|
140
|
+
}
|
|
141
|
+
if (totalMemory < this._maxMemoryBytes) return;
|
|
142
|
+
const candidates = [...this._pool.entries()].filter(([, e]) => e.activeOps === 0).sort(([, a], [, b]) => a.lastAccess - b.lastAccess);
|
|
143
|
+
for (const [key, entry] of candidates) {
|
|
144
|
+
if (totalMemory < this._maxMemoryBytes) break;
|
|
145
|
+
totalMemory -= entry.brain.memoryHint();
|
|
146
|
+
this._evictEntry(key);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Evict a single entry by key. */
|
|
150
|
+
_evictEntry(key) {
|
|
151
|
+
const entry = this._pool.get(key);
|
|
152
|
+
if (!entry) return;
|
|
153
|
+
try {
|
|
154
|
+
entry.brain.close();
|
|
155
|
+
} catch (err) {
|
|
156
|
+
this._onError?.(key, err);
|
|
157
|
+
}
|
|
158
|
+
this._pool.delete(key);
|
|
159
|
+
this._onEvict?.(key);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/workspace-factory.ts
|
|
164
|
+
import * as fs from "fs";
|
|
165
|
+
import * as path from "path";
|
|
166
|
+
function findRepoRoot(startDir) {
|
|
167
|
+
let dir = path.resolve(startDir);
|
|
168
|
+
while (true) {
|
|
169
|
+
if (fs.existsSync(path.join(dir, ".git"))) return dir;
|
|
170
|
+
const parent = path.dirname(dir);
|
|
171
|
+
if (parent === dir) break;
|
|
172
|
+
dir = parent;
|
|
173
|
+
}
|
|
174
|
+
return path.resolve(startDir);
|
|
175
|
+
}
|
|
176
|
+
__name(findRepoRoot, "findRepoRoot");
|
|
177
|
+
function resolveRepoPath(targetRepo) {
|
|
178
|
+
const rp = targetRepo ?? process.env.BRAINBANK_REPO ?? findRepoRoot(process.cwd());
|
|
179
|
+
return rp.replace(/\/+$/, "");
|
|
180
|
+
}
|
|
181
|
+
__name(resolveRepoPath, "resolveRepoPath");
|
|
182
|
+
async function createWorkspaceBrain(repoPath) {
|
|
183
|
+
const { createBrain, resetFactoryCache } = await import("brainbank");
|
|
184
|
+
resetFactoryCache();
|
|
185
|
+
const context = {
|
|
186
|
+
repoPath,
|
|
187
|
+
env: process.env
|
|
188
|
+
};
|
|
189
|
+
const origLog = console.log;
|
|
190
|
+
console.log = (...args) => console.error(...args);
|
|
191
|
+
try {
|
|
192
|
+
const brain = await createBrain(context);
|
|
193
|
+
await brain.initialize();
|
|
194
|
+
return brain;
|
|
195
|
+
} finally {
|
|
196
|
+
console.log = origLog;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
__name(createWorkspaceBrain, "createWorkspaceBrain");
|
|
200
|
+
|
|
201
|
+
// src/mcp-server.ts
|
|
202
|
+
var pool = new WorkspacePool({
|
|
203
|
+
factory: createWorkspaceBrain,
|
|
204
|
+
maxMemoryMB: parseInt(process.env.BRAINBANK_MAX_MEMORY_MB ?? "2048", 10),
|
|
205
|
+
ttlMinutes: parseInt(process.env.BRAINBANK_TTL_MINUTES ?? "30", 10),
|
|
206
|
+
onError: /* @__PURE__ */ __name((repo, err) => {
|
|
207
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
208
|
+
console.error(`BrainBank pool error [${repo}]: ${msg}`);
|
|
209
|
+
}, "onError")
|
|
210
|
+
});
|
|
211
|
+
async function getBrainBank(targetRepo) {
|
|
212
|
+
return pool.get(resolveRepoPath(targetRepo));
|
|
213
|
+
}
|
|
214
|
+
__name(getBrainBank, "getBrainBank");
|
|
215
|
+
var server = new McpServer({
|
|
216
|
+
name: "brainbank",
|
|
217
|
+
version: "0.4.0"
|
|
218
|
+
});
|
|
219
|
+
server.registerTool(
|
|
220
|
+
"brainbank_context",
|
|
221
|
+
{
|
|
222
|
+
title: "BrainBank Context",
|
|
223
|
+
description: "Get a formatted knowledge context block for a task. Returns a Workflow Trace: search hits + full call tree with `called by` annotations, topologically ordered. All source code included \u2014 no trimming, no truncation.",
|
|
224
|
+
inputSchema: z.object({
|
|
225
|
+
task: z.string().describe("Description of the task you need context for"),
|
|
226
|
+
affectedFiles: z.array(z.string()).optional().default([]).describe("Files you plan to modify (improves co-edit suggestions)"),
|
|
227
|
+
codeResults: z.number().optional().default(20).describe("Max code results"),
|
|
228
|
+
gitResults: z.number().optional().default(5).describe("Max git commit results"),
|
|
229
|
+
docsResults: z.number().optional().describe("Max document results (omit to skip docs)"),
|
|
230
|
+
sources: z.record(z.number()).optional().describe("Per-source result limits, overrides codeResults/gitResults/docsResults (e.g. { code: 10, git: 0, docs: 5 })"),
|
|
231
|
+
path: z.string().optional().describe("Filter results to files under this path prefix (e.g. src/services/)"),
|
|
232
|
+
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)"),
|
|
233
|
+
// BrainBankQL context fields
|
|
234
|
+
lines: z.boolean().optional().describe("Prefix each code line with its source line number (e.g. 127| code)"),
|
|
235
|
+
symbols: z.boolean().optional().describe("Append symbol index (all functions, classes, interfaces) for matched files"),
|
|
236
|
+
compact: z.boolean().optional().describe("Show only function/class signatures, skip bodies"),
|
|
237
|
+
callTree: z.union([z.boolean(), z.object({ depth: z.number() })]).optional().describe("Include call tree expansion. Pass { depth: N } to control depth"),
|
|
238
|
+
imports: z.boolean().optional().describe("Include dependency/import summary section"),
|
|
239
|
+
expander: z.boolean().optional().describe("Enable LLM-powered context expansion to discover related chunks not found by search")
|
|
240
|
+
})
|
|
241
|
+
},
|
|
242
|
+
async ({ task, affectedFiles, codeResults, gitResults, docsResults, sources, path: path2, repo, lines, symbols, compact, callTree, imports, expander }) => {
|
|
243
|
+
const repoPath = resolveRepoPath(repo);
|
|
244
|
+
const brainbank = await getBrainBank(repo);
|
|
245
|
+
const base = { code: codeResults, git: gitResults };
|
|
246
|
+
if (docsResults !== void 0) base.docs = docsResults;
|
|
247
|
+
const resolvedSources = sources ? { ...base, ...sources } : base;
|
|
248
|
+
const fields = {};
|
|
249
|
+
if (lines !== void 0) fields.lines = lines;
|
|
250
|
+
if (symbols !== void 0) fields.symbols = symbols;
|
|
251
|
+
if (compact !== void 0) fields.compact = compact;
|
|
252
|
+
if (callTree !== void 0) fields.callTree = callTree;
|
|
253
|
+
if (imports !== void 0) fields.imports = imports;
|
|
254
|
+
if (expander !== void 0) fields.expander = expander;
|
|
255
|
+
const context = await brainbank.getContext(task, {
|
|
256
|
+
affectedFiles,
|
|
257
|
+
sources: resolvedSources,
|
|
258
|
+
pathPrefix: path2,
|
|
259
|
+
source: "mcp",
|
|
260
|
+
fields: Object.keys(fields).length > 0 ? fields : void 0
|
|
261
|
+
});
|
|
262
|
+
return { content: [{ type: "text", text: context }] };
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
server.registerTool(
|
|
266
|
+
"brainbank_index",
|
|
267
|
+
{
|
|
268
|
+
title: "BrainBank Index",
|
|
269
|
+
description: "Re-index code, git history, and docs. Requires .brainbank/config.json to exist. Incremental \u2014 only changed files are processed.",
|
|
270
|
+
inputSchema: z.object({
|
|
271
|
+
forceReindex: z.boolean().optional().default(false).describe("Force re-index of all files"),
|
|
272
|
+
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
|
|
273
|
+
})
|
|
274
|
+
},
|
|
275
|
+
async ({ forceReindex, repo }) => {
|
|
276
|
+
const repoPath = resolveRepoPath(repo);
|
|
277
|
+
if (!existsSync2(`${repoPath}/.brainbank/config.json`)) {
|
|
278
|
+
return {
|
|
279
|
+
content: [{
|
|
280
|
+
type: "text",
|
|
281
|
+
text: `BrainBank: No .brainbank/config.json found at ${repoPath}.
|
|
282
|
+
|
|
283
|
+
## How to set up
|
|
284
|
+
|
|
285
|
+
Create \`${repoPath}/.brainbank/config.json\` with:
|
|
286
|
+
|
|
287
|
+
\`\`\`json
|
|
288
|
+
{
|
|
289
|
+
"plugins": ["code"],
|
|
290
|
+
"code": {
|
|
291
|
+
"embedding": "perplexity-context",
|
|
292
|
+
"ignore": [
|
|
293
|
+
"node_modules/**", "dist/**", "build/**",
|
|
294
|
+
".next/**", "coverage/**", "__pycache__/**",
|
|
295
|
+
"**/*.min.js", "**/*.min.css",
|
|
296
|
+
"tests/**", "test/**",
|
|
297
|
+
"**/test_*.py", "**/*_test.py",
|
|
298
|
+
"**/*.test.ts", "**/*.spec.ts"
|
|
299
|
+
]
|
|
300
|
+
},
|
|
301
|
+
"embedding": "perplexity-context"
|
|
302
|
+
}
|
|
303
|
+
\`\`\`
|
|
304
|
+
|
|
305
|
+
**Embedding options:** \`local\` (free, offline), \`openai\`, \`perplexity\`, \`perplexity-context\` (best quality)
|
|
306
|
+
**Plugins available:** \`code\`, \`git\`, \`docs\`
|
|
307
|
+
|
|
308
|
+
Then run:
|
|
309
|
+
\`\`\`bash
|
|
310
|
+
brainbank index . --force --yes
|
|
311
|
+
\`\`\``
|
|
312
|
+
}]
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
const brainbank = await getBrainBank(repo);
|
|
316
|
+
const result = await brainbank.index({ forceReindex });
|
|
317
|
+
const lines = ["## Indexing Complete", ""];
|
|
318
|
+
const codeResult = result.code;
|
|
319
|
+
const gitResult = result.git;
|
|
320
|
+
lines.push(`**Code**: ${codeResult?.indexed ?? 0} files indexed, ${codeResult?.skipped ?? 0} skipped, ${codeResult?.chunks ?? 0} chunks`);
|
|
321
|
+
lines.push(`**Git**: ${gitResult?.indexed ?? 0} commits indexed, ${gitResult?.skipped ?? 0} skipped`);
|
|
322
|
+
const docsResult = result.docs;
|
|
323
|
+
if (docsResult) {
|
|
324
|
+
for (const [name, stat] of Object.entries(docsResult)) {
|
|
325
|
+
lines.push(`**Docs [${name}]**: ${stat.indexed} indexed, ${stat.skipped} skipped, ${stat.chunks} chunks`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const stats = brainbank.stats();
|
|
329
|
+
const codeStats = stats.code;
|
|
330
|
+
const gitStats = stats.git;
|
|
331
|
+
const docStats = stats.documents;
|
|
332
|
+
lines.push("");
|
|
333
|
+
lines.push(`**Totals**: ${codeStats?.chunks ?? 0} code chunks, ${gitStats?.commits ?? 0} commits, ${docStats?.documents ?? 0} docs`);
|
|
334
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
server.registerTool(
|
|
338
|
+
"brainbank_files",
|
|
339
|
+
{
|
|
340
|
+
title: "BrainBank Files",
|
|
341
|
+
description: 'Fetch full file contents from the index. Use AFTER brainbank_context to view complete files identified by search. No semantic search runs \u2014 this is a direct file viewer.\n\nSupports:\n- Exact paths: "src/auth/login.ts"\n- Directories: "src/graph/" (trailing / = all files under path)\n- Glob patterns: "src/**/*.service.ts"\n- Fuzzy basename: "plugin.ts" (matches src/plugin.ts when exact fails)',
|
|
342
|
+
inputSchema: z.object({
|
|
343
|
+
files: z.array(z.string()).describe(
|
|
344
|
+
"File paths to fetch. Exact paths, directories (trailing /), glob patterns (e.g. src/**/*.ts), or fuzzy basenames."
|
|
345
|
+
),
|
|
346
|
+
repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)"),
|
|
347
|
+
lines: z.boolean().optional().describe("Prefix each line with source line number")
|
|
348
|
+
})
|
|
349
|
+
},
|
|
350
|
+
async ({ files, repo, lines }) => {
|
|
351
|
+
const brainbank = await getBrainBank(repo);
|
|
352
|
+
const results = brainbank.resolveFiles(files);
|
|
353
|
+
if (results.length === 0) {
|
|
354
|
+
return { content: [{ type: "text", text: "No matching files found in the index." }] };
|
|
355
|
+
}
|
|
356
|
+
const parts = [];
|
|
357
|
+
for (const r of results) {
|
|
358
|
+
const meta = r.metadata;
|
|
359
|
+
const lang = meta.language ?? "";
|
|
360
|
+
const startLine = meta.startLine ?? 1;
|
|
361
|
+
parts.push(`## ${r.filePath}
|
|
362
|
+
`);
|
|
363
|
+
parts.push("```" + lang);
|
|
364
|
+
if (lines) {
|
|
365
|
+
const codeLines = r.content.split("\n");
|
|
366
|
+
const pad = String(startLine + codeLines.length - 1).length;
|
|
367
|
+
parts.push(codeLines.map(
|
|
368
|
+
(l, i) => `${String(startLine + i).padStart(pad)}| ${l}`
|
|
369
|
+
).join("\n"));
|
|
370
|
+
} else {
|
|
371
|
+
parts.push(r.content);
|
|
372
|
+
}
|
|
373
|
+
parts.push("```\n");
|
|
374
|
+
}
|
|
375
|
+
return { content: [{ type: "text", text: parts.join("\n") }] };
|
|
376
|
+
}
|
|
377
|
+
);
|
|
378
|
+
async function main() {
|
|
379
|
+
const transport = new StdioServerTransport();
|
|
380
|
+
await server.connect(transport);
|
|
381
|
+
}
|
|
382
|
+
__name(main, "main");
|
|
383
|
+
main().catch((err) => {
|
|
384
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
385
|
+
console.error(`BrainBank MCP Server Error: ${message}`);
|
|
386
|
+
process.exit(1);
|
|
387
|
+
});
|
|
388
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp-server.ts","../src/workspace-pool.ts","../src/workspace-factory.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 mcp_config.json:\n * {\n * \"mcpServers\": {\n * \"brainbank\": {\n * \"command\": \"npx\",\n * \"args\": [\"-y\", \"@brainbank/mcp\"]\n * }\n * }\n * }\n * \n * Tools (2):\n * brainbank_context — Workflow Trace: search + call tree + called-by annotations\n * brainbank_index — Re-index (requires .brainbank/config.json)\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod/v3';\nimport { existsSync } from 'node:fs';\nimport { WorkspacePool } from './workspace-pool.js';\nimport { createWorkspaceBrain, resolveRepoPath } from './workspace-factory.js';\n\n\n\n// ── Multi-Workspace BrainBank Pool ─────────────────────\n\nconst pool = new WorkspacePool({\n factory: createWorkspaceBrain,\n maxMemoryMB: parseInt(process.env.BRAINBANK_MAX_MEMORY_MB ?? '2048', 10),\n ttlMinutes: parseInt(process.env.BRAINBANK_TTL_MINUTES ?? '30', 10),\n onError: (repo, err) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`BrainBank pool error [${repo}]: ${msg}`);\n },\n});\n\n/** Resolve repo and get a BrainBank from the pool. */\nasync function getBrainBank(targetRepo?: string) {\n return pool.get(resolveRepoPath(targetRepo));\n}\n\n// ── MCP Server Setup ────────────────────────────────\n\nconst server = new McpServer({\n name: 'brainbank',\n version: '0.4.0',\n});\n\n// ── Tool: brainbank_context ─────────────────────────\n\nserver.registerTool(\n 'brainbank_context',\n {\n title: 'BrainBank Context',\n description:\n 'Get a formatted knowledge context block for a task. Returns a Workflow Trace: ' +\n 'search hits + full call tree with `called by` annotations, topologically ordered. ' +\n 'All source code included — no trimming, no truncation.',\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(20).describe('Max code results'),\n gitResults: z.number().optional().default(5).describe('Max git commit results'),\n docsResults: z.number().optional().describe('Max document results (omit to skip docs)'),\n sources: z.record(z.number()).optional().describe('Per-source result limits, overrides codeResults/gitResults/docsResults (e.g. { code: 10, git: 0, docs: 5 })'),\n path: z.string().optional().describe('Filter results to files under this path prefix (e.g. src/services/)'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n // BrainBankQL context fields\n lines: z.boolean().optional().describe('Prefix each code line with its source line number (e.g. 127| code)'),\n symbols: z.boolean().optional().describe('Append symbol index (all functions, classes, interfaces) for matched files'),\n compact: z.boolean().optional().describe('Show only function/class signatures, skip bodies'),\n callTree: z.union([z.boolean(), z.object({ depth: z.number() })]).optional().describe('Include call tree expansion. Pass { depth: N } to control depth'),\n imports: z.boolean().optional().describe('Include dependency/import summary section'),\n expander: z.boolean().optional().describe('Enable LLM-powered context expansion to discover related chunks not found by search'),\n }),\n },\n async ({ task, affectedFiles, codeResults, gitResults, docsResults, sources, path, repo, lines, symbols, compact, callTree, imports, expander }) => {\n const repoPath = resolveRepoPath(repo);\n const brainbank = await getBrainBank(repo);\n\n // Build sources from explicit params, then let `sources` override\n const base: Record<string, number> = { code: codeResults, git: gitResults };\n if (docsResults !== undefined) base.docs = docsResults;\n const resolvedSources = sources ? { ...base, ...sources } : base;\n\n // Build fields from explicit params (only include defined values)\n const fields: Record<string, unknown> = {};\n if (lines !== undefined) fields.lines = lines;\n if (symbols !== undefined) fields.symbols = symbols;\n if (compact !== undefined) fields.compact = compact;\n if (callTree !== undefined) fields.callTree = callTree;\n if (imports !== undefined) fields.imports = imports;\n if (expander !== undefined) fields.expander = expander;\n\n const context = await brainbank.getContext(task, {\n affectedFiles,\n sources: resolvedSources,\n pathPrefix: path,\n source: 'mcp',\n fields: Object.keys(fields).length > 0 ? fields : undefined,\n });\n\n return { content: [{ type: 'text' as const, text: context }] };\n },\n);\n\n// ── Tool: brainbank_index ───────────────────────────\n\nserver.registerTool(\n 'brainbank_index',\n {\n title: 'BrainBank Index',\n description:\n 'Re-index code, git history, and docs. Requires .brainbank/config.json to exist. ' +\n 'Incremental — only changed files are processed.',\n inputSchema: z.object({\n forceReindex: z.boolean().optional().default(false).describe('Force re-index of all files'),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n }),\n },\n async ({ forceReindex, repo }) => {\n const repoPath = resolveRepoPath(repo);\n\n // Require config.json — force users to set up structure first\n if (!existsSync(`${repoPath}/.brainbank/config.json`)) {\n return {\n content: [{\n type: 'text' as const,\n text:\n `BrainBank: No .brainbank/config.json found at ${repoPath}.\\n\\n` +\n `## How to set up\\n\\n` +\n `Create \\`${repoPath}/.brainbank/config.json\\` with:\\n\\n` +\n '```json\\n' +\n '{\\n' +\n ' \"plugins\": [\"code\"],\\n' +\n ' \"code\": {\\n' +\n ' \"embedding\": \"perplexity-context\",\\n' +\n ' \"ignore\": [\\n' +\n ' \"node_modules/**\", \"dist/**\", \"build/**\",\\n' +\n ' \".next/**\", \"coverage/**\", \"__pycache__/**\",\\n' +\n ' \"**/*.min.js\", \"**/*.min.css\",\\n' +\n ' \"tests/**\", \"test/**\",\\n' +\n ' \"**/test_*.py\", \"**/*_test.py\",\\n' +\n ' \"**/*.test.ts\", \"**/*.spec.ts\"\\n' +\n ' ]\\n' +\n ' },\\n' +\n ' \"embedding\": \"perplexity-context\"\\n' +\n '}\\n' +\n '```\\n\\n' +\n `**Embedding options:** \\`local\\` (free, offline), \\`openai\\`, \\`perplexity\\`, \\`perplexity-context\\` (best quality)\\n` +\n `**Plugins available:** \\`code\\`, \\`git\\`, \\`docs\\`\\n\\n` +\n `Then run:\\n` +\n '```bash\\nbrainbank index . --force --yes\\n```',\n }],\n };\n }\n\n const brainbank = await getBrainBank(repo);\n const result = await brainbank.index({ forceReindex });\n\n const lines = ['## Indexing Complete', ''];\n\n const codeResult = result.code as { indexed?: number; skipped?: number; chunks?: number } | undefined;\n const gitResult = result.git as { indexed?: number; skipped?: number } | undefined;\n\n lines.push(`**Code**: ${codeResult?.indexed ?? 0} files indexed, ${codeResult?.skipped ?? 0} skipped, ${codeResult?.chunks ?? 0} chunks`);\n lines.push(`**Git**: ${gitResult?.indexed ?? 0} commits indexed, ${gitResult?.skipped ?? 0} skipped`);\n\n const docsResult = result.docs as Record<string, { indexed: number; skipped: number; chunks: number }> | undefined;\n if (docsResult) {\n for (const [name, stat] of Object.entries(docsResult)) {\n lines.push(`**Docs [${name}]**: ${stat.indexed} indexed, ${stat.skipped} skipped, ${stat.chunks} chunks`);\n }\n }\n\n const stats = brainbank.stats();\n const codeStats = stats.code as { chunks?: number } | undefined;\n const gitStats = stats.git as { commits?: number } | undefined;\n const docStats = stats.documents as { documents?: number } | undefined;\n lines.push('');\n lines.push(`**Totals**: ${codeStats?.chunks ?? 0} code chunks, ${gitStats?.commits ?? 0} commits, ${docStats?.documents ?? 0} docs`);\n\n return { content: [{ type: 'text' as const, text: lines.join('\\n') }] };\n },\n);\n\n// ── Tool: brainbank_files ───────────────────────────\n\nserver.registerTool(\n 'brainbank_files',\n {\n title: 'BrainBank Files',\n description:\n 'Fetch full file contents from the index. Use AFTER brainbank_context ' +\n 'to view complete files identified by search. No semantic search runs — ' +\n 'this is a direct file viewer.\\n\\n' +\n 'Supports:\\n' +\n '- Exact paths: \"src/auth/login.ts\"\\n' +\n '- Directories: \"src/graph/\" (trailing / = all files under path)\\n' +\n '- Glob patterns: \"src/**/*.service.ts\"\\n' +\n '- Fuzzy basename: \"plugin.ts\" (matches src/plugin.ts when exact fails)',\n inputSchema: z.object({\n files: z.array(z.string()).describe(\n 'File paths to fetch. Exact paths, directories (trailing /), ' +\n 'glob patterns (e.g. src/**/*.ts), or fuzzy basenames.',\n ),\n repo: z.string().optional().describe('Repository path (default: BRAINBANK_REPO)'),\n lines: z.boolean().optional().describe('Prefix each line with source line number'),\n }),\n },\n async ({ files, repo, lines }) => {\n const brainbank = await getBrainBank(repo);\n const results = brainbank.resolveFiles(files);\n\n if (results.length === 0) {\n return { content: [{ type: 'text' as const, text: 'No matching files found in the index.' }] };\n }\n\n // Format: markdown with file headers + fenced code blocks\n const parts: string[] = [];\n for (const r of results) {\n const meta = r.metadata as Record<string, unknown>;\n const lang = (meta.language as string) ?? '';\n const startLine = (meta.startLine as number) ?? 1;\n\n parts.push(`## ${r.filePath}\\n`);\n parts.push('```' + lang);\n\n if (lines) {\n const codeLines = r.content.split('\\n');\n const pad = String(startLine + codeLines.length - 1).length;\n parts.push(codeLines.map((l, i) =>\n `${String(startLine + i).padStart(pad)}| ${l}`,\n ).join('\\n'));\n } else {\n parts.push(r.content);\n }\n\n parts.push('```\\n');\n }\n\n return { content: [{ type: 'text' as const, text: parts.join('\\n') }] };\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 const message = err instanceof Error ? err.message : String(err);\n console.error(`BrainBank MCP Server Error: ${message}`);\n process.exit(1);\n});\n","/**\n * WorkspacePool — BrainBank instance lifecycle manager.\n *\n * Manages cached BrainBank instances per workspace with:\n * - Memory-pressure eviction (oldest idle first)\n * - TTL eviction for inactive workspaces\n * - Active-operation tracking (prevents mid-query eviction)\n * - Hot-reload of stale HNSW indices\n */\n\nimport type { BrainBank } from 'brainbank';\n\n/** Pool configuration. */\nexport interface PoolOptions {\n /** Max total estimated memory in MB. Default: 2048. */\n maxMemoryMB?: number;\n /** Minutes of inactivity before eviction. Default: 30. */\n ttlMinutes?: number;\n /** Factory function to create a BrainBank for a repo path. */\n factory: (repoPath: string) => Promise<BrainBank>;\n /** Called when a workspace is evicted. */\n onEvict?: (repoPath: string) => void;\n /** Called when an error occurs during pool operations. */\n onError?: (repoPath: string, err: unknown) => void;\n}\n\n/** Internal pool entry. */\ninterface PoolEntry {\n brain: BrainBank;\n repoPath: string;\n lastAccess: number;\n createdAt: number;\n activeOps: number;\n}\n\n/** Public pool statistics. */\nexport interface PoolStats {\n size: number;\n totalMemoryMB: number;\n entries: PoolEntryStats[];\n}\n\n/** Per-entry statistics. */\nexport interface PoolEntryStats {\n repoPath: string;\n lastAccessAgo: string;\n memoryMB: number;\n activeOps: number;\n}\n\nconst DEFAULT_MAX_MEMORY_MB = 2048;\nconst DEFAULT_TTL_MINUTES = 30;\nconst EVICTION_INTERVAL_MS = 60_000;\n\n/** Format milliseconds as a human-readable \"ago\" string. */\nfunction formatAgo(ms: number): string {\n if (ms < 60_000) return `${Math.round(ms / 1000)}s ago`;\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m ago`;\n return `${Math.round(ms / 3_600_000)}h ago`;\n}\n\nexport class WorkspacePool {\n private _pool = new Map<string, PoolEntry>();\n private _timer: ReturnType<typeof setInterval> | null = null;\n private _maxMemoryBytes: number;\n private _ttlMs: number;\n private _factory: (repoPath: string) => Promise<BrainBank>;\n private _onEvict?: (repoPath: string) => void;\n private _onError?: (repoPath: string, err: unknown) => void;\n\n constructor(options: PoolOptions) {\n this._maxMemoryBytes = (options.maxMemoryMB ?? DEFAULT_MAX_MEMORY_MB) * 1024 * 1024;\n this._ttlMs = (options.ttlMinutes ?? DEFAULT_TTL_MINUTES) * 60 * 1000;\n this._factory = options.factory;\n this._onEvict = options.onEvict;\n this._onError = options.onError;\n\n this._timer = setInterval(() => this._evictStale(), EVICTION_INTERVAL_MS);\n // Don't hold the process open for the timer\n if (this._timer.unref) this._timer.unref();\n }\n\n /** Number of cached workspaces. */\n get size(): number {\n return this._pool.size;\n }\n\n /**\n * Get a BrainBank for the given repo path.\n * Returns a cached instance (with hot-reload) or creates a new one.\n */\n async get(repoPath: string): Promise<BrainBank> {\n const key = repoPath.replace(/\\/+$/, '');\n\n const existing = this._pool.get(key);\n if (existing) {\n existing.lastAccess = Date.now();\n try { await existing.brain.ensureFresh(); } catch { /* stale is better than nothing */ }\n return existing.brain;\n }\n\n this._evictByMemoryPressure();\n\n const brain = await this._factory(key);\n this._pool.set(key, {\n brain,\n repoPath: key,\n lastAccess: Date.now(),\n createdAt: Date.now(),\n activeOps: 0,\n });\n\n return brain;\n }\n\n /**\n * Execute an operation with active-op tracking.\n * Prevents the workspace from being evicted while the operation runs.\n */\n async withBrain<T>(repoPath: string, fn: (brain: BrainBank) => Promise<T>): Promise<T> {\n const brain = await this.get(repoPath);\n const key = repoPath.replace(/\\/+$/, '');\n const entry = this._pool.get(key);\n if (entry) entry.activeOps++;\n\n try {\n return await fn(brain);\n } finally {\n if (entry) {\n entry.activeOps--;\n entry.lastAccess = Date.now();\n }\n }\n }\n\n /** Manually evict a specific workspace. */\n evict(repoPath: string): void {\n const key = repoPath.replace(/\\/+$/, '');\n this._evictEntry(key);\n }\n\n /** Get pool statistics. */\n stats(): PoolStats {\n const now = Date.now();\n let totalMemory = 0;\n const entries: PoolEntryStats[] = [];\n\n for (const entry of this._pool.values()) {\n const memBytes = entry.brain.memoryHint();\n const memMB = Math.round(memBytes / 1024 / 1024 * 100) / 100;\n totalMemory += memBytes;\n\n entries.push({\n repoPath: entry.repoPath,\n lastAccessAgo: formatAgo(now - entry.lastAccess),\n memoryMB: memMB,\n activeOps: entry.activeOps,\n });\n }\n\n return {\n size: this._pool.size,\n totalMemoryMB: Math.round(totalMemory / 1024 / 1024 * 100) / 100,\n entries,\n };\n }\n\n /** Close all entries and stop the eviction timer. */\n close(): void {\n if (this._timer) {\n clearInterval(this._timer);\n this._timer = null;\n }\n for (const key of [...this._pool.keys()]) {\n this._evictEntry(key);\n }\n }\n\n /** Evict workspaces that haven't been accessed within the TTL. */\n private _evictStale(): void {\n const cutoff = Date.now() - this._ttlMs;\n for (const [key, entry] of this._pool) {\n if (entry.lastAccess < cutoff && entry.activeOps === 0) {\n this._evictEntry(key);\n }\n }\n }\n\n /** Evict oldest idle entries until total memory is under the limit. */\n private _evictByMemoryPressure(): void {\n let totalMemory = 0;\n for (const entry of this._pool.values()) {\n totalMemory += entry.brain.memoryHint();\n }\n\n if (totalMemory < this._maxMemoryBytes) return;\n\n // Sort by lastAccess ascending (oldest first), filter idle\n const candidates = [...this._pool.entries()]\n .filter(([, e]) => e.activeOps === 0)\n .sort(([, a], [, b]) => a.lastAccess - b.lastAccess);\n\n for (const [key, entry] of candidates) {\n if (totalMemory < this._maxMemoryBytes) break;\n totalMemory -= entry.brain.memoryHint();\n this._evictEntry(key);\n }\n }\n\n /** Evict a single entry by key. */\n private _evictEntry(key: string): void {\n const entry = this._pool.get(key);\n if (!entry) return;\n\n try {\n entry.brain.close();\n } catch (err: unknown) {\n this._onError?.(key, err);\n }\n\n this._pool.delete(key);\n this._onEvict?.(key);\n }\n}\n","/**\n * WorkspaceFactory — creates BrainBank instances via the core factory.\n *\n * Delegates to `createBrain()` from the `brainbank` package, passing\n * a portable `BrainContext`. No plugin hardcoding — the factory handles\n * plugin discovery from config and installed packages.\n */\n\nimport type { BrainBank, BrainContext } from 'brainbank';\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * Detect repo root by walking up from startDir until we find `.git/`.\n * Returns startDir itself if no `.git/` is found.\n */\nexport function 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;\n dir = parent;\n }\n return path.resolve(startDir);\n}\n\n/** Resolve the effective repo path from an optional target, env, or cwd. */\nexport function resolveRepoPath(targetRepo?: string): string {\n const rp = targetRepo\n ?? process.env.BRAINBANK_REPO\n ?? findRepoRoot(process.cwd());\n return rp.replace(/\\/+$/, '');\n}\n\n/**\n * Create a BrainBank instance for a workspace.\n * Uses the core factory which handles:\n * - Config loading from .brainbank/config.json\n * - Dynamic plugin discovery and registration\n * - Embedding/reranker provider setup\n * - Folder plugin auto-discovery\n */\nexport async function createWorkspaceBrain(repoPath: string): Promise<BrainBank> {\n const { createBrain, resetFactoryCache } = await import('brainbank') as typeof import('brainbank');\n resetFactoryCache();\n\n const context: BrainContext = {\n repoPath,\n env: process.env as Record<string, string | undefined>,\n };\n\n // Silence stdout during initialization — the core factory emits ANSI-colored\n // console.log messages (plugin loading, multi-repo detection) that corrupt\n // the MCP JSON-RPC stdio transport. Redirect console.log → stderr temporarily.\n const origLog = console.log;\n console.log = (...args: unknown[]) => console.error(...args);\n try {\n const brain = await createBrain(context);\n await brain.initialize();\n return brain;\n } finally {\n console.log = origLog;\n }\n}\n"],"mappings":"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","names":["existsSync","path","existsSync"]}
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainbank/mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server for BrainBank",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "
|
|
7
|
-
"bin": "
|
|
6
|
+
"main": "dist/mcp-server.js",
|
|
7
|
+
"bin": "dist/mcp-server.js",
|
|
8
8
|
"files": [
|
|
9
|
+
"dist/",
|
|
9
10
|
"src/"
|
|
10
11
|
],
|
|
11
12
|
"scripts": {
|
|
12
|
-
"build": "tsup"
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
13
15
|
},
|
|
14
16
|
"dependencies": {
|
|
15
17
|
"brainbank": ">=0.2.2",
|