cf-memory-mcp 3.3.0 → 3.3.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.
Files changed (2) hide show
  1. package/bin/cf-memory-mcp.js +240 -26
  2. package/package.json +2 -2
@@ -17,12 +17,15 @@ const https = require('https');
17
17
  const { URL } = require('url');
18
18
  const os = require('os');
19
19
  const process = require('process');
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+ const crypto = require('crypto');
20
23
 
21
24
  // Configuration
22
- const STREAMABLE_HTTP_URL = 'https://cf-memory-mcp.johnlam90.workers.dev/mcp';
23
- const LEGACY_SERVER_URL = 'https://cf-memory-mcp.johnlam90.workers.dev/mcp/message';
25
+ const STREAMABLE_HTTP_URL = 'https://cf-memory-mcp-simplified.johnlam90.workers.dev/mcp/message';
26
+ const LEGACY_SERVER_URL = 'https://cf-memory-mcp-simplified.johnlam90.workers.dev/mcp/message';
24
27
  const PACKAGE_VERSION = require('../package.json').version;
25
- const TIMEOUT_MS = 30000;
28
+ const TIMEOUT_MS = 60000; // Increased timeout for batch operations
26
29
  const CONNECT_TIMEOUT_MS = 10000;
27
30
 
28
31
  // Get API key from environment variable (will be checked later)
@@ -56,7 +59,7 @@ class CFMemoryMCP {
56
59
  this.logDebug(`Legacy Server URL: ${this.legacyServerUrl}`);
57
60
  this.logDebug(`User Agent: ${this.userAgent}`);
58
61
  }
59
-
62
+
60
63
  /**
61
64
  * Log debug messages to stderr (won't interfere with MCP communication)
62
65
  */
@@ -65,7 +68,7 @@ class CFMemoryMCP {
65
68
  process.stderr.write(`[DEBUG] ${new Date().toISOString()} ${message}\n`);
66
69
  }
67
70
  }
68
-
71
+
69
72
  /**
70
73
  * Log error messages to stderr
71
74
  */
@@ -76,7 +79,7 @@ class CFMemoryMCP {
76
79
  process.stderr.write(`[ERROR] ${timestamp} ${error.stack}\n`);
77
80
  }
78
81
  }
79
-
82
+
80
83
  /**
81
84
  * Start listening for MCP messages on stdin
82
85
  */
@@ -90,7 +93,7 @@ class CFMemoryMCP {
90
93
  process.exit(1);
91
94
  }
92
95
  }
93
-
96
+
94
97
  /**
95
98
  * Test connectivity to the Cloudflare Worker
96
99
  */
@@ -133,37 +136,37 @@ class CFMemoryMCP {
133
136
  }
134
137
  }
135
138
  }
136
-
139
+
137
140
  /**
138
141
  * Process stdio input/output for MCP communication
139
142
  */
140
143
  async processStdio() {
141
144
  let buffer = '';
142
-
145
+
143
146
  // Handle stdin data
144
147
  for await (const chunk of process.stdin) {
145
148
  buffer += chunk;
146
-
149
+
147
150
  // Process complete JSON-RPC messages (one per line)
148
151
  let newlineIndex;
149
152
  while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
150
153
  const line = buffer.slice(0, newlineIndex).trim();
151
154
  buffer = buffer.slice(newlineIndex + 1);
152
-
155
+
153
156
  if (line) {
154
157
  await this.handleMessage(line);
155
158
  }
156
159
  }
157
160
  }
158
-
161
+
159
162
  // Process any remaining buffer content
160
163
  if (buffer.trim()) {
161
164
  await this.handleMessage(buffer.trim());
162
165
  }
163
-
166
+
164
167
  this.logDebug('Stdin closed, shutting down...');
165
168
  }
166
-
169
+
167
170
  /**
168
171
  * Handle a single MCP message
169
172
  */
@@ -171,15 +174,46 @@ class CFMemoryMCP {
171
174
  try {
172
175
  const message = JSON.parse(messageStr);
173
176
  this.logDebug(`Processing message: ${message.method} (id: ${message.id})`);
174
-
177
+
178
+ // Handle lifecycle methods locally
179
+ if (message.method === 'initialize') {
180
+ const response = {
181
+ jsonrpc: '2.0',
182
+ id: message.id,
183
+ result: {
184
+ protocolVersion: '2025-03-26',
185
+ capabilities: {
186
+ tools: {}
187
+ },
188
+ serverInfo: {
189
+ name: 'cf-memory-mcp',
190
+ version: PACKAGE_VERSION
191
+ }
192
+ }
193
+ };
194
+ process.stdout.write(JSON.stringify(response) + '\n');
195
+ return;
196
+ }
197
+
198
+ if (message.method === 'notifications/initialized') {
199
+ this.logDebug('MCP handshake completed');
200
+ return;
201
+ }
202
+
203
+ // Intercept index_project tool call to perform local scanning
204
+ if (message.method === 'tools/call' && message.params && message.params.name === 'index_project') {
205
+ await this.handleIndexProject(message);
206
+ return;
207
+ }
208
+
175
209
  const response = await this.makeRequest(message);
176
-
210
+
177
211
  // Send response to stdout
178
212
  process.stdout.write(JSON.stringify(response) + '\n');
179
-
213
+
180
214
  } catch (error) {
181
215
  this.logError('Error handling message:', error);
182
-
216
+
183
217
  // Send error response
184
218
  const errorResponse = {
185
219
  jsonrpc: '2.0',
@@ -193,16 +227,196 @@ class CFMemoryMCP {
193
227
  process.stdout.write(JSON.stringify(errorResponse) + '\n');
194
228
  }
195
229
  }
196
-
230
+
197
231
  /**
198
- * Make HTTP request to the Cloudflare Worker
232
+ * Handle local project indexing
233
+ */
234
+ async handleIndexProject(message) {
235
+ const { project_path, project_name } = message.params.arguments;
236
+ const resolvedPath = path.resolve(project_path);
237
+ const name = project_name || path.basename(resolvedPath);
238
+
239
+ this.logDebug(`Intercepted index_project for: ${resolvedPath} (${name})`);
240
+
241
+ try {
242
+ // 1. Check/Create Project remotely
243
+ const projectResult = await this.makeRequest({
244
+ jsonrpc: '2.0',
245
+ id: `create-proj-${Date.now()}`,
246
+ method: 'tools/call',
247
+ params: {
248
+ name: 'index_project',
249
+ arguments: {
250
+ project_path: resolvedPath,
251
+ project_name: name
252
+ }
253
+ }
254
+ });
255
+
256
+ if (projectResult.error) {
257
+ throw new Error(`Remote project creation failed: ${projectResult.error.message}`);
258
+ }
259
+
260
+ // Parse the result to get project ID
261
+ let projectId;
262
+ try {
263
+ const content = JSON.parse(projectResult.result.content[0].text);
264
+ projectId = content.project_id;
265
+ } catch (e) {
266
+ // Fallback: try to find existing project if creation didn't return generic ID
267
+ this.logDebug('Could not parse create result, listing projects...');
268
+ const listResp = await this.makeAuthenticatedRequest('/api/projects', 'GET');
269
+ const existing = listResp.projects?.find(p => p.name === name);
270
+ if (existing) {
271
+ projectId = existing.id;
272
+ } else {
273
+ throw new Error('Could not determine project ID');
274
+ }
275
+ }
276
+
277
+ // 2. Scan Local Files
278
+ this.logDebug(`Scanning files in ${resolvedPath}...`);
279
+ const files = this.scanDirectory(resolvedPath);
280
+ this.logDebug(`Found ${files.length} files to index.`);
281
+
282
+ // 3. Batch Upload
283
+ let indexed = 0;
284
+ const BATCH_SIZE = 10;
285
+
286
+ for (let i = 0; i < files.length; i += BATCH_SIZE) {
287
+ const batch = files.slice(i, i + BATCH_SIZE);
288
+ try {
289
+ const payload = {
290
+ files: batch.map(f => ({
291
+ path: f.relativePath,
292
+ content: f.content
293
+ }))
294
+ };
295
+
296
+ await this.makeAuthenticatedRequest(`/api/projects/${projectId}/files/batch`, 'POST', payload);
297
+ indexed += batch.length;
298
+
299
+ // Optional: Send progress notification to client (if supported)
300
+ // this.logDebug(`Indexed ${indexed}/${files.length}`);
301
+ } catch (err) {
302
+ this.logError(`Batch upload failed at index ${i}`, err);
303
+ }
304
+ }
305
+
306
+ // 4. Return success
307
+ const response = {
308
+ jsonrpc: '2.0',
309
+ id: message.id,
310
+ result: {
311
+ content: [{
312
+ type: 'text',
313
+ text: JSON.stringify({
314
+ project_id: projectId,
315
+ files_found: files.length,
316
+ files_indexed: indexed,
317
+ status: 'completed'
318
+ }, null, 2)
319
+ }]
320
+ }
321
+ };
322
+ process.stdout.write(JSON.stringify(response) + '\n');
323
+
324
+ } catch (error) {
325
+ this.logError('Index project failed:', error);
326
+ const response = {
327
+ jsonrpc: '2.0',
328
+ id: message.id,
329
+ error: {
330
+ code: -32000,
331
+ message: `Indexing failed: ${error.message}`
332
+ }
333
+ };
334
+ process.stdout.write(JSON.stringify(response) + '\n');
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Recursive scan
340
+ */
341
+ scanDirectory(dir, basePath = '') {
342
+ let files = [];
343
+ const EXCLUDE_DIRS = ['node_modules', '.git', 'dist', 'build', '.wrangler', 'coverage'];
344
+ const INCLUDE_EXTS = ['.ts', '.tsx', '.js', '.jsx', '.py', '.md', '.sql', '.css', '.html'];
345
+
346
+ try {
347
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
348
+
349
+ for (const entry of entries) {
350
+ const fullPath = path.join(dir, entry.name);
351
+ const relPath = path.join(basePath, entry.name);
352
+
353
+ if (entry.isDirectory()) {
354
+ if (!EXCLUDE_DIRS.includes(entry.name)) {
355
+ files = files.concat(this.scanDirectory(fullPath, relPath));
356
+ }
357
+ } else if (entry.isFile()) {
358
+ if (INCLUDE_EXTS.includes(path.extname(entry.name).toLowerCase())) {
359
+ try {
360
+ const content = fs.readFileSync(fullPath, 'utf8');
361
+ // Skip huge files > 1MB
362
+ if (content.length < 1024 * 1024) {
363
+ files.push({
364
+ relativePath: relPath,
365
+ content: content
366
+ });
367
+ }
368
+ } catch (e) { /* ignore read errors */ }
369
+ }
370
+ }
371
+ }
372
+ } catch (e) {
373
+ this.logError(`Scan failed for ${dir}:`, e);
374
+ }
375
+ return files;
376
+ }
377
+
378
+ /**
379
+ * Helper for Direct API calls (non-JSON-RPC)
380
+ */
381
+ async makeAuthenticatedRequest(endpoint, method, body) {
382
+ return new Promise((resolve, reject) => {
383
+ const serverUrl = 'https://cf-memory-mcp-simplified.johnlam90.workers.dev' + endpoint; // Hardcoded base for simplicity in this helper
384
+ const url = new URL(serverUrl);
385
+ const postData = body ? JSON.stringify(body) : '';
386
+
387
+ const options = {
388
+ hostname: url.hostname,
389
+ port: 443,
390
+ path: url.pathname,
391
+ method: method,
392
+ headers: {
393
+ 'Content-Type': 'application/json',
394
+ 'X-API-Key': process.env.CF_MEMORY_API_KEY || API_KEY
395
+ }
396
+ };
397
+
398
+ const req = https.request(options, (res) => {
399
+ let data = '';
400
+ res.on('data', c => data += c);
401
+ res.on('end', () => {
402
+ try { resolve(JSON.parse(data)); } catch (e) { resolve({}); }
403
+ });
404
+ });
405
+ req.on('error', reject);
406
+ if (postData) req.write(postData);
407
+ req.end();
408
+ });
409
+ }
410
+
411
+ /**
412
+ * Make HTTP request to the Cloudflare Worker (Standard MCP)
199
413
  */
200
414
  async makeRequest(message) {
201
415
  return new Promise((resolve) => {
202
416
  const serverUrl = this.useStreamableHttp ? this.streamableHttpUrl : this.legacyServerUrl;
203
417
  const url = new URL(serverUrl);
204
418
  const postData = JSON.stringify(message);
205
-
419
+
206
420
  const options = {
207
421
  hostname: url.hostname,
208
422
  port: url.port || 443,
@@ -218,7 +432,7 @@ class CFMemoryMCP {
218
432
  },
219
433
  timeout: TIMEOUT_MS
220
434
  };
221
-
435
+
222
436
  const req = https.request(options, (res) => {
223
437
  let body = '';
224
438
 
@@ -243,7 +457,7 @@ class CFMemoryMCP {
243
457
  }
244
458
  });
245
459
  });
246
-
460
+
247
461
  req.on('error', (error) => {
248
462
  resolve({
249
463
  jsonrpc: '2.0',
@@ -255,7 +469,7 @@ class CFMemoryMCP {
255
469
  }
256
470
  });
257
471
  });
258
-
472
+
259
473
  req.on('timeout', () => {
260
474
  req.destroy();
261
475
  resolve({
@@ -268,12 +482,12 @@ class CFMemoryMCP {
268
482
  }
269
483
  });
270
484
  });
271
-
485
+
272
486
  req.write(postData);
273
487
  req.end();
274
488
  });
275
489
  }
276
-
490
+
277
491
  /**
278
492
  * Graceful shutdown
279
493
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cf-memory-mcp",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "Best-in-class MCP (Model Context Protocol) server for AI memory storage with MIRIX-Inspired Specialized Memory Types (Core, Episodic, Semantic, Procedural, Resource, Knowledge Vault), Progressive Disclosure, AI-Powered Summaries, Context Window Optimization, Summary Memory Feature (TL;DR), Enhanced JSON Processing, Temporal Relationship Tracking, AI-Enhanced Context-Aware + Temporal Intelligence, Smart Tool Recommendation Engine, Memory Intelligence Engine, autonomous optimization, A/B testing, self-improving algorithms, intelligent search, smart auto-features, memory collections, project onboarding workflows, advanced lifecycle management, Project Intelligence for seamless agent handoff, and Unified Project Intelligence Tool with 57% performance improvement",
5
5
  "main": "bin/cf-memory-mcp.js",
6
6
  "bin": {
@@ -131,4 +131,4 @@
131
131
  "typescript": "^5.7.3",
132
132
  "wrangler": "^4.21.2"
133
133
  }
134
- }
134
+ }