@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.
Files changed (3) hide show
  1. package/README.md +16 -5
  2. package/dist/bin.js +90 -16
  3. 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). Prebuilt binaries are fetched automatically on
61
- common platforms (macOS x64/arm64, Linux x64/arm64, Windows x64) no compiler
62
- needed. On an unsupported platform/Node ABI, install C/C++ build tools so the
63
- module can compile (Windows: VS Build Tools). Requires Node 20.10.
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
- var NATIVE_BUILD_PACKAGES = "better-sqlite3,tree-sitter";
44
- function upgradeCommandFor(npmMajor) {
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 limited = query.limit !== void 0 ? deduped.slice(0, query.limit) : deduped;
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 = 50;
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
- records: records.map(recordToJson)
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
- records: records.map(recordToJson)
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.0", { isDevBuild: false });
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.0",
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 = '\nThis looks like the native `better-sqlite3` module failed to load \u2014 the\ninstall script that builds it was skipped, so the binary was never produced.\nOn npm v12+ install scripts are opt-in by default; on older npm it\'s usually\n`ignore-scripts=true` hardening. Install globally, allowing the native build,\nthen point Claude Code at the built binary instead of npx:\n npm v12+: npm install -g @prom.codes/memory-mcp --allow-scripts=better-sqlite3\n npm \u2264 11: npm install -g @prom.codes/memory-mcp --ignore-scripts=false --foreground-scripts\n claude mcp add memory -- node "$(npm root -g)/@prom.codes/memory-mcp/dist/bin.js"\nDocs: https://prom.codes/docs/guides/troubleshooting#could-not-locate-the-bindings-file\n';
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prom.codes/memory-mcp",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "description": "prom.codes Memory — persistent, local-first agent memory as an MCP server.",
5
5
  "type": "module",
6
6
  "bin": {