cf-memory-mcp 3.9.8 → 3.9.10

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.
@@ -102,7 +102,8 @@ const TOOLS_LIST = [
102
102
  properties: {
103
103
  project_path: { type: 'string', description: 'Absolute path to the project root directory' },
104
104
  project_name: { type: 'string', description: 'Display name for the project (defaults to directory basename)' },
105
- force_reindex: { type: 'boolean', description: 'If true, wipes existing chunks and rebuilds from scratch. Use only when needed; incremental is much faster.' }
105
+ force_reindex: { type: 'boolean', description: 'If true, wipes existing chunks and rebuilds from scratch. Use only when needed; incremental is much faster.' },
106
+ allow_system_path: { type: 'boolean', description: 'Allow indexing a path outside the current workspace root. Defaults false for safety.' }
106
107
  },
107
108
  required: ['project_path']
108
109
  }
@@ -370,14 +371,6 @@ class CFMemoryMCP {
370
371
  this.useStreamableHttp = true; // Try Streamable HTTP first
371
372
  this.autoWatcher = null; // Background file watcher
372
373
 
373
- // Handle process termination gracefully
374
- process.on('SIGINT', () => this.shutdown('SIGINT'));
375
- process.on('SIGTERM', () => this.shutdown('SIGTERM'));
376
- process.on('uncaughtException', (error) => {
377
- this.logError('Uncaught exception:', error);
378
- this.shutdown('ERROR');
379
- });
380
-
381
374
  // Set up stdio encoding
382
375
  process.stdin.setEncoding('utf8');
383
376
  // Note: stdout.setEncoding doesn't exist on writable streams
@@ -414,6 +407,7 @@ class CFMemoryMCP {
414
407
  async start() {
415
408
  try {
416
409
  this.logDebug('Starting MCP message processing...');
410
+ this.installProcessHandlers();
417
411
 
418
412
  // Pre-warm the HTTPS connection in the background so the first
419
413
  // real tool call doesn't pay the TLS handshake cost.
@@ -436,6 +430,18 @@ class CFMemoryMCP {
436
430
  }
437
431
  }
438
432
 
433
+ installProcessHandlers() {
434
+ if (CFMemoryMCP._processHandlersInstalled) return;
435
+ CFMemoryMCP._processHandlersInstalled = true;
436
+
437
+ process.on('SIGINT', () => this.shutdown('SIGINT'));
438
+ process.on('SIGTERM', () => this.shutdown('SIGTERM'));
439
+ process.on('uncaughtException', (error) => {
440
+ this.logError('Uncaught exception:', error);
441
+ this.shutdown('ERROR');
442
+ });
443
+ }
444
+
439
445
  /**
440
446
  * Resolve the current cwd to a project_id in the background and
441
447
  * cache the result, so the first retrieve_context query that
@@ -1169,7 +1175,7 @@ class CFMemoryMCP {
1169
1175
 
1170
1176
  async handleIndexProject(message) {
1171
1177
  const args = message.params?.arguments || {};
1172
- const { project_path, project_name, include_patterns, exclude_patterns, force_reindex } = args;
1178
+ const { project_path, project_name, include_patterns, exclude_patterns, force_reindex, allow_system_path } = args;
1173
1179
 
1174
1180
  // Boundary validation: bad inputs were producing unhelpful Node errors
1175
1181
  // ("path must be string"). Return a clean MCP error with the hint
@@ -1188,6 +1194,32 @@ class CFMemoryMCP {
1188
1194
 
1189
1195
  const resolvedPath = path.resolve(project_path);
1190
1196
  const name = project_name || path.basename(resolvedPath);
1197
+ const workspaceRoot = (() => {
1198
+ const root = process.env.CF_MEMORY_WATCH_PATH || process.cwd();
1199
+ try { return fs.realpathSync(path.resolve(root)); }
1200
+ catch (_) { return path.resolve(root); }
1201
+ })();
1202
+ const resolvedRealPath = (() => {
1203
+ try { return fs.realpathSync(resolvedPath); }
1204
+ catch (_) { return resolvedPath; }
1205
+ })();
1206
+ const relativeToWorkspace = path.relative(workspaceRoot, resolvedRealPath);
1207
+ const insideWorkspace = relativeToWorkspace && !relativeToWorkspace.startsWith('..') && !path.isAbsolute(relativeToWorkspace);
1208
+ const sameAsWorkspace = relativeToWorkspace === '';
1209
+
1210
+ if (!allow_system_path && !insideWorkspace && !sameAsWorkspace) {
1211
+ process.stdout.write(JSON.stringify({
1212
+ jsonrpc: '2.0',
1213
+ id: message.id,
1214
+ result: { content: [{ type: 'text', text: JSON.stringify({
1215
+ error: 'project_path_outside_workspace',
1216
+ project_path: resolvedPath,
1217
+ workspace_root: workspaceRoot,
1218
+ hint: 'index_project is confined to the current workspace root by default. Run the bridge from that project directory, set CF_MEMORY_WATCH_PATH to that root, or pass allow_system_path:true if you intentionally want to index another readable local directory.',
1219
+ }, null, 2) }] },
1220
+ }) + '\n');
1221
+ return;
1222
+ }
1191
1223
 
1192
1224
  this.logDebug(`Intercepted index_project for: ${resolvedPath} (${name})`);
1193
1225
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cf-memory-mcp",
3
- "version": "3.9.8",
3
+ "version": "3.9.10",
4
4
  "description": "Cloudflare-hosted MCP server for code indexing, retrieval, and assistant memory with a direct remote MCP endpoint and local stdio bridge.",
5
5
  "main": "bin/cf-memory-mcp.js",
6
6
  "bin": {