@aeriondyseti/vector-memory-mcp 0.5.0 → 0.8.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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A production-ready MCP (Model Context Protocol) server that provides semantic memory storage for AI assistants. Uses local embeddings and vector search to automatically retrieve relevant context without cloud dependencies.
6
6
 
7
- **Perfect for:** Software teams maintaining architectural knowledge, developers juggling multiple projects, and anyone building with AI assistants like Claude Code.
7
+ **Perfect for:** Software teams maintaining architectural knowledge, developers juggling multiple projects, and anyone building with MCP-compatible AI assistants.
8
8
 
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
@@ -36,9 +36,7 @@ A production-ready MCP (Model Context Protocol) server that provides semantic me
36
36
  - CPU-optimized local embeddings (no GPU required)
37
37
 
38
38
  ### šŸ”Œ **MCP Native Integration**
39
- - Works seamlessly with Claude Code
40
- - Session hooks for automatic context injection
41
- - Standard MCP protocol (compatible with future clients)
39
+ - Standard MCP protocol (compatible with any client)
42
40
 
43
41
  ### šŸ› ļø **Developer-Friendly**
44
42
  - Zero-configuration setup
@@ -53,7 +51,7 @@ A production-ready MCP (Model Context Protocol) server that provides semantic me
53
51
  ### Prerequisites
54
52
 
55
53
  - [Bun](https://bun.sh/) 1.0+
56
- - Claude Code or another MCP-compatible client
54
+ - An MCP-compatible client
57
55
 
58
56
  > **Note:** This server requires Bun to run.
59
57
 
@@ -68,7 +66,7 @@ bun install -g @aeriondyseti/vector-memory-mcp
68
66
 
69
67
  > **Note:** The installation automatically downloads ML models (~90MB) and verifies native dependencies. This may take a minute on first install.
70
68
 
71
- **Configure Claude Code** - Add to `~/.claude/config.json`:
69
+ **Configure your MCP client** (example config for clients that use `~/.claude/config.json`):
72
70
  ```json
73
71
  {
74
72
  "mcpServers": {
@@ -94,7 +92,7 @@ cd vector-memory-mcp
94
92
  bun install
95
93
  ```
96
94
 
97
- **Configure Claude Code** - Add to `~/.claude/config.json`:
95
+ **Configure your MCP client** (example config for clients that use `~/.claude/config.json`):
98
96
  ```json
99
97
  {
100
98
  "mcpServers": {
@@ -119,11 +117,14 @@ bun install
119
117
 
120
118
  ### Start Using It
121
119
 
122
- That's it! Restart Claude Code and you'll have access to memory tools:
123
- - `store_memory` - Save information for later recall
120
+ That's it! Restart your MCP client and you'll have access to memory tools:
121
+ - `store_memories` - Save memories for later recall (always pass array)
124
122
  - `search_memories` - Find relevant memories semantically
125
- - `get_memory` - Retrieve a specific memory by ID
126
- - `delete_memory` - Remove a memory
123
+ - `get_memories` - Retrieve memories by ID (always pass array)
124
+ - `update_memories` - Update existing memories in place
125
+ - `delete_memories` - Remove memories (always pass array of IDs)
126
+ - `store_handoff` - Store a handoff-style project snapshot
127
+ - `get_handoff` - Retrieve the latest handoff (includes referenced memories)
127
128
 
128
129
  ---
129
130
 
@@ -131,14 +132,14 @@ That's it! Restart Claude Code and you'll have access to memory tools:
131
132
 
132
133
  ### Storing Memories
133
134
 
134
- Ask Claude Code to remember things for you:
135
+ Ask your MCP client/agent to remember things for you:
135
136
 
136
137
  ```
137
138
  You: "Remember that we use Drizzle ORM for database access"
138
- Claude: [calls store_memory tool]
139
+ Claude: [calls store_memories tool]
139
140
  ```
140
141
 
141
- Or Claude Code can store memories directly:
142
+ Or your MCP client/agent can store memories directly:
142
143
  ```json
143
144
  {
144
145
  "content": "Use Drizzle ORM for type-safe database access",
@@ -151,7 +152,7 @@ Or Claude Code can store memories directly:
151
152
 
152
153
  ### Searching Memories
153
154
 
154
- Claude Code automatically searches memories when relevant, or you can ask:
155
+ Your MCP client/agent can automatically search memories when relevant, or you can ask:
155
156
 
156
157
  ```
157
158
  You: "What did we decide about the database?"
@@ -223,7 +224,7 @@ vector-memory-mcp/
223
224
  ### 1. Memory Storage
224
225
 
225
226
  ```
226
- Claude Code calls store_memory tool
227
+ An MCP client calls store_memories tool
227
228
  ↓
228
229
  Content → @huggingface/transformers → 384d vector
229
230
  ↓
@@ -235,7 +236,7 @@ Store in LanceDB with metadata
235
236
  ### 2. Memory Retrieval
236
237
 
237
238
  ```
238
- Claude Code calls search_memories
239
+ An MCP client calls search_memories
239
240
  ↓
240
241
  Query → @huggingface/transformers → 384d vector
241
242
  ↓
@@ -252,7 +253,9 @@ Return top N relevant memories
252
253
 
253
254
  The server uses environment variables for configuration:
254
255
 
255
- - `VECTOR_MEMORY_DB_PATH` - Custom database path (default: `~/.local/share/vector-memory-mcp/memories.db`)
256
+ - `VECTOR_MEMORY_DB_PATH` - Custom database path (default: `./.claude/vector-memories.db`)
257
+
258
+ > Note: if you point multiple projects at the same DB path, `store_handoff` uses UUID.ZERO and will overwrite the previous handoff (by design).
256
259
  - `VECTOR_MEMORY_MODEL` - Embedding model to use (default: `Xenova/all-MiniLM-L6-v2`)
257
260
 
258
261
  Example:
@@ -261,7 +264,7 @@ export VECTOR_MEMORY_DB_PATH="/path/to/custom/memories.db"
261
264
  export VECTOR_MEMORY_MODEL="Xenova/all-MiniLM-L6-v2"
262
265
  ```
263
266
 
264
- Or in your Claude Code config:
267
+ Or in your MCP client config:
265
268
  ```json
266
269
  {
267
270
  "mcpServers": {
@@ -282,7 +285,10 @@ Or in your Claude Code config:
282
285
  ### Running Tests
283
286
 
284
287
  ```bash
285
- # Run all tests
288
+ # Run all tests (recommended - includes model preload)
289
+ bun run test
290
+
291
+ # Run tests directly (skips 19 embedding tests, faster)
286
292
  bun test
287
293
 
288
294
  # Run with coverage
@@ -292,6 +298,8 @@ bun test --coverage
292
298
  bun run typecheck
293
299
  ```
294
300
 
301
+ > **Note:** `bun run test` uses a wrapper that preloads the embedding model, running all 98 tests. `bun test` directly is faster but skips embedding-specific tests.
302
+
295
303
  ### Development Mode
296
304
 
297
305
  ```bash
@@ -387,7 +395,7 @@ MIT License - see [LICENSE](LICENSE) for details.
387
395
  ## šŸ”— Related Projects
388
396
 
389
397
  - [Model Context Protocol](https://modelcontextprotocol.io) - Official MCP specification
390
- - [Claude Code](https://claude.ai/code) - AI coding assistant from Anthropic
398
+ - Any MCP-compatible client
391
399
  - [LanceDB](https://lancedb.com/) - Fast, local vector search
392
400
  - [Transformers.js](https://huggingface.co/docs/transformers.js) - Run transformers in JavaScript
393
401
 
@@ -408,7 +416,7 @@ MIT License - see [LICENSE](LICENSE) for details.
408
416
  ```
409
417
  You: "Remember that we decided to use Drizzle ORM for type-safe database access"
410
418
  Claude: I'll store that for you.
411
- [Calls store_memory tool with content and metadata]
419
+ [Calls store_memories tool with content and metadata]
412
420
  āœ“ Memory stored successfully
413
421
  ```
414
422
 
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * SessionStart hook for Claude Code
4
+ *
5
+ * Fetches config from the running vector-memory server's /health endpoint,
6
+ * then retrieves and outputs the latest handoff.
7
+ *
8
+ * Requires the server to be running with HTTP enabled.
9
+ *
10
+ * Usage in ~/.claude/settings.json:
11
+ * {
12
+ * "hooks": {
13
+ * "SessionStart": [{
14
+ * "hooks": [{
15
+ * "type": "command",
16
+ * "command": "bun /path/to/vector-memory-mcp/hooks/session-start.ts"
17
+ * }]
18
+ * }]
19
+ * }
20
+ * }
21
+ */
22
+
23
+ import { existsSync } from "fs";
24
+ import { connectToDatabase } from "../src/db/connection.js";
25
+ import { MemoryRepository } from "../src/db/memory.repository.js";
26
+ import { EmbeddingsService } from "../src/services/embeddings.service.js";
27
+ import { MemoryService } from "../src/services/memory.service.js";
28
+
29
+ const VECTOR_MEMORY_URL = process.env.VECTOR_MEMORY_URL ?? "http://127.0.0.1:3271";
30
+
31
+ interface HealthResponse {
32
+ status: string;
33
+ config: {
34
+ dbPath: string;
35
+ embeddingModel: string;
36
+ embeddingDimension: number;
37
+ };
38
+ }
39
+
40
+ async function main() {
41
+ // Get config from running server
42
+ let health: HealthResponse;
43
+ try {
44
+ const response = await fetch(`${VECTOR_MEMORY_URL}/health`);
45
+ if (!response.ok) {
46
+ throw new Error(`Server returned ${response.status}`);
47
+ }
48
+ health = await response.json();
49
+ } catch (error) {
50
+ if (error instanceof Error && error.message.includes("ECONNREFUSED")) {
51
+ console.log("Vector memory server not running. Starting fresh session.");
52
+ return;
53
+ }
54
+ throw error;
55
+ }
56
+
57
+ const { dbPath, embeddingModel, embeddingDimension } = health.config;
58
+
59
+ // Check if DB exists
60
+ if (!existsSync(dbPath)) {
61
+ console.log("Vector memory database not found. Starting fresh session.");
62
+ return;
63
+ }
64
+
65
+ const db = await connectToDatabase(dbPath);
66
+ const repository = new MemoryRepository(db);
67
+ const embeddings = new EmbeddingsService(embeddingModel, embeddingDimension);
68
+ const service = new MemoryService(repository, embeddings);
69
+
70
+ const handoff = await service.getLatestHandoff();
71
+
72
+ if (!handoff) {
73
+ console.log("No handoff found. Starting fresh session.");
74
+ return;
75
+ }
76
+
77
+ // Fetch referenced memories if any
78
+ const memoryIds = (handoff.metadata.memory_ids as string[] | undefined) ?? [];
79
+ let memoriesSection = "";
80
+
81
+ if (memoryIds.length > 0) {
82
+ const memories: string[] = [];
83
+ for (const id of memoryIds) {
84
+ const memory = await service.get(id);
85
+ if (memory) {
86
+ memories.push(`### Memory: ${id}\n${memory.content}`);
87
+ }
88
+ }
89
+ if (memories.length > 0) {
90
+ memoriesSection = `\n\n## Referenced Memories\n\n${memories.join("\n\n")}`;
91
+ }
92
+ }
93
+
94
+ console.log(handoff.content + memoriesSection);
95
+ }
96
+
97
+ main().catch((err) => {
98
+ console.error("Error loading handoff:", err.message);
99
+ process.exit(1);
100
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aeriondyseti/vector-memory-mcp",
3
- "version": "0.5.0",
3
+ "version": "0.8.0",
4
4
  "description": "A zero-configuration RAG memory server for MCP clients",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "src",
12
12
  "scripts",
13
+ "hooks",
13
14
  "README.md",
14
15
  "LICENSE"
15
16
  ],
@@ -25,6 +26,7 @@
25
26
  "scripts": {
26
27
  "start": "bun run src/index.ts",
27
28
  "dev": "bun --watch run src/index.ts",
29
+ "build": "bun run typecheck",
28
30
  "typecheck": "bunx tsc --noEmit",
29
31
  "test": "bun run scripts/test-runner.ts",
30
32
  "test:raw": "bun test --preload ./tests/preload.ts",
@@ -32,14 +34,23 @@
32
34
  "test:coverage": "bun test --preload ./tests/preload.ts --coverage",
33
35
  "test:preload": "bun run tests/preload.ts",
34
36
  "warmup": "bun run scripts/warmup.ts",
35
- "postinstall": "bun run scripts/warmup.ts"
37
+ "postinstall": "bun run scripts/warmup.ts",
38
+ "publish:check": "bun run scripts/publish.ts --dry-run",
39
+ "publish:npm": "bun run scripts/publish.ts"
36
40
  },
37
- "keywords": ["mcp", "memory", "rag", "embeddings", "lancedb"],
41
+ "keywords": [
42
+ "mcp",
43
+ "memory",
44
+ "rag",
45
+ "embeddings",
46
+ "lancedb"
47
+ ],
38
48
  "license": "MIT",
39
49
  "dependencies": {
40
50
  "@huggingface/transformers": "^3.8.0",
41
51
  "@lancedb/lancedb": "^0.22.3",
42
52
  "@modelcontextprotocol/sdk": "^1.0.0",
53
+ "arg": "^5.0.2",
43
54
  "hono": "^4.11.3"
44
55
  },
45
56
  "devDependencies": {
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Publish script for vector-memory-mcp
4
+ *
5
+ * Prerequisites:
6
+ * 1. Create a granular access token at https://www.npmjs.com/settings/tokens
7
+ * 2. Store it: npm config set //registry.npmjs.org/:_authToken=npm_YOUR_TOKEN
8
+ * Or set NPM_TOKEN environment variable
9
+ *
10
+ * Usage: bun run scripts/publish.ts [--dry-run]
11
+ */
12
+
13
+ import { $ } from "bun";
14
+
15
+ const dryRun = process.argv.includes("--dry-run");
16
+
17
+ async function main() {
18
+ // Check for authentication
19
+ console.log("šŸ” Checking NPM authentication...");
20
+ try {
21
+ const whoami = await $`npm whoami`.text();
22
+ console.log(`āœ… Authenticated as: ${whoami.trim()}`);
23
+ } catch {
24
+ console.error("āŒ Not authenticated with NPM.");
25
+ console.error(" Option 1: npm login");
26
+ console.error(" Option 2: npm config set //registry.npmjs.org/:_authToken=npm_YOUR_TOKEN");
27
+ console.error(" Option 3: Set NPM_TOKEN environment variable");
28
+ process.exit(1);
29
+ }
30
+
31
+ // Run tests
32
+ console.log("🧪 Running tests...");
33
+ const testResult = await $`bun run test`.quiet();
34
+ if (testResult.exitCode !== 0) {
35
+ console.error("āŒ Tests failed. Aborting publish.");
36
+ process.exit(1);
37
+ }
38
+ console.log("āœ… Tests passed");
39
+
40
+ // Build
41
+ console.log("šŸ”Ø Building...");
42
+ await $`bun run build`;
43
+ console.log("āœ… Build complete");
44
+
45
+ // Get version info
46
+ const pkg = await Bun.file("package.json").json();
47
+ console.log(`\nšŸ“¦ Publishing ${pkg.name}@${pkg.version}...`);
48
+
49
+ if (dryRun) {
50
+ console.log("šŸ” Dry run - would publish:");
51
+ await $`npm publish --dry-run`;
52
+ } else {
53
+ await $`npm publish --access public`;
54
+ console.log(`\nāœ… Published ${pkg.name}@${pkg.version}`);
55
+ }
56
+ }
57
+
58
+ main().catch((err) => {
59
+ console.error("āŒ Publish failed:", err.message);
60
+ process.exit(1);
61
+ });
package/scripts/warmup.ts CHANGED
@@ -52,8 +52,7 @@ async function warmup(): Promise<void> {
52
52
  console.log();
53
53
  console.log(`āœ… Warmup complete! (${duration}s)`);
54
54
  console.log();
55
- console.log("Ready to use! Configure Claude Code and restart to get started.");
56
- console.log("See: https://github.com/AerionDyseti/vector-memory-mcp#configure-claude-code");
55
+ console.log("Ready to use! Configure your MCP client and restart to get started.");
57
56
  console.log();
58
57
  } catch (error) {
59
58
  console.error();
@@ -1,5 +1,7 @@
1
+ import arg from "arg";
1
2
  import { join } from "path";
2
- import { homedir } from "os";
3
+
4
+ export type TransportMode = "stdio" | "http" | "both";
3
5
 
4
6
  export interface Config {
5
7
  dbPath: string;
@@ -8,30 +10,66 @@ export interface Config {
8
10
  httpPort: number;
9
11
  httpHost: string;
10
12
  enableHttp: boolean;
13
+ transportMode: TransportMode;
11
14
  }
12
15
 
13
- const DEFAULT_DB_PATH = join(
14
- homedir(),
15
- ".local",
16
- "share",
17
- "vector-memory-mcp",
18
- "memories.db"
19
- );
16
+ export interface ConfigOverrides {
17
+ dbPath?: string;
18
+ httpPort?: number;
19
+ enableHttp?: boolean;
20
+ transportMode?: TransportMode;
21
+ }
20
22
 
23
+ // Defaults - always use repo-local .vector-memory folder
24
+ const DEFAULT_DB_PATH = join(process.cwd(), ".vector-memory", "memories.db");
21
25
  const DEFAULT_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
22
26
  const DEFAULT_EMBEDDING_DIMENSION = 384;
23
27
  const DEFAULT_HTTP_PORT = 3271;
24
28
  const DEFAULT_HTTP_HOST = "127.0.0.1";
25
29
 
26
- export function loadConfig(): Config {
30
+ function resolvePath(path: string): string {
31
+ return path.startsWith("/") ? path : join(process.cwd(), path);
32
+ }
33
+
34
+ export function loadConfig(overrides: ConfigOverrides = {}): Config {
35
+ const transportMode = overrides.transportMode ?? "stdio";
36
+ // HTTP enabled by default (needed for hooks), can disable with --no-http
37
+ const enableHttp = overrides.enableHttp ?? true;
38
+
27
39
  return {
28
- dbPath: process.env.VECTOR_MEMORY_DB_PATH ?? DEFAULT_DB_PATH,
29
- embeddingModel: process.env.VECTOR_MEMORY_MODEL ?? DEFAULT_EMBEDDING_MODEL,
40
+ dbPath: resolvePath(overrides.dbPath ?? DEFAULT_DB_PATH),
41
+ embeddingModel: DEFAULT_EMBEDDING_MODEL,
30
42
  embeddingDimension: DEFAULT_EMBEDDING_DIMENSION,
31
- httpPort: parseInt(process.env.VECTOR_MEMORY_HTTP_PORT ?? String(DEFAULT_HTTP_PORT), 10),
32
- httpHost: process.env.VECTOR_MEMORY_HTTP_HOST ?? DEFAULT_HTTP_HOST,
33
- enableHttp: process.env.VECTOR_MEMORY_ENABLE_HTTP !== "false",
43
+ httpPort: overrides.httpPort ?? DEFAULT_HTTP_PORT,
44
+ httpHost: DEFAULT_HTTP_HOST,
45
+ enableHttp,
46
+ transportMode,
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Parse CLI arguments into config overrides.
52
+ */
53
+ export function parseCliArgs(argv: string[]): ConfigOverrides {
54
+ const args = arg(
55
+ {
56
+ "--db-file": String,
57
+ "--port": Number,
58
+ "--no-http": Boolean,
59
+
60
+ // Aliases
61
+ "-d": "--db-file",
62
+ "-p": "--port",
63
+ },
64
+ { argv, permissive: true }
65
+ );
66
+
67
+ return {
68
+ dbPath: args["--db-file"],
69
+ httpPort: args["--port"],
70
+ enableHttp: args["--no-http"] ? false : undefined,
34
71
  };
35
72
  }
36
73
 
74
+ // Default config for imports that don't use CLI args
37
75
  export const config = loadConfig();
@@ -33,6 +33,27 @@ export class MemoryRepository {
33
33
  ]);
34
34
  }
35
35
 
36
+ async upsert(memory: Memory): Promise<void> {
37
+ const table = await this.getTable();
38
+ const existing = await table.query().where(`id = '${memory.id}'`).limit(1).toArray();
39
+
40
+ if (existing.length === 0) {
41
+ return await this.insert(memory);
42
+ }
43
+
44
+ await table.update({
45
+ where: `id = '${memory.id}'`,
46
+ values: {
47
+ vector: memory.embedding,
48
+ content: memory.content,
49
+ metadata: JSON.stringify(memory.metadata),
50
+ created_at: memory.createdAt.getTime(),
51
+ updated_at: memory.updatedAt.getTime(),
52
+ superseded_by: memory.supersededBy,
53
+ },
54
+ });
55
+ }
56
+
36
57
  async findById(id: string): Promise<Memory | null> {
37
58
  const table = await this.getTable();
38
59
  const results = await table.query().where(`id = '${id}'`).limit(1).toArray();
package/src/db/schema.ts CHANGED
@@ -31,3 +31,4 @@ export const memorySchema = new Schema([
31
31
  ),
32
32
  new Field("superseded_by", new Utf8(), true), // Nullable
33
33
  ]);
34
+