brain-mcp
Version:
Brain MCP Server - Semantic knowledge base access for Claude Code via Model Context Protocol. Provides intelligent search and navigation of files from multiple locations through native MCP tools.
312 lines (310 loc) • 10.5 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileRegistry = void 0;
const sqlite3 = __importStar(require("sqlite3"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const uuid_1 = require("uuid");
class FileRegistry {
db;
dbPath;
constructor(configDir) {
this.dbPath = path.join(configDir, 'brain-registry.db');
// Ensure config directory exists
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true });
}
}
async initialize() {
return new Promise((resolve, reject) => {
this.db = new sqlite3.Database(this.dbPath, (err) => {
if (err) {
reject(err);
return;
}
// Create tables if they don't exist
this.createTables()
.then(() => resolve())
.catch(reject);
});
});
}
async createTables() {
const fileTableSql = `
CREATE TABLE IF NOT EXISTS files (
id TEXT PRIMARY KEY,
absolute_path TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
file_type TEXT NOT NULL,
date_added DATETIME DEFAULT CURRENT_TIMESTAMP,
last_modified DATETIME NOT NULL,
file_size INTEGER NOT NULL,
content_hash TEXT
);
CREATE INDEX IF NOT EXISTS idx_files_display_name ON files(display_name);
CREATE INDEX IF NOT EXISTS idx_files_absolute_path ON files(absolute_path);
`;
const chunkTableSql = `
CREATE TABLE IF NOT EXISTS chunks (
id TEXT PRIMARY KEY,
file_id TEXT NOT NULL,
chunk_index INTEGER NOT NULL,
chunk_content TEXT NOT NULL,
vector_store_key TEXT NOT NULL UNIQUE,
FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_chunks_file_id ON chunks(file_id);
CREATE INDEX IF NOT EXISTS idx_chunks_vector_store_key ON chunks(vector_store_key);
`;
return new Promise((resolve, reject) => {
this.db.serialize(() => {
this.db.run(fileTableSql, (err) => {
if (err) {
reject(err);
return;
}
this.db.run(chunkTableSql, (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
});
});
});
}
async addFile(absolutePath, displayName, fileType) {
const stats = fs.statSync(absolutePath);
const fileRecord = {
id: (0, uuid_1.v4)(),
absolutePath,
displayName,
fileType,
dateAdded: new Date(),
lastModified: stats.mtime,
fileSize: stats.size
};
return new Promise((resolve, reject) => {
const sql = `
INSERT INTO files (id, absolute_path, display_name, file_type, last_modified, file_size)
VALUES (?, ?, ?, ?, ?, ?)
`;
this.db.run(sql, [
fileRecord.id,
fileRecord.absolutePath,
fileRecord.displayName,
fileRecord.fileType,
fileRecord.lastModified.toISOString(),
fileRecord.fileSize
], (err) => {
if (err) {
reject(err);
return;
}
resolve(fileRecord);
});
});
}
async addChunk(fileId, chunkIndex, chunkContent) {
const chunkRecord = {
id: (0, uuid_1.v4)(),
fileId,
chunkIndex,
chunkContent,
vectorStoreKey: `${fileId}#chunk-${chunkIndex}`
};
return new Promise((resolve, reject) => {
const sql = `
INSERT INTO chunks (id, file_id, chunk_index, chunk_content, vector_store_key)
VALUES (?, ?, ?, ?, ?)
`;
this.db.run(sql, [
chunkRecord.id,
chunkRecord.fileId,
chunkRecord.chunkIndex,
chunkRecord.chunkContent,
chunkRecord.vectorStoreKey
], (err) => {
if (err) {
reject(err);
return;
}
resolve(chunkRecord);
});
});
}
async getFileByPath(absolutePath) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM files WHERE absolute_path = ?';
this.db.get(sql, [absolutePath], (err, row) => {
if (err) {
reject(err);
return;
}
if (!row) {
resolve(null);
return;
}
resolve(this.rowToFileRecord(row));
});
});
}
async getFileByDisplayName(displayName) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM files WHERE display_name = ?';
this.db.get(sql, [displayName], (err, row) => {
if (err) {
reject(err);
return;
}
if (!row) {
resolve(null);
return;
}
resolve(this.rowToFileRecord(row));
});
});
}
async getFileById(id) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM files WHERE id = ?';
this.db.get(sql, [id], (err, row) => {
if (err) {
reject(err);
return;
}
if (!row) {
resolve(null);
return;
}
resolve(this.rowToFileRecord(row));
});
});
}
async getChunkByVectorKey(vectorStoreKey) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM chunks WHERE vector_store_key = ?';
this.db.get(sql, [vectorStoreKey], (err, row) => {
if (err) {
reject(err);
return;
}
if (!row) {
resolve(null);
return;
}
resolve(this.rowToChunkRecord(row));
});
});
}
async getChunksByFileId(fileId) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM chunks WHERE file_id = ? ORDER BY chunk_index';
this.db.all(sql, [fileId], (err, rows) => {
if (err) {
reject(err);
return;
}
resolve(rows.map(row => this.rowToChunkRecord(row)));
});
});
}
async removeFile(fileId) {
return new Promise((resolve, reject) => {
const sql = 'DELETE FROM files WHERE id = ?';
this.db.run(sql, [fileId], (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
});
}
async getAllFiles() {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM files ORDER BY display_name';
this.db.all(sql, [], (err, rows) => {
if (err) {
reject(err);
return;
}
resolve(rows.map(row => this.rowToFileRecord(row)));
});
});
}
async updateFileModified(fileId, lastModified) {
return new Promise((resolve, reject) => {
const sql = 'UPDATE files SET last_modified = ? WHERE id = ?';
this.db.run(sql, [lastModified.toISOString(), fileId], (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
});
}
rowToFileRecord(row) {
return {
id: row.id,
absolutePath: row.absolute_path,
displayName: row.display_name,
fileType: row.file_type,
dateAdded: new Date(row.date_added),
lastModified: new Date(row.last_modified),
fileSize: row.file_size,
contentHash: row.content_hash
};
}
rowToChunkRecord(row) {
return {
id: row.id,
fileId: row.file_id,
chunkIndex: row.chunk_index,
chunkContent: row.chunk_content,
vectorStoreKey: row.vector_store_key
};
}
async close() {
return new Promise((resolve) => {
this.db.close(() => resolve());
});
}
}
exports.FileRegistry = FileRegistry;
//# sourceMappingURL=FileRegistry.js.map