cf-memory-mcp 3.23.0 → 3.25.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/bin/cf-memory-mcp.js +182 -3
- package/package.json +1 -1
package/bin/cf-memory-mcp.js
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
478
|
+
const line1 = `[ERROR] ${timestamp} ${message}\n`;
|
|
479
|
+
process.stderr.write(line1);
|
|
480
|
+
this.appendBridgeLog(line1);
|
|
452
481
|
if (error && error.stack) {
|
|
453
|
-
|
|
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,9 @@ 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
|
|
3753
|
+
npx cf-memory-mcp clean Delete local disk cache for current cwd
|
|
3754
|
+
npx cf-memory-mcp clean --all Delete ALL local disk caches
|
|
3721
3755
|
npx cf-memory-mcp --version Show version
|
|
3722
3756
|
npx cf-memory-mcp --help Show this help
|
|
3723
3757
|
npx cf-memory-mcp --diagnose Test connectivity and report issues
|
|
@@ -3896,6 +3930,141 @@ async function runListCli() {
|
|
|
3896
3930
|
}
|
|
3897
3931
|
}
|
|
3898
3932
|
|
|
3933
|
+
async function runCleanCli() {
|
|
3934
|
+
const { flags, positional } = parseCliArgs(process.argv.slice(3));
|
|
3935
|
+
const all = positional.includes('--all') || process.argv.includes('--all');
|
|
3936
|
+
const server = new CFMemoryMCP();
|
|
3937
|
+
server.logDebug = () => {};
|
|
3938
|
+
const removed = [];
|
|
3939
|
+
try {
|
|
3940
|
+
if (all) {
|
|
3941
|
+
// Clear ALL disk caches in ~/.cf-memory/.
|
|
3942
|
+
const dir = path.join(os.homedir(), '.cf-memory');
|
|
3943
|
+
if (fs.existsSync(dir)) {
|
|
3944
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
3945
|
+
if (entry.startsWith('handoff-') && entry.endsWith('.json')) {
|
|
3946
|
+
const full = path.join(dir, entry);
|
|
3947
|
+
fs.unlinkSync(full);
|
|
3948
|
+
removed.push(full);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
} else {
|
|
3953
|
+
const p = server.getDiskCachePath();
|
|
3954
|
+
if (p && fs.existsSync(p)) {
|
|
3955
|
+
fs.unlinkSync(p);
|
|
3956
|
+
removed.push(p);
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
if (flags.json) {
|
|
3960
|
+
process.stdout.write(JSON.stringify({ removed }, null, 2) + '\n');
|
|
3961
|
+
process.exit(0);
|
|
3962
|
+
}
|
|
3963
|
+
if (removed.length === 0) {
|
|
3964
|
+
process.stdout.write('(nothing to clean)\n');
|
|
3965
|
+
} else {
|
|
3966
|
+
process.stdout.write(`Removed ${removed.length} cache file${removed.length === 1 ? '' : 's'}:\n`);
|
|
3967
|
+
for (const r of removed) process.stdout.write(` ${r}\n`);
|
|
3968
|
+
}
|
|
3969
|
+
process.exit(0);
|
|
3970
|
+
} catch (err) {
|
|
3971
|
+
console.error('clean command failed:', err.message);
|
|
3972
|
+
process.exit(1);
|
|
3973
|
+
}
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3976
|
+
async function runStatusCli() {
|
|
3977
|
+
const { flags } = parseCliArgs(process.argv.slice(3));
|
|
3978
|
+
const server = new CFMemoryMCP();
|
|
3979
|
+
server.logDebug = () => {};
|
|
3980
|
+
server.logError = (...a) => process.stderr.write(a.join(' ') + '\n');
|
|
3981
|
+
try {
|
|
3982
|
+
// Local bridge state (no network call required).
|
|
3983
|
+
const meta = server.getRepoMetadata();
|
|
3984
|
+
const cwd = process.env.CF_MEMORY_WATCH_PATH || process.cwd();
|
|
3985
|
+
const diskCachePath = server.getDiskCachePath();
|
|
3986
|
+
const diskCacheExists = diskCachePath && fs.existsSync(diskCachePath);
|
|
3987
|
+
let diskCacheAge = null;
|
|
3988
|
+
if (diskCacheExists) {
|
|
3989
|
+
try {
|
|
3990
|
+
const entry = JSON.parse(fs.readFileSync(diskCachePath, 'utf8'));
|
|
3991
|
+
diskCacheAge = Math.round((Date.now() - new Date(entry.cached_at).getTime()) / 60000);
|
|
3992
|
+
} catch (_) { /* unreadable cache */ }
|
|
3993
|
+
}
|
|
3994
|
+
// Lookup the live worker count of handoffs for this cwd (best-effort).
|
|
3995
|
+
let serverHandoffCount = null;
|
|
3996
|
+
let resumeAvailable = false;
|
|
3997
|
+
let latestGoal = null;
|
|
3998
|
+
if (API_KEY) {
|
|
3999
|
+
try {
|
|
4000
|
+
const args = { resume: true };
|
|
4001
|
+
if (meta.repo_path) args.repo_path = meta.repo_path;
|
|
4002
|
+
if (meta.branch) args.branch = meta.branch;
|
|
4003
|
+
const fake = { params: { name: 'retrieve_context', arguments: {} } };
|
|
4004
|
+
await server.maybeFillProjectId(fake);
|
|
4005
|
+
if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
|
|
4006
|
+
const response = await server.makeRequest({
|
|
4007
|
+
jsonrpc: '2.0',
|
|
4008
|
+
id: `cli-status-${Date.now()}`,
|
|
4009
|
+
method: 'tools/call',
|
|
4010
|
+
params: { name: 'get_context_bootstrap', arguments: args },
|
|
4011
|
+
});
|
|
4012
|
+
const text = response?.result?.content?.[0]?.text;
|
|
4013
|
+
const payload = JSON.parse(text || '{}');
|
|
4014
|
+
serverHandoffCount = Array.isArray(payload.recent_handoffs) ? payload.recent_handoffs.length : 0;
|
|
4015
|
+
resumeAvailable = !!payload.resume_handoff;
|
|
4016
|
+
latestGoal = payload.resume_handoff?.handoff?.goal || null;
|
|
4017
|
+
} catch (err) {
|
|
4018
|
+
// Network unreachable — that's OK, we still have local info.
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
const status = {
|
|
4023
|
+
version: PACKAGE_VERSION,
|
|
4024
|
+
cwd,
|
|
4025
|
+
repo_path: meta.repo_path || null,
|
|
4026
|
+
branch: meta.branch || null,
|
|
4027
|
+
base_url: BASE_URL,
|
|
4028
|
+
api_key_set: !!API_KEY,
|
|
4029
|
+
disk_cache: diskCacheExists
|
|
4030
|
+
? { path: diskCachePath, age_minutes: diskCacheAge }
|
|
4031
|
+
: null,
|
|
4032
|
+
server: API_KEY
|
|
4033
|
+
? { handoff_count: serverHandoffCount, resume_available: resumeAvailable, latest_goal: latestGoal }
|
|
4034
|
+
: { reachable: false, reason: 'CF_MEMORY_API_KEY not set' },
|
|
4035
|
+
};
|
|
4036
|
+
|
|
4037
|
+
if (flags.json) {
|
|
4038
|
+
process.stdout.write(JSON.stringify(status, null, 2) + '\n');
|
|
4039
|
+
process.exit(0);
|
|
4040
|
+
}
|
|
4041
|
+
process.stdout.write(`cf-memory-mcp v${PACKAGE_VERSION}\n`);
|
|
4042
|
+
process.stdout.write(` cwd: ${cwd}\n`);
|
|
4043
|
+
if (meta.repo_path) process.stdout.write(` repo: ${meta.repo_path}\n`);
|
|
4044
|
+
if (meta.branch) process.stdout.write(` branch: ${meta.branch}\n`);
|
|
4045
|
+
process.stdout.write(` server: ${BASE_URL}${API_KEY ? '' : ' (no API key set)'}\n`);
|
|
4046
|
+
if (diskCacheExists) {
|
|
4047
|
+
process.stdout.write(` cache: ${diskCachePath} (${diskCacheAge}m old)\n`);
|
|
4048
|
+
} else {
|
|
4049
|
+
process.stdout.write(` cache: (none)\n`);
|
|
4050
|
+
}
|
|
4051
|
+
if (API_KEY) {
|
|
4052
|
+
if (resumeAvailable) {
|
|
4053
|
+
process.stdout.write(` resume: ✓ available — "${latestGoal || '(no goal)'}"\n`);
|
|
4054
|
+
} else {
|
|
4055
|
+
process.stdout.write(` resume: (no handoff for this context)\n`);
|
|
4056
|
+
}
|
|
4057
|
+
if (serverHandoffCount !== null) {
|
|
4058
|
+
process.stdout.write(` threads: ${serverHandoffCount} recent handoff${serverHandoffCount === 1 ? '' : 's'}\n`);
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
process.exit(0);
|
|
4062
|
+
} catch (err) {
|
|
4063
|
+
console.error('status command failed:', err.message);
|
|
4064
|
+
process.exit(1);
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
|
|
3899
4068
|
async function runCheckpointCli() {
|
|
3900
4069
|
if (!API_KEY) {
|
|
3901
4070
|
console.error('Error: CF_MEMORY_API_KEY environment variable is required');
|
|
@@ -3983,6 +4152,16 @@ if (process.argv[2] === 'checkpoint') {
|
|
|
3983
4152
|
return;
|
|
3984
4153
|
}
|
|
3985
4154
|
|
|
4155
|
+
if (process.argv[2] === 'status') {
|
|
4156
|
+
runStatusCli();
|
|
4157
|
+
return;
|
|
4158
|
+
}
|
|
4159
|
+
|
|
4160
|
+
if (process.argv[2] === 'clean') {
|
|
4161
|
+
runCleanCli();
|
|
4162
|
+
return;
|
|
4163
|
+
}
|
|
4164
|
+
|
|
3986
4165
|
if (process.argv.includes('--diagnose')) {
|
|
3987
4166
|
(async () => {
|
|
3988
4167
|
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.
|
|
3
|
+
"version": "3.25.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": {
|