@prom.codes/memory-mcp 0.15.0 → 0.15.2
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 +16 -5
- package/dist/bin.js +90 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,11 +55,22 @@ work, is a newer version published?). Secrets are rejected on every write.
|
|
|
55
55
|
Your memories never leave your machine (only short query/record text
|
|
56
56
|
transits when embeddings are enabled).
|
|
57
57
|
|
|
58
|
-
## Native modules
|
|
58
|
+
## Native modules — no install script needed
|
|
59
59
|
|
|
60
|
-
Uses `better-sqlite3` (native)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
Uses `better-sqlite3` (native), but **nothing is built on your machine and no
|
|
61
|
+
install script runs**. The addon ships prebuilt in a platform package
|
|
62
|
+
(`@prom.codes/native-<platform>`) listed as an optional dependency: npm picks
|
|
63
|
+
the one matching your `os`/`cpu`/`libc` and installs it by copying files. So a
|
|
64
|
+
hardened npm needs no special handling — `ignore-scripts=true` (a sensible
|
|
65
|
+
policy, and npm v12's default) has nothing left to suppress:
|
|
66
|
+
```bash
|
|
67
|
+
npm install -g @prom.codes/memory-mcp
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Prebuilt for macOS / Linux (glibc + musl) / Windows on x64 + arm64, Node 22, 24,
|
|
71
|
+
25 and 26. **Requires Node ≥ 22** — upstream `better-sqlite3` publishes no
|
|
72
|
+
prebuild for Node 20's ABI. On an unshipped combination the install still
|
|
73
|
+
succeeds and falls back to building from source, which needs install scripts
|
|
74
|
+
allowed (`--allow-scripts=better-sqlite3`) and C/C++ build tools.
|
|
64
75
|
|
|
65
76
|
Docs: https://prom.codes/docs/mcp/memory
|
package/dist/bin.js
CHANGED
|
@@ -40,12 +40,8 @@ import { homedir } from "node:os";
|
|
|
40
40
|
import { join } from "node:path";
|
|
41
41
|
import { fileURLToPath } from "node:url";
|
|
42
42
|
var UPGRADE_BASE = "npm install -g @prom.codes/context-mcp @prom.codes/memory-mcp @prom.codes/saver";
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (npmMajor !== null && npmMajor >= 12) {
|
|
46
|
-
return `${UPGRADE_BASE} --allow-scripts=${NATIVE_BUILD_PACKAGES}`;
|
|
47
|
-
}
|
|
48
|
-
return `${UPGRADE_BASE} --ignore-scripts=false --foreground-scripts`;
|
|
43
|
+
function upgradeCommandFor(_npmMajor) {
|
|
44
|
+
return UPGRADE_BASE;
|
|
49
45
|
}
|
|
50
46
|
var UPGRADE_COMMAND = upgradeCommandFor(null);
|
|
51
47
|
var npmMajorPromise;
|
|
@@ -2692,7 +2688,8 @@ var SqliteMemoryBackend = class {
|
|
|
2692
2688
|
const rows = this.db.prepare(sql).all(...params);
|
|
2693
2689
|
const resolved = resolveScopeChain(rows.map(rowToRecord), query.chain);
|
|
2694
2690
|
const deduped = this.dedupeRecords(resolved);
|
|
2695
|
-
const
|
|
2691
|
+
const offset = query.offset ?? 0;
|
|
2692
|
+
const limited = query.limit !== void 0 ? deduped.slice(offset, offset + query.limit) : deduped.slice(offset);
|
|
2696
2693
|
const bump = this.db.prepare(`UPDATE agent_memory SET use_count = use_count + 1 WHERE id = ?`);
|
|
2697
2694
|
for (const rec of limited) {
|
|
2698
2695
|
bump.run(rec.id);
|
|
@@ -3988,8 +3985,10 @@ function weave(records, options = {}) {
|
|
|
3988
3985
|
|
|
3989
3986
|
// dist/tools.js
|
|
3990
3987
|
var MAX_LIMIT = 100;
|
|
3991
|
-
var DEFAULT_READ_LIMIT =
|
|
3988
|
+
var DEFAULT_READ_LIMIT = 25;
|
|
3992
3989
|
var MAX_VALUE_CHARS = 64 * 1024;
|
|
3990
|
+
var RECORDS_TOKEN_BUDGET = 5e3;
|
|
3991
|
+
var MAX_VALUE_CHARS_IN_VIEW = 4e3;
|
|
3993
3992
|
function textResult(payload) {
|
|
3994
3993
|
return {
|
|
3995
3994
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
|
|
@@ -4013,6 +4012,52 @@ function recordToJson(rec) {
|
|
|
4013
4012
|
updatedAt: rec.updatedAt
|
|
4014
4013
|
};
|
|
4015
4014
|
}
|
|
4015
|
+
function recordToJsonPreview(rec, maxValueChars) {
|
|
4016
|
+
const view = recordToJson(rec);
|
|
4017
|
+
if (rec.value.length > maxValueChars) {
|
|
4018
|
+
const omitted = rec.value.length - maxValueChars;
|
|
4019
|
+
view.value = `${rec.value.slice(0, maxValueChars)}
|
|
4020
|
+
\u2026[+${omitted} chars omitted \u2014 use \`search\` for the full value]`;
|
|
4021
|
+
view.valueTruncated = true;
|
|
4022
|
+
view.valueChars = rec.value.length;
|
|
4023
|
+
}
|
|
4024
|
+
return view;
|
|
4025
|
+
}
|
|
4026
|
+
function boundRecordsForResult(records) {
|
|
4027
|
+
const views = [];
|
|
4028
|
+
let valuesPreviewed = 0;
|
|
4029
|
+
let tokens = 0;
|
|
4030
|
+
let overBudget = false;
|
|
4031
|
+
for (const rec of records) {
|
|
4032
|
+
const view = recordToJsonPreview(rec, MAX_VALUE_CHARS_IN_VIEW);
|
|
4033
|
+
if (view.valueTruncated === true)
|
|
4034
|
+
valuesPreviewed += 1;
|
|
4035
|
+
const cost = estimateTokens(JSON.stringify(view));
|
|
4036
|
+
if (views.length > 0 && tokens + cost > RECORDS_TOKEN_BUDGET) {
|
|
4037
|
+
overBudget = true;
|
|
4038
|
+
break;
|
|
4039
|
+
}
|
|
4040
|
+
views.push(view);
|
|
4041
|
+
tokens += cost;
|
|
4042
|
+
}
|
|
4043
|
+
return { views, shown: views.length, fetched: records.length, valuesPreviewed, overBudget };
|
|
4044
|
+
}
|
|
4045
|
+
function boundedRecordsNote(b, opts) {
|
|
4046
|
+
if (!b.overBudget && b.valuesPreviewed === 0 && b.fetched < opts.limit)
|
|
4047
|
+
return void 0;
|
|
4048
|
+
const parts = [];
|
|
4049
|
+
if (b.overBudget) {
|
|
4050
|
+
parts.push(`Showing ${b.shown} of ${b.fetched} fetched records \u2014 the result was trimmed to stay under the token budget. The \`woven\` block already carries the highest-value facts within its own cap.`);
|
|
4051
|
+
}
|
|
4052
|
+
if (b.valuesPreviewed > 0) {
|
|
4053
|
+
parts.push(`${b.valuesPreviewed} value(s) previewed to ${MAX_VALUE_CHARS_IN_VIEW} chars.`);
|
|
4054
|
+
}
|
|
4055
|
+
if (b.fetched >= opts.limit) {
|
|
4056
|
+
parts.push(`More records may exist beyond limit=${opts.limit}.`);
|
|
4057
|
+
}
|
|
4058
|
+
parts.push(`To see more: narrow with \`types\`, page with \`offset\` (e.g. offset=${(opts.offset ?? 0) + b.shown}), raise \`limit\`, or use \`search\` for specific facts.`);
|
|
4059
|
+
return parts.join(" ");
|
|
4060
|
+
}
|
|
4016
4061
|
function clampLimit(limit, def) {
|
|
4017
4062
|
if (limit === void 0)
|
|
4018
4063
|
return def;
|
|
@@ -4030,7 +4075,9 @@ var scopeEnum = z.enum(MEMORY_SCOPES);
|
|
|
4030
4075
|
var typeEnum = z.enum(MEMORY_TYPES);
|
|
4031
4076
|
var readInput = {
|
|
4032
4077
|
types: z.array(typeEnum).min(1).optional(),
|
|
4033
|
-
limit: z.number().int().positive().max(MAX_LIMIT).optional()
|
|
4078
|
+
limit: z.number().int().positive().max(MAX_LIMIT).optional(),
|
|
4079
|
+
/** Skip the first N resolved records — page through large memory sets. */
|
|
4080
|
+
offset: z.number().int().nonnegative().optional()
|
|
4034
4081
|
};
|
|
4035
4082
|
var writeInput = {
|
|
4036
4083
|
scope: scopeEnum.optional(),
|
|
@@ -4117,19 +4164,23 @@ function registerTools(server, source, hooks = {}) {
|
|
|
4117
4164
|
}));
|
|
4118
4165
|
reg("read", {
|
|
4119
4166
|
title: "Recall agent memory",
|
|
4120
|
-
description: "Read agent memory for this project along the scope chain (project \u2192 workspace \u2192 tenant \u2192 system; narrowest scope wins). Syncs `.prometheus/memories/*.md` first, then returns the resolved records plus a prompt-ready `woven` markdown block (token-capped). Call this at the START of a session or task to recall what earlier sessions learned.",
|
|
4167
|
+
description: "Read agent memory for this project along the scope chain (project \u2192 workspace \u2192 tenant \u2192 system; narrowest scope wins). Syncs `.prometheus/memories/*.md` first, then returns the resolved records plus a prompt-ready `woven` markdown block (token-capped). The `records` array is bounded to a token budget; a `note` explains how to page (`offset`) or narrow (`types`, `search`) when trimmed. Call this at the START of a session or task to recall what earlier sessions learned.",
|
|
4121
4168
|
inputSchema: readInput
|
|
4122
4169
|
}, async (args) => {
|
|
4123
4170
|
const deps = await ready();
|
|
4124
4171
|
const { backend, workspaceRoot, projectId, projectName } = deps;
|
|
4125
4172
|
const mirrorToFiles = !deps.rootIsHomeOrFsRoot;
|
|
4126
4173
|
const limit = clampLimit(args.limit, DEFAULT_READ_LIMIT);
|
|
4174
|
+
const offset = args.offset ?? 0;
|
|
4127
4175
|
const sync = mirrorToFiles ? await syncProjectFiles(backend, { projectId, workspaceRoot }) : { synced: 0, pruned: 0, skipped: [] };
|
|
4128
4176
|
const records = await backend.read({
|
|
4129
4177
|
chain: defaultScopeChain(projectId),
|
|
4130
4178
|
types: args.types,
|
|
4131
|
-
limit
|
|
4179
|
+
limit,
|
|
4180
|
+
offset
|
|
4132
4181
|
});
|
|
4182
|
+
const bounded = boundRecordsForResult(records);
|
|
4183
|
+
const note = boundedRecordsNote(bounded, { limit, offset });
|
|
4133
4184
|
return textResult({
|
|
4134
4185
|
projectId,
|
|
4135
4186
|
projectName,
|
|
@@ -4137,7 +4188,12 @@ function registerTools(server, source, hooks = {}) {
|
|
|
4137
4188
|
projectFilesPruned: sync.pruned,
|
|
4138
4189
|
...sync.skipped.length > 0 ? { skippedFiles: sync.skipped } : {},
|
|
4139
4190
|
woven: weave(records),
|
|
4140
|
-
|
|
4191
|
+
recordsShown: bounded.shown,
|
|
4192
|
+
recordsFetched: bounded.fetched,
|
|
4193
|
+
limit,
|
|
4194
|
+
offset,
|
|
4195
|
+
...note !== void 0 ? { note } : {},
|
|
4196
|
+
records: bounded.views
|
|
4141
4197
|
});
|
|
4142
4198
|
});
|
|
4143
4199
|
reg("write", {
|
|
@@ -4289,11 +4345,17 @@ ${f.value}`);
|
|
|
4289
4345
|
keyContains: args.keyContains,
|
|
4290
4346
|
limit
|
|
4291
4347
|
});
|
|
4348
|
+
const bounded = boundRecordsForResult(records);
|
|
4349
|
+
const note = boundedRecordsNote(bounded, { limit });
|
|
4292
4350
|
return textResult({
|
|
4293
4351
|
projectId,
|
|
4294
4352
|
projectName,
|
|
4295
4353
|
dbPath,
|
|
4296
|
-
|
|
4354
|
+
recordsShown: bounded.shown,
|
|
4355
|
+
recordsFetched: bounded.fetched,
|
|
4356
|
+
limit,
|
|
4357
|
+
...note !== void 0 ? { note } : {},
|
|
4358
|
+
records: bounded.views
|
|
4297
4359
|
});
|
|
4298
4360
|
});
|
|
4299
4361
|
reg("delete", {
|
|
@@ -4437,7 +4499,7 @@ ${f.value}`);
|
|
|
4437
4499
|
embeddingsError = err instanceof Error ? err.message : String(err);
|
|
4438
4500
|
}
|
|
4439
4501
|
}
|
|
4440
|
-
const update = await buildUpdateStatus("@prom.codes/memory-mcp", "0.15.
|
|
4502
|
+
const update = await buildUpdateStatus("@prom.codes/memory-mcp", "0.15.2", { isDevBuild: false });
|
|
4441
4503
|
let recorder;
|
|
4442
4504
|
try {
|
|
4443
4505
|
const scopes = ["project-local", "project", "user"];
|
|
@@ -4519,7 +4581,7 @@ ${f.value}`);
|
|
|
4519
4581
|
// dist/server.js
|
|
4520
4582
|
var SERVER_IDENTITY = {
|
|
4521
4583
|
name: "prometheus-memory-mcp",
|
|
4522
|
-
version: "0.15.
|
|
4584
|
+
version: "0.15.2",
|
|
4523
4585
|
title: "prom.codes Memory"
|
|
4524
4586
|
};
|
|
4525
4587
|
var SERVER_INSTRUCTIONS = "Persistent agent memory for this workspace \u2014 USE IT PROACTIVELY; the user will not tell you to. Protocol:\n1. ONE-TIME: if this workspace has no Prometheus memory rule yet, call memory_setup now (idempotent) so the protocol is installed into the runtime rule files and survives future sessions. (The server also auto-installs it on startup when a project rule file already exists \u2014 memory_setup covers the rest.)\n2. SESSION START: before any non-trivial task, call memory_read to recall facts, decisions and procedures from earlier sessions.\n3. DURING WORK: when the user states a durable preference, decision, correction or project fact, store it with memory_write (semantic for facts, procedural for how-tos) \u2014 without being asked.\n4. LOOK-UP: use memory_search for keyword recall when memory_read is not specific enough.\n5. SESSION END: consolidate what was learned with memory_capture.\nCall memory_status anytime to check what is stored and whether the rule is installed. Never store secrets, API keys or credentials \u2014 such writes are rejected.";
|
|
@@ -4528,7 +4590,19 @@ var SERVER_INSTRUCTIONS = "Persistent agent memory for this workspace \u2014 USE
|
|
|
4528
4590
|
function looksLikeMissingNativeBinding(msg) {
|
|
4529
4591
|
return /bindings file|better_sqlite3\.node|could not locate the bindings|node_module_version|was compiled against a different|invalid elf|\.node['"\s]/i.test(msg);
|
|
4530
4592
|
}
|
|
4531
|
-
var NATIVE_BINDING_HINT =
|
|
4593
|
+
var NATIVE_BINDING_HINT = `
|
|
4594
|
+
The native SQLite module failed to load. Since 0.15.0 the addon ships
|
|
4595
|
+
prebuilt in a platform package (no install script runs), so this almost always
|
|
4596
|
+
means we ship no build for THIS platform + Node combination:
|
|
4597
|
+
you are on ${process.platform}-${process.arch}, Node ${process.versions.node} (ABI v${process.versions.modules})
|
|
4598
|
+
shipped: win32/darwin/linux (glibc+musl) x x64/arm64, on Node 22, 24, 25, 26
|
|
4599
|
+
Most likely fix \u2014 use a supported Node (22+); Node 20 has no prebuilt addon
|
|
4600
|
+
upstream and must compile from source. To build from source instead, allow the
|
|
4601
|
+
install scripts:
|
|
4602
|
+
npm install -g @prom.codes/memory-mcp --allow-scripts=better-sqlite3 # npm v12+
|
|
4603
|
+
npm install -g @prom.codes/memory-mcp --ignore-scripts=false --foreground-scripts # npm <= 11
|
|
4604
|
+
Docs: https://prom.codes/docs/guides/troubleshooting#could-not-locate-the-bindings-file
|
|
4605
|
+
`;
|
|
4532
4606
|
async function main() {
|
|
4533
4607
|
const env = process.env;
|
|
4534
4608
|
const explicitRoot = (env.PROMETHEUS_WORKSPACE_ROOT ?? "").trim();
|