cf-memory-mcp 3.20.0 → 3.22.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 +185 -3
- package/package.json +1 -1
package/bin/cf-memory-mcp.js
CHANGED
|
@@ -3580,12 +3580,17 @@ A portable MCP (Model Context Protocol) server for AI memory storage using Cloud
|
|
|
3580
3580
|
|
|
3581
3581
|
Usage:
|
|
3582
3582
|
npx cf-memory-mcp Start the MCP server
|
|
3583
|
-
npx cf-memory-mcp resume
|
|
3584
|
-
npx cf-memory-mcp
|
|
3583
|
+
npx cf-memory-mcp resume [id] Print the prior resume handoff (markdown)
|
|
3584
|
+
npx cf-memory-mcp list List recent handoffs for cwd
|
|
3585
|
+
npx cf-memory-mcp checkpoint ["<goal>"] Snapshot current state (keep_open)
|
|
3585
3586
|
npx cf-memory-mcp --version Show version
|
|
3586
3587
|
npx cf-memory-mcp --help Show this help
|
|
3587
3588
|
npx cf-memory-mcp --diagnose Test connectivity and report issues
|
|
3588
3589
|
|
|
3590
|
+
Subcommand flags:
|
|
3591
|
+
--json, -j Emit JSON instead of formatted text (resume/list/checkpoint)
|
|
3592
|
+
--limit N, -n N For 'list': max number of handoffs (default 5, max 50)
|
|
3593
|
+
|
|
3589
3594
|
Environment Variables:
|
|
3590
3595
|
CF_MEMORY_API_KEY=<key> Your CF Memory API key (required)
|
|
3591
3596
|
CF_MEMORY_BASE_URL=<url> Override the default deployed worker
|
|
@@ -3598,6 +3603,25 @@ For more information, visit: https://github.com/johnlam90/cf-memory-mcp
|
|
|
3598
3603
|
process.exit(0);
|
|
3599
3604
|
}
|
|
3600
3605
|
|
|
3606
|
+
// Parse positional + flag args for CLI subcommands. Returns
|
|
3607
|
+
// { positional: string[], flags: { json: boolean, limit?: number } }.
|
|
3608
|
+
function parseCliArgs(rest) {
|
|
3609
|
+
const positional = [];
|
|
3610
|
+
const flags = { json: false };
|
|
3611
|
+
for (let i = 0; i < rest.length; i++) {
|
|
3612
|
+
const a = rest[i];
|
|
3613
|
+
if (a === '--json' || a === '-j') flags.json = true;
|
|
3614
|
+
else if (a === '--limit' || a === '-n') {
|
|
3615
|
+
const n = parseInt(rest[++i], 10);
|
|
3616
|
+
if (Number.isFinite(n) && n > 0) flags.limit = Math.min(n, 50);
|
|
3617
|
+
} else if (a.startsWith('--limit=')) {
|
|
3618
|
+
const n = parseInt(a.slice('--limit='.length), 10);
|
|
3619
|
+
if (Number.isFinite(n) && n > 0) flags.limit = Math.min(n, 50);
|
|
3620
|
+
} else positional.push(a);
|
|
3621
|
+
}
|
|
3622
|
+
return { positional, flags };
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3601
3625
|
// Read-only inspection command: print the prior resume handoff to stdout.
|
|
3602
3626
|
// Lets a human see what state is captured without involving an MCP client.
|
|
3603
3627
|
async function runResumeCli() {
|
|
@@ -3605,6 +3629,7 @@ async function runResumeCli() {
|
|
|
3605
3629
|
console.error('Error: CF_MEMORY_API_KEY environment variable is required');
|
|
3606
3630
|
process.exit(1);
|
|
3607
3631
|
}
|
|
3632
|
+
const { positional, flags } = parseCliArgs(process.argv.slice(3));
|
|
3608
3633
|
const server = new CFMemoryMCP();
|
|
3609
3634
|
server.logDebug = () => {};
|
|
3610
3635
|
server.logError = (...a) => process.stderr.write(a.join(' ') + '\n');
|
|
@@ -3614,7 +3639,7 @@ async function runResumeCli() {
|
|
|
3614
3639
|
if (meta.repo_path) args.repo_path = meta.repo_path;
|
|
3615
3640
|
if (meta.branch) args.branch = meta.branch;
|
|
3616
3641
|
// Optional positional session_id argument.
|
|
3617
|
-
const sessionArg =
|
|
3642
|
+
const sessionArg = positional[0];
|
|
3618
3643
|
if (sessionArg) args.session_id_hint = sessionArg;
|
|
3619
3644
|
const fake = { params: { name: 'retrieve_context', arguments: {} } };
|
|
3620
3645
|
await server.maybeFillProjectId(fake);
|
|
@@ -3632,6 +3657,12 @@ async function runResumeCli() {
|
|
|
3632
3657
|
process.exit(2);
|
|
3633
3658
|
}
|
|
3634
3659
|
const payload = JSON.parse(text);
|
|
3660
|
+
// JSON output mode: emit the full payload as a single JSON object.
|
|
3661
|
+
// Scripts can pipe through jq to extract specific fields.
|
|
3662
|
+
if (flags.json) {
|
|
3663
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
3664
|
+
process.exit(payload.resume_handoff ? 0 : 0);
|
|
3665
|
+
}
|
|
3635
3666
|
if (payload.resume_handoff?.handoff) {
|
|
3636
3667
|
if (payload.resume_prompt) {
|
|
3637
3668
|
process.stdout.write(payload.resume_prompt + '\n');
|
|
@@ -3657,11 +3688,162 @@ async function runResumeCli() {
|
|
|
3657
3688
|
}
|
|
3658
3689
|
}
|
|
3659
3690
|
|
|
3691
|
+
async function runListCli() {
|
|
3692
|
+
if (!API_KEY) {
|
|
3693
|
+
console.error('Error: CF_MEMORY_API_KEY environment variable is required');
|
|
3694
|
+
process.exit(1);
|
|
3695
|
+
}
|
|
3696
|
+
const { flags } = parseCliArgs(process.argv.slice(3));
|
|
3697
|
+
const server = new CFMemoryMCP();
|
|
3698
|
+
server.logDebug = () => {};
|
|
3699
|
+
server.logError = (...a) => process.stderr.write(a.join(' ') + '\n');
|
|
3700
|
+
try {
|
|
3701
|
+
const meta = server.getRepoMetadata();
|
|
3702
|
+
const args = { resume: true };
|
|
3703
|
+
if (meta.repo_path) args.repo_path = meta.repo_path;
|
|
3704
|
+
if (meta.branch) args.branch = meta.branch;
|
|
3705
|
+
const fake = { params: { name: 'retrieve_context', arguments: {} } };
|
|
3706
|
+
await server.maybeFillProjectId(fake);
|
|
3707
|
+
if (fake.params.arguments.project_id) args.project_id = fake.params.arguments.project_id;
|
|
3708
|
+
|
|
3709
|
+
const response = await server.makeRequest({
|
|
3710
|
+
jsonrpc: '2.0',
|
|
3711
|
+
id: `cli-list-${Date.now()}`,
|
|
3712
|
+
method: 'tools/call',
|
|
3713
|
+
params: { name: 'get_context_bootstrap', arguments: args },
|
|
3714
|
+
});
|
|
3715
|
+
const text = response?.result?.content?.[0]?.text;
|
|
3716
|
+
const payload = JSON.parse(text || '{}');
|
|
3717
|
+
const allList = Array.isArray(payload.recent_handoffs) ? payload.recent_handoffs : [];
|
|
3718
|
+
const list = flags.limit ? allList.slice(0, flags.limit) : allList;
|
|
3719
|
+
|
|
3720
|
+
if (flags.json) {
|
|
3721
|
+
process.stdout.write(JSON.stringify({
|
|
3722
|
+
repo_path: meta.repo_path,
|
|
3723
|
+
branch: meta.branch,
|
|
3724
|
+
count: list.length,
|
|
3725
|
+
handoffs: list,
|
|
3726
|
+
empty_hint: list.length === 0 ? payload.empty_hint : undefined,
|
|
3727
|
+
}, null, 2) + '\n');
|
|
3728
|
+
process.exit(0);
|
|
3729
|
+
}
|
|
3730
|
+
if (list.length === 0) {
|
|
3731
|
+
process.stdout.write((payload.empty_hint || 'No handoffs for this context.') + '\n');
|
|
3732
|
+
process.exit(0);
|
|
3733
|
+
}
|
|
3734
|
+
// Render a compact table to terminal.
|
|
3735
|
+
process.stdout.write(`Recent handoffs for ${meta.repo_path || 'this cwd'}:\n\n`);
|
|
3736
|
+
const fmtAge = (iso) => {
|
|
3737
|
+
if (!iso) return '?';
|
|
3738
|
+
const min = Math.round((Date.now() - new Date(iso).getTime()) / 60000);
|
|
3739
|
+
if (min < 60) return `${min}m`;
|
|
3740
|
+
if (min < 1440) return `${Math.round(min/60)}h`;
|
|
3741
|
+
return `${Math.round(min/1440)}d`;
|
|
3742
|
+
};
|
|
3743
|
+
for (const h of list) {
|
|
3744
|
+
const shortId = (h.session_id || '').slice(0, 8);
|
|
3745
|
+
const age = fmtAge(h.ended_at || h.started_at);
|
|
3746
|
+
const status = (h.status || 'unknown').padEnd(11);
|
|
3747
|
+
const branch = h.branch ? `[${h.branch}] ` : '';
|
|
3748
|
+
const goal = (h.goal || '(no goal)').slice(0, 80);
|
|
3749
|
+
process.stdout.write(` ${shortId} ${status} ${age.padStart(4)} ago ${branch}${goal}\n`);
|
|
3750
|
+
if (h.next_step) process.stdout.write(` next: ${h.next_step.slice(0, 80)}\n`);
|
|
3751
|
+
}
|
|
3752
|
+
process.stdout.write(`\n(Run "npx cf-memory-mcp resume <id>" to see a specific handoff)\n`);
|
|
3753
|
+
process.exit(0);
|
|
3754
|
+
} catch (err) {
|
|
3755
|
+
console.error('list command failed:', err.message);
|
|
3756
|
+
process.exit(1);
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
|
|
3760
|
+
async function runCheckpointCli() {
|
|
3761
|
+
if (!API_KEY) {
|
|
3762
|
+
console.error('Error: CF_MEMORY_API_KEY environment variable is required');
|
|
3763
|
+
process.exit(1);
|
|
3764
|
+
}
|
|
3765
|
+
const { positional, flags } = parseCliArgs(process.argv.slice(3));
|
|
3766
|
+
const server = new CFMemoryMCP();
|
|
3767
|
+
server.logDebug = () => {};
|
|
3768
|
+
server.logError = (...a) => process.stderr.write(a.join(' ') + '\n');
|
|
3769
|
+
try {
|
|
3770
|
+
// Optional positional goal argument: `cf-memory-mcp checkpoint "<goal text>"`
|
|
3771
|
+
const goalArg = positional.join(' ').trim();
|
|
3772
|
+
const meta = server.getRepoMetadata();
|
|
3773
|
+
// Need a session to attach the handoff to. Use or create the
|
|
3774
|
+
// implicit session for this cwd (creates a new one if no implicit).
|
|
3775
|
+
const sessionId = await server.getOrCreateImplicitSession();
|
|
3776
|
+
if (!sessionId) {
|
|
3777
|
+
console.error('Could not create or find an implicit session for this cwd.');
|
|
3778
|
+
process.exit(1);
|
|
3779
|
+
}
|
|
3780
|
+
// Build handoff: if agent provided a goal, use it; otherwise
|
|
3781
|
+
// synthesize from bridge activity (which is likely sparse since
|
|
3782
|
+
// this is a fresh process).
|
|
3783
|
+
const handoff = server.synthesizeMinimalHandoff();
|
|
3784
|
+
if (goalArg) handoff.goal = goalArg;
|
|
3785
|
+
if (meta.repo_path) handoff.repo_path = meta.repo_path;
|
|
3786
|
+
if (meta.branch) handoff.branch = meta.branch;
|
|
3787
|
+
const fake = { params: { name: 'retrieve_context', arguments: {} } };
|
|
3788
|
+
await server.maybeFillProjectId(fake);
|
|
3789
|
+
if (fake.params.arguments.project_id) handoff.project_id = fake.params.arguments.project_id;
|
|
3790
|
+
|
|
3791
|
+
const response = await server.makeRequest({
|
|
3792
|
+
jsonrpc: '2.0',
|
|
3793
|
+
id: `cli-checkpoint-${Date.now()}`,
|
|
3794
|
+
method: 'tools/call',
|
|
3795
|
+
params: { name: 'end_session', arguments: {
|
|
3796
|
+
session_id: sessionId,
|
|
3797
|
+
keep_open: true,
|
|
3798
|
+
handoff,
|
|
3799
|
+
} },
|
|
3800
|
+
});
|
|
3801
|
+
const text = response?.result?.content?.[0]?.text;
|
|
3802
|
+
const payload = JSON.parse(text || '{}');
|
|
3803
|
+
if (flags.json) {
|
|
3804
|
+
process.stdout.write(JSON.stringify({
|
|
3805
|
+
session_id: sessionId,
|
|
3806
|
+
short_id: sessionId.slice(0, 8),
|
|
3807
|
+
handoff_stored: !!payload.handoff_stored,
|
|
3808
|
+
kept_open: !!payload.kept_open,
|
|
3809
|
+
goal: handoff.goal,
|
|
3810
|
+
repo_path: handoff.repo_path,
|
|
3811
|
+
branch: handoff.branch,
|
|
3812
|
+
resume_command: `npx cf-memory-mcp resume ${sessionId.slice(0, 8)}`,
|
|
3813
|
+
}, null, 2) + '\n');
|
|
3814
|
+
process.exit(payload.handoff_stored ? 0 : 2);
|
|
3815
|
+
}
|
|
3816
|
+
if (payload.handoff_stored) {
|
|
3817
|
+
const shortId = sessionId.slice(0, 8);
|
|
3818
|
+
process.stdout.write(`Checkpoint saved (session ${shortId}).\n`);
|
|
3819
|
+
process.stdout.write(`Goal: ${handoff.goal}\n`);
|
|
3820
|
+
process.stdout.write(`To resume later: npx cf-memory-mcp resume ${shortId}\n`);
|
|
3821
|
+
process.exit(0);
|
|
3822
|
+
}
|
|
3823
|
+
process.stdout.write('Checkpoint did not store a handoff.\n');
|
|
3824
|
+
if (payload.error) process.stdout.write(`Error: ${payload.error}\n`);
|
|
3825
|
+
process.exit(2);
|
|
3826
|
+
} catch (err) {
|
|
3827
|
+
console.error('checkpoint command failed:', err.message);
|
|
3828
|
+
process.exit(1);
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
|
|
3660
3832
|
if (process.argv[2] === 'resume') {
|
|
3661
3833
|
runResumeCli();
|
|
3662
3834
|
return;
|
|
3663
3835
|
}
|
|
3664
3836
|
|
|
3837
|
+
if (process.argv[2] === 'list') {
|
|
3838
|
+
runListCli();
|
|
3839
|
+
return;
|
|
3840
|
+
}
|
|
3841
|
+
|
|
3842
|
+
if (process.argv[2] === 'checkpoint') {
|
|
3843
|
+
runCheckpointCli();
|
|
3844
|
+
return;
|
|
3845
|
+
}
|
|
3846
|
+
|
|
3665
3847
|
if (process.argv.includes('--diagnose')) {
|
|
3666
3848
|
(async () => {
|
|
3667
3849
|
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.22.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": {
|