@cybermem/mcp 0.9.12 → 0.13.3

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/src/index.ts CHANGED
@@ -1,12 +1,5 @@
1
- #!/usr/bin/env node
1
+ import "./console-fix.js";
2
2
  import "./env.js";
3
- /**
4
- * CyberMem MCP Server
5
- *
6
- * Supports two modes:
7
- * 1. Local/Server Mode (default): Uses openmemory-js SDK directly.
8
- * 2. Remote Client Mode (with --url): Proxies requests to a remote CyberMem server via HTTP.
9
- */
10
3
 
11
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -18,21 +11,6 @@ import cors from "cors";
18
11
  import express from "express";
19
12
  import { z } from "zod";
20
13
 
21
- // Redirect all stdout to stderr IMMEDIATELY to protect Stdio protocol
22
- const originalStdoutWrite = process.stdout.write.bind(process.stdout);
23
- (process.stdout as any).write = (chunk: any, encoding: any, callback: any) => {
24
- const str = typeof chunk === "string" ? chunk : chunk.toString();
25
- // Allow ONLY protocol messages (must be JSON-RPC)
26
- if (str.includes('"jsonrpc":')) {
27
- return originalStdoutWrite(chunk, encoding, callback);
28
- }
29
- return process.stderr.write(chunk, encoding, callback);
30
- };
31
-
32
- // Also redirect console outputs
33
- console.log = console.error;
34
- console.info = console.error;
35
-
36
14
  // Async Storage for Request Context (User ID and Client Name)
37
15
  const requestContext = new AsyncLocalStorage<{
38
16
  userId?: string;
@@ -53,18 +31,27 @@ async function startServer() {
53
31
 
54
32
  const cliUrl = getArg("--url");
55
33
  const cliToken = getArg("--token") || getArg("--api-key");
34
+ const cliEnv = getArg("--env");
35
+
36
+ if (cliEnv === "staging") {
37
+ console.error("[MCP] Running in Staging environment");
38
+ process.env.CYBERMEM_ENV = "staging";
39
+ }
40
+
56
41
  let stdioClientName: string | undefined = undefined;
57
42
 
58
43
  // Protocol Instructions
59
44
  const CYBERMEM_INSTRUCTIONS = `CyberMem is a persistent context daemon for AI agents.
60
45
  PROTOCOL:
61
46
  1. On session start: call query_memory("user context profile")
62
- 2. Store new insights immediately with add_memory (FULL content)
63
- 3. Always include tags: [topic, year, source:your-client-name]
47
+ 2. Store new insights immediately with add_memory (STABLE data)
48
+ 3. For corrections: use update_memory (STRUCTURAL mutation, high cost)
49
+ 4. To prevent decay: use reinforce_memory (METABOLIC boost, low cost)
50
+ 5. Always include tags: [topic, year, source:your-client-name]
64
51
  For full protocol: https://docs.cybermem.dev/agent-protocol`;
65
52
 
66
53
  const server = new McpServer(
67
- { name: "cybermem", version: "0.7.5" },
54
+ { name: "cybermem", version: "0.12.4" },
68
55
  {
69
56
  instructions: CYBERMEM_INSTRUCTIONS,
70
57
  },
@@ -98,7 +85,7 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
98
85
  },
99
86
  serverInfo: {
100
87
  name: "cybermem",
101
- version: "0.7.5",
88
+ version: "0.12.4",
102
89
  },
103
90
  };
104
91
  });
@@ -107,6 +94,8 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
107
94
 
108
95
  let memory: any = null;
109
96
  let apiClient: any = null;
97
+ let sdk_update_memory: any = null;
98
+ let sdk_reinforce_memory: any = null;
110
99
 
111
100
  if (cliUrl) {
112
101
  // REMOTE CLIENT MODE
@@ -119,91 +108,43 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
119
108
  "Content-Type": "application/json",
120
109
  },
121
110
  });
122
- // Dynamically inject client name from context or discovery
123
111
  apiClient.interceptors.request.use((config: any) => {
124
112
  const ctx = requestContext.getStore();
125
113
  config.headers["X-Client-Name"] =
126
- ctx?.clientName || stdioClientName || "unknown-mcp-client";
114
+ ctx?.clientName || stdioClientName || "antigravity-client";
127
115
  return config;
128
116
  });
129
117
  } else {
130
118
  // LOCAL SDK MODE
131
- const homedir = process.env.HOME || process.env.USERPROFILE || "";
132
- // FORCE absolute standardized path for consistency across components
133
- const path = await import("path");
134
- const dbPath = path.resolve(homedir, ".cybermem/data/openmemory.sqlite");
135
- process.env.OM_DB_PATH = dbPath;
136
-
137
- // Ensure directory exists
119
+ const dbPath = process.env.OM_DB_PATH!;
138
120
  const fs = await import("fs");
121
+ const path = await import("path");
139
122
  try {
140
123
  const dir = path.dirname(dbPath);
141
124
  if (dir) fs.mkdirSync(dir, { recursive: true });
142
125
  } catch {}
143
126
 
144
127
  try {
145
- // Dynamic import to ensure env vars are set before loading SDK
146
- // We import from dist/core/memory directly to avoid triggering the server side-effects in openmemory-js/dist/index.js
147
- // @ts-ignore
148
128
  const { Memory } = await import("openmemory-js/dist/core/memory.js");
129
+ const hsg = await import("openmemory-js/dist/memory/hsg.js");
130
+ sdk_update_memory = hsg.update_memory;
131
+ sdk_reinforce_memory = hsg.reinforce_memory;
149
132
  memory = new Memory();
150
133
  (server as any)._memoryReady = true;
151
134
 
152
- // --- INITIALIZE LOGGING TABLES ---
135
+ // Initialize Tables
153
136
  const sqlite3 = await import("sqlite3");
154
137
  const db = new sqlite3.default.Database(dbPath);
155
138
  db.configure("busyTimeout", 5000);
156
139
  db.serialize(() => {
157
- db.run("PRAGMA journal_mode=WAL;", (err: any) => {
158
- if (err) console.error("[MCP] Init WAL error:", err.message);
159
- });
160
140
  db.run(
161
- `CREATE TABLE IF NOT EXISTS cybermem_stats (
162
- id INTEGER PRIMARY KEY AUTOINCREMENT,
163
- client_name TEXT NOT NULL,
164
- operation TEXT NOT NULL,
165
- count INTEGER DEFAULT 0,
166
- errors INTEGER DEFAULT 0,
167
- last_updated INTEGER NOT NULL,
168
- UNIQUE(client_name, operation)
169
- );`,
170
- (err: any) => {
171
- if (err)
172
- console.error("[MCP] Init stats table error:", err.message);
173
- },
141
+ "CREATE TABLE IF NOT EXISTS cybermem_stats (id INTEGER PRIMARY KEY AUTOINCREMENT, client_name TEXT NOT NULL, operation TEXT NOT NULL, count INTEGER DEFAULT 0, errors INTEGER DEFAULT 0, last_updated INTEGER NOT NULL, UNIQUE(client_name, operation));",
174
142
  );
175
143
  db.run(
176
- `CREATE TABLE IF NOT EXISTS cybermem_access_log (
177
- id INTEGER PRIMARY KEY AUTOINCREMENT,
178
- timestamp INTEGER NOT NULL,
179
- client_name TEXT NOT NULL,
180
- client_version TEXT,
181
- method TEXT NOT NULL,
182
- endpoint TEXT NOT NULL,
183
- operation TEXT NOT NULL,
184
- status TEXT NOT NULL,
185
- is_error INTEGER DEFAULT 0
186
- );`,
187
- (err: any) => {
188
- if (err)
189
- console.error("[MCP] Init access_log table error:", err.message);
190
- },
144
+ "CREATE TABLE IF NOT EXISTS cybermem_access_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, client_name TEXT NOT NULL, client_version TEXT, method TEXT NOT NULL, endpoint TEXT NOT NULL, operation TEXT NOT NULL, status TEXT NOT NULL, is_error INTEGER DEFAULT 0);",
191
145
  );
192
- // Access keys table for token-based auth
193
146
  db.run(
194
- `CREATE TABLE IF NOT EXISTS access_keys (
195
- id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
196
- key_hash TEXT NOT NULL,
197
- name TEXT DEFAULT 'default',
198
- user_id TEXT DEFAULT 'default',
199
- created_at TEXT DEFAULT (datetime('now')),
200
- last_used_at TEXT,
201
- is_active INTEGER DEFAULT 1
202
- );`,
203
- (err: any) => {
204
- if (err)
205
- console.error("[MCP] Init access_keys table error:", err.message);
206
- },
147
+ "CREATE TABLE IF NOT EXISTS access_keys (id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))), key_hash TEXT NOT NULL, name TEXT DEFAULT 'default', user_id TEXT DEFAULT 'default', created_at TEXT DEFAULT (datetime('now')), last_used_at TEXT, is_active INTEGER DEFAULT 1);",
207
148
  );
208
149
  });
209
150
  db.close();
@@ -213,7 +154,22 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
213
154
  }
214
155
  }
215
156
 
216
- // Helper to log activity to SQLite (Local SDK Mode only)
157
+ // PERSISTENT LOGGING DB
158
+ let loggingDb: any = null;
159
+ const initLoggingDb = async () => {
160
+ if (loggingDb || cliUrl) return loggingDb;
161
+ const dbPath = process.env.OM_DB_PATH!;
162
+ const sqlite3 = await import("sqlite3");
163
+ loggingDb = new sqlite3.default.Database(dbPath);
164
+ loggingDb.configure("busyTimeout", 10000);
165
+ return new Promise((resolve) => {
166
+ loggingDb.serialize(() => {
167
+ loggingDb.run("PRAGMA journal_mode=WAL;");
168
+ loggingDb.run("PRAGMA synchronous=NORMAL;", () => resolve(loggingDb));
169
+ });
170
+ });
171
+ };
172
+
217
173
  const logActivity = async (
218
174
  operation: string,
219
175
  opts: {
@@ -224,131 +180,68 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
224
180
  } = {},
225
181
  ) => {
226
182
  if (cliUrl || !memory) return;
227
-
228
183
  const {
229
184
  client: providedClient,
230
185
  method = "POST",
231
186
  endpoint = "/mcp",
232
187
  status = 200,
233
188
  } = opts;
234
-
235
189
  const ctx = requestContext.getStore();
236
190
  const client =
237
- providedClient || ctx?.clientName || stdioClientName || "unknown-client";
238
-
191
+ providedClient ||
192
+ ctx?.clientName ||
193
+ stdioClientName ||
194
+ "antigravity-client";
239
195
  try {
240
- const dbPath = process.env.OM_DB_PATH!;
241
- const sqlite3 = await import("sqlite3");
242
- const db = new sqlite3.default.Database(dbPath);
243
- db.configure("busyTimeout", 5000);
244
-
196
+ const db = (await initLoggingDb()) as any;
245
197
  const ts = Date.now();
246
198
  const is_error = status >= 400 ? 1 : 0;
247
-
248
- console.error(
249
- `[MCP] Logging ${operation} for ${client} (status: ${status})`,
250
- );
251
-
252
199
  db.serialize(() => {
253
- // Log to access_log
254
200
  db.run(
255
- `INSERT INTO cybermem_access_log
256
- (timestamp, client_name, client_version, method, endpoint, operation, status, is_error)
257
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
201
+ "INSERT INTO cybermem_access_log (timestamp, client_name, client_version, method, endpoint, operation, status, is_error) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
258
202
  [
259
203
  ts,
260
204
  client,
261
- "0.7.0",
205
+ "0.12.4",
262
206
  method,
263
207
  endpoint,
264
208
  operation,
265
209
  status.toString(),
266
210
  is_error,
267
211
  ],
268
- (err: any) => {
269
- if (err) console.error("[MCP] Log access error:", err.message);
270
- },
271
212
  );
272
-
273
- // Log to stats (Upsert)
274
213
  db.run(
275
- `INSERT INTO cybermem_stats (client_name, operation, count, errors, last_updated)
276
- VALUES (?, ?, 1, ?, ?)
277
- ON CONFLICT(client_name, operation) DO UPDATE SET
278
- count = count + 1,
279
- errors = errors + ?,
280
- last_updated = ?`,
214
+ "INSERT INTO cybermem_stats (client_name, operation, count, errors, last_updated) VALUES (?, ?, 1, ?, ?) ON CONFLICT(client_name, operation) DO UPDATE SET count = count + 1, errors = errors + ?, last_updated = ?",
281
215
  [client, operation, is_error, ts, is_error, ts],
282
- (err: any) => {
283
- if (err) console.error("[MCP] Log stats error:", err.message);
284
- },
285
216
  );
286
217
  });
287
-
288
- db.close();
289
- } catch (e) {
290
- console.error("Failed to log activity to SQLite:", e);
291
- }
292
- };
293
-
294
- const addSourceTag = (tags: string[] = []) => {
295
- if (!tags.some((t) => t.startsWith("source:"))) {
296
- const clientName =
297
- requestContext.getStore()?.clientName || stdioClientName || "unknown";
298
- tags.push(`source:${clientName}`);
299
- }
300
- return tags;
301
- };
302
-
303
- // Helper to get current User ID from context or args
304
- const getContextUserId = (argsUserId?: string) => {
305
- const store = requestContext.getStore();
306
- return argsUserId || store?.userId;
218
+ } catch {}
307
219
  };
308
220
 
309
- // --- TOOLS ---
310
-
221
+ // TOOLS
311
222
  server.registerTool(
312
223
  "add_memory",
313
224
  {
314
- description: "Store a new memory. " + CYBERMEM_INSTRUCTIONS,
225
+ description:
226
+ "Store a new memory. Use for high-quality, stable data. " +
227
+ CYBERMEM_INSTRUCTIONS,
315
228
  inputSchema: z.object({
316
229
  content: z.string(),
317
- user_id: z.string().optional(),
318
230
  tags: z.array(z.string()).optional(),
319
231
  }),
320
232
  },
321
233
  async (args: any) => {
322
- const tags = addSourceTag(args.tags);
323
- const userId = getContextUserId(args.user_id);
324
-
325
234
  if (cliUrl) {
326
- const res = await apiClient.post("/add", {
327
- ...args,
328
- user_id: userId,
329
- tags,
330
- });
235
+ const res = await apiClient.post("/add", args);
331
236
  return { content: [{ type: "text", text: JSON.stringify(res.data) }] };
332
237
  } else {
333
- try {
334
- const res = await memory!.add(args.content, {
335
- user_id: userId,
336
- tags,
337
- });
338
- await logActivity("create", {
339
- method: "POST",
340
- endpoint: "/memory/add",
341
- status: 200,
342
- });
343
- return { content: [{ type: "text", text: JSON.stringify(res) }] };
344
- } catch (e: any) {
345
- await logActivity("create", {
346
- method: "POST",
347
- endpoint: "/memory/add",
348
- status: 500,
349
- });
350
- throw e;
351
- }
238
+ const res = await memory!.add(args.content, { tags: args.tags });
239
+ await logActivity("create", {
240
+ method: "POST",
241
+ endpoint: "/memory/add",
242
+ status: 200,
243
+ });
244
+ return { content: [{ type: "text", text: JSON.stringify(res) }] };
352
245
  }
353
246
  },
354
247
  );
@@ -360,67 +253,42 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
360
253
  inputSchema: z.object({ query: z.string(), k: z.number().default(5) }),
361
254
  },
362
255
  async (args: any) => {
363
- const userId = getContextUserId(); // Search is scoped to user if provided
364
-
365
256
  if (cliUrl) {
366
257
  const res = await apiClient.post("/query", args);
367
258
  return { content: [{ type: "text", text: JSON.stringify(res.data) }] };
368
259
  } else {
369
- try {
370
- const res = await memory!.search(args.query, {
371
- limit: args.k,
372
- user_id: userId,
373
- });
374
- await logActivity("read", {
375
- method: "POST",
376
- endpoint: "/memory/query",
377
- status: 200,
378
- });
379
- return { content: [{ type: "text", text: JSON.stringify(res) }] };
380
- } catch (e: any) {
381
- await logActivity("read", {
382
- method: "POST",
383
- endpoint: "/memory/query",
384
- status: 500,
385
- });
386
- throw e;
387
- }
260
+ const res = await memory!.search(args.query, { limit: args.k });
261
+ await logActivity("read", {
262
+ method: "POST",
263
+ endpoint: "/memory/query",
264
+ status: 200,
265
+ });
266
+ return { content: [{ type: "text", text: JSON.stringify(res) }] };
388
267
  }
389
268
  },
390
269
  );
391
270
 
392
271
  server.registerTool(
393
- "list_memories",
272
+ "update_memory",
394
273
  {
395
- description: "List recent memories",
396
- inputSchema: z.object({ limit: z.number().default(10) }),
274
+ description:
275
+ "Mutate existing memory (content/tags). HIGH COST: re-embeds and re-links. Use for corrections.",
276
+ inputSchema: z.object({
277
+ id: z.string(),
278
+ content: z.string().optional(),
279
+ tags: z.array(z.string()).optional(),
280
+ }),
397
281
  },
398
- async (args) => {
399
- const userId = getContextUserId();
400
-
282
+ async (args: any) => {
401
283
  if (cliUrl) {
402
- try {
403
- const res = await apiClient.get(`/all?limit=${args.limit}`);
404
- return {
405
- content: [{ type: "text", text: JSON.stringify(res.data) }],
406
- };
407
- } catch {
408
- const res = await apiClient.post("/query", {
409
- query: "",
410
- k: args.limit,
411
- });
412
- return {
413
- content: [{ type: "text", text: JSON.stringify(res.data) }],
414
- };
415
- }
284
+ const res = await apiClient.patch(`/memory/${args.id}`, args);
285
+ return { content: [{ type: "text", text: JSON.stringify(res.data) }] };
416
286
  } else {
417
- const res = await memory!.search("", {
418
- limit: args.limit,
419
- user_id: userId,
420
- });
421
- await logActivity("read", {
422
- method: "GET",
423
- endpoint: "/memory/all",
287
+ if (!sdk_update_memory) throw new Error("Update not available in SDK");
288
+ const res = await sdk_update_memory(args.id, args.content, args.tags);
289
+ await logActivity("update", {
290
+ method: "PATCH",
291
+ endpoint: `/memory/${args.id}`,
424
292
  status: 200,
425
293
  });
426
294
  return { content: [{ type: "text", text: JSON.stringify(res) }] };
@@ -428,269 +296,196 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
428
296
  },
429
297
  );
430
298
 
299
+ server.registerTool(
300
+ "reinforce_memory",
301
+ {
302
+ description:
303
+ "Metabolic boost (salience). LOW COST: prevents decay without mutation. Use for active topics.",
304
+ inputSchema: z.object({ id: z.string(), boost: z.number().default(0.1) }),
305
+ },
306
+ async (args: any) => {
307
+ if (cliUrl) {
308
+ const res = await apiClient.post(`/memory/${args.id}/reinforce`, args);
309
+ return { content: [{ type: "text", text: JSON.stringify(res.data) }] };
310
+ } else {
311
+ if (!sdk_reinforce_memory)
312
+ throw new Error("Reinforce not available in SDK");
313
+ await sdk_reinforce_memory(args.id, args.boost);
314
+ await logActivity("update", {
315
+ method: "POST",
316
+ endpoint: `/memory/${args.id}/reinforce`,
317
+ status: 200,
318
+ });
319
+ return { content: [{ type: "text", text: "Reinforced" }] };
320
+ }
321
+ },
322
+ );
323
+
431
324
  server.registerTool(
432
325
  "delete_memory",
433
326
  {
434
- description: "Delete memory by ID",
327
+ description: "Delete memory",
435
328
  inputSchema: z.object({ id: z.string() }),
436
329
  },
437
330
  async (args: any) => {
438
331
  if (cliUrl) {
439
- const res = await apiClient.delete(`/${args.id}`);
332
+ const res = await apiClient.delete(`/memory/${args.id}`);
440
333
  return { content: [{ type: "text", text: JSON.stringify(res.data) }] };
441
334
  } else {
442
335
  const dbPath = process.env.OM_DB_PATH!;
443
336
  const sqlite3 = await import("sqlite3");
444
337
  const db = new sqlite3.default.Database(dbPath);
445
- db.configure("busyTimeout", 5000);
446
-
447
338
  return new Promise((resolve, reject) => {
448
339
  db.serialize(() => {
449
- db.run("BEGIN TRANSACTION");
450
340
  db.run("DELETE FROM memories WHERE id = ?", [args.id]);
451
- db.run("DELETE FROM vectors WHERE id = ?", [args.id]);
452
- db.run("DELETE FROM waypoints WHERE src_id = ? OR dst_id = ?", [
453
- args.id,
454
- args.id,
455
- ]);
456
- db.run("COMMIT", async (err: any) => {
457
- db.close();
458
- if (err) {
459
- await logActivity("delete", {
460
- method: "DELETE",
461
- endpoint: `/memory/${args.id}`,
462
- status: 500,
463
- });
464
- reject(
465
- new Error(
466
- `Failed to delete memory ${args.id}: ${err.message}`,
467
- ),
468
- );
469
- } else {
341
+ db.run(
342
+ "DELETE FROM vectors WHERE id = ?",
343
+ [args.id],
344
+ async (err: any) => {
345
+ db.close();
470
346
  await logActivity("delete", {
471
347
  method: "DELETE",
472
348
  endpoint: `/memory/${args.id}`,
473
- status: 200,
474
- });
475
- resolve({
476
- content: [
477
- { type: "text", text: `Memory ${args.id} deleted` },
478
- ],
349
+ status: err ? 500 : 200,
479
350
  });
480
- }
481
- });
351
+ if (err) reject(err);
352
+ else resolve({ content: [{ type: "text", text: "Deleted" }] });
353
+ },
354
+ );
482
355
  });
483
356
  });
484
357
  }
485
358
  },
486
359
  );
487
360
 
488
- server.registerTool(
489
- "update_memory",
490
- {
491
- description: "Update memory",
492
- inputSchema: z.object({ id: z.string(), content: z.string().optional() }),
493
- },
494
- async (args: any) => {
495
- await logActivity("update", {
496
- method: "PATCH",
497
- endpoint: `/memory/${args.id}`,
498
- status: 501,
499
- });
500
- return { content: [{ type: "text", text: "Update not implemented" }] };
501
- },
502
- );
503
-
504
- // --- TRANSPORT ---
505
-
361
+ // EXPRESS SERVER
506
362
  const useHttp = args.includes("--http") || args.includes("--port");
507
-
508
363
  if (useHttp) {
509
364
  const port = parseInt(getArg("--port") || "3100", 10);
510
365
  const app = express();
511
366
  app.use(cors());
512
367
  app.use(express.json());
368
+ app.get("/health", (req, res) => res.json({ ok: true, version: "0.12.4" }));
513
369
 
514
- app.get("/health", (req: express.Request, res: express.Response) =>
515
- res.json({
516
- ok: (server as any)._memoryReady,
517
- version: "0.7.5",
518
- mode: cliUrl ? "proxy" : "sdk",
519
- ready: (server as any)._memoryReady,
520
- }),
521
- );
522
-
523
- app.get("/metrics", async (req: express.Request, res: express.Response) => {
524
- try {
525
- const dbPath = process.env.OM_DB_PATH!;
526
- const sqlite3 = await import("sqlite3");
527
- const db = new sqlite3.default.Database(dbPath);
528
- db.configure("busyTimeout", 5000);
529
-
530
- const getCount = (query: string): Promise<number> =>
531
- new Promise((resolve) =>
532
- db.get(query, (err, row: any) => resolve(row?.count || 0)),
533
- );
534
-
535
- const memoriesCount = await getCount(
536
- "SELECT COUNT(*) as count FROM memories",
537
- );
538
- const totalRequests = await getCount(
539
- "SELECT COUNT(*) as count FROM cybermem_access_log",
540
- );
541
- const errorRequests = await getCount(
542
- "SELECT COUNT(*) as count FROM cybermem_access_log WHERE is_error = 1",
543
- );
544
- const uniqueClients = await getCount(
545
- "SELECT COUNT(DISTINCT client_name) as count FROM cybermem_access_log",
546
- );
547
-
548
- db.close();
549
-
550
- const metrics = [
551
- "# HELP openmemory_memories_total Total number of memories",
552
- "# TYPE openmemory_memories_total gauge",
553
- `openmemory_memories_total ${memoriesCount}`,
554
- "# HELP openmemory_requests_aggregate_total Total requests logged in SQLite",
555
- "# TYPE openmemory_requests_aggregate_total counter",
556
- `openmemory_requests_aggregate_total ${totalRequests}`,
557
- "# HELP openmemory_errors_total Total errors logged in SQLite",
558
- "# TYPE openmemory_errors_total counter",
559
- `openmemory_errors_total ${errorRequests}`,
560
- "# HELP openmemory_clients_total Total unique clients logged in SQLite",
561
- "# TYPE openmemory_clients_total gauge",
562
- `openmemory_clients_total ${uniqueClients}`,
563
- "# HELP openmemory_success_rate_aggregate Success rate from SQLite logs",
564
- "# TYPE openmemory_success_rate_aggregate gauge",
565
- `openmemory_success_rate_aggregate ${totalRequests > 0 ? ((totalRequests - errorRequests) / totalRequests) * 100 : 100}`,
566
- ].join("\n");
567
-
568
- res.set("Content-Type", "text/plain").send(metrics);
569
- } catch (e: any) {
570
- res.status(500).send(`# Error: ${e.message}`);
571
- }
370
+ app.use((req, res, next) => {
371
+ const clientName =
372
+ (req.headers["x-client-name"] as string) || "antigravity-client";
373
+ requestContext.run({ clientName }, next);
374
+ // next(); // DELETED! Correctly handled by requestContext.run
572
375
  });
573
376
 
574
- app.use(
575
- (
576
- req: express.Request,
577
- res: express.Response,
578
- next: express.NextFunction,
579
- ) => {
580
- const userId = req.headers["x-user-id"] as string | undefined;
581
- const clientName =
582
- (req.headers["x-client-name"] as string) ||
583
- (req.headers["user-agent"] as string);
584
- requestContext.run({ userId, clientName }, next);
585
- },
586
- );
587
-
588
377
  if (!cliUrl && memory) {
589
- app.post("/add", async (req: express.Request, res: express.Response) => {
378
+ app.post("/add", async (req, res) => {
590
379
  try {
591
- const contextUserId = requestContext.getStore()?.userId;
592
- const { content, user_id, tags } = req.body;
593
- const finalTags = addSourceTag(tags);
594
- const result = await memory!.add(content, {
595
- user_id: user_id || contextUserId,
596
- tags: finalTags,
380
+ const result = await (memory as any)!.add(req.body.content, {
381
+ id: req.body.id,
382
+ tags: req.body.tags,
597
383
  });
598
384
  await logActivity("create", {
599
- client: "rest-api",
600
385
  method: "POST",
601
386
  endpoint: "/add",
602
387
  status: 200,
603
388
  });
604
389
  res.json(result);
605
390
  } catch (e: any) {
606
- await logActivity("create", {
607
- client: "rest-api",
608
- method: "POST",
609
- endpoint: "/add",
610
- status: 500,
611
- });
612
391
  res.status(500).json({ error: e.message });
613
392
  }
614
393
  });
615
-
616
- app.post(
617
- "/query",
618
- async (req: express.Request, res: express.Response) => {
619
- try {
620
- const contextUserId = requestContext.getStore()?.userId;
621
- const { query, k } = req.body;
622
- const result = await memory!.search(query || "", {
623
- limit: k || 5,
624
- user_id: contextUserId,
625
- });
626
- await logActivity("read", {
627
- client: "rest-api",
628
- method: "POST",
629
- endpoint: "/query",
630
- status: 200,
631
- });
632
- res.json(result);
633
- } catch (e: any) {
634
- await logActivity("read", {
635
- client: "rest-api",
636
- method: "POST",
637
- endpoint: "/query",
638
- status: 500,
639
- });
640
- res.status(500).json({ error: e.message });
641
- }
642
- },
643
- );
644
-
645
- app.get("/all", async (req: express.Request, res: express.Response) => {
394
+ app.post("/query", async (req, res) => {
646
395
  try {
647
- const contextUserId = requestContext.getStore()?.userId;
648
- const limit = parseInt(req.query.limit as string) || 10;
649
- const result = await memory!.search("", {
650
- limit,
651
- user_id: contextUserId,
396
+ const result = await memory!.search(req.body.query || "", {
397
+ limit: req.body.k || 5,
652
398
  });
653
399
  await logActivity("read", {
654
- client: "rest-api",
655
- method: "GET",
656
- endpoint: "/all",
400
+ method: "POST",
401
+ endpoint: "/query",
657
402
  status: 200,
658
403
  });
659
404
  res.json(result);
660
405
  } catch (e: any) {
406
+ res.status(500).json({ error: e.message });
407
+ }
408
+ });
409
+ app.get("/all", async (req, res) => {
410
+ try {
411
+ const result = await memory!.search("", { limit: 10 });
661
412
  await logActivity("read", {
662
- client: "rest-api",
663
413
  method: "GET",
664
414
  endpoint: "/all",
665
- status: 500,
415
+ status: 200,
666
416
  });
417
+ res.json(result);
418
+ } catch (e: any) {
667
419
  res.status(500).json({ error: e.message });
668
420
  }
669
421
  });
422
+ app.patch("/memory/:id", async (req, res) => {
423
+ try {
424
+ const result = await sdk_update_memory(
425
+ req.params.id,
426
+ req.body.content,
427
+ req.body.tags,
428
+ req.body.metadata,
429
+ );
430
+ await logActivity("update", {
431
+ method: "PATCH",
432
+ endpoint: `/memory/${req.params.id}`,
433
+ status: 200,
434
+ });
435
+ res.json(result);
436
+ } catch (e: any) {
437
+ res.status(500).json({ error: e.message });
438
+ }
439
+ });
440
+ app.post("/memory/:id/reinforce", async (req, res) => {
441
+ try {
442
+ await sdk_reinforce_memory(req.params.id, req.body.boost);
443
+ await logActivity("update", {
444
+ method: "POST",
445
+ endpoint: `/memory/${req.params.id}/reinforce`,
446
+ status: 200,
447
+ });
448
+ res.json({ ok: true });
449
+ } catch (e: any) {
450
+ res.status(500).json({ error: e.message });
451
+ }
452
+ });
453
+ app.delete("/memory/:id", async (req, res) => {
454
+ const dbPath = process.env.OM_DB_PATH!;
455
+ const sqlite3 = await import("sqlite3");
456
+ const db = new sqlite3.default.Database(dbPath);
457
+ db.run(
458
+ "DELETE FROM memories WHERE id = ?",
459
+ [req.params.id],
460
+ async () => {
461
+ db.close();
462
+ await logActivity("delete", {
463
+ method: "DELETE",
464
+ endpoint: `/memory/${req.params.id}`,
465
+ status: 200,
466
+ });
467
+ res.json({ ok: true });
468
+ },
469
+ );
470
+ });
670
471
  }
671
472
 
672
473
  const transport = new StreamableHTTPServerTransport({
673
474
  sessionIdGenerator: () => crypto.randomUUID(),
674
475
  });
675
-
676
476
  app.all(
677
477
  "/mcp",
678
- async (req: express.Request, res: express.Response) =>
679
- await transport.handleRequest(req, res, req.body),
478
+ async (req, res) => await transport.handleRequest(req, res, req.body),
680
479
  );
681
-
682
480
  app.all(
683
481
  "/sse",
684
- async (req: express.Request, res: express.Response) =>
685
- await transport.handleRequest(req, res, req.body),
482
+ async (req, res) => await transport.handleRequest(req, res, req.body),
686
483
  );
687
484
 
688
485
  server.connect(transport).then(() => {
689
- app.listen(port, () => {
690
- console.error(
691
- `CyberMem MCP (ready: ${(server as any)._memoryReady}) running on http://localhost:${port}`,
692
- );
693
- });
486
+ app.listen(port, () =>
487
+ console.error(`CyberMem MCP running on http://localhost:${port}`),
488
+ );
694
489
  });
695
490
  } else {
696
491
  const transport = new StdioServerTransport();