@cybermem/mcp 0.6.10 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,35 +1,71 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ /**
4
+ * CyberMem MCP Server
5
+ *
6
+ * MCP server for AI agents to interact with CyberMem memory system.
7
+ * Uses openmemory-js SDK directly (no HTTP, embedded SQLite).
8
+ */
3
9
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
10
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
11
  };
6
12
  Object.defineProperty(exports, "__esModule", { value: true });
7
- const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
8
- const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
13
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
9
14
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
10
- const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
11
- const axios_1 = __importDefault(require("axios"));
15
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
12
16
  const cors_1 = __importDefault(require("cors"));
13
17
  const dotenv_1 = __importDefault(require("dotenv"));
14
18
  const express_1 = __importDefault(require("express"));
19
+ const openmemory_js_1 = require("openmemory-js");
20
+ const zod_1 = require("zod");
21
+ const auth_js_1 = require("./auth.js");
15
22
  dotenv_1.default.config();
16
- // Parse CLI args for remote mode
23
+ // Handle CLI auth commands first
17
24
  const args = process.argv.slice(2);
18
- const getArg = (name) => {
19
- const idx = args.indexOf(name);
20
- return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
21
- };
22
- const cliUrl = getArg("--url");
23
- const cliApiKey = getArg("--api-key");
24
- const cliClientName = getArg("--client-name");
25
- // Use CLI args first, then env, then defaults
26
- // Default to local CyberMem backend (via Traefik on port 8626)
27
- const API_URL = cliUrl || process.env.CYBERMEM_URL || "http://localhost:8626/memory";
28
- const API_KEY = cliApiKey || process.env.OM_API_KEY || "";
29
- // Track client name per session
30
- let currentClientName = cliClientName || "cybermem-mcp";
31
- // CyberMem Agent Protocol - instructions sent to clients on handshake
32
- const CYBERMEM_INSTRUCTIONS = `CyberMem is a persistent context daemon for AI agents.
25
+ if (args.includes("--login")) {
26
+ (0, auth_js_1.login)()
27
+ .then(() => process.exit(0))
28
+ .catch((err) => {
29
+ console.error("Login failed:", err.message);
30
+ process.exit(1);
31
+ });
32
+ }
33
+ else if (args.includes("--logout")) {
34
+ (0, auth_js_1.logout)();
35
+ process.exit(0);
36
+ }
37
+ else if (args.includes("--status")) {
38
+ (0, auth_js_1.showStatus)();
39
+ process.exit(0);
40
+ }
41
+ else {
42
+ // Continue with MCP server startup
43
+ startServer();
44
+ }
45
+ async function startServer() {
46
+ // Parse CLI args
47
+ const getArg = (name) => {
48
+ const idx = args.indexOf(name);
49
+ return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
50
+ };
51
+ const cliClientName = getArg("--client-name");
52
+ // Track client name per session (used in tags)
53
+ const currentClientName = cliClientName || "cybermem-mcp";
54
+ // Configure openmemory-js SDK data path
55
+ // Use ~/.cybermem/data/ so db-exporter can mount it
56
+ const homedir = process.env.HOME || process.env.USERPROFILE || "";
57
+ const dataDir = `${homedir}/.cybermem/data`;
58
+ process.env.OM_DB_PATH = `${dataDir}/openmemory.sqlite`;
59
+ // Ensure data directory exists
60
+ const fs = require("fs");
61
+ try {
62
+ fs.mkdirSync(dataDir, { recursive: true });
63
+ }
64
+ catch { }
65
+ // Initialize openmemory-js SDK (embedded SQLite)
66
+ const memory = new openmemory_js_1.Memory();
67
+ // CyberMem Agent Protocol - instructions sent to clients on handshake
68
+ const CYBERMEM_INSTRUCTIONS = `CyberMem is a persistent context daemon for AI agents.
33
69
 
34
70
  PROTOCOL:
35
71
  1. On session start: call query_memory("user context profile") to load persona
@@ -48,240 +84,152 @@ INTEGRITY RULES:
48
84
  - Sync before critical decisions
49
85
  - Last-write-wins for conflicts
50
86
 
51
- For full protocol: https://cybermem.dev/docs/agent-protocol`;
52
- // Short protocol reminder for tool descriptions (derived from main instructions)
53
- const PROTOCOL_REMINDER = "CyberMem Protocol: Store FULL content (no summaries), always include tags [topic, year, source:client-name]. Query 'user context profile' on session start.";
54
- const server = new index_js_1.Server({
55
- name: "cybermem",
56
- version: "0.6.8",
57
- }, {
58
- capabilities: {
59
- tools: {},
60
- resources: {}, // Enable resources for protocol document
61
- },
62
- instructions: CYBERMEM_INSTRUCTIONS,
63
- });
64
- // Register resources handler for protocol document
65
- server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => ({
66
- resources: [
67
- {
68
- uri: "cybermem://protocol",
69
- name: "CyberMem Agent Protocol",
70
- description: "Instructions for AI agents using CyberMem memory system",
71
- mimeType: "text/plain",
87
+ For full protocol: https://docs.cybermem.dev/agent-protocol`;
88
+ // Short protocol reminder for tool descriptions
89
+ const PROTOCOL_REMINDER = "CyberMem Protocol: Store FULL content (no summaries), always include tags [topic, year, source:client-name]. Query 'user context profile' on session start.";
90
+ // Create McpServer instance
91
+ const server = new mcp_js_1.McpServer({
92
+ name: "cybermem",
93
+ version: "0.8.0",
94
+ }, {
95
+ capabilities: {
96
+ tools: {},
97
+ resources: {},
72
98
  },
73
- ],
74
- }));
75
- server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => {
76
- if (request.params.uri === "cybermem://protocol") {
99
+ instructions: CYBERMEM_INSTRUCTIONS,
100
+ });
101
+ // Register resources
102
+ server.registerResource("CyberMem Agent Protocol", "cybermem://protocol", {
103
+ description: "Instructions for AI agents using CyberMem memory system",
104
+ mimeType: "text/plain",
105
+ }, async () => ({
106
+ contents: [
107
+ {
108
+ uri: "cybermem://protocol",
109
+ mimeType: "text/plain",
110
+ text: CYBERMEM_INSTRUCTIONS,
111
+ },
112
+ ],
113
+ }));
114
+ // Register tools using openmemory-js SDK
115
+ server.registerTool("add_memory", {
116
+ description: `Store a new memory in CyberMem. ${PROTOCOL_REMINDER}`,
117
+ inputSchema: zod_1.z.object({
118
+ content: zod_1.z
119
+ .string()
120
+ .describe("Full content with all details - NO truncation or summarization"),
121
+ user_id: zod_1.z.string().optional(),
122
+ tags: zod_1.z
123
+ .array(zod_1.z.string())
124
+ .optional()
125
+ .describe("Always include [topic, year, source:your-client-name]"),
126
+ }),
127
+ }, async (args) => {
128
+ // Add source tag automatically
129
+ const tags = args.tags || [];
130
+ if (!tags.some((t) => t.startsWith("source:"))) {
131
+ tags.push(`source:${currentClientName}`);
132
+ }
133
+ const result = await memory.add(args.content, {
134
+ user_id: args.user_id,
135
+ tags,
136
+ });
77
137
  return {
78
- contents: [
79
- {
80
- uri: "cybermem://protocol",
81
- mimeType: "text/plain",
82
- text: CYBERMEM_INSTRUCTIONS,
83
- },
84
- ],
138
+ content: [{ type: "text", text: JSON.stringify(result) }],
85
139
  };
86
- }
87
- throw new Error(`Unknown resource: ${request.params.uri}`);
88
- });
89
- const tools = [
90
- {
91
- name: "add_memory",
92
- description: `Store a new memory in CyberMem. ${PROTOCOL_REMINDER}`,
93
- inputSchema: {
94
- type: "object",
95
- properties: {
96
- content: {
97
- type: "string",
98
- description: "Full content with all details - NO truncation or summarization",
99
- },
100
- user_id: { type: "string" },
101
- tags: {
102
- type: "array",
103
- items: { type: "string" },
104
- description: "Always include [topic, year, source:your-client-name]",
105
- },
106
- },
107
- required: ["content"],
108
- },
109
- },
110
- {
111
- name: "query_memory",
140
+ });
141
+ server.registerTool("query_memory", {
112
142
  description: `Search for relevant memories. On session start, call query_memory("user context profile") first.`,
113
- inputSchema: {
114
- type: "object",
115
- properties: {
116
- query: { type: "string" },
117
- k: { type: "number", default: 5 },
118
- },
119
- required: ["query"],
120
- },
121
- },
122
- {
123
- name: "list_memories",
124
- description: "List recent memories",
125
- inputSchema: {
126
- type: "object",
127
- properties: {
128
- limit: { type: "number", default: 10 },
129
- },
130
- },
131
- },
132
- {
133
- name: "delete_memory",
134
- description: "Delete a memory by ID",
135
- inputSchema: {
136
- type: "object",
137
- properties: {
138
- id: { type: "string" },
139
- },
140
- required: ["id"],
141
- },
142
- },
143
- {
144
- name: "update_memory",
145
- description: "Update a memory by ID",
146
- inputSchema: {
147
- type: "object",
148
- properties: {
149
- id: { type: "string" },
150
- content: { type: "string" },
151
- tags: { type: "array", items: { type: "string" } },
152
- metadata: { type: "object" },
153
- },
154
- required: ["id"],
155
- },
156
- },
157
- ];
158
- server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
159
- tools,
160
- }));
161
- // Create axios instance
162
- const apiClient = axios_1.default.create({
163
- baseURL: API_URL,
164
- headers: {
165
- Authorization: `Bearer ${API_KEY}`,
166
- },
167
- });
168
- // Helper to get client with context
169
- function getClient(customHeaders = {}) {
170
- // Get client name from MCP protocol (sent during initialize) or fallback to CLI arg
171
- const clientVersion = server.getClientVersion();
172
- const clientName = customHeaders["X-Client-Name"] || clientVersion?.name || currentClientName;
173
- return {
174
- ...apiClient,
175
- get: (url, config) => apiClient.get(url, {
176
- ...config,
177
- headers: { "X-Client-Name": clientName, ...config?.headers },
143
+ inputSchema: zod_1.z.object({
144
+ query: zod_1.z.string(),
145
+ k: zod_1.z.number().default(5),
178
146
  }),
179
- post: (url, data, config) => apiClient.post(url, data, {
180
- ...config,
181
- headers: { "X-Client-Name": clientName, ...config?.headers },
182
- }),
183
- put: (url, data, config) => apiClient.put(url, data, {
184
- ...config,
185
- headers: { "X-Client-Name": clientName, ...config?.headers },
147
+ }, async (args) => {
148
+ const results = await memory.search(args.query, { limit: args.k });
149
+ return {
150
+ content: [{ type: "text", text: JSON.stringify(results) }],
151
+ };
152
+ });
153
+ server.registerTool("list_memories", {
154
+ description: "List recent memories",
155
+ inputSchema: zod_1.z.object({
156
+ limit: zod_1.z.number().default(10),
186
157
  }),
187
- patch: (url, data, config) => apiClient.patch(url, data, {
188
- ...config,
189
- headers: { "X-Client-Name": clientName, ...config?.headers },
158
+ }, async (args) => {
159
+ // Use search with empty query to list recent
160
+ const results = await memory.search("", { limit: args.limit || 10 });
161
+ return {
162
+ content: [{ type: "text", text: JSON.stringify(results) }],
163
+ };
164
+ });
165
+ server.registerTool("delete_memory", {
166
+ description: "Delete a memory by ID",
167
+ inputSchema: zod_1.z.object({
168
+ id: zod_1.z.string(),
190
169
  }),
191
- delete: (url, config) => apiClient.delete(url, {
192
- ...config,
193
- headers: { "X-Client-Name": clientName, ...config?.headers },
170
+ }, async (args) => {
171
+ // openmemory-js doesn't have delete by ID, use wipe for now
172
+ // TODO: Implement delete_by_id in SDK or via direct DB query
173
+ return {
174
+ content: [
175
+ {
176
+ type: "text",
177
+ text: `Delete not yet implemented in SDK. Memory ID: ${args.id}`,
178
+ },
179
+ ],
180
+ };
181
+ });
182
+ server.registerTool("update_memory", {
183
+ description: "Update a memory by ID",
184
+ inputSchema: zod_1.z.object({
185
+ id: zod_1.z.string(),
186
+ content: zod_1.z.string().optional(),
187
+ tags: zod_1.z.array(zod_1.z.string()).optional(),
194
188
  }),
195
- };
196
- }
197
- server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
198
- const { name, arguments: args } = request.params;
199
- try {
200
- switch (name) {
201
- case "add_memory": {
202
- const response = await getClient().post("/add", args);
203
- return {
204
- content: [{ type: "text", text: JSON.stringify(response.data) }],
205
- };
206
- }
207
- case "query_memory": {
208
- const response = await getClient().post("/query", args);
209
- return {
210
- content: [{ type: "text", text: JSON.stringify(response.data) }],
211
- };
212
- }
213
- case "list_memories": {
214
- const limit = args?.limit || 10;
215
- const response = await getClient().get(`/all?l=${limit}`);
216
- return {
217
- content: [{ type: "text", text: JSON.stringify(response.data) }],
218
- };
219
- }
220
- case "delete_memory": {
221
- const { id } = args;
222
- await getClient().delete(`/${id}`);
223
- return { content: [{ type: "text", text: `Memory ${id} deleted` }] };
224
- }
225
- case "update_memory": {
226
- const { id, ...updates } = args;
227
- const response = await getClient().patch(`/${id}`, updates);
228
- return {
229
- content: [{ type: "text", text: JSON.stringify(response.data) }],
230
- };
231
- }
232
- default:
233
- throw new Error(`Unknown tool: ${name}`);
234
- }
235
- }
236
- catch (error) {
189
+ }, async (args) => {
190
+ // TODO: Implement update in SDK
237
191
  return {
238
- content: [{ type: "text", text: `Error: ${error.message}` }],
239
- isError: true,
192
+ content: [
193
+ {
194
+ type: "text",
195
+ text: `Update not yet implemented in SDK. Memory ID: ${args.id}`,
196
+ },
197
+ ],
240
198
  };
241
- }
242
- });
243
- async function run() {
244
- const isSse = process.argv.includes("--sse") || !!process.env.PORT;
245
- if (isSse) {
199
+ });
200
+ // Determine transport mode
201
+ const transportArg = args.find((arg) => arg === "--stdio" || arg === "--http");
202
+ const useHttp = transportArg === "--http" || args.includes("--port");
203
+ if (useHttp) {
204
+ // HTTP mode for testing/development
205
+ const port = parseInt(getArg("--port") || "3100", 10);
246
206
  const app = (0, express_1.default)();
247
207
  app.use((0, cors_1.default)());
248
- const port = process.env.PORT || 8627;
249
- let transport = null;
250
- app.get("/sse", async (req, res) => {
251
- // Extract client name from header
252
- const clientName = req.headers["x-client-name"];
253
- if (clientName) {
254
- currentClientName = clientName;
255
- }
256
- transport = new sse_js_1.SSEServerTransport("/messages", res);
257
- await server.connect(transport);
208
+ app.use(express_1.default.json());
209
+ app.get("/health", (_req, res) => {
210
+ res.json({ ok: true, version: "0.8.0", mode: "sdk" });
258
211
  });
259
- app.post("/messages", async (req, res) => {
260
- // Also check headers on messages
261
- const clientName = req.headers["x-client-name"];
262
- if (clientName) {
263
- currentClientName = clientName;
264
- }
265
- if (transport) {
266
- await transport.handlePostMessage(req, res);
267
- }
268
- else {
269
- res.status(400).send("Session not established");
270
- }
212
+ const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
213
+ sessionIdGenerator: () => crypto.randomUUID(),
271
214
  });
272
- app.listen(port, () => {
273
- console.error(`CyberMem MCP Server running on SSE at http://localhost:${port}`);
274
- console.error(` - SSE endpoint: http://localhost:${port}/sse`);
275
- console.error(` - Message endpoint: http://localhost:${port}/messages`);
215
+ app.all("/mcp", async (req, res) => {
216
+ await transport.handleRequest(req, res, req.body);
217
+ });
218
+ app.all("/sse", async (req, res) => {
219
+ await transport.handleRequest(req, res, req.body);
220
+ });
221
+ server.connect(transport).then(() => {
222
+ app.listen(port, () => {
223
+ console.log(`CyberMem MCP (SDK mode) running on http://localhost:${port}`);
224
+ console.log("Health: /health | MCP: /mcp");
225
+ });
276
226
  });
277
227
  }
278
228
  else {
229
+ // STDIO mode (default for MCP clients)
279
230
  const transport = new stdio_js_1.StdioServerTransport();
280
- await server.connect(transport);
281
- console.error("CyberMem MCP Server running on stdio");
231
+ server.connect(transport).then(() => {
232
+ console.error("CyberMem MCP (SDK mode) connected via STDIO");
233
+ });
282
234
  }
283
235
  }
284
- run().catch((error) => {
285
- console.error("Fatal error running server:", error);
286
- process.exit(1);
287
- });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cybermem/mcp",
3
- "version": "0.6.10",
4
- "description": "CyberMem MCP Server (TypeScript)",
3
+ "version": "0.8.1",
4
+ "description": "CyberMem MCP Server - AI Memory with openmemory-js SDK",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "cybermem-mcp": "./dist/index.js"
@@ -36,10 +36,11 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@modelcontextprotocol/sdk": "^1.0.0",
39
- "axios": "^1.13.2",
40
39
  "cors": "^2.8.5",
41
40
  "dotenv": "^16.0.0",
42
- "express": "^5.2.1"
41
+ "express": "^5.2.1",
42
+ "openmemory-js": "^1.3.2",
43
+ "zod": "^3.25.76"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@types/cors": "^2.8.19",