@brainbank/mcp 0.1.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 ADDED
@@ -0,0 +1,127 @@
1
+ # @brainbank/mcp
2
+
3
+ [MCP](https://modelcontextprotocol.io/) server for [BrainBank](https://github.com/pinecall/brainbank) — exposes code search, git history, and collections as tools for AI agents via stdio transport.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @brainbank/mcp
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### Antigravity
14
+
15
+ Add to `~/.gemini/antigravity/mcp_config.json`:
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "brainbank": {
21
+ "command": "npx",
22
+ "args": ["-y", "@brainbank/mcp"],
23
+ "env": {
24
+ "BRAINBANK_EMBEDDING": "openai"
25
+ }
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ ### Claude Desktop
32
+
33
+ Add to Claude Desktop settings → Developer → MCP Servers:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "brainbank": {
39
+ "command": "npx",
40
+ "args": ["-y", "@brainbank/mcp"],
41
+ "env": {
42
+ "BRAINBANK_EMBEDDING": "openai",
43
+ "OPENAI_API_KEY": "sk-..."
44
+ }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Cursor
51
+
52
+ Add to `.cursor/mcp.json` in your project:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "brainbank": {
58
+ "command": "npx",
59
+ "args": ["-y", "@brainbank/mcp"],
60
+ "env": {
61
+ "BRAINBANK_EMBEDDING": "openai"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ ### CLI (standalone)
69
+
70
+ ```bash
71
+ brainbank serve
72
+ ```
73
+
74
+ ## Environment Variables
75
+
76
+ | Variable | Description | Default |
77
+ |----------|-------------|---------|
78
+ | `BRAINBANK_REPO` | Default repo path (fallback if `repo` param not provided) | — |
79
+ | `BRAINBANK_EMBEDDING` | Embedding provider: `openai` or `local` | `local` |
80
+ | `OPENAI_API_KEY` | Required when using `openai` embeddings | — |
81
+
82
+ > The agent passes the `repo` parameter per tool call based on the active workspace — no hardcoded paths needed.
83
+
84
+ ## Available Tools
85
+
86
+ | Tool | Description |
87
+ |------|-------------|
88
+ | `brainbank_hybrid_search` | Best quality: vector + BM25 fused search |
89
+ | `brainbank_search` | Semantic vector search |
90
+ | `brainbank_keyword_search` | Instant BM25 full-text search |
91
+ | `brainbank_context` | Formatted context for system prompt injection |
92
+ | `brainbank_index` | Trigger incremental code/git indexing |
93
+ | `brainbank_stats` | Index statistics (files, commits, chunks) |
94
+ | `brainbank_history` | Git history for a specific file |
95
+ | `brainbank_coedits` | Files that frequently change together |
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 |
99
+
100
+ ## Multi-Workspace
101
+
102
+ The MCP server maintains a pool of BrainBank instances — one per unique `repo` path. Each tool call can target a different workspace:
103
+
104
+ ```typescript
105
+ // Agent working in one workspace
106
+ brainbank_hybrid_search({ query: "login form", repo: "/Users/you/project-a" })
107
+
108
+ // Agent switches to another project — new instance auto-created
109
+ brainbank_hybrid_search({ query: "API routes", repo: "/Users/you/project-b" })
110
+ ```
111
+
112
+ Instances are cached in memory after first initialization (~480ms), so subsequent queries to the same repo are fast.
113
+
114
+ ## How it works
115
+
116
+ ```
117
+ AI Agent ←→ stdio ←→ @brainbank/mcp ←→ BrainBank core ←→ SQLite
118
+ ```
119
+
120
+ 1. Agent sends an MCP tool call (e.g., `brainbank_hybrid_search`)
121
+ 2. Server routes to the correct BrainBank instance (by `repo` path)
122
+ 3. BrainBank executes the search against its local SQLite database
123
+ 4. Results returned as structured JSON to the agent
124
+
125
+ ## License
126
+
127
+ MIT
@@ -0,0 +1,405 @@
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 * as fs from "fs";
10
+ import * as path from "path";
11
+ import { BrainBank } from "brainbank";
12
+ import { code } from "brainbank/code";
13
+ import { git } from "brainbank/git";
14
+ import { docs } from "brainbank/docs";
15
+ var defaultRepoPath = process.env.BRAINBANK_REPO || void 0;
16
+ async function createReranker() {
17
+ const rerankerEnv = process.env.BRAINBANK_RERANKER ?? "none";
18
+ if (rerankerEnv === "none") return void 0;
19
+ if (rerankerEnv === "qwen3") {
20
+ const { Qwen3Reranker } = await import("@brainbank/reranker");
21
+ return new Qwen3Reranker();
22
+ }
23
+ return void 0;
24
+ }
25
+ __name(createReranker, "createReranker");
26
+ async function createEmbeddingProvider() {
27
+ const embeddingEnv = process.env.BRAINBANK_EMBEDDING ?? "local";
28
+ if (embeddingEnv === "openai") {
29
+ const { OpenAIEmbedding } = await import("brainbank");
30
+ return new OpenAIEmbedding();
31
+ }
32
+ return void 0;
33
+ }
34
+ __name(createEmbeddingProvider, "createEmbeddingProvider");
35
+ var _pool = /* @__PURE__ */ new Map();
36
+ var _sharedReranker = void 0;
37
+ var _sharedEmbedding = void 0;
38
+ var _sharedReady = false;
39
+ async function ensureShared() {
40
+ if (_sharedReady) return;
41
+ _sharedReranker = await createReranker();
42
+ _sharedEmbedding = await createEmbeddingProvider();
43
+ _sharedReady = true;
44
+ }
45
+ __name(ensureShared, "ensureShared");
46
+ async function getBrainBank(targetRepo) {
47
+ const rp = targetRepo ?? defaultRepoPath;
48
+ if (!rp) {
49
+ throw new Error(
50
+ "No repository specified. Pass the `repo` parameter with the workspace path, or set BRAINBANK_REPO environment variable."
51
+ );
52
+ }
53
+ const resolved = rp.replace(/\/+$/, "");
54
+ if (_pool.has(resolved)) return _pool.get(resolved);
55
+ await ensureShared();
56
+ const opts = { repoPath: resolved, reranker: _sharedReranker };
57
+ if (_sharedEmbedding) {
58
+ opts.embeddingProvider = _sharedEmbedding;
59
+ opts.embeddingDims = _sharedEmbedding.dims;
60
+ }
61
+ const brain = new BrainBank(opts).use(code({ repoPath: resolved })).use(git({ repoPath: resolved })).use(docs());
62
+ await brain.initialize();
63
+ _pool.set(resolved, brain);
64
+ return brain;
65
+ }
66
+ __name(getBrainBank, "getBrainBank");
67
+ var server = new McpServer({
68
+ name: "brainbank",
69
+ version: "0.1.0"
70
+ });
71
+ server.registerTool(
72
+ "brainbank_search",
73
+ {
74
+ title: "BrainBank Search",
75
+ description: "Semantic search across indexed code and git commits. Returns the most relevant results sorted by similarity score.",
76
+ inputSchema: z.object({
77
+ query: z.string().describe("Natural language search query describing what you are looking for"),
78
+ codeK: z.number().optional().default(6).describe("Max code results to return"),
79
+ gitK: z.number().optional().default(5).describe("Max git commit results to return"),
80
+ minScore: z.number().optional().default(0.25).describe("Minimum similarity score threshold (0-1)"),
81
+ repo: z.string().optional().describe("Repository path to search (default: BRAINBANK_REPO)")
82
+ })
83
+ },
84
+ async ({ query, codeK, gitK, minScore, repo }) => {
85
+ const brainbank = await getBrainBank(repo);
86
+ const results = await brainbank.search(query, { codeK, gitK, minScore });
87
+ if (results.length === 0) {
88
+ return { content: [{ type: "text", text: "No results found for this query." }] };
89
+ }
90
+ return { content: [{ type: "text", text: formatResults(results, "Semantic Search") }] };
91
+ }
92
+ );
93
+ server.registerTool(
94
+ "brainbank_context",
95
+ {
96
+ title: "BrainBank Context",
97
+ 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.",
98
+ inputSchema: z.object({
99
+ task: z.string().describe("Description of the task you need context for"),
100
+ affectedFiles: z.array(z.string()).optional().default([]).describe("Files you plan to modify (improves co-edit suggestions)"),
101
+ codeResults: z.number().optional().default(6).describe("Max code results"),
102
+ gitResults: z.number().optional().default(5).describe("Max git commit results"),
103
+ repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
104
+ })
105
+ },
106
+ async ({ task, affectedFiles, codeResults, gitResults, repo }) => {
107
+ const brainbank = await getBrainBank(repo);
108
+ const context = await brainbank.getContext(task, {
109
+ affectedFiles,
110
+ codeResults,
111
+ gitResults
112
+ });
113
+ return { content: [{ type: "text", text: context }] };
114
+ }
115
+ );
116
+ server.registerTool(
117
+ "brainbank_index",
118
+ {
119
+ title: "BrainBank Index",
120
+ description: "Index (or re-index) the repository code and git history. Run this before searching if the codebase has changed. Indexing is incremental \u2014 only changed files are processed.",
121
+ inputSchema: z.object({
122
+ forceReindex: z.boolean().optional().default(false).describe("Force re-index of all files, even unchanged ones"),
123
+ gitDepth: z.number().optional().default(500).describe("Number of git commits to index"),
124
+ repo: z.string().optional().describe("Repository path to index (default: BRAINBANK_REPO)")
125
+ })
126
+ },
127
+ async ({ forceReindex, gitDepth, repo }) => {
128
+ const brainbank = await getBrainBank(repo);
129
+ const result = await brainbank.index({ forceReindex, gitDepth });
130
+ const lines = [
131
+ "## Indexing Complete",
132
+ "",
133
+ `**Code**: ${result.code?.indexed ?? 0} files indexed, ${result.code?.skipped ?? 0} skipped, ${result.code?.chunks ?? 0} chunks`,
134
+ `**Git**: ${result.git?.indexed ?? 0} commits indexed, ${result.git?.skipped ?? 0} skipped`
135
+ ];
136
+ const stats = brainbank.stats();
137
+ lines.push("");
138
+ lines.push(`**Totals**: ${stats.code?.chunks ?? 0} code chunks, ${stats.git?.commits ?? 0} commits`);
139
+ return { content: [{ type: "text", text: lines.join("\n") }] };
140
+ }
141
+ );
142
+ server.registerTool(
143
+ "brainbank_stats",
144
+ {
145
+ title: "BrainBank Stats",
146
+ description: "Get statistics about the indexed knowledge base: file count, code chunks, git commits, HNSW index sizes, and KV collections.",
147
+ inputSchema: z.object({
148
+ repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
149
+ })
150
+ },
151
+ async ({ repo }) => {
152
+ const brainbank = await getBrainBank(repo);
153
+ const s = brainbank.stats();
154
+ const lines = [
155
+ "## BrainBank Knowledge Base Stats",
156
+ ""
157
+ ];
158
+ if (s.code) {
159
+ lines.push("### Code");
160
+ lines.push(`- Files indexed: ${s.code.files}`);
161
+ lines.push(`- Code chunks: ${s.code.chunks}`);
162
+ lines.push(`- HNSW vectors: ${s.code.hnswSize}`);
163
+ lines.push("");
164
+ }
165
+ if (s.git) {
166
+ lines.push("### Git History");
167
+ lines.push(`- Commits indexed: ${s.git.commits}`);
168
+ lines.push(`- Files tracked: ${s.git.filesTracked}`);
169
+ lines.push(`- Co-edit pairs: ${s.git.coEdits}`);
170
+ lines.push(`- HNSW vectors: ${s.git.hnswSize}`);
171
+ lines.push("");
172
+ }
173
+ if (s.documents) {
174
+ lines.push("### Documents");
175
+ lines.push(`- Collections: ${s.documents.collections}`);
176
+ lines.push(`- Documents: ${s.documents.documents}`);
177
+ lines.push(`- HNSW vectors: ${s.documents.hnswSize}`);
178
+ lines.push("");
179
+ }
180
+ const kvNames = brainbank.listCollectionNames();
181
+ if (kvNames.length > 0) {
182
+ lines.push("### KV Collections");
183
+ for (const name of kvNames) {
184
+ const coll = brainbank.collection(name);
185
+ lines.push(`- ${name}: ${coll.count()} items`);
186
+ }
187
+ }
188
+ return { content: [{ type: "text", text: lines.join("\n") }] };
189
+ }
190
+ );
191
+ server.registerTool(
192
+ "brainbank_history",
193
+ {
194
+ title: "BrainBank File History",
195
+ description: "Get the git commit history for a specific file. Shows recent changes, authors, and line counts.",
196
+ inputSchema: z.object({
197
+ filePath: z.string().describe('File path (relative or partial match, e.g. "auth.ts" or "src/core/agent.ts")'),
198
+ limit: z.number().optional().default(20).describe("Max commits to return"),
199
+ repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
200
+ })
201
+ },
202
+ async ({ filePath, limit, repo }) => {
203
+ const brainbank = await getBrainBank(repo);
204
+ const history = await brainbank.fileHistory(filePath, limit);
205
+ if (history.length === 0) {
206
+ return { content: [{ type: "text", text: `No git history found for "${filePath}"` }] };
207
+ }
208
+ const lines = [`## Git History: ${filePath}`, ""];
209
+ for (const h of history) {
210
+ lines.push(`**[${h.short_hash}]** ${h.message} *(${h.author}, +${h.additions}/-${h.deletions})*`);
211
+ }
212
+ return { content: [{ type: "text", text: lines.join("\n") }] };
213
+ }
214
+ );
215
+ server.registerTool(
216
+ "brainbank_coedits",
217
+ {
218
+ title: "BrainBank Co-Edits",
219
+ description: "Find files that historically change together with a given file. Useful for understanding dependencies and ensuring you do not miss related changes.",
220
+ inputSchema: z.object({
221
+ filePath: z.string().describe("File path to check co-edit relationships for"),
222
+ limit: z.number().optional().default(5).describe("Max co-edit suggestions"),
223
+ repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
224
+ })
225
+ },
226
+ async ({ filePath, limit, repo }) => {
227
+ const brainbank = await getBrainBank(repo);
228
+ const suggestions = brainbank.coEdits(filePath, limit);
229
+ if (suggestions.length === 0) {
230
+ return { content: [{ type: "text", text: `No co-edit patterns found for "${filePath}"` }] };
231
+ }
232
+ const lines = [`## Co-Edits for: ${filePath}`, ""];
233
+ lines.push("Files that frequently change together:");
234
+ for (const s of suggestions) {
235
+ lines.push(`- **${s.file}** (changed together ${s.count} times)`);
236
+ }
237
+ return { content: [{ type: "text", text: lines.join("\n") }] };
238
+ }
239
+ );
240
+ server.registerTool(
241
+ "brainbank_hybrid_search",
242
+ {
243
+ title: "BrainBank Hybrid Search",
244
+ 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.",
245
+ inputSchema: z.object({
246
+ query: z.string().describe("Search query \u2014 works with both keywords and natural language"),
247
+ codeK: z.number().optional().default(8).describe("Max code results (shorthand for collections.code)"),
248
+ gitK: z.number().optional().default(5).describe("Max git results (shorthand for collections.git)"),
249
+ collections: z.record(z.string(), z.number()).optional().describe(
250
+ '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 }'
251
+ ),
252
+ repo: z.string().optional().describe("Repository path to search (default: BRAINBANK_REPO)")
253
+ })
254
+ },
255
+ async ({ query, codeK, gitK, collections, repo }) => {
256
+ const t0 = performance.now();
257
+ const brainbank = await getBrainBank(repo);
258
+ const t1 = performance.now();
259
+ const results = await brainbank.hybridSearch(query, { codeK, gitK, collections });
260
+ const t2 = performance.now();
261
+ const timing = `
262
+
263
+ \u23F1 getBrainBank: ${(t1 - t0).toFixed(0)}ms | hybridSearch: ${(t2 - t1).toFixed(0)}ms | total: ${(t2 - t0).toFixed(0)}ms`;
264
+ if (results.length === 0) {
265
+ return { content: [{ type: "text", text: "No results found for this query." + timing }] };
266
+ }
267
+ return { content: [{ type: "text", text: formatResults(results, "Hybrid Search (Vector + BM25 \u2192 RRF)") + timing }] };
268
+ }
269
+ );
270
+ server.registerTool(
271
+ "brainbank_keyword_search",
272
+ {
273
+ title: "BrainBank Keyword Search",
274
+ description: "Instant BM25 keyword search (no embedding computation needed). Best for exact terms, function names, variable names, error messages, and specific identifiers.",
275
+ inputSchema: z.object({
276
+ query: z.string().describe("Keywords to search for"),
277
+ codeK: z.number().optional().default(8).describe("Max code results to return"),
278
+ gitK: z.number().optional().default(5).describe("Max git commit results to return"),
279
+ repo: z.string().optional().describe("Repository path to search (default: BRAINBANK_REPO)")
280
+ })
281
+ },
282
+ async ({ query, codeK, gitK, repo }) => {
283
+ const brainbank = await getBrainBank(repo);
284
+ const results = brainbank.searchBM25(query, { codeK, gitK });
285
+ if (results.length === 0) {
286
+ return { content: [{ type: "text", text: "No keyword matches found." }] };
287
+ }
288
+ return { content: [{ type: "text", text: formatResults(results, "Keyword Search (BM25)") }] };
289
+ }
290
+ );
291
+ server.registerTool(
292
+ "brainbank_collection_add",
293
+ {
294
+ title: "BrainBank Collection Add",
295
+ 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.",
296
+ inputSchema: z.object({
297
+ collection: z.string().describe('Collection name (e.g. "errors", "decisions", "context")'),
298
+ content: z.string().describe("Content to store"),
299
+ metadata: z.record(z.any()).optional().default({}).describe("Optional metadata object"),
300
+ repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
301
+ })
302
+ },
303
+ async ({ collection, content, metadata, repo }) => {
304
+ const brainbank = await getBrainBank(repo);
305
+ const coll = brainbank.collection(collection);
306
+ const id = await coll.add(content, metadata);
307
+ return {
308
+ content: [{ type: "text", text: `\u2713 Item #${id} added to '${collection}' (${coll.count()} total)` }]
309
+ };
310
+ }
311
+ );
312
+ server.registerTool(
313
+ "brainbank_collection_search",
314
+ {
315
+ title: "BrainBank Collection Search",
316
+ description: "Search a dynamic KV collection using hybrid vector + keyword search. Returns semantically similar items.",
317
+ inputSchema: z.object({
318
+ collection: z.string().describe("Collection name to search"),
319
+ query: z.string().describe("Search query"),
320
+ k: z.number().optional().default(5).describe("Max results"),
321
+ mode: z.enum(["hybrid", "vector", "keyword"]).optional().default("hybrid").describe("Search mode"),
322
+ repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
323
+ })
324
+ },
325
+ async ({ collection, query, k, mode, repo }) => {
326
+ const brainbank = await getBrainBank(repo);
327
+ const coll = brainbank.collection(collection);
328
+ const results = await coll.search(query, { k, mode });
329
+ if (results.length === 0) {
330
+ return { content: [{ type: "text", text: `No results in '${collection}' for "${query}"` }] };
331
+ }
332
+ const lines = [`## Collection: ${collection} \u2014 "${query}"`, ""];
333
+ for (const r of results) {
334
+ const score = Math.round((r.score ?? 0) * 100);
335
+ lines.push(`[${score}%] ${r.content}`);
336
+ if (Object.keys(r.metadata).length > 0) {
337
+ lines.push(` ${JSON.stringify(r.metadata)}`);
338
+ }
339
+ lines.push("");
340
+ }
341
+ return { content: [{ type: "text", text: lines.join("\n") }] };
342
+ }
343
+ );
344
+ server.registerTool(
345
+ "brainbank_collection_trim",
346
+ {
347
+ title: "BrainBank Collection Trim",
348
+ description: "Trim a dynamic collection to keep only the N most recent items. Use this to prevent collections from growing unbounded.",
349
+ inputSchema: z.object({
350
+ collection: z.string().describe("Collection name"),
351
+ keep: z.number().describe("Number of most recent items to keep"),
352
+ repo: z.string().optional().describe("Repository path (default: BRAINBANK_REPO)")
353
+ })
354
+ },
355
+ async ({ collection, keep, repo }) => {
356
+ const brainbank = await getBrainBank(repo);
357
+ const coll = brainbank.collection(collection);
358
+ const result = await coll.trim({ keep });
359
+ return {
360
+ content: [{
361
+ type: "text",
362
+ text: `\u2713 Trimmed ${result.removed} items from '${collection}' (kept ${keep})`
363
+ }]
364
+ };
365
+ }
366
+ );
367
+ function formatResults(results, mode) {
368
+ const lines = [`## ${mode}`, ""];
369
+ for (const r of results) {
370
+ const score = Math.round(r.score * 100);
371
+ if (r.type === "code") {
372
+ const m = r.metadata;
373
+ lines.push(`[CODE ${score}%] ${r.filePath} \u2014 ${m.name || m.chunkType} (L${m.startLine}-${m.endLine})`);
374
+ lines.push(r.content);
375
+ lines.push("");
376
+ } else if (r.type === "commit") {
377
+ const m = r.metadata;
378
+ lines.push(`[COMMIT ${score}%] ${m.shortHash} \u2014 ${r.content} (${m.author})`);
379
+ if (m.files?.length) lines.push(` Files: ${m.files.join(", ")}`);
380
+ lines.push("");
381
+ } else if (r.type === "document") {
382
+ const ctx = r.context ? ` \u2014 ${r.context}` : "";
383
+ lines.push(`[DOC ${score}%] ${r.filePath} [${r.metadata.collection}]${ctx}`);
384
+ lines.push(r.content);
385
+ lines.push("");
386
+ } else if (r.type === "collection") {
387
+ const col = r.metadata?.collection ?? "unknown";
388
+ lines.push(`[COLLECTION ${score}%] [${col}]`);
389
+ lines.push(r.content);
390
+ lines.push("");
391
+ }
392
+ }
393
+ return lines.join("\n");
394
+ }
395
+ __name(formatResults, "formatResults");
396
+ async function main() {
397
+ const transport = new StdioServerTransport();
398
+ await server.connect(transport);
399
+ }
400
+ __name(main, "main");
401
+ main().catch((err) => {
402
+ console.error(`BrainBank MCP Server Error: ${err.message}`);
403
+ process.exit(1);
404
+ });
405
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +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 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 ({ forceReindex, gitDepth, repo }) => {\n const brainbank = await getBrainBank(repo);\n const result = await brainbank.index({ 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 const stats = brainbank.stats();\n lines.push('');\n lines.push(`**Totals**: ${stats.code?.chunks ?? 0} code chunks, ${stats.git?.commits ?? 0} commits`);\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":[]}
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@brainbank/mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP (Model Context Protocol) server for BrainBank",
5
+ "type": "module",
6
+ "main": "dist/mcp-server.js",
7
+ "bin": { "brainbank-mcp": "./dist/mcp-server.js" },
8
+ "files": ["dist/"],
9
+ "scripts": { "build": "tsup" },
10
+ "peerDependencies": {
11
+ "brainbank": ">=0.2.0"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.27.1",
15
+ "zod": "^4.3.6"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/pinecall/brainbank.git",
20
+ "directory": "packages/mcp"
21
+ },
22
+ "author": "Bernardo Castro <bernardo@pinecall.io>",
23
+ "license": "MIT"
24
+ }