cf-memory-mcp 3.23.0 → 3.24.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.
@@ -434,13 +434,40 @@ class CFMemoryMCP {
434
434
  this.logDebug(`User Agent: ${this.userAgent}`);
435
435
  }
436
436
 
437
+ /**
438
+ * Append a line to the persistent bridge log file if configured.
439
+ * Honors CF_MEMORY_LOG_FILE (explicit path) or CF_MEMORY_LOG=1/true
440
+ * (defaults to ~/.cf-memory/bridge.log). Bounded: rotates when the
441
+ * file exceeds 5MB to prevent runaway disk usage.
442
+ */
443
+ appendBridgeLog(line) {
444
+ try {
445
+ const explicit = process.env.CF_MEMORY_LOG_FILE;
446
+ const enabled = explicit || process.env.CF_MEMORY_LOG === '1' || process.env.CF_MEMORY_LOG === 'true';
447
+ if (!enabled) return;
448
+ const target = explicit || path.join(os.homedir(), '.cf-memory', 'bridge.log');
449
+ const dir = path.dirname(target);
450
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
451
+ // Rotate if file > 5MB.
452
+ try {
453
+ const st = fs.statSync(target);
454
+ if (st.size > 5 * 1024 * 1024) {
455
+ fs.renameSync(target, target + '.1');
456
+ }
457
+ } catch (_) { /* file doesn't exist yet */ }
458
+ fs.appendFileSync(target, line);
459
+ } catch (_) { /* logging failure is non-fatal */ }
460
+ }
461
+
437
462
  /**
438
463
  * Log debug messages to stderr (won't interfere with MCP communication)
439
464
  */
440
465
  logDebug(message) {
466
+ const line = `[DEBUG] ${new Date().toISOString()} ${message}\n`;
441
467
  if (process.env.DEBUG || process.env.MCP_DEBUG) {
442
- process.stderr.write(`[DEBUG] ${new Date().toISOString()} ${message}\n`);
468
+ process.stderr.write(line);
443
469
  }
470
+ this.appendBridgeLog(line);
444
471
  }
445
472
 
446
473
  /**
@@ -448,9 +475,13 @@ class CFMemoryMCP {
448
475
  */
449
476
  logError(message, error = null) {
450
477
  const timestamp = new Date().toISOString();
451
- process.stderr.write(`[ERROR] ${timestamp} ${message}\n`);
478
+ const line1 = `[ERROR] ${timestamp} ${message}\n`;
479
+ process.stderr.write(line1);
480
+ this.appendBridgeLog(line1);
452
481
  if (error && error.stack) {
453
- process.stderr.write(`[ERROR] ${timestamp} ${error.stack}\n`);
482
+ const line2 = `[ERROR] ${timestamp} ${error.stack}\n`;
483
+ process.stderr.write(line2);
484
+ this.appendBridgeLog(line2);
454
485
  }
455
486
  }
456
487
 
@@ -3718,6 +3749,7 @@ Usage:
3718
3749
  npx cf-memory-mcp resume [id] Print the prior resume handoff (markdown)
3719
3750
  npx cf-memory-mcp list List recent handoffs for cwd
3720
3751
  npx cf-memory-mcp checkpoint ["<goal>"] Snapshot current state (keep_open)
3752
+ npx cf-memory-mcp status Show bridge state + server resume availability
3721
3753
  npx cf-memory-mcp --version Show version
3722
3754
  npx cf-memory-mcp --help Show this help
3723
3755
  npx cf-memory-mcp --diagnose Test connectivity and report issues
@@ -3896,6 +3928,98 @@ async function runListCli() {
3896
3928
  }
3897
3929
  }
3898
3930
 
3931
+ async function runStatusCli() {
3932
+ const { flags } = parseCliArgs(process.argv.slice(3));
3933
+ const server = new CFMemoryMCP();
3934
+ server.logDebug = () => {};
3935
+ server.logError = (...a) => process.stderr.write(a.join(' ') + '\n');
3936
+ try {
3937
+ // Local bridge state (no network call required).
3938
+ const meta = server.getRepoMetadata();
3939
+ const cwd = process.env.CF_MEMORY_WATCH_PATH || process.cwd();
3940
+ const diskCachePath = server.getDiskCachePath();
3941
+ const diskCacheExists = diskCachePath && fs.existsSync(diskCachePath);
3942
+ let diskCacheAge = null;
3943
+ if (diskCacheExists) {
3944
+ try {
3945
+ const entry = JSON.parse(fs.readFileSync(diskCachePath, 'utf8'));
3946
+ diskCacheAge = Math.round((Date.now() - new Date(entry.cached_at).getTime()) / 60000);
3947
+ } catch (_) { /* unreadable cache */ }
3948
+ }
3949
+ // Lookup the live worker count of handoffs for this cwd (best-effort).
3950
+ let serverHandoffCount = null;
3951
+ let resumeAvailable = false;
3952
+ let latestGoal = null;
3953
+ if (API_KEY) {
3954
+ try {
3955
+ const args = { resume: true };
3956
+ if (meta.repo_path) args.repo_path = meta.repo_path;
3957
+ if (meta.branch) args.branch = meta.branch;
3958
+ const fake = { params: { name: 'retrieve_context', arguments: {} } };
3959
+ await server.maybeFillProjectId(fake);
3960
+ if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
3961
+ const response = await server.makeRequest({
3962
+ jsonrpc: '2.0',
3963
+ id: `cli-status-${Date.now()}`,
3964
+ method: 'tools/call',
3965
+ params: { name: 'get_context_bootstrap', arguments: args },
3966
+ });
3967
+ const text = response?.result?.content?.[0]?.text;
3968
+ const payload = JSON.parse(text || '{}');
3969
+ serverHandoffCount = Array.isArray(payload.recent_handoffs) ? payload.recent_handoffs.length : 0;
3970
+ resumeAvailable = !!payload.resume_handoff;
3971
+ latestGoal = payload.resume_handoff?.handoff?.goal || null;
3972
+ } catch (err) {
3973
+ // Network unreachable — that's OK, we still have local info.
3974
+ }
3975
+ }
3976
+
3977
+ const status = {
3978
+ version: PACKAGE_VERSION,
3979
+ cwd,
3980
+ repo_path: meta.repo_path || null,
3981
+ branch: meta.branch || null,
3982
+ base_url: BASE_URL,
3983
+ api_key_set: !!API_KEY,
3984
+ disk_cache: diskCacheExists
3985
+ ? { path: diskCachePath, age_minutes: diskCacheAge }
3986
+ : null,
3987
+ server: API_KEY
3988
+ ? { handoff_count: serverHandoffCount, resume_available: resumeAvailable, latest_goal: latestGoal }
3989
+ : { reachable: false, reason: 'CF_MEMORY_API_KEY not set' },
3990
+ };
3991
+
3992
+ if (flags.json) {
3993
+ process.stdout.write(JSON.stringify(status, null, 2) + '\n');
3994
+ process.exit(0);
3995
+ }
3996
+ process.stdout.write(`cf-memory-mcp v${PACKAGE_VERSION}\n`);
3997
+ process.stdout.write(` cwd: ${cwd}\n`);
3998
+ if (meta.repo_path) process.stdout.write(` repo: ${meta.repo_path}\n`);
3999
+ if (meta.branch) process.stdout.write(` branch: ${meta.branch}\n`);
4000
+ process.stdout.write(` server: ${BASE_URL}${API_KEY ? '' : ' (no API key set)'}\n`);
4001
+ if (diskCacheExists) {
4002
+ process.stdout.write(` cache: ${diskCachePath} (${diskCacheAge}m old)\n`);
4003
+ } else {
4004
+ process.stdout.write(` cache: (none)\n`);
4005
+ }
4006
+ if (API_KEY) {
4007
+ if (resumeAvailable) {
4008
+ process.stdout.write(` resume: ✓ available — "${latestGoal || '(no goal)'}"\n`);
4009
+ } else {
4010
+ process.stdout.write(` resume: (no handoff for this context)\n`);
4011
+ }
4012
+ if (serverHandoffCount !== null) {
4013
+ process.stdout.write(` threads: ${serverHandoffCount} recent handoff${serverHandoffCount === 1 ? '' : 's'}\n`);
4014
+ }
4015
+ }
4016
+ process.exit(0);
4017
+ } catch (err) {
4018
+ console.error('status command failed:', err.message);
4019
+ process.exit(1);
4020
+ }
4021
+ }
4022
+
3899
4023
  async function runCheckpointCli() {
3900
4024
  if (!API_KEY) {
3901
4025
  console.error('Error: CF_MEMORY_API_KEY environment variable is required');
@@ -3983,6 +4107,11 @@ if (process.argv[2] === 'checkpoint') {
3983
4107
  return;
3984
4108
  }
3985
4109
 
4110
+ if (process.argv[2] === 'status') {
4111
+ runStatusCli();
4112
+ return;
4113
+ }
4114
+
3986
4115
  if (process.argv.includes('--diagnose')) {
3987
4116
  (async () => {
3988
4117
  console.log(`CF Memory MCP v${PACKAGE_VERSION} - Diagnostics`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cf-memory-mcp",
3
- "version": "3.23.0",
3
+ "version": "3.24.0",
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": {