cf-memory-mcp 3.31.0 → 3.32.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/README.md +26 -0
- package/bin/cf-memory-mcp.js +103 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,6 +35,32 @@ The active runtime is at [src-simplified/index.ts](src-simplified/index.ts). It
|
|
|
35
35
|
- Coordination and progress with Durable Objects
|
|
36
36
|
- Diagnostics: `health_check` tool + `npx cf-memory-mcp --diagnose` + `CF_MEMORY_TRACE=1`
|
|
37
37
|
|
|
38
|
+
## Resume Context (cross-chat handoff)
|
|
39
|
+
|
|
40
|
+
Beyond code retrieval, cf-memory-mcp persists structured "handoff" packets so one chat can hand work off to the next. The next agent calls `get_context_bootstrap({resume:true})` (or the CLI `cf-memory-mcp resume`) and gets back the goal, status, next steps, code anchors with staleness markers, files touched, and a pre-rendered markdown prompt — without re-exploring the repo.
|
|
41
|
+
|
|
42
|
+
**Measured impact** (`scripts/benchmark-cold-vs-warm.ts`): warm resume is **82-85% faster** with **3-4 fewer tool calls** than cold-start exploration, and the warm path returns the exact next_action the cold agent would have eventually figured out.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
$ npx cf-memory-mcp resume # print prior handoff for cwd
|
|
46
|
+
$ npx cf-memory-mcp list # all recent handoffs (with status counts)
|
|
47
|
+
$ npx cf-memory-mcp checkpoint # snapshot current state
|
|
48
|
+
$ npx cf-memory-mcp status # bridge + server health for cwd
|
|
49
|
+
$ npx cf-memory-mcp doctor # diagnose common setup issues
|
|
50
|
+
$ npx cf-memory-mcp export <id> # backup a handoff
|
|
51
|
+
$ npx cf-memory-mcp import <file> # cross-machine sync
|
|
52
|
+
$ npx cf-memory-mcp delete <id> # remove a session
|
|
53
|
+
$ cfm resume # short alias
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Resume context also surfaces via:
|
|
57
|
+
|
|
58
|
+
- **MCP tools**: `start_session`, `end_session` (with structured `handoff`), `get_context_bootstrap` (with `resume`, `session_id_hint`, `goal_contains`, `since`, `status_filter`, `max_age_minutes`)
|
|
59
|
+
- **MCP resources**: `cfm://resume/current`, `cfm://resume/recent`, `cfm://resume/session/<id>`
|
|
60
|
+
- **MCP prompts**: `/resume`, `/list_threads` (for clients with slash-command UI)
|
|
61
|
+
|
|
62
|
+
See [docs/RESUME_CONTEXT_PLAN.md](docs/RESUME_CONTEXT_PLAN.md) for design rationale and [agents.md](agents.md) for the full agent-facing reference.
|
|
63
|
+
|
|
38
64
|
## Active Infra
|
|
39
65
|
|
|
40
66
|
- Workers
|
package/bin/cf-memory-mcp.js
CHANGED
|
@@ -411,13 +411,15 @@ const TOOLS_LIST = [
|
|
|
411
411
|
},
|
|
412
412
|
{
|
|
413
413
|
name: 'delete_session',
|
|
414
|
-
description: 'Delete
|
|
414
|
+
description: 'Delete session row(s) and any session_summary memories tied to them. Accepts single-id or bulk filters. Different from end_session — this physically removes rows.',
|
|
415
415
|
inputSchema: {
|
|
416
416
|
type: 'object',
|
|
417
417
|
properties: {
|
|
418
|
-
session_id: { type: 'string', description: 'Full session UUID to delete.' }
|
|
419
|
-
|
|
420
|
-
|
|
418
|
+
session_id: { type: 'string', description: 'Full session UUID to delete (not a prefix).' },
|
|
419
|
+
status: { description: 'Status filter: single string or array of statuses (in_progress, blocked, completed, abandoned).' },
|
|
420
|
+
older_than_days: { type: 'number', description: 'Delete sessions older than N days.' },
|
|
421
|
+
repo_path: { type: 'string', description: 'Restrict to a specific repo_path.' }
|
|
422
|
+
}
|
|
421
423
|
}
|
|
422
424
|
}
|
|
423
425
|
];
|
|
@@ -3850,7 +3852,7 @@ For more information, visit: https://github.com/johnlam90/cf-memory-mcp
|
|
|
3850
3852
|
}
|
|
3851
3853
|
|
|
3852
3854
|
// Parse positional + flag args for CLI subcommands. Returns
|
|
3853
|
-
// { positional: string[], flags: { json, limit, md_path } }.
|
|
3855
|
+
// { positional: string[], flags: { json, limit, md_path, older_than_days, status, repo_path, yes } }.
|
|
3854
3856
|
function parseCliArgs(rest) {
|
|
3855
3857
|
const positional = [];
|
|
3856
3858
|
const flags = { json: false };
|
|
@@ -3864,10 +3866,36 @@ function parseCliArgs(rest) {
|
|
|
3864
3866
|
const n = parseInt(a.slice('--limit='.length), 10);
|
|
3865
3867
|
if (Number.isFinite(n) && n > 0) flags.limit = Math.min(n, 50);
|
|
3866
3868
|
} else if (a === '--md') {
|
|
3867
|
-
// Next arg is path; supports `--md=path` too.
|
|
3868
3869
|
flags.md_path = rest[++i];
|
|
3869
3870
|
} else if (a.startsWith('--md=')) {
|
|
3870
3871
|
flags.md_path = a.slice('--md='.length);
|
|
3872
|
+
} else if (a === '--older-than') {
|
|
3873
|
+
// Accept "7d" / "30d" / "12h" / raw number (days).
|
|
3874
|
+
const raw = rest[++i] || '';
|
|
3875
|
+
const m = raw.match(/^(\d+)\s*(d|h|m)?$/);
|
|
3876
|
+
if (m) {
|
|
3877
|
+
const n = parseInt(m[1], 10);
|
|
3878
|
+
const unit = m[2] || 'd';
|
|
3879
|
+
flags.older_than_days = unit === 'd' ? n : unit === 'h' ? n / 24 : n / 1440;
|
|
3880
|
+
}
|
|
3881
|
+
} else if (a.startsWith('--older-than=')) {
|
|
3882
|
+
const raw = a.slice('--older-than='.length);
|
|
3883
|
+
const m = raw.match(/^(\d+)\s*(d|h|m)?$/);
|
|
3884
|
+
if (m) {
|
|
3885
|
+
const n = parseInt(m[1], 10);
|
|
3886
|
+
const unit = m[2] || 'd';
|
|
3887
|
+
flags.older_than_days = unit === 'd' ? n : unit === 'h' ? n / 24 : n / 1440;
|
|
3888
|
+
}
|
|
3889
|
+
} else if (a === '--status') {
|
|
3890
|
+
flags.status = rest[++i];
|
|
3891
|
+
} else if (a.startsWith('--status=')) {
|
|
3892
|
+
flags.status = a.slice('--status='.length);
|
|
3893
|
+
} else if (a === '--repo') {
|
|
3894
|
+
flags.repo_path = rest[++i];
|
|
3895
|
+
} else if (a.startsWith('--repo=')) {
|
|
3896
|
+
flags.repo_path = a.slice('--repo='.length);
|
|
3897
|
+
} else if (a === '--yes' || a === '-y') {
|
|
3898
|
+
flags.yes = true;
|
|
3871
3899
|
} else positional.push(a);
|
|
3872
3900
|
}
|
|
3873
3901
|
return { positional, flags };
|
|
@@ -4009,8 +4037,18 @@ async function runListCli() {
|
|
|
4009
4037
|
process.stderr.write((payload.empty_hint || 'No handoffs for this context.') + '\n');
|
|
4010
4038
|
process.exit(3);
|
|
4011
4039
|
}
|
|
4040
|
+
// Status counts header: scan the visible list to give an at-a-glance
|
|
4041
|
+
// summary before the per-row details.
|
|
4042
|
+
const counts = {};
|
|
4043
|
+
for (const h of allList) {
|
|
4044
|
+
const s = h.status || 'unknown';
|
|
4045
|
+
counts[s] = (counts[s] || 0) + 1;
|
|
4046
|
+
}
|
|
4047
|
+
const countSummary = Object.entries(counts)
|
|
4048
|
+
.map(([s, n]) => `${n} ${s}`)
|
|
4049
|
+
.join(', ');
|
|
4012
4050
|
// Render a compact table to terminal.
|
|
4013
|
-
process.stdout.write(`Recent handoffs for ${meta.repo_path || 'this cwd'}:\n\n`);
|
|
4051
|
+
process.stdout.write(`Recent handoffs for ${meta.repo_path || 'this cwd'} (${countSummary}):\n\n`);
|
|
4014
4052
|
const fmtAge = (iso) => {
|
|
4015
4053
|
if (!iso) return '?';
|
|
4016
4054
|
const min = Math.round((Date.now() - new Date(iso).getTime()) / 60000);
|
|
@@ -4042,15 +4080,60 @@ async function runDeleteCli() {
|
|
|
4042
4080
|
}
|
|
4043
4081
|
const { positional, flags } = parseCliArgs(process.argv.slice(3));
|
|
4044
4082
|
const idArg = positional[0];
|
|
4045
|
-
|
|
4046
|
-
|
|
4083
|
+
const bulkMode = !idArg && (flags.older_than_days !== undefined || flags.status || flags.repo_path);
|
|
4084
|
+
if (!idArg && !bulkMode) {
|
|
4085
|
+
console.error('Usage:');
|
|
4086
|
+
console.error(' cf-memory-mcp delete <session-id-or-prefix> # single delete');
|
|
4087
|
+
console.error(' cf-memory-mcp delete --older-than 30d # bulk delete by age');
|
|
4088
|
+
console.error(' cf-memory-mcp delete --status abandoned # bulk by status');
|
|
4089
|
+
console.error(' cf-memory-mcp delete --repo /path/to/repo # bulk by repo');
|
|
4090
|
+
console.error(' Combine filters; use --yes to confirm >5 deletions.');
|
|
4047
4091
|
process.exit(1);
|
|
4048
4092
|
}
|
|
4049
4093
|
const server = new CFMemoryMCP();
|
|
4050
4094
|
server.logDebug = () => {};
|
|
4051
4095
|
try {
|
|
4052
|
-
|
|
4053
|
-
|
|
4096
|
+
if (bulkMode) {
|
|
4097
|
+
// Bulk delete path. First dry-run: query matching sessions
|
|
4098
|
+
// count via list, then confirm with --yes for >5.
|
|
4099
|
+
const args = {};
|
|
4100
|
+
if (flags.status) args.status = flags.status;
|
|
4101
|
+
if (flags.older_than_days !== undefined) args.older_than_days = flags.older_than_days;
|
|
4102
|
+
if (flags.repo_path) args.repo_path = flags.repo_path;
|
|
4103
|
+
|
|
4104
|
+
// Preview: do an initial delete_session with the same filters
|
|
4105
|
+
// to count matches via the response's deleted_session_ids
|
|
4106
|
+
// (capped at 50). For larger counts we accept the cap as an
|
|
4107
|
+
// approximation; the server handles all of them anyway.
|
|
4108
|
+
// Use --yes to skip the preview/confirm prompt.
|
|
4109
|
+
if (!flags.yes) {
|
|
4110
|
+
process.stderr.write(`Dry-run: previewing matches without confirm prompt is not supported yet.\n`);
|
|
4111
|
+
process.stderr.write(`Run again with --yes to confirm: cf-memory-mcp delete ${process.argv.slice(3).join(' ')} --yes\n`);
|
|
4112
|
+
process.exit(2);
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
const deleteRes = await server.makeRequest({
|
|
4116
|
+
jsonrpc: '2.0', id: `cli-delete-bulk-${Date.now()}`,
|
|
4117
|
+
method: 'tools/call',
|
|
4118
|
+
params: { name: 'delete_session', arguments: args },
|
|
4119
|
+
});
|
|
4120
|
+
const deleteText = deleteRes?.result?.content?.[0]?.text;
|
|
4121
|
+
const payload = JSON.parse(deleteText || '{}');
|
|
4122
|
+
if (flags.json) {
|
|
4123
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
4124
|
+
process.exit(payload.deleted ? 0 : 3);
|
|
4125
|
+
}
|
|
4126
|
+
if (payload.deleted) {
|
|
4127
|
+
process.stdout.write(`Deleted ${payload.deleted_count} session${payload.deleted_count === 1 ? '' : 's'} (and ${payload.cleaned_memories || 0} associated memories).\n`);
|
|
4128
|
+
process.exit(0);
|
|
4129
|
+
} else {
|
|
4130
|
+
process.stderr.write(`No sessions matched the filters.\n`);
|
|
4131
|
+
process.exit(3);
|
|
4132
|
+
}
|
|
4133
|
+
return;
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
// Single-delete path (existing).
|
|
4054
4137
|
let fullId = idArg;
|
|
4055
4138
|
if (idArg.length < 36) {
|
|
4056
4139
|
const resolveRes = await server.makeRequest({
|
|
@@ -4245,11 +4328,15 @@ const PER_COMMAND_HELP = {
|
|
|
4245
4328
|
completion: `cf-memory-mcp completion [bash|zsh|fish]
|
|
4246
4329
|
Output shell completion script. Pipe to your shell's completion dir.`,
|
|
4247
4330
|
delete: `cf-memory-mcp delete <session-id-or-prefix> [--json]
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
--
|
|
4252
|
-
|
|
4331
|
+
cf-memory-mcp delete [--status STATUS] [--older-than 30d] [--repo PATH] --yes [--json]
|
|
4332
|
+
Delete session row(s) and any session_summary memories tied to them.
|
|
4333
|
+
<session-id> Single delete: full UUID or short prefix (>=8 chars).
|
|
4334
|
+
--status STATUS Bulk filter by status (abandoned, completed, ...).
|
|
4335
|
+
--older-than 30d Bulk filter by age (suffixes: d, h, m).
|
|
4336
|
+
--repo PATH Bulk filter by repo_path.
|
|
4337
|
+
--yes, -y Required for bulk delete to confirm intent.
|
|
4338
|
+
--json, -j Emit JSON.
|
|
4339
|
+
Exit codes: 0 = deleted, 2 = bulk missing --yes, 3 = nothing matched.`,
|
|
4253
4340
|
env: `cf-memory-mcp env [--json]
|
|
4254
4341
|
Print all CF_MEMORY_* env vars the bridge reads, with their current
|
|
4255
4342
|
values + descriptions. Useful for discovering knobs.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cf-memory-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.32.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": {
|