Releases: studiomeyer-io/local-memory-mcp
Release list
local-memory-mcp v2.4.3
Security + correctness release. 239 → 251 tests. No schema change, no API change.
Fixed — memory_import bypassed the validation every other write path enforces (#29)
Reported by @pmario with a runnable repro.
memoryImport() guarded incoming records with isStr() / asNum() instead of the Zod shapes the interactive tools use, which made the import envelope the one route into the database that skipped every constraint. An envelope could therefore write rows no tool could create: an open-string category, confidence of 999, 50 000 characters of content (embedded before the row was evaluated), an off-enum memoryType, and — the part that matters most — an arbitrary id.
A non-UUID id does not stay inside its row. It becomes the primary key in the embeddings table, a key in the FTS index, and the key of the in-memory vector map, which is only collision-free across record types because ids are UUIDs. It also reaches downstream filenames in tooling built on top of an export. ../x was therefore a path-traversal vector rather than a data-quality wart.
Every record type is now parsed with a Zod shape mirroring the bounds of its interactive counterpart — learnSchema, decideSchema, entityCreateSchema, entityObserveSchema, entityRelateSchema — and every id and foreign key is normalised to a UUID (see below). Failures land in the existing skipped.malformed counter, so an import stays additive and one bad record never aborts the envelope. Unknown fields are still stripped rather than rejected, so an envelope from a newer exporter remains importable by an older server.
The embedding pass now shares those parsed results instead of running its own parallel copy of the guards, which closes a second gap: an oversized field used to be paid for in inference and thrown away afterwards. Schema validity can no longer drift between the two phases. Referential eligibility still cannot be known before the transaction — an observation whose entity is missing, or a row INSERT OR IGNORE drops as a duplicate, is embedded before that is decidable — so a wasted embedding is still possible; it is simply never attached.
LEARNING_CATEGORIES is exported from learn.ts so import and learn() validate against one list rather than two copies that drift.
Ids are canonicalised, not rejected. Refusing a non-UUID id looks safer and is worse. Until v2.4.2 the import accepted any string, so anyone who fed this server through a hand-rolled importer has non-UUID rows in their database today; their next export carries those ids, and a strict import would drop exactly the rows a restore exists to save. A non-UUID id is therefore mapped to an RFC 4122 §4.3 name-based (v5) UUID derived from it. The mapping is deterministic, so re-importing the same envelope stays idempotent and INSERT OR IGNORE still dedupes; and pure, so a foreign key resolves to the same value as the id it points at with no bookkeeping. Ids that are already UUIDs pass through untouched, so a normal export round-trips unchanged. An empty id remains malformed — there is nothing to derive from.
Duplicate ids within one envelope are rejected. Valid UUID syntax is not uniqueness. Two records sharing an id — within a type or across two — collide in the id-keyed vector map, and INSERT OR IGNORE would drop the second row after the first had already been handed its embedding, attaching one record's vector to another record's text. First occurrence wins; every later one counts as malformed.
The last unbounded writes are bounded. profile values and goal reached the meta table with no length check, and profile took its key straight from the envelope. Values are capped, keys must be short and match [A-Za-z0-9_.-]. Each envelope array is capped at 100 000 items, with the overflow reported through skipped.malformed rather than truncated silently.
Changed — English for all user-facing strings (#25, #28)
Also reported and fixed by @pmario. message: values were German while error: values and the tool descriptions in tools/registry.ts were already English — 26 against 26, with 15 of the German ones containing English fragments. All 29 literals across 9 files are now English. No behaviour change. The PR also corrected an assertion in session.test.ts that checked for 'Projekt:' and would otherwise have kept passing while testing nothing.
v2.4.2 — MCPB bundle boot fix
Same-day patch on v2.4.1. The dynamic-version read introduced in 2.4.1 crashed the Claude Desktop (MCPB) bundle at import time — the bundle places code under server/ and did not ship package.json, so the probe threw before the server started. If you downloaded a v2.4.1 .mcpb bundle, replace it with a v2.4.2 one. npm installs of 2.4.1 were unaffected.
build-mcpb.shshipspackage.jsonat the bundle root; the version probe tries both layouts and on total failure advertises0.0.0with a warning instead of dying — a version string can never again kill the boot.- The version-consistency test now also covers
package-lock.jsonand the bundle copy step. - The rewritten
updated_similarregression test seeds a bm25-threshold-reaching corpus so it genuinely detects any reintroduction of the removed fuzzy-merge branch.
v2.4.1 — every version carrier now agrees
Same-day patch on v2.4.0, from a cross-model review pass. v2.4.0 shipped three diverging versions at once: npm said 2.4.0, the MCP handshake advertised a hardcoded 2.3.0, and the MCPB manifest still said 2.2.0 — clients and Claude Desktop bundles each self-identified as older code.
- The server now reads its version from
package.jsonat boot — the handshake can no longer go stale. build-mcpb.shsyncs the bundle manifest frompackage.jsonbefore packing; freshly built bundles self-identify correctly.- A version-consistency test pins every carrier (package.json, server.json ×2, MCPB manifest, no hardcoded server literal), so the next added carrier must be registered there.
- The
learn.tsdocstring no longer describes the fuzzy-merge path removed in 2.4.0, and the legacyupdated_similartest — which accepted either outcome — now pins the new contract: similar content is always added, the shorter original survives.
No functional changes beyond the advertised version. If you installed 2.4.0, the handshake reporting 2.3.0 was cosmetic — the fixes were active.
v2.4.0 — hybrid search actually on, memory_learn no longer destroys data
Both core fixes in this release were contributed by @pmario, who read the whitepaper, evaluated the code against it, and filed four issues with file:line root causes, cross-platform repros, and an explanation of why 233 green tests could not see either bug. This is the kind of contribution maintainers hope for.
Fixed
- Vector search never loaded (#23, #24 by @pmario).
require('sqlite-vec')threwReferenceErroron every boot under"type": "module", and the catch misreported it as a missing platform binary — hybrid retrieval was silently FTS5-only for every install since release. Now loads viacreateRequire. On boot you should see[vector] sqlite-vec extension loaded;memory_healthreportsvector.enabled: true. memory_learncould silently overwrite an unrelated learning (#21, #22 by @pmario). The FTS5 similarity gate OR-ed every token of the new content, so ordinary words matched almost any stored row;bm25()is an unbounded relevance score, not a similarity, so the remaining decision was effectively "is the new entry longer?". The fuzzy-merge branch is removed: exact duplicates still bumpusage_count, and enriching an existing entry is explicit viamemory_learn_update. A regression test now asserts that storing one learning never removes or rewrites another.- Windows:
npm testandnpm run buildwork (env-var prefix andcp/mkdir -p/chmodreplaced withvitest.config.tsandscripts/copy-assets.mjs) (#22). - README: the SessionStart hook example was missing
hookEventName, so Claude Code silently dropped the injected context (#20 by @pmario). - README/WHITEPAPER no longer describe a similarity measure the code never had.
Upgrading
npm i -g @studiomeyer/local-memory-mcp@2.4.0 (or bump your npx pin). No schema migration; existing databases work as-is. If a learning of yours was previously overwritten, the data is unfortunately not recoverable — this class of write is exactly what this release removes.
v1.0.8 — trust + adoption sweep
🎉 New: One-click install via MCPB bundle for Claude Desktop on Linux x64. Download
local-memory-mcp-1.0.8-linux-x64.mcpbfrom the assets below, double-click, done. No JSON editing, nonpm install, no terminal. macOS / Windows users: keep using thenpx -yinstall path — it rebuilds the SQLite native binary for your platform automatically.
Trust + adoption polish based on an outside-the-fleet audit
Three version drifts and one architectural omission. All small fixes with disproportionate trust impact for a project that is small but used.
Quickstart
npm install -g @studiomeyer/local-memory-mcp
local-memory-mcpAdd to Claude Desktop, Claude Code, Cursor, Codex, Continue, or any MCP client — one SQLite file under your OS data dir, no cloud, no API keys.
Fixed — Version drift sweep
src/server.tshardcodedSERVER_VERSION = '1.0.6'whilepackage.jsonwas at v1.0.7. Every MCPinitializeresponse saw the wrong version. Now reads1.0.8consistently.server.json(MCP Registry manifest) was at1.0.1— six releases behind. Bumped to1.0.8.package.jsonbumped to1.0.8.
Added — Four tools that already had handlers + tests, but were unreachable
entityCreate, entityDelete, goal, and health were exported, had Zod schemas, had unit-test coverage — but were missing from the TOOLS array. MCP clients calling tools/list got 13 tools instead of 17. Now registered:
memory_entity_create— explicit entity creation (idempotent onname + entityType)memory_entity_delete— destructive entity removal (entity + observations + relations)memory_goal— read / set / clear a user goal in the profile tablememory_health— SQLite integrity + page-count + DB-size + WAL status (zero input)
TOOLS.length is now 17. Updated the registry drift-test count assertion + added a new test that pins the four formerly-orphan tools by name so this class of drift cannot recur silently.
Added — CI workflow
.github/workflows/test.yml runs tsc --noEmit + npm test + npm run build on Node 20 / 22 / 24 for every push + PR to main. The repo previously had only the tag-driven publish-registry.yml — no green-on-PR signal for contributors.
Removed
- Duplicate root
CONTRIBUTING.md(canonical guide is.github/CONTRIBUTING.md). bun.lock— two lockfiles is a known drift source.package-lock.jsonstays as source of truth.
Notes
- 88/88 tests green, tsc clean, build clean.
- No API breakage. The four newly-registered tools were already callable as importable functions; they are now also reachable over MCP.
- Recommended upgrade for everyone on 1.0.x — wire-version is finally correct.
What's unique about this project
We're the only relevant local-memory MCP player with zero external dependencies beyond SQLite (no embedding model, no vector DB, no Docker, no API key), MIT-licensed knowledge graph (Mem0 has Graph behind Pro paywall, Zep / Cognee are cloud-only), and decision-tracking as a first-class surface (memory_decide — unique among memory servers).
— StudioMeyer, Palma de Mallorca
v1.0.7 — entitySearch fix + obs_au trigger + 71 new tests
Correctness patch + test expansion. No API changes.
Fix
memory_entity_search silently lost summary and observation hits.
The primary FTS5 query joined search_fts to entities with an OR that mixed entity-level and observation-level matches. That puts the MATCH subquery behind a derived JOIN, which SQLite rejects with "unable to use function bm25 in the requested context". The try/catch caught it and fell back to WHERE name LIKE ? — so any hit whose match lived in the entity summary or in an observation disappeared.
Rewrite: two MATCH-ed subqueries UNION ALL'd by entity_id, then JOIN entities + GROUP BY with MIN(rank). The fallback LIKE now also covers summary and observations.content via a LEFT JOIN, so an FTS outage still returns sensible rows.
Add
obs_autrigger onentity_observationsso anUPDATEto an observation'scontentrefreshes the FTS5 row instead of leaving a stale one pointing at the old text. Symmetric with the existing triggers onlearnings/decisions/entities.
Tests
Expanded from 12 → 83 tests (+71). New files:
src/tools/entity.test.ts— 35 tests coveringentity_create,entity_observe,entity_search,entity_open,entity_relate,entity_delete. Exercises the UNION-ALL rewrite, theobs_autrigger, and every zod-schema edge case.src/tools/session.test.ts— 19 tests coveringsession_start/session_end, same-project ordering, the auto-close-latest-open path, archived-learning exclusion inrecentLearnings.src/tools/search.test.ts— 17 tests covering cross-type hits (learning / decision / entity / observation), type filters, the v1.0.6 archived-learning regression guard, bare-LIMIT semantics.
All 83 tests green, tsc --noEmit clean.
Install
npx -y @studiomeyer/local-memory-mcp