@cybermem/mcp 0.14.15 → 0.15.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/dist/index.js CHANGED
@@ -1,458 +1,264 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
3
  Object.defineProperty(exports, "__esModule", { value: true });
6
4
  require("./console-fix.js");
7
5
  require("./env.js");
8
- const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
9
- const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
10
- const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
11
- const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
12
6
  const async_hooks_1 = require("async_hooks");
13
- const cors_1 = __importDefault(require("cors"));
14
- const express_1 = __importDefault(require("express"));
7
+ const fastmcp_1 = require("fastmcp");
15
8
  const fs_1 = require("fs");
16
9
  const path_1 = require("path");
17
10
  const zod_1 = require("zod");
18
- // Async Storage for Request Context (User ID and Client Name)
11
+ // --- CORE SDK IMPORTS ---
12
+ const db_js_1 = require("openmemory-js/dist/core/db.js");
13
+ const memory_js_1 = require("openmemory-js/dist/core/memory.js");
14
+ const hsg_js_1 = require("openmemory-js/dist/memory/hsg.js");
15
+ // --- GLOBALS & CONTEXT ---
19
16
  const requestContext = new async_hooks_1.AsyncLocalStorage();
20
- // CLI args processing
21
- const args = process.argv.slice(2);
22
- // Read version from package.json
23
17
  let PACKAGE_VERSION = "0.0.0";
24
18
  try {
25
19
  const packageJsonPath = (0, path_1.join)(__dirname, "../package.json");
26
20
  const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, "utf-8"));
27
21
  PACKAGE_VERSION = packageJson.version;
28
22
  }
29
- catch (error) {
30
- console.error("[MCP] Failed to read package.json version", error);
31
- }
32
- // Start the server
33
- startServer();
34
- async function startServer() {
35
- const getArg = (name) => {
36
- const idx = args.indexOf(name);
37
- return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
38
- };
39
- const cliEnv = getArg("--env");
40
- if (cliEnv === "staging") {
41
- console.error("[MCP] Running in Staging environment");
42
- process.env.CYBERMEM_ENV = "staging";
43
- }
44
- // --- IMPLEMENTATION LOGIC ---
45
- let memory = null;
46
- let sdk_update_memory = null;
47
- let sdk_reinforce_memory = null;
48
- // LOCAL SDK MODE
49
- const dbPath = process.env.OM_DB_PATH;
50
- const fs = await import("fs");
51
- const path = await import("path");
23
+ catch { }
24
+ const VALID_VERSION = (PACKAGE_VERSION.match(/^\d+\.\d+\.\d+$/) ? PACKAGE_VERSION : "0.0.0");
25
+ const CYBERMEM_INSTRUCTIONS = `CyberMem is a persistent context daemon for AI agents.
26
+ PROTOCOL:
27
+ 1. On session start: call query_memory("user context profile")
28
+ 2. Store insights immediately with add_memory
29
+ 3. Corrections: update_memory
30
+ 4. Decay prevention: reinforce_memory
31
+ Full protocol: https://docs.cybermem.dev/agent-protocol`;
32
+ // --- ERROR TRAPPING ---
33
+ process.on("uncaughtException", (err) => {
34
+ console.error("[CRITICAL] Uncaught Exception:", err);
35
+ });
36
+ process.on("unhandledRejection", (reason) => {
37
+ console.error("[CRITICAL] Unhandled Rejection:", reason);
38
+ });
39
+ // --- LOGGING ---
40
+ const logActivity = async (tool, status = 200) => {
52
41
  try {
53
- const dir = path.dirname(dbPath);
54
- if (dir)
55
- fs.mkdirSync(dir, { recursive: true });
42
+ const ctx = requestContext.getStore();
43
+ const client = ctx?.clientName || "unknown";
44
+ console.log(`[MCP-LOG] client=${client} tool=${tool} status=${status}`);
45
+ const ts = Date.now();
46
+ const isError = status >= 400 ? 1 : 0;
47
+ await (0, db_js_1.run_async)("INSERT INTO cybermem_access_log (timestamp, client_name, client_version, method, endpoint, tool, status, is_error) VALUES (?, ?, ?, 'POST', '/mcp', ?, ?, ?)", [ts, client, PACKAGE_VERSION, tool, status.toString(), isError]);
48
+ await (0, db_js_1.run_async)("INSERT INTO cybermem_stats (client_name, tool, count, errors, last_updated) VALUES (?, ?, 1, ?, ?) ON CONFLICT(client_name, tool) DO UPDATE SET count=count+1, errors=errors+?, last_updated=?", [client, tool, isError, ts, isError, ts]);
56
49
  }
57
- catch { }
58
- try {
59
- const { Memory } = await import("openmemory-js/dist/core/memory.js");
60
- const hsg = await import("openmemory-js/dist/memory/hsg.js");
61
- sdk_update_memory = hsg.update_memory;
62
- sdk_reinforce_memory = hsg.reinforce_memory;
63
- memory = new Memory();
64
- // Initialize Tables
65
- const sqlite3 = await import("sqlite3");
66
- const db = new sqlite3.default.Database(dbPath);
67
- db.configure("busyTimeout", 5000);
68
- db.serialize(() => {
69
- db.run("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));");
70
- db.run("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);");
71
- db.run("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);");
72
- });
73
- db.close();
50
+ catch (err) {
51
+ console.error("[MCP] Log Error:", err.message);
74
52
  }
75
- catch (e) {
76
- console.error("Failed to initialize OpenMemory SDK:", e);
77
- console.error("[FATAL] CyberMem cannot start without a working database. " +
78
- "Check OM_DB_PATH and ensure sqlite3 native bindings are installed.");
53
+ };
54
+ // --- INITIALIZATION ---
55
+ async function initialize() {
56
+ const dbPath = process.env.OM_DB_PATH;
57
+ if (!dbPath) {
58
+ console.error("[INIT] Environment variable OM_DB_PATH is not set. Please configure OM_DB_PATH to point to the OpenMemory database file (e.g., /path/to/openmemory.db).");
79
59
  process.exit(1);
80
60
  }
81
- // PERSISTENT LOGGING DB
82
- let loggingDb = null;
83
- const initLoggingDb = async () => {
84
- if (loggingDb)
85
- return loggingDb;
86
- const dbPath = process.env.OM_DB_PATH;
87
- const sqlite3 = await import("sqlite3");
88
- loggingDb = new sqlite3.default.Database(dbPath);
89
- loggingDb.configure("busyTimeout", 10000);
90
- return new Promise((resolve) => {
91
- loggingDb.serialize(() => {
92
- loggingDb.run("PRAGMA journal_mode=WAL;");
93
- loggingDb.run("PRAGMA synchronous=NORMAL;", () => resolve(loggingDb));
94
- });
95
- });
96
- };
97
- let stdioClientName = undefined;
98
- // Protocol Instructions
99
- const CYBERMEM_INSTRUCTIONS = `CyberMem is a persistent context daemon for AI agents.
100
- PROTOCOL:
101
- 1. On session start: call query_memory("user context profile")
102
- 2. Store new insights immediately with add_memory (STABLE data)
103
- 3. For corrections: use update_memory (STRUCTURAL mutation, high cost)
104
- 4. To prevent decay: use reinforce_memory (METABOLIC boost, low cost)
105
- 5. Always include tags: [topic, year, source:your-client-name]
106
- For full protocol: https://docs.cybermem.dev/agent-protocol`;
107
- const logActivity = async (operation, opts = {}) => {
108
- // Determine client name (priority: specific > store > default)
109
- let client;
110
- const ctx = requestContext.getStore();
111
- if (opts.sessionId) {
112
- // For SSE sessions, prefer the real client name from the request context when available
113
- client = ctx?.clientName || "sse-client";
61
+ const dir = (0, path_1.dirname)(dbPath);
62
+ if (dir && !(0, fs_1.existsSync)(dir))
63
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
64
+ // Migrations
65
+ try {
66
+ await (0, db_js_1.run_async)("CREATE TABLE IF NOT EXISTS cybermem_stats (id INTEGER PRIMARY KEY AUTOINCREMENT, client_name TEXT NOT NULL, tool TEXT NOT NULL, count INTEGER DEFAULT 0, errors INTEGER DEFAULT 0, last_updated INTEGER NOT NULL, UNIQUE(client_name, tool));");
67
+ await (0, db_js_1.run_async)("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, tool TEXT NOT NULL, status TEXT NOT NULL, is_error INTEGER DEFAULT 0);");
68
+ // Robustly check for and rename 'operation' to 'tool'
69
+ const statsInfo = (await (0, db_js_1.all_async)("PRAGMA table_info(cybermem_stats);"));
70
+ if (statsInfo.some((col) => col.name === "operation")) {
71
+ await (0, db_js_1.run_async)("ALTER TABLE cybermem_stats RENAME COLUMN operation TO tool;");
114
72
  }
115
- else if (ctx) {
116
- client = ctx.clientName || stdioClientName || "unknown";
73
+ else if (!statsInfo.some((col) => col.name === "tool")) {
74
+ await (0, db_js_1.run_async)("ALTER TABLE cybermem_stats ADD COLUMN tool TEXT DEFAULT 'unknown';");
117
75
  }
118
- else {
119
- client = stdioClientName || "unknown";
76
+ const logInfo = (await (0, db_js_1.all_async)("PRAGMA table_info(cybermem_access_log);"));
77
+ if (logInfo.some((col) => col.name === "operation")) {
78
+ await (0, db_js_1.run_async)("ALTER TABLE cybermem_access_log RENAME COLUMN operation TO tool;");
120
79
  }
121
- const { method = "POST", endpoint = "/mcp", status = 200 } = opts;
122
- try {
123
- const db = await initLoggingDb();
124
- const ts = Date.now();
125
- const is_error = status >= 400 ? 1 : 0;
126
- db.serialize(() => {
127
- db.run("INSERT INTO cybermem_access_log (timestamp, client_name, client_version, method, endpoint, operation, status, is_error) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [
128
- ts,
129
- client,
130
- PACKAGE_VERSION,
131
- method,
132
- endpoint,
133
- operation,
134
- status.toString(),
135
- is_error,
136
- ]);
137
- db.run("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 = ?", [client, operation, is_error, ts, is_error, ts]);
138
- });
80
+ else if (!logInfo.some((col) => col.name === "tool")) {
81
+ await (0, db_js_1.run_async)("ALTER TABLE cybermem_access_log ADD COLUMN tool TEXT DEFAULT 'unknown';");
139
82
  }
140
- catch { }
141
- };
142
- // Factory to create configured McpServer instance
143
- const createConfiguredServer = (onClientConnected) => {
144
- const server = new mcp_js_1.McpServer({ name: "cybermem", version: PACKAGE_VERSION }, {
145
- instructions: CYBERMEM_INSTRUCTIONS,
146
- });
147
- // access underlying server to set internal state for direct memory access
148
- // Casting to unknown first to allow adding private property
149
- server._memoryReady = true;
150
- server.registerResource("CyberMem Agent Protocol", "cybermem://protocol", { description: "Instructions for AI agents", mimeType: "text/plain" }, async () => ({
151
- contents: [
152
- {
153
- uri: "cybermem://protocol",
154
- mimeType: "text/plain",
155
- text: CYBERMEM_INSTRUCTIONS,
156
- },
157
- ],
158
- }));
159
- // access underlying server
160
- server.server.setRequestHandler(types_js_1.InitializeRequestSchema, async (request) => {
161
- // For SSE multiple clients, stdioClientName global is less useful,
162
- // but we can set it for context if running in single-user mode.
163
- // For multi-user, rely on requestContext.
164
- // For SSE multiple clients, rely on per-request context instead of a global.
165
- const clientName = request.params.clientInfo.name;
166
- if (onClientConnected) {
167
- onClientConnected(clientName);
83
+ // Backfill NULL tool values for SQLite safety
84
+ await (0, db_js_1.run_async)("UPDATE cybermem_access_log SET tool = 'unknown' WHERE tool IS NULL;");
85
+ await (0, db_js_1.run_async)("UPDATE cybermem_stats SET tool = 'unknown' WHERE tool IS NULL;");
86
+ }
87
+ catch (e) {
88
+ console.error("[INIT] Migration Error: Failed to apply database migrations:", e?.message ?? e);
89
+ process.exit(1);
90
+ }
91
+ }
92
+ const server = new fastmcp_1.FastMCP({
93
+ name: "cybermem",
94
+ version: VALID_VERSION,
95
+ instructions: CYBERMEM_INSTRUCTIONS,
96
+ health: { enabled: true, path: "/health" },
97
+ authenticate: async (req) => {
98
+ const clientName = (req.headers["x-client-name"] ||
99
+ req.headers["X-Client-Name"] ||
100
+ "unknown");
101
+ // Extract versioned naming if present (e.g. "antigravity/v1.0.0" -> "antigravity")
102
+ return { clientName: clientName.split("/")[0] };
103
+ },
104
+ });
105
+ const memory = new memory_js_1.Memory();
106
+ const app = server.getApp();
107
+ // Keep Hono middleware for custom routes if any, though FastMCP transport bypasses it
108
+ app.use("*", async (c, next) => {
109
+ const clientName = (c.req.header("X-Client-Name") ||
110
+ c.req.header("x-client-name") ||
111
+ "unknown").split("/")[0];
112
+ return requestContext.run({ clientName }, next);
113
+ });
114
+ // TOOLS
115
+ server.addTool({
116
+ name: "add_memory",
117
+ description: "Store a new memory with optional tags for semantic retrieval.",
118
+ parameters: zod_1.z.object({
119
+ content: zod_1.z.string().describe("The text content of the memory"),
120
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe("Category tags"),
121
+ }),
122
+ execute: async (args, context) => {
123
+ return requestContext.run({ clientName: context.session?.clientName }, async () => {
124
+ try {
125
+ const res = await memory.add(args.content, { tags: args.tags });
126
+ await logActivity("add_memory");
127
+ return JSON.stringify(res);
168
128
  }
169
- else {
170
- stdioClientName = clientName;
129
+ catch (err) {
130
+ await logActivity("add_memory", 500);
131
+ throw err;
171
132
  }
172
- console.error(`[MCP] Client identified via handshake: ${clientName}`);
173
- return {
174
- protocolVersion: "2024-11-05",
175
- capabilities: {
176
- tools: { listChanged: true },
177
- resources: { subscribe: true },
178
- },
179
- serverInfo: {
180
- name: "cybermem",
181
- version: PACKAGE_VERSION,
182
- },
183
- };
184
- });
185
- // TOOLS
186
- server.registerTool("add_memory", {
187
- description: "Store a new memory. Use for high-quality, stable data. " +
188
- CYBERMEM_INSTRUCTIONS,
189
- inputSchema: zod_1.z.object({
190
- content: zod_1.z.string(),
191
- tags: zod_1.z.array(zod_1.z.string()).optional(),
192
- }),
193
- }, async (args) => {
194
- const res = await memory.add(args.content, { tags: args.tags });
195
- await logActivity("create", {
196
- method: "POST",
197
- endpoint: "/memory/add",
198
- status: 200,
199
- });
200
- return { content: [{ type: "text", text: JSON.stringify(res) }] };
201
- });
202
- server.registerTool("query_memory", {
203
- description: "Search memories.",
204
- inputSchema: zod_1.z.object({ query: zod_1.z.string(), k: zod_1.z.number().default(5) }),
205
- }, async (args) => {
206
- const res = await memory.search(args.query, { limit: args.k });
207
- await logActivity("read", {
208
- method: "POST",
209
- endpoint: "/memory/query",
210
- status: 200,
211
- });
212
- return { content: [{ type: "text", text: JSON.stringify(res) }] };
213
133
  });
214
- server.registerTool("update_memory", {
215
- description: "Mutate existing memory (content/tags). HIGH COST: re-embeds and re-links. Use for corrections.",
216
- inputSchema: zod_1.z.object({
217
- id: zod_1.z.string(),
218
- content: zod_1.z.string().optional(),
219
- tags: zod_1.z.array(zod_1.z.string()).optional(),
220
- }),
221
- }, async (args) => {
222
- if (!sdk_update_memory)
223
- throw new Error("Update not available in SDK");
224
- if (args.content === undefined && args.tags === undefined) {
225
- throw new Error("At least one of 'content' or 'tags' must be provided to update_memory");
134
+ },
135
+ });
136
+ server.addTool({
137
+ name: "query_memory",
138
+ description: "Retrieve relevant memories using semantic search.",
139
+ parameters: zod_1.z.object({
140
+ query: zod_1.z.string().describe("Search query string"),
141
+ k: zod_1.z.number().default(5).describe("Number of results"),
142
+ }),
143
+ execute: async (args, context) => {
144
+ return requestContext.run({ clientName: context.session?.clientName }, async () => {
145
+ try {
146
+ const res = await memory.search(args.query, { limit: args.k });
147
+ await logActivity("query_memory");
148
+ return JSON.stringify(res);
226
149
  }
227
- const res = await sdk_update_memory(args.id, args.content, args.tags);
228
- await logActivity("update", {
229
- method: "PATCH",
230
- endpoint: `/memory/${args.id}`,
231
- status: 200,
232
- });
233
- return { content: [{ type: "text", text: JSON.stringify(res) }] };
234
- });
235
- server.registerTool("reinforce_memory", {
236
- description: "Metabolic boost (salience). LOW COST: prevents decay without mutation. Use for active topics.",
237
- inputSchema: zod_1.z.object({
238
- id: zod_1.z.string(),
239
- boost: zod_1.z.number().default(0.1),
240
- }),
241
- }, async (args) => {
242
- if (!sdk_reinforce_memory)
243
- throw new Error("Reinforce not available in SDK");
244
- const res = await sdk_reinforce_memory(args.id, args.boost);
245
- await logActivity("update", {
246
- method: "POST",
247
- endpoint: `/memory/${args.id}/reinforce`,
248
- status: 200,
249
- });
250
- return { content: [{ type: "text", text: JSON.stringify(res) }] };
251
- });
252
- server.registerTool("delete_memory", {
253
- description: "Delete memory",
254
- inputSchema: zod_1.z.object({ id: zod_1.z.string() }),
255
- }, async (args) => {
256
- const dbPath = process.env.OM_DB_PATH;
257
- const sqlite3 = await import("sqlite3");
258
- const db = new sqlite3.default.Database(dbPath);
259
- return new Promise((resolve, reject) => {
260
- db.serialize(() => {
261
- db.run("DELETE FROM memories WHERE id = ?", [args.id]);
262
- db.run("DELETE FROM vectors WHERE id = ?", [args.id], async (err) => {
263
- db.close();
264
- await logActivity("delete", {
265
- method: "DELETE",
266
- endpoint: `/memory/${args.id}`,
267
- status: err ? 500 : 200,
268
- });
269
- if (err)
270
- reject(err);
271
- else
272
- resolve({ content: [{ type: "text", text: "Deleted" }] });
273
- });
274
- });
275
- });
276
- });
277
- return server;
278
- };
279
- // EXPRESS SERVER
280
- // HTTP server mode for Docker/Traefik deployment
281
- const useHttp = args.includes("--http") || args.includes("--port");
282
- if (useHttp) {
283
- const port = parseInt(getArg("--port") || "3100", 10);
284
- const app = (0, express_1.default)();
285
- app.use((0, cors_1.default)());
286
- app.use((req, res, next) => {
287
- // Skip JSON parsing for SSE message endpoint - it needs raw body stream
288
- // Use req.url to handle query params like /message?sessionId=...
289
- if (req.url.startsWith("/message")) {
290
- return next();
150
+ catch (err) {
151
+ await logActivity("query_memory", 500);
152
+ throw err;
291
153
  }
292
- express_1.default.json()(req, res, next);
293
- });
294
- app.get("/health", (req, res) => res.json({ ok: true, version: PACKAGE_VERSION }));
295
- app.use((req, res, next) => {
296
- const clientName = req.headers["x-client-name"] || "antigravity-client";
297
- requestContext.run({ clientName }, next);
298
- });
299
- if (memory) {
300
- app.post("/add", async (req, res) => {
301
- try {
302
- const result = await memory.add(req.body.content, {
303
- id: req.body.id,
304
- tags: req.body.tags,
305
- });
306
- await logActivity("create", {
307
- method: "POST",
308
- endpoint: "/add",
309
- status: 200,
310
- });
311
- res.json(result);
312
- }
313
- catch (e) {
314
- res.status(500).json({ error: e.message });
315
- }
316
- });
317
- app.post("/query", async (req, res) => {
318
- try {
319
- const result = await memory.search(req.body.query || "", {
320
- limit: req.body.k || 5,
321
- });
322
- await logActivity("read", {
323
- method: "POST",
324
- endpoint: "/query",
325
- status: 200,
326
- });
327
- res.json(result);
328
- }
329
- catch (e) {
330
- res.status(500).json({ error: e.message });
331
- }
332
- });
333
- app.get("/all", async (req, res) => {
334
- try {
335
- const result = await memory.search("", { limit: 10 });
336
- await logActivity("read", {
337
- method: "GET",
338
- endpoint: "/all",
339
- status: 200,
340
- });
341
- res.json(result);
342
- }
343
- catch (e) {
344
- res.status(500).json({ error: e.message });
345
- }
346
- });
347
- app.patch("/memory/:id", async (req, res) => {
348
- try {
349
- const result = await sdk_update_memory(req.params.id, req.body.content, req.body.tags, req.body.metadata);
350
- await logActivity("update", {
351
- method: "PATCH",
352
- endpoint: `/memory/${req.params.id}`,
353
- status: 200,
354
- });
355
- res.json(result);
356
- }
357
- catch (e) {
358
- res.status(500).json({ error: e.message });
359
- }
360
- });
361
- app.post("/memory/:id/reinforce", async (req, res) => {
362
- try {
363
- await sdk_reinforce_memory(req.params.id, req.body.boost);
364
- await logActivity("update", {
365
- method: "POST",
366
- endpoint: `/memory/${req.params.id}/reinforce`,
367
- status: 200,
368
- });
369
- res.json({ ok: true });
370
- }
371
- catch (e) {
372
- res.status(500).json({ error: e.message });
373
- }
374
- });
375
- app.delete("/memory/:id", async (req, res) => {
376
- const dbPath = process.env.OM_DB_PATH;
377
- const sqlite3 = await import("sqlite3");
378
- const db = new sqlite3.default.Database(dbPath);
379
- db.run("DELETE FROM memories WHERE id = ?", [req.params.id], async () => {
380
- db.close();
381
- await logActivity("delete", {
382
- method: "DELETE",
383
- endpoint: `/memory/${req.params.id}`,
384
- status: 200,
385
- });
386
- res.json({ ok: true });
387
- });
388
- });
389
- }
390
- // MULTI-SESSION SSE SUPPORT
391
- const sessions = new Map();
392
- // Legacy MCP endpoint - 410 Gone
393
- app.all("/mcp", (req, res) => {
394
- res
395
- .status(410)
396
- .send("Endpoint /mcp is deprecated. Please update your client configuration to use /sse for Server-Sent Events.");
397
154
  });
398
- app.get("/sse", async (req, res) => {
399
- console.error("[MCP] Attempting SSE Connection...");
400
- const transport = new sse_js_1.SSEServerTransport("/message", res);
401
- const newServer = createConfiguredServer((name) => {
402
- const session = sessions.get(transport.sessionId);
403
- if (session)
404
- session.clientName = name;
405
- });
155
+ },
156
+ });
157
+ server.addTool({
158
+ name: "update_memory",
159
+ description: "Update an existing memory's content or tags. At least one must be provided.",
160
+ parameters: zod_1.z
161
+ .object({
162
+ id: zod_1.z.string().describe("Memory ID"),
163
+ content: zod_1.z.string().optional().describe("New content"),
164
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe("New tags"),
165
+ })
166
+ .refine((data) => data.content !== undefined || data.tags !== undefined, {
167
+ message: "Either content or tags must be provided for update",
168
+ path: ["content"],
169
+ }),
170
+ execute: async (args, context) => {
171
+ return requestContext.run({ clientName: context.session?.clientName }, async () => {
406
172
  try {
407
- await newServer.connect(transport);
408
- sessions.set(transport.sessionId, { server: newServer, transport });
409
- transport.onclose = () => {
410
- console.error(`[MCP] SSE Connection Closed: ${transport.sessionId}`);
411
- sessions.delete(transport.sessionId);
412
- };
413
- transport.onerror = (err) => {
414
- console.error(`[MCP] SSE Connection Error: ${transport.sessionId}`, err);
415
- sessions.delete(transport.sessionId);
416
- };
417
- // await transport.start(); // FIXED: connect() starts it automatically
173
+ const res = await (0, hsg_js_1.update_memory)(args.id, args.content, args.tags);
174
+ await logActivity("update_memory");
175
+ return JSON.stringify(res);
418
176
  }
419
177
  catch (err) {
420
- console.error("[MCP] Failed to start SSE transport:", err);
421
- sessions.delete(transport.sessionId);
422
- // If headers haven't been sent, send 500
423
- if (!res.headersSent) {
424
- res.status(500).send("Internal Server Error during SSE handshake");
425
- }
178
+ await logActivity("update_memory", 500);
179
+ throw err;
426
180
  }
427
181
  });
428
- app.post("/message", async (req, res) => {
429
- const sessionId = req.query.sessionId;
430
- const session = sessions.get(sessionId);
431
- if (!session) {
432
- res.status(404).send("Session not found");
433
- return;
182
+ },
183
+ });
184
+ server.addTool({
185
+ name: "reinforce_memory",
186
+ description: "Boost a memory's relevance score to prevent decay.",
187
+ parameters: zod_1.z.object({
188
+ id: zod_1.z.string().describe("Memory ID"),
189
+ boost: zod_1.z
190
+ .number()
191
+ .default(0.1)
192
+ .describe("Relevance boost amount (0.0 to 1.0)"),
193
+ }),
194
+ execute: async (args, context) => {
195
+ return requestContext.run({ clientName: context.session?.clientName }, async () => {
196
+ try {
197
+ await (0, hsg_js_1.reinforce_memory)(args.id, args.boost);
198
+ await logActivity("reinforce_memory");
199
+ return `Memory reinforced: ${args.id}`;
200
+ }
201
+ catch (err) {
202
+ await logActivity("reinforce_memory", 500);
203
+ throw err;
434
204
  }
205
+ });
206
+ },
207
+ });
208
+ server.addTool({
209
+ name: "delete_memory",
210
+ description: "Permanently delete a memory and its associated vectors.",
211
+ parameters: zod_1.z.object({
212
+ id: zod_1.z.string().describe("Memory ID"),
213
+ }),
214
+ execute: async (args, context) => {
215
+ return requestContext.run({ clientName: context.session?.clientName }, async () => {
435
216
  try {
436
- const clientName = session.clientName || "sse-client";
437
- await requestContext.run({ clientName }, async () => {
438
- await session.transport.handlePostMessage(req, res);
439
- });
217
+ await (0, db_js_1.run_async)("DELETE FROM memories WHERE id=?", [args.id]);
218
+ await (0, db_js_1.run_async)("DELETE FROM vectors WHERE id=?", [args.id]);
219
+ await logActivity("delete_memory");
220
+ return "Deleted";
440
221
  }
441
222
  catch (err) {
442
- console.error(`[MCP] Error handling message for session ${sessionId}:`, err);
443
- if (!res.headersSent) {
444
- res.status(500).send("Internal Server Error processing message");
445
- }
223
+ await logActivity("delete_memory", 500);
224
+ throw err;
446
225
  }
447
226
  });
448
- app.listen(port, () => console.error(`CyberMem MCP running on http://localhost:${port}`));
227
+ },
228
+ });
229
+ // START
230
+ async function main() {
231
+ console.error("[INIT] Starting CyberMem MCP...");
232
+ await initialize();
233
+ console.error("[INIT] Database initialized.");
234
+ const argsArr = process.argv.slice(2);
235
+ const getArg = (name) => {
236
+ const idx = argsArr.indexOf(name);
237
+ return idx !== -1 ? argsArr[idx + 1] : undefined;
238
+ };
239
+ const port = parseInt(getArg("--port") || "3100", 10);
240
+ const useHttp = argsArr.includes("--http") || argsArr.includes("--port");
241
+ console.error(`[INIT] Starting ${useHttp ? "HTTP" : "STDIO"} server...`);
242
+ await server.start({
243
+ transportType: useHttp ? "httpStream" : "stdio",
244
+ httpStream: useHttp
245
+ ? {
246
+ port,
247
+ host: "0.0.0.0",
248
+ endpoint: "/mcp",
249
+ stateless: false,
250
+ enableJsonResponse: true,
251
+ }
252
+ : undefined,
253
+ });
254
+ if (useHttp) {
255
+ console.error(`CyberMem MCP running on http://localhost:${port}/mcp`);
449
256
  }
450
257
  else {
451
- // STDIO
452
- const transport = new stdio_js_1.StdioServerTransport();
453
- const server = createConfiguredServer();
454
- server
455
- .connect(transport)
456
- .then(() => console.error("CyberMem MCP connected via STDIO"));
258
+ console.error(`CyberMem MCP ${VALID_VERSION} [STDIO]`);
457
259
  }
458
260
  }
261
+ main().catch((err) => {
262
+ console.error("[CRITICAL] Main Failure:", err);
263
+ process.exit(1);
264
+ });