@cybermem/mcp 0.8.3 → 0.8.7

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/dist/env.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ /**
7
+ * Environment Initialization for CyberMem MCP
8
+ * Must be imported first to set side-effect vars.
9
+ */
10
+ const dotenv_1 = __importDefault(require("dotenv"));
11
+ dotenv_1.default.config();
12
+ process.env.OM_TIER = process.env.OM_TIER || "hybrid";
13
+ process.env.OM_PORT = process.env.OM_PORT || "0";
14
+ process.env.PORT = process.env.PORT || "0";
package/dist/index.js CHANGED
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ require("./env.js");
3
8
  /**
4
9
  * CyberMem MCP Server
5
10
  *
@@ -7,50 +12,42 @@
7
12
  * 1. Local/Server Mode (default): Uses openmemory-js SDK directly.
8
13
  * 2. Remote Client Mode (with --url): Proxies requests to a remote CyberMem server via HTTP.
9
14
  */
10
- var __importDefault = (this && this.__importDefault) || function (mod) {
11
- return (mod && mod.__esModule) ? mod : { "default": mod };
12
- };
13
- Object.defineProperty(exports, "__esModule", { value: true });
14
15
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
15
16
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
16
17
  const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
18
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
19
+ const async_hooks_1 = require("async_hooks");
17
20
  const axios_1 = __importDefault(require("axios"));
18
21
  const cors_1 = __importDefault(require("cors"));
19
- const dotenv_1 = __importDefault(require("dotenv"));
20
22
  const express_1 = __importDefault(require("express"));
21
- const openmemory_js_1 = require("openmemory-js");
22
23
  const zod_1 = require("zod");
23
- const auth_js_1 = require("./auth.js");
24
- dotenv_1.default.config();
25
- // Handle CLI auth commands first
24
+ // Redirect all stdout to stderr IMMEDIATELY to protect Stdio protocol
25
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
26
+ process.stdout.write = (chunk, encoding, callback) => {
27
+ const str = typeof chunk === "string" ? chunk : chunk.toString();
28
+ // Allow ONLY protocol messages (must be JSON-RPC)
29
+ if (str.includes('"jsonrpc":')) {
30
+ return originalStdoutWrite(chunk, encoding, callback);
31
+ }
32
+ return process.stderr.write(chunk, encoding, callback);
33
+ };
34
+ // Also redirect console outputs
35
+ console.log = console.error;
36
+ console.info = console.error;
37
+ // Async Storage for Request Context (User ID and Client Name)
38
+ const requestContext = new async_hooks_1.AsyncLocalStorage();
39
+ // CLI args processing
26
40
  const args = process.argv.slice(2);
27
- if (args.includes("--login")) {
28
- (0, auth_js_1.login)()
29
- .then(() => process.exit(0))
30
- .catch((err) => {
31
- console.error("Login failed:", err.message);
32
- process.exit(1);
33
- });
34
- }
35
- else if (args.includes("--logout")) {
36
- (0, auth_js_1.logout)();
37
- process.exit(0);
38
- }
39
- else if (args.includes("--status")) {
40
- (0, auth_js_1.showStatus)();
41
- process.exit(0);
42
- }
43
- else {
44
- startServer();
45
- }
41
+ // Start the server
42
+ startServer();
46
43
  async function startServer() {
47
44
  const getArg = (name) => {
48
45
  const idx = args.indexOf(name);
49
46
  return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
50
47
  };
51
- const cliClientName = getArg("--client-name") || "cybermem-mcp";
52
48
  const cliUrl = getArg("--url");
53
- const cliApiKey = getArg("--api-key");
49
+ const cliToken = getArg("--token") || getArg("--api-key");
50
+ let stdioClientName = undefined;
54
51
  // Protocol Instructions
55
52
  const CYBERMEM_INSTRUCTIONS = `CyberMem is a persistent context daemon for AI agents.
56
53
  PROTOCOL:
@@ -58,7 +55,7 @@ PROTOCOL:
58
55
  2. Store new insights immediately with add_memory (FULL content)
59
56
  3. Always include tags: [topic, year, source:your-client-name]
60
57
  For full protocol: https://docs.cybermem.dev/agent-protocol`;
61
- const server = new mcp_js_1.McpServer({ name: "cybermem", version: "0.8.2" }, {
58
+ const server = new mcp_js_1.McpServer({ name: "cybermem", version: "0.7.5" }, {
62
59
  instructions: CYBERMEM_INSTRUCTIONS,
63
60
  });
64
61
  server.registerResource("CyberMem Agent Protocol", "cybermem://protocol", { description: "Instructions for AI agents", mimeType: "text/plain" }, async () => ({
@@ -70,6 +67,23 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
70
67
  },
71
68
  ],
72
69
  }));
70
+ // Capture client info from handshake
71
+ // @ts-ignore - access underlying server
72
+ server.server.setRequestHandler(types_js_1.InitializeRequestSchema, async (request) => {
73
+ stdioClientName = request.params.clientInfo.name;
74
+ console.error(`[MCP] Client identified via handshake: ${stdioClientName}`);
75
+ return {
76
+ protocolVersion: "2024-11-05",
77
+ capabilities: {
78
+ tools: { listChanged: true },
79
+ resources: { subscribe: true },
80
+ },
81
+ serverInfo: {
82
+ name: "cybermem",
83
+ version: "0.7.5",
84
+ },
85
+ };
86
+ });
73
87
  // --- IMPLEMENTATION LOGIC ---
74
88
  let memory = null;
75
89
  let apiClient = null;
@@ -79,35 +93,158 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
79
93
  apiClient = axios_1.default.create({
80
94
  baseURL: cliUrl,
81
95
  headers: {
82
- Authorization: `Bearer ${cliApiKey}`,
83
- "X-Client-Name": cliClientName,
96
+ "X-API-Key": cliToken,
97
+ Accept: "application/json, text/event-stream",
98
+ "Content-Type": "application/json",
84
99
  },
85
100
  });
101
+ // Dynamically inject client name from context or discovery
102
+ apiClient.interceptors.request.use((config) => {
103
+ const ctx = requestContext.getStore();
104
+ config.headers["X-Client-Name"] =
105
+ ctx?.clientName || stdioClientName || "unknown-mcp-client";
106
+ return config;
107
+ });
86
108
  }
87
109
  else {
88
110
  // LOCAL SDK MODE
89
111
  const homedir = process.env.HOME || process.env.USERPROFILE || "";
90
- // Default to ~/.cybermem/data if OM_DB_PATH not set
91
- if (!process.env.OM_DB_PATH) {
92
- process.env.OM_DB_PATH = `${homedir}/.cybermem/data/openmemory.sqlite`;
93
- }
112
+ // FORCE absolute standardized path for consistency across components
113
+ const path = await import("path");
114
+ const dbPath = path.resolve(homedir, ".cybermem/data/openmemory.sqlite");
115
+ process.env.OM_DB_PATH = dbPath;
94
116
  // Ensure directory exists
95
- const fs = require("fs");
117
+ const fs = await import("fs");
96
118
  try {
97
- const dbPath = process.env.OM_DB_PATH;
98
- const dir = dbPath.substring(0, dbPath.lastIndexOf("/"));
119
+ const dir = path.dirname(dbPath);
99
120
  if (dir)
100
121
  fs.mkdirSync(dir, { recursive: true });
101
122
  }
102
123
  catch { }
103
- memory = new openmemory_js_1.Memory();
124
+ try {
125
+ // Dynamic import to ensure env vars are set before loading SDK
126
+ // We import from dist/core/memory directly to avoid triggering the server side-effects in openmemory-js/dist/index.js
127
+ // @ts-ignore
128
+ const { Memory } = await import("openmemory-js/dist/core/memory.js");
129
+ memory = new Memory();
130
+ server._memoryReady = true;
131
+ // --- INITIALIZE LOGGING TABLES ---
132
+ const sqlite3 = await import("sqlite3");
133
+ const db = new sqlite3.default.Database(dbPath);
134
+ db.configure("busyTimeout", 5000);
135
+ db.serialize(() => {
136
+ db.run("PRAGMA journal_mode=WAL;", (err) => {
137
+ if (err)
138
+ console.error("[MCP] Init WAL error:", err.message);
139
+ });
140
+ db.run(`CREATE TABLE IF NOT EXISTS cybermem_stats (
141
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
142
+ client_name TEXT NOT NULL,
143
+ operation TEXT NOT NULL,
144
+ count INTEGER DEFAULT 0,
145
+ errors INTEGER DEFAULT 0,
146
+ last_updated INTEGER NOT NULL,
147
+ UNIQUE(client_name, operation)
148
+ );`, (err) => {
149
+ if (err)
150
+ console.error("[MCP] Init stats table error:", err.message);
151
+ });
152
+ db.run(`CREATE TABLE IF NOT EXISTS cybermem_access_log (
153
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
154
+ timestamp INTEGER NOT NULL,
155
+ client_name TEXT NOT NULL,
156
+ client_version TEXT,
157
+ method TEXT NOT NULL,
158
+ endpoint TEXT NOT NULL,
159
+ operation TEXT NOT NULL,
160
+ status TEXT NOT NULL,
161
+ is_error INTEGER DEFAULT 0
162
+ );`, (err) => {
163
+ if (err)
164
+ console.error("[MCP] Init access_log table error:", err.message);
165
+ });
166
+ // Access keys table for token-based auth
167
+ db.run(`CREATE TABLE IF NOT EXISTS access_keys (
168
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
169
+ key_hash TEXT NOT NULL,
170
+ name TEXT DEFAULT 'default',
171
+ user_id TEXT DEFAULT 'default',
172
+ created_at TEXT DEFAULT (datetime('now')),
173
+ last_used_at TEXT,
174
+ is_active INTEGER DEFAULT 1
175
+ );`, (err) => {
176
+ if (err)
177
+ console.error("[MCP] Init access_keys table error:", err.message);
178
+ });
179
+ });
180
+ db.close();
181
+ }
182
+ catch (e) {
183
+ console.error("Failed to initialize OpenMemory SDK:", e);
184
+ server._memoryReady = false;
185
+ }
104
186
  }
105
- // Helper to add source tag
187
+ // Helper to log activity to SQLite (Local SDK Mode only)
188
+ const logActivity = async (operation, opts = {}) => {
189
+ if (cliUrl || !memory)
190
+ return;
191
+ const { client: providedClient, method = "POST", endpoint = "/mcp", status = 200, } = opts;
192
+ const ctx = requestContext.getStore();
193
+ const client = providedClient || ctx?.clientName || stdioClientName || "unknown-client";
194
+ try {
195
+ const dbPath = process.env.OM_DB_PATH;
196
+ const sqlite3 = await import("sqlite3");
197
+ const db = new sqlite3.default.Database(dbPath);
198
+ db.configure("busyTimeout", 5000);
199
+ const ts = Date.now();
200
+ const is_error = status >= 400 ? 1 : 0;
201
+ console.error(`[MCP] Logging ${operation} for ${client} (status: ${status})`);
202
+ db.serialize(() => {
203
+ // Log to access_log
204
+ db.run(`INSERT INTO cybermem_access_log
205
+ (timestamp, client_name, client_version, method, endpoint, operation, status, is_error)
206
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
207
+ ts,
208
+ client,
209
+ "0.7.0",
210
+ method,
211
+ endpoint,
212
+ operation,
213
+ status.toString(),
214
+ is_error,
215
+ ], (err) => {
216
+ if (err)
217
+ console.error("[MCP] Log access error:", err.message);
218
+ });
219
+ // Log to stats (Upsert)
220
+ db.run(`INSERT INTO cybermem_stats (client_name, operation, count, errors, last_updated)
221
+ VALUES (?, ?, 1, ?, ?)
222
+ ON CONFLICT(client_name, operation) DO UPDATE SET
223
+ count = count + 1,
224
+ errors = errors + ?,
225
+ last_updated = ?`, [client, operation, is_error, ts, is_error, ts], (err) => {
226
+ if (err)
227
+ console.error("[MCP] Log stats error:", err.message);
228
+ });
229
+ });
230
+ db.close();
231
+ }
232
+ catch (e) {
233
+ console.error("Failed to log activity to SQLite:", e);
234
+ }
235
+ };
106
236
  const addSourceTag = (tags = []) => {
107
- if (!tags.some((t) => t.startsWith("source:")))
108
- tags.push(`source:${cliClientName}`);
237
+ if (!tags.some((t) => t.startsWith("source:"))) {
238
+ const clientName = requestContext.getStore()?.clientName || stdioClientName || "unknown";
239
+ tags.push(`source:${clientName}`);
240
+ }
109
241
  return tags;
110
242
  };
243
+ // Helper to get current User ID from context or args
244
+ const getContextUserId = (argsUserId) => {
245
+ const store = requestContext.getStore();
246
+ return argsUserId || store?.userId;
247
+ };
111
248
  // --- TOOLS ---
112
249
  server.registerTool("add_memory", {
113
250
  description: "Store a new memory. " + CYBERMEM_INSTRUCTIONS,
@@ -118,38 +255,76 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
118
255
  }),
119
256
  }, async (args) => {
120
257
  const tags = addSourceTag(args.tags);
258
+ const userId = getContextUserId(args.user_id);
121
259
  if (cliUrl) {
122
- const res = await apiClient.post("/add", { ...args, tags });
260
+ const res = await apiClient.post("/add", {
261
+ ...args,
262
+ user_id: userId,
263
+ tags,
264
+ });
123
265
  return { content: [{ type: "text", text: JSON.stringify(res.data) }] };
124
266
  }
125
267
  else {
126
- const res = await memory.add(args.content, {
127
- user_id: args.user_id,
128
- tags,
129
- });
130
- return { content: [{ type: "text", text: JSON.stringify(res) }] };
268
+ try {
269
+ const res = await memory.add(args.content, {
270
+ user_id: userId,
271
+ tags,
272
+ });
273
+ await logActivity("create", {
274
+ method: "POST",
275
+ endpoint: "/memory/add",
276
+ status: 200,
277
+ });
278
+ return { content: [{ type: "text", text: JSON.stringify(res) }] };
279
+ }
280
+ catch (e) {
281
+ await logActivity("create", {
282
+ method: "POST",
283
+ endpoint: "/memory/add",
284
+ status: 500,
285
+ });
286
+ throw e;
287
+ }
131
288
  }
132
289
  });
133
290
  server.registerTool("query_memory", {
134
291
  description: "Search memories.",
135
292
  inputSchema: zod_1.z.object({ query: zod_1.z.string(), k: zod_1.z.number().default(5) }),
136
293
  }, async (args) => {
294
+ const userId = getContextUserId(); // Search is scoped to user if provided
137
295
  if (cliUrl) {
138
296
  const res = await apiClient.post("/query", args);
139
297
  return { content: [{ type: "text", text: JSON.stringify(res.data) }] };
140
298
  }
141
299
  else {
142
- const res = await memory.search(args.query, { limit: args.k });
143
- return { content: [{ type: "text", text: JSON.stringify(res) }] };
300
+ try {
301
+ const res = await memory.search(args.query, {
302
+ limit: args.k,
303
+ user_id: userId,
304
+ });
305
+ await logActivity("read", {
306
+ method: "POST",
307
+ endpoint: "/memory/query",
308
+ status: 200,
309
+ });
310
+ return { content: [{ type: "text", text: JSON.stringify(res) }] };
311
+ }
312
+ catch (e) {
313
+ await logActivity("read", {
314
+ method: "POST",
315
+ endpoint: "/memory/query",
316
+ status: 500,
317
+ });
318
+ throw e;
319
+ }
144
320
  }
145
321
  });
146
322
  server.registerTool("list_memories", {
147
323
  description: "List recent memories",
148
324
  inputSchema: zod_1.z.object({ limit: zod_1.z.number().default(10) }),
149
325
  }, async (args) => {
326
+ const userId = getContextUserId();
150
327
  if (cliUrl) {
151
- // Fallback to /query with empty string if /list not available, or use /all
152
- // Old API had /all
153
328
  try {
154
329
  const res = await apiClient.get(`/all?limit=${args.limit}`);
155
330
  return {
@@ -167,7 +342,15 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
167
342
  }
168
343
  }
169
344
  else {
170
- const res = await memory.search("", { limit: args.limit });
345
+ const res = await memory.search("", {
346
+ limit: args.limit,
347
+ user_id: userId,
348
+ });
349
+ await logActivity("read", {
350
+ method: "GET",
351
+ endpoint: "/memory/all",
352
+ status: 200,
353
+ });
171
354
  return { content: [{ type: "text", text: JSON.stringify(res) }] };
172
355
  }
173
356
  });
@@ -180,17 +363,55 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
180
363
  return { content: [{ type: "text", text: JSON.stringify(res.data) }] };
181
364
  }
182
365
  else {
183
- return {
184
- content: [
185
- { type: "text", text: "Delete not implemented in SDK yet" },
186
- ],
187
- };
366
+ const dbPath = process.env.OM_DB_PATH;
367
+ const sqlite3 = await import("sqlite3");
368
+ const db = new sqlite3.default.Database(dbPath);
369
+ db.configure("busyTimeout", 5000);
370
+ return new Promise((resolve, reject) => {
371
+ db.serialize(() => {
372
+ db.run("BEGIN TRANSACTION");
373
+ db.run("DELETE FROM memories WHERE id = ?", [args.id]);
374
+ db.run("DELETE FROM vectors WHERE id = ?", [args.id]);
375
+ db.run("DELETE FROM waypoints WHERE src_id = ? OR dst_id = ?", [
376
+ args.id,
377
+ args.id,
378
+ ]);
379
+ db.run("COMMIT", async (err) => {
380
+ db.close();
381
+ if (err) {
382
+ await logActivity("delete", {
383
+ method: "DELETE",
384
+ endpoint: `/memory/${args.id}`,
385
+ status: 500,
386
+ });
387
+ reject(new Error(`Failed to delete memory ${args.id}: ${err.message}`));
388
+ }
389
+ else {
390
+ await logActivity("delete", {
391
+ method: "DELETE",
392
+ endpoint: `/memory/${args.id}`,
393
+ status: 200,
394
+ });
395
+ resolve({
396
+ content: [
397
+ { type: "text", text: `Memory ${args.id} deleted` },
398
+ ],
399
+ });
400
+ }
401
+ });
402
+ });
403
+ });
188
404
  }
189
405
  });
190
406
  server.registerTool("update_memory", {
191
407
  description: "Update memory",
192
408
  inputSchema: zod_1.z.object({ id: zod_1.z.string(), content: zod_1.z.string().optional() }),
193
409
  }, async (args) => {
410
+ await logActivity("update", {
411
+ method: "PATCH",
412
+ endpoint: `/memory/${args.id}`,
413
+ status: 501,
414
+ });
194
415
  return { content: [{ type: "text", text: "Update not implemented" }] };
195
416
  });
196
417
  // --- TRANSPORT ---
@@ -200,41 +421,130 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
200
421
  const app = (0, express_1.default)();
201
422
  app.use((0, cors_1.default)());
202
423
  app.use(express_1.default.json());
203
- app.get("/health", (req, res) => res.json({ ok: true, version: "0.8.2", mode: cliUrl ? "proxy" : "sdk" }));
204
- // REST API Compatibility (for Remote Clients)
205
- // Only enable if in SDK mode (Server)
424
+ app.get("/health", (req, res) => res.json({
425
+ ok: server._memoryReady,
426
+ version: "0.7.5",
427
+ mode: cliUrl ? "proxy" : "sdk",
428
+ ready: server._memoryReady,
429
+ }));
430
+ app.get("/metrics", async (req, res) => {
431
+ try {
432
+ const dbPath = process.env.OM_DB_PATH;
433
+ const sqlite3 = await import("sqlite3");
434
+ const db = new sqlite3.default.Database(dbPath);
435
+ db.configure("busyTimeout", 5000);
436
+ const getCount = (query) => new Promise((resolve) => db.get(query, (err, row) => resolve(row?.count || 0)));
437
+ const memoriesCount = await getCount("SELECT COUNT(*) as count FROM memories");
438
+ const totalRequests = await getCount("SELECT COUNT(*) as count FROM cybermem_access_log");
439
+ const errorRequests = await getCount("SELECT COUNT(*) as count FROM cybermem_access_log WHERE is_error = 1");
440
+ const uniqueClients = await getCount("SELECT COUNT(DISTINCT client_name) as count FROM cybermem_access_log");
441
+ db.close();
442
+ const metrics = [
443
+ "# HELP openmemory_memories_total Total number of memories",
444
+ "# TYPE openmemory_memories_total gauge",
445
+ `openmemory_memories_total ${memoriesCount}`,
446
+ "# HELP openmemory_requests_aggregate_total Total requests logged in SQLite",
447
+ "# TYPE openmemory_requests_aggregate_total counter",
448
+ `openmemory_requests_aggregate_total ${totalRequests}`,
449
+ "# HELP openmemory_errors_total Total errors logged in SQLite",
450
+ "# TYPE openmemory_errors_total counter",
451
+ `openmemory_errors_total ${errorRequests}`,
452
+ "# HELP openmemory_clients_total Total unique clients logged in SQLite",
453
+ "# TYPE openmemory_clients_total gauge",
454
+ `openmemory_clients_total ${uniqueClients}`,
455
+ "# HELP openmemory_success_rate_aggregate Success rate from SQLite logs",
456
+ "# TYPE openmemory_success_rate_aggregate gauge",
457
+ `openmemory_success_rate_aggregate ${totalRequests > 0 ? ((totalRequests - errorRequests) / totalRequests) * 100 : 100}`,
458
+ ].join("\n");
459
+ res.set("Content-Type", "text/plain").send(metrics);
460
+ }
461
+ catch (e) {
462
+ res.status(500).send(`# Error: ${e.message}`);
463
+ }
464
+ });
465
+ app.use((req, res, next) => {
466
+ const userId = req.headers["x-user-id"];
467
+ const clientName = req.headers["x-client-name"] ||
468
+ req.headers["user-agent"];
469
+ requestContext.run({ userId, clientName }, next);
470
+ });
206
471
  if (!cliUrl && memory) {
207
472
  app.post("/add", async (req, res) => {
208
473
  try {
474
+ const contextUserId = requestContext.getStore()?.userId;
209
475
  const { content, user_id, tags } = req.body;
210
476
  const finalTags = addSourceTag(tags);
211
477
  const result = await memory.add(content, {
212
- user_id,
478
+ user_id: user_id || contextUserId,
213
479
  tags: finalTags,
214
480
  });
481
+ await logActivity("create", {
482
+ client: "rest-api",
483
+ method: "POST",
484
+ endpoint: "/add",
485
+ status: 200,
486
+ });
215
487
  res.json(result);
216
488
  }
217
489
  catch (e) {
490
+ await logActivity("create", {
491
+ client: "rest-api",
492
+ method: "POST",
493
+ endpoint: "/add",
494
+ status: 500,
495
+ });
218
496
  res.status(500).json({ error: e.message });
219
497
  }
220
498
  });
221
499
  app.post("/query", async (req, res) => {
222
500
  try {
501
+ const contextUserId = requestContext.getStore()?.userId;
223
502
  const { query, k } = req.body;
224
- const result = await memory.search(query || "", { limit: k || 5 });
503
+ const result = await memory.search(query || "", {
504
+ limit: k || 5,
505
+ user_id: contextUserId,
506
+ });
507
+ await logActivity("read", {
508
+ client: "rest-api",
509
+ method: "POST",
510
+ endpoint: "/query",
511
+ status: 200,
512
+ });
225
513
  res.json(result);
226
514
  }
227
515
  catch (e) {
516
+ await logActivity("read", {
517
+ client: "rest-api",
518
+ method: "POST",
519
+ endpoint: "/query",
520
+ status: 500,
521
+ });
228
522
  res.status(500).json({ error: e.message });
229
523
  }
230
524
  });
231
525
  app.get("/all", async (req, res) => {
232
526
  try {
527
+ const contextUserId = requestContext.getStore()?.userId;
233
528
  const limit = parseInt(req.query.limit) || 10;
234
- const result = await memory.search("", { limit });
529
+ const result = await memory.search("", {
530
+ limit,
531
+ user_id: contextUserId,
532
+ });
533
+ await logActivity("read", {
534
+ client: "rest-api",
535
+ method: "GET",
536
+ endpoint: "/all",
537
+ status: 200,
538
+ });
235
539
  res.json(result);
236
540
  }
237
541
  catch (e) {
542
+ await logActivity("read", {
543
+ client: "rest-api",
544
+ method: "GET",
545
+ endpoint: "/all",
546
+ status: 500,
547
+ });
238
548
  res.status(500).json({ error: e.message });
239
549
  }
240
550
  });
@@ -246,7 +556,7 @@ For full protocol: https://docs.cybermem.dev/agent-protocol`;
246
556
  app.all("/sse", async (req, res) => await transport.handleRequest(req, res, req.body));
247
557
  server.connect(transport).then(() => {
248
558
  app.listen(port, () => {
249
- console.log(`CyberMem MCP running on http://localhost:${port}`);
559
+ console.error(`CyberMem MCP (ready: ${server._memoryReady}) running on http://localhost:${port}`);
250
560
  });
251
561
  });
252
562
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cybermem/mcp",
3
- "version": "0.8.3",
3
+ "version": "0.8.7",
4
4
  "description": "CyberMem MCP Server - AI Memory with openmemory-js SDK",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -25,7 +25,6 @@
25
25
  "ai-memory",
26
26
  "claude",
27
27
  "cursor",
28
- "antigravity",
29
28
  "openmemory",
30
29
  "llm"
31
30
  ],
@@ -40,7 +39,11 @@
40
39
  "cors": "^2.8.5",
41
40
  "dotenv": "^16.0.0",
42
41
  "express": "^5.2.1",
42
+ "keytar": "^7.9.0",
43
+ "open": "^11.0.0",
43
44
  "openmemory-js": "^1.3.2",
45
+ "sqlite": "^5.1.1",
46
+ "sqlite3": "^5.1.7",
44
47
  "zod": "^3.25.76"
45
48
  },
46
49
  "devDependencies": {
package/src/env.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Environment Initialization for CyberMem MCP
3
+ * Must be imported first to set side-effect vars.
4
+ */
5
+ import dotenv from "dotenv";
6
+ dotenv.config();
7
+
8
+ process.env.OM_TIER = process.env.OM_TIER || "hybrid";
9
+ process.env.OM_PORT = process.env.OM_PORT || "0";
10
+ process.env.PORT = process.env.PORT || "0";