@aeriondyseti/vector-memory-mcp 2.2.3 → 2.2.6-dev.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.
Files changed (32) hide show
  1. package/README.md +33 -13
  2. package/package.json +12 -12
  3. package/{src → server}/config/index.ts +10 -2
  4. package/{src/db → server/core}/connection.ts +10 -2
  5. package/{src/db → server/core}/conversation.repository.ts +1 -1
  6. package/{src/services → server/core}/conversation.service.ts +2 -2
  7. package/server/core/embeddings.service.ts +125 -0
  8. package/{src/db → server/core}/memory.repository.ts +5 -1
  9. package/{src/services → server/core}/memory.service.ts +20 -4
  10. package/server/core/migration.service.ts +882 -0
  11. package/server/core/migrations.ts +263 -0
  12. package/{src/services → server/core}/parsers/claude-code.parser.ts +1 -1
  13. package/{src/services → server/core}/parsers/types.ts +1 -1
  14. package/{src → server}/index.ts +16 -48
  15. package/{src → server/transports}/http/mcp-transport.ts +2 -2
  16. package/{src → server/transports}/http/server.ts +6 -4
  17. package/{src → server/transports}/mcp/handlers.ts +5 -5
  18. package/server/transports/mcp/resources.ts +20 -0
  19. package/{src → server/transports}/mcp/server.ts +14 -3
  20. package/server/utils/formatting.ts +143 -0
  21. package/scripts/lancedb-extract.ts +0 -181
  22. package/scripts/migrate-from-lancedb.ts +0 -56
  23. package/scripts/smoke-test.ts +0 -699
  24. package/scripts/test-runner.ts +0 -76
  25. package/scripts/warmup.ts +0 -72
  26. package/src/db/migrations.ts +0 -108
  27. package/src/migration.ts +0 -203
  28. package/src/services/embeddings.service.ts +0 -48
  29. /package/{src/types → server/core}/conversation.ts +0 -0
  30. /package/{src/types → server/core}/memory.ts +0 -0
  31. /package/{src/db → server/core}/sqlite-utils.ts +0 -0
  32. /package/{src → server/transports}/mcp/tools.ts +0 -0
@@ -1,76 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * Test runner wrapper that handles Bun's post-test crash gracefully.
4
- *
5
- * Bun crashes during native module cleanup after tests complete successfully.
6
- * This wrapper captures the output, verifies tests passed, and exits cleanly.
7
- */
8
-
9
- import { spawn } from "bun";
10
-
11
- // Exclude benchmark tests — they're probabilistic quality metrics, not pass/fail gates.
12
- // Run benchmarks separately with: bun run benchmark
13
- const args = ["bun", "test", "--preload", "./tests/preload.ts"];
14
-
15
- // Collect all test files except benchmarks
16
- const glob = new Bun.Glob("tests/**/*.test.ts");
17
- for (const path of glob.scanSync(".")) {
18
- if (!path.includes("benchmark")) args.push(path);
19
- }
20
-
21
- const proc = spawn(args, {
22
- stdout: "pipe",
23
- stderr: "pipe",
24
- env: { ...process.env, FORCE_COLOR: "1" },
25
- });
26
-
27
- let stdout = "";
28
- let stderr = "";
29
-
30
- const decoder = new TextDecoder();
31
-
32
- // Stream stdout in real-time
33
- const stdoutReader = proc.stdout.getReader();
34
- (async () => {
35
- while (true) {
36
- const { done, value } = await stdoutReader.read();
37
- if (done) break;
38
- const text = decoder.decode(value);
39
- stdout += text;
40
- process.stdout.write(text);
41
- }
42
- })();
43
-
44
- // Stream stderr in real-time
45
- const stderrReader = proc.stderr.getReader();
46
- (async () => {
47
- while (true) {
48
- const { done, value } = await stderrReader.read();
49
- if (done) break;
50
- const text = decoder.decode(value);
51
- stderr += text;
52
- process.stderr.write(text);
53
- }
54
- })();
55
-
56
- await proc.exited;
57
-
58
- // Check if tests actually passed by looking for the summary line
59
- const output = stdout + stderr;
60
- const passMatch = output.match(/(\d+) pass/);
61
- const failMatch = output.match(/(\d+) fail/);
62
-
63
- const passed = passMatch ? parseInt(passMatch[1], 10) : 0;
64
- const failed = failMatch ? parseInt(failMatch[1], 10) : 0;
65
-
66
- // Exit based on test results, not Bun's crash
67
- if (failed > 0) {
68
- console.error(`\n❌ ${failed} test(s) failed`);
69
- process.exit(1);
70
- } else if (passed > 0) {
71
- console.log(`\n✅ All ${passed} tests passed (ignoring Bun cleanup crash)`);
72
- process.exit(0);
73
- } else {
74
- console.error("\n⚠️ Could not determine test results");
75
- process.exit(1);
76
- }
package/scripts/warmup.ts DELETED
@@ -1,72 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- /**
4
- * Warmup script to pre-download ML models and verify dependencies
5
- * This runs during installation to ensure everything is ready to use
6
- */
7
-
8
- import { config } from "../src/config/index.js";
9
- import { EmbeddingsService } from "../src/services/embeddings.service.js";
10
-
11
- async function warmup(): Promise<void> {
12
- console.log("🔥 Warming up vector-memory-mcp...");
13
- console.log();
14
-
15
- try {
16
- // Check native dependencies
17
- console.log("✓ Checking native dependencies...");
18
- try {
19
- await import("onnxruntime-node");
20
- console.log(" ✓ onnxruntime-node loaded");
21
- } catch (e) {
22
- console.error(" ✗ onnxruntime-node failed:", (e as Error).message);
23
- process.exit(1);
24
- }
25
-
26
- try {
27
- await import("sharp");
28
- console.log(" ✓ sharp loaded");
29
- } catch (e) {
30
- console.error(" ✗ sharp failed:", (e as Error).message);
31
- process.exit(1);
32
- }
33
-
34
- console.log();
35
-
36
- // Initialize embeddings service to download model
37
- console.log("📥 Downloading ML model (this may take a minute)...");
38
- console.log(` Model: ${config.embeddingModel}`);
39
- console.log(` Cache: ~/.cache/huggingface/`);
40
- console.log();
41
-
42
- const embeddings = new EmbeddingsService(
43
- config.embeddingModel,
44
- config.embeddingDimension
45
- );
46
-
47
- // Trigger model download by generating a test embedding
48
- const startTime = Date.now();
49
- await embeddings.embed("warmup test");
50
- const duration = ((Date.now() - startTime) / 1000).toFixed(2);
51
-
52
- console.log();
53
- console.log(`✅ Warmup complete! (${duration}s)`);
54
- console.log();
55
- console.log("Ready to use! Configure your MCP client and restart to get started.");
56
- console.log();
57
- } catch (error) {
58
- console.error();
59
- console.error("❌ Warmup failed:", error);
60
- console.error();
61
- console.error("This is not a critical error - the server will download models on first run.");
62
- console.error("You can try running 'vector-memory-mcp warmup' manually later.");
63
- process.exit(0); // Exit successfully to not block installation
64
- }
65
- }
66
-
67
- // Only run if this is the main module
68
- if (import.meta.url === `file://${process.argv[1]}`) {
69
- warmup();
70
- }
71
-
72
- export { warmup };
@@ -1,108 +0,0 @@
1
- import type { Database } from "bun:sqlite";
2
-
3
- /**
4
- * Check if a table exists and is a vec0 virtual table (from the old sqlite-vec schema).
5
- */
6
- function isVec0Table(db: Database, tableName: string): boolean {
7
- const row = db
8
- .prepare(
9
- `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?`,
10
- )
11
- .get(tableName) as { sql: string } | null;
12
- return row?.sql?.toLowerCase().includes("vec0") ?? false;
13
- }
14
-
15
- /**
16
- * Migrate a vec0 virtual table to a plain BLOB table.
17
- * Copies id + vector data, drops the vec0 table and its shadow tables, then
18
- * creates the new plain table with the copied data.
19
- */
20
- function migrateVec0ToBlob(db: Database, tableName: string): void {
21
- const tmpTable = `${tableName}_migration_tmp`;
22
-
23
- db.exec(`CREATE TABLE IF NOT EXISTS ${tmpTable} (id TEXT PRIMARY KEY, vector BLOB NOT NULL)`);
24
- db.exec(`INSERT OR IGNORE INTO ${tmpTable} (id, vector) SELECT id, vector FROM ${tableName}`);
25
- db.exec(`DROP TABLE ${tableName}`);
26
- db.exec(`CREATE TABLE ${tableName} (id TEXT PRIMARY KEY, vector BLOB NOT NULL)`);
27
- db.exec(`INSERT INTO ${tableName} (id, vector) SELECT id, vector FROM ${tmpTable}`);
28
- db.exec(`DROP TABLE ${tmpTable}`);
29
- }
30
-
31
- /**
32
- * Run all schema migrations. Safe to call on every startup (uses IF NOT EXISTS).
33
- */
34
- export function runMigrations(db: Database): void {
35
- // -- Memories --
36
- db.exec(`
37
- CREATE TABLE IF NOT EXISTS memories (
38
- id TEXT PRIMARY KEY,
39
- content TEXT NOT NULL,
40
- metadata TEXT NOT NULL DEFAULT '{}',
41
- created_at INTEGER NOT NULL,
42
- updated_at INTEGER NOT NULL,
43
- superseded_by TEXT,
44
- usefulness REAL NOT NULL DEFAULT 0.0,
45
- access_count INTEGER NOT NULL DEFAULT 0,
46
- last_accessed INTEGER
47
- )
48
- `);
49
-
50
- // Migrate vec0 -> plain blob table if upgrading from sqlite-vec schema
51
- if (isVec0Table(db, "memories_vec")) {
52
- migrateVec0ToBlob(db, "memories_vec");
53
- } else {
54
- db.exec(`
55
- CREATE TABLE IF NOT EXISTS memories_vec (
56
- id TEXT PRIMARY KEY,
57
- vector BLOB NOT NULL
58
- )
59
- `);
60
- }
61
-
62
- db.exec(`
63
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
64
- id UNINDEXED,
65
- content
66
- )
67
- `);
68
-
69
- // -- Conversation History --
70
- db.exec(`
71
- CREATE TABLE IF NOT EXISTS conversation_history (
72
- id TEXT PRIMARY KEY,
73
- content TEXT NOT NULL,
74
- metadata TEXT NOT NULL DEFAULT '{}',
75
- created_at INTEGER NOT NULL,
76
- session_id TEXT NOT NULL,
77
- role TEXT NOT NULL,
78
- message_index_start INTEGER NOT NULL,
79
- message_index_end INTEGER NOT NULL,
80
- project TEXT NOT NULL
81
- )
82
- `);
83
-
84
- // Migrate vec0 -> plain blob table if upgrading from sqlite-vec schema
85
- if (isVec0Table(db, "conversation_history_vec")) {
86
- migrateVec0ToBlob(db, "conversation_history_vec");
87
- } else {
88
- db.exec(`
89
- CREATE TABLE IF NOT EXISTS conversation_history_vec (
90
- id TEXT PRIMARY KEY,
91
- vector BLOB NOT NULL
92
- )
93
- `);
94
- }
95
-
96
- db.exec(`
97
- CREATE VIRTUAL TABLE IF NOT EXISTS conversation_history_fts USING fts5(
98
- id UNINDEXED,
99
- content
100
- )
101
- `);
102
-
103
- // -- Indexes --
104
- db.exec(`CREATE INDEX IF NOT EXISTS idx_conversation_session_id ON conversation_history(session_id)`);
105
- db.exec(`CREATE INDEX IF NOT EXISTS idx_conversation_project ON conversation_history(project)`);
106
- db.exec(`CREATE INDEX IF NOT EXISTS idx_conversation_role ON conversation_history(role)`);
107
- db.exec(`CREATE INDEX IF NOT EXISTS idx_conversation_created_at ON conversation_history(created_at)`);
108
- }
package/src/migration.ts DELETED
@@ -1,203 +0,0 @@
1
- /**
2
- * LanceDB -> SQLite migration logic.
3
- *
4
- * Reads LanceDB data in a child process (scripts/lancedb-extract.ts) to avoid
5
- * a native symbol collision between @lancedb/lancedb and bun:sqlite.
6
- * The extracted JSON is then written to SQLite in-process.
7
- *
8
- * @deprecated Will be removed in the next major version once LanceDB
9
- * support is dropped.
10
- */
11
-
12
- import { existsSync, statSync } from "fs";
13
- import { resolve, dirname } from "path";
14
- import { fileURLToPath } from "url";
15
- import { connectToDatabase } from "./db/connection.js";
16
- import { serializeVector } from "./db/sqlite-utils.js";
17
-
18
- const __dirname = dirname(fileURLToPath(import.meta.url));
19
-
20
- // ── Detection ───────────────────────────────────────────────────────
21
-
22
- export function isLanceDbDirectory(dbPath: string): boolean {
23
- return existsSync(dbPath) && statSync(dbPath).isDirectory();
24
- }
25
-
26
- // ── Types ───────────────────────────────────────────────────────────
27
-
28
- export interface MigrateOptions {
29
- source: string;
30
- target: string;
31
- }
32
-
33
- export interface MigrateResult {
34
- memoriesMigrated: number;
35
- conversationChunksMigrated: number;
36
- outputSizeMB: string;
37
- }
38
-
39
- interface ExtractedData {
40
- memories: Array<{
41
- id: string;
42
- content: string;
43
- metadata: string;
44
- vector: number[];
45
- created_at: number;
46
- updated_at: number;
47
- last_accessed: number | null;
48
- superseded_by: string | null;
49
- usefulness: number;
50
- access_count: number;
51
- }>;
52
- conversations: Array<{
53
- id: string;
54
- content: string;
55
- metadata: string;
56
- vector: number[];
57
- created_at: number;
58
- session_id: string;
59
- role: string;
60
- message_index_start: number;
61
- message_index_end: number;
62
- project: string;
63
- }>;
64
- }
65
-
66
- // ── Migration ───────────────────────────────────────────────────────
67
-
68
- export async function migrate(opts: MigrateOptions): Promise<MigrateResult> {
69
- const { source, target } = opts;
70
-
71
- if (!existsSync(source)) {
72
- throw new Error(`Source not found: ${source}`);
73
- }
74
- if (!statSync(source).isDirectory()) {
75
- throw new Error(`Source is not a directory (expected LanceDB): ${source}`);
76
- }
77
- if (existsSync(target)) {
78
- throw new Error(
79
- `Target already exists: ${target}\n Delete it first or choose a different target path.`
80
- );
81
- }
82
-
83
- console.error(`📂 Source (LanceDB): ${source}`);
84
- console.error(`📄 Target (SQLite): ${target}`);
85
- console.error();
86
-
87
- // Phase 1: Extract data from LanceDB in a subprocess.
88
- // This avoids a native symbol collision between @lancedb/lancedb and bun:sqlite.
89
- const extractScript = resolve(__dirname, "..", "scripts", "lancedb-extract.ts");
90
- const proc = Bun.spawn(["bun", extractScript, source], {
91
- stdout: "pipe",
92
- stderr: "inherit",
93
- });
94
-
95
- const output = await new Response(proc.stdout).text();
96
- const exitCode = await proc.exited;
97
-
98
- if (exitCode !== 0) {
99
- throw new Error(`LanceDB extraction failed (exit code ${exitCode})`);
100
- }
101
-
102
- const data: ExtractedData = JSON.parse(output);
103
-
104
- // Phase 2: Write to SQLite (no LanceDB in this process).
105
- const sqliteDb = connectToDatabase(target);
106
-
107
- let memoriesMigrated = 0;
108
- let conversationChunksMigrated = 0;
109
-
110
- if (data.memories.length > 0) {
111
- console.error(`\n🧠 Writing ${data.memories.length} memories to SQLite...`);
112
-
113
- const insertMain = sqliteDb.prepare(
114
- `INSERT OR REPLACE INTO memories
115
- (id, content, metadata, created_at, updated_at, superseded_by, usefulness, access_count, last_accessed)
116
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
117
- );
118
- const deleteVec = sqliteDb.prepare(`DELETE FROM memories_vec WHERE id = ?`);
119
- const insertVec = sqliteDb.prepare(
120
- `INSERT INTO memories_vec (id, vector) VALUES (?, ?)`
121
- );
122
- const insertFts = sqliteDb.prepare(
123
- `INSERT OR REPLACE INTO memories_fts (id, content) VALUES (?, ?)`
124
- );
125
-
126
- const tx = sqliteDb.transaction(() => {
127
- for (const row of data.memories) {
128
- insertMain.run(
129
- row.id, row.content, row.metadata,
130
- row.created_at, row.updated_at,
131
- row.superseded_by, row.usefulness,
132
- row.access_count, row.last_accessed,
133
- );
134
- if (row.vector.length > 0) {
135
- deleteVec.run(row.id);
136
- insertVec.run(row.id, serializeVector(row.vector));
137
- }
138
- insertFts.run(row.id, row.content);
139
- }
140
- });
141
- tx();
142
- memoriesMigrated = data.memories.length;
143
- console.error(` ✅ ${memoriesMigrated} memories migrated`);
144
- }
145
-
146
- if (data.conversations.length > 0) {
147
- console.error(`\n💬 Writing ${data.conversations.length} conversation chunks to SQLite...`);
148
-
149
- const insertMain = sqliteDb.prepare(
150
- `INSERT OR REPLACE INTO conversation_history
151
- (id, content, metadata, created_at, session_id, role, message_index_start, message_index_end, project)
152
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
153
- );
154
- const deleteVec = sqliteDb.prepare(`DELETE FROM conversation_history_vec WHERE id = ?`);
155
- const insertVec = sqliteDb.prepare(
156
- `INSERT INTO conversation_history_vec (id, vector) VALUES (?, ?)`
157
- );
158
- const insertFts = sqliteDb.prepare(
159
- `INSERT OR REPLACE INTO conversation_history_fts (id, content) VALUES (?, ?)`
160
- );
161
-
162
- const tx = sqliteDb.transaction(() => {
163
- for (const row of data.conversations) {
164
- insertMain.run(
165
- row.id, row.content, row.metadata,
166
- row.created_at, row.session_id, row.role,
167
- row.message_index_start, row.message_index_end, row.project,
168
- );
169
- if (row.vector.length > 0) {
170
- deleteVec.run(row.id);
171
- insertVec.run(row.id, serializeVector(row.vector));
172
- }
173
- insertFts.run(row.id, row.content);
174
- }
175
- });
176
- tx();
177
- conversationChunksMigrated = data.conversations.length;
178
- console.error(` ✅ ${conversationChunksMigrated} conversation chunks migrated`);
179
- }
180
-
181
- sqliteDb.close();
182
-
183
- const { size } = statSync(target);
184
- const outputSizeMB = (size / 1024 / 1024).toFixed(2);
185
-
186
- return { memoriesMigrated, conversationChunksMigrated, outputSizeMB };
187
- }
188
-
189
- export function formatMigrationSummary(
190
- source: string,
191
- target: string,
192
- result: MigrateResult,
193
- ): string {
194
- return `
195
- ✅ Migration complete! (${result.outputSizeMB} MB)
196
- ${result.memoriesMigrated} memories, ${result.conversationChunksMigrated} conversation chunks
197
-
198
- Next steps:
199
- 1. Backup: mv "${source}" "${source}.lance-backup"
200
- 2. Activate: mv "${target}" "${source}"
201
- 3. Restart your MCP server
202
- `;
203
- }
@@ -1,48 +0,0 @@
1
- import { pipeline, type FeatureExtractionPipeline } from "@huggingface/transformers";
2
-
3
- export class EmbeddingsService {
4
- private modelName: string;
5
- private extractor: FeatureExtractionPipeline | null = null;
6
- private initPromise: Promise<FeatureExtractionPipeline> | null = null;
7
- private _dimension: number;
8
-
9
- constructor(modelName: string, dimension: number) {
10
- this.modelName = modelName;
11
- this._dimension = dimension;
12
- }
13
-
14
- get dimension(): number {
15
- return this._dimension;
16
- }
17
-
18
- private async getExtractor(): Promise<FeatureExtractionPipeline> {
19
- if (this.extractor) {
20
- return this.extractor;
21
- }
22
-
23
- if (!this.initPromise) {
24
- this.initPromise = pipeline(
25
- "feature-extraction",
26
- this.modelName,
27
- { dtype: "fp32" } as any
28
- ) as Promise<FeatureExtractionPipeline>;
29
- }
30
-
31
- this.extractor = await this.initPromise;
32
- return this.extractor;
33
- }
34
-
35
- async embed(text: string): Promise<number[]> {
36
- const extractor = await this.getExtractor();
37
- const output = await extractor(text, { pooling: "mean", normalize: true });
38
- return Array.from(output.data as Float32Array);
39
- }
40
-
41
- async embedBatch(texts: string[]): Promise<number[][]> {
42
- const results: number[][] = [];
43
- for (const text of texts) {
44
- results.push(await this.embed(text));
45
- }
46
- return results;
47
- }
48
- }
File without changes
File without changes
File without changes
File without changes