th-memory-mcp 2.2.6 → 2.2.8
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/ARCHITECTURE_v2.md +8 -1
- package/README.md +1 -1
- package/README.th.md +1 -1
- package/design.md +3 -1
- package/dist/db/index.js +1 -1
- package/dist/db/migrations.js +49 -14
- package/package.json +6 -2
package/ARCHITECTURE_v2.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# th-memory-mcp v2 — Architecture & Implementation Specification
|
|
2
2
|
|
|
3
3
|
**Status:** ✅ Released — `th-memory-mcp v2.0.0` is published (npm + Official MCP Registry + Glama).
|
|
4
|
-
**Baseline:** v1.2.2 → **Current:** v2.2.
|
|
4
|
+
**Baseline:** v1.2.2 → **Current:** v2.2.8
|
|
5
5
|
**Primary goal:** evolve th-memory-mcp from a structured local memory MCP into a durable, temporal, conflict-aware, hybrid-retrieval memory engine for AI agents.
|
|
6
6
|
|
|
7
7
|
> **Audience guide:** End users should read [README.md](README.md) (install, tools, usage). This document is the **canonical architecture & agent-rules spec** for developers and AI coding agents — the single source of truth for structure and behavior. The former `design.md` build log has been folded into §40 Implementation Status.
|
|
@@ -1597,5 +1597,12 @@ All previously-deferred future features are implemented. AI-assisted extraction
|
|
|
1597
1597
|
- v2.1.0: implemented and tested locally; publish skipped (superseded by v2.2.0).
|
|
1598
1598
|
- v2.2.0: fully released — tag + GitHub Release, **npm published** (`th-memory-mcp@2.2.0`), Official MCP Registry auto-ingested from npm (manual `mcp-publisher publish` is redundant and errors `duplicate version`), Glama synced. Only `npm publish` + Glama Sync are required.
|
|
1599
1599
|
- v2.2.1: Glama quality fix (server failed to start — better-sqlite3 native binding missing under pnpm 10 on Node 24). Added `pnpm.onlyBuiltDependencies: ["better-sqlite3"]` so pnpm runs the install script that downloads the Node 24 prebuild; bumped better-sqlite3 to `^12.9.0`; added `ip-address@^10.2.0` overrides (npm + pnpm) to clear transitive XSS via `@modelcontextprotocol/sdk`. No code changes. Pending `npm publish` + Glama re-test.
|
|
1600
|
+
- v2.2.2: baseline for benchmark spec v1.0 (retrieval quality harness initial).
|
|
1601
|
+
- v2.2.3: security + performance hardening per `report_checkup.md` (scope-enforced retrieval, graph scope isolation, export/import round-trip, realpath symlink protection, strict import validation, N+1 elimination, cold/ablation benchmark, `MEMORY_RETRIEVAL_MODE` switch — 25/25 tests).
|
|
1602
|
+
- v2.2.4: docs — tidy README badge layout.
|
|
1603
|
+
- v2.2.5: docs — sync README/design/ARCHITECTURE/PUBLISH to project (lexical fuzzy matching, 25 suites, benchmark viewer, result versioning) + SECURITY.md.
|
|
1604
|
+
- v2.2.6: docs — translate all docs to English except `README.th.md` (benchmark/README, METHODOLOGY, repro/README, viewer HTML, PUBLISH checklist).
|
|
1605
|
+
- v2.2.7: bugfix — synced secret filter (6-pattern redact) between Claude hook (`scripts/claude-capture.mjs`) and `capture-core.ts`, fixed `err()` to return `isError:true` per MCP spec (`src/db/index.ts`), fixed backup rotation to backup only when migrations pending + prune to 5 (`src/db/migrations.ts`), added `data/hook-errors.log` logging for SessionEnd distill failures; benchmark — upgraded to v2.3 spec draft (semantic-hard 8 categories, enhanced scope contamination, graph effectiveness, token efficiency @128 budget, scalability profiles quick→extreme up to 10M with resumable generation, ablation with scope+graph, reliability suite).
|
|
1606
|
+
- v2.2.8: bugfix — fixed scope contamination 0.75→0 (critical, GLOBAL leak) and conflict false 0→1 (GLOBAL vs projectId) via `benchmark/suites/temporal.mjs` reset + GLOBAL-exclusion, fixed graph hop1 0.52→1.0 via `includeGraph:true`; benchmark — rescaled profiles 5K/20K/100K/500K/1M (from 10K/100K/1M/5M/10M) for dev machine, viewer upgraded to profiles within version (5K/20K/100K), pre-commit now 2 profiles (quick 5K + normal 20K).
|
|
1600
1607
|
|
|
1601
1608
|
**Packaging note (Glama / Smithery):** these registries build with `pnpm install` + `pnpm run build` on **Node 24** and disable postinstall scripts by default (pnpm 10). Any native dependency (e.g. `better-sqlite3`) MUST be listed in `pnpm.onlyBuiltDependencies` in `package.json`, otherwise its native binary is never fetched and the server crashes at startup with `Could not locate the bindings file`.
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://glama.ai/mcp/servers/worakorn-prince/th-memory-mcp)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
**Status:** v2.2.
|
|
14
|
+
**Status:** v2.2.8 — a temporal, conflict-aware, hybrid-retrieval memory engine. 16 MCP tools, 25 passing test suites. Non-destructive schema migration from v1 (all v1 data preserved). New in v2.2: lifecycle states, temporal validity, conflict/dedup resolution with USER/SESSION/PROJECT/GLOBAL scope, hybrid FTS+vector retrieval (RRF), memory graph, `get_context` assembly, periodic consolidation, and `link_memory` / `merge_memory` / `update_memory` / `import_memory` / `extract_memories`. New in v2.2.3: scope-enforced retrieval, graph scope isolation, export/import round-trip, hardened import path (realpath), strict import validation, N+1 query elimination, cold/ablation benchmark, and `MEMORY_RETRIEVAL_MODE` switch. New in v2.2.7: synced secret filter between Claude hook and capture-core (6-pattern redact instead of line-drop), fixed `err()` to return `isError:true` per MCP spec, fixed backup rotation (backup only when migrations pending + prune to 5 files), and added hook error logging for SessionEnd distill. New in v2.2.8: fixed scope contamination 0.75→0 (critical) and conflict false 0→1 (GLOBAL leak), fixed graph hop1 0.52→1.0 via includeGraph, and rescaled benchmark profiles to 5K/20K/100K/500K/1M (pre-commit now quick 5K + normal 20K only).
|
|
15
15
|
|
|
16
16
|
## Requirements
|
|
17
17
|
|
package/README.th.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
MCP server ความจำระยะยาวสำหรับ OpenCode — เก็บ preferences, lessons, ประวัติการใช้งาน ลง SQLite ไฟล์เดียว (local 100%, ไม่มี external API) เพื่อให้ AI "จำและปรับตัว" กับผู้ใช้ผ่าน context-based learning
|
|
9
9
|
|
|
10
|
-
**สถานะ:** v2.2.
|
|
10
|
+
**สถานะ:** v2.2.8 — engine ความจำแบบ temporal, conflict-aware, hybrid-retrieval 16 MCP tools, 25 ชุดเทสผ่าน อัปเกรด schema แบบ non-destructive จาก v1 (ข้อมูล v1 ทั้งหมดถูกเก็บรักษา) ฟีเจอร์ใหม่ใน v2.2: lifecycle states, temporal validity, การแก้ conflict/dedup พร้อม scope USER/SESSION/PROJECT/GLOBAL, hybrid FTS+vector retrieval (RRF), memory graph, ประกอบ `get_context`, consolidation, และ `link_memory` / `merge_memory` / `update_memory` / `import_memory` / `extract_memories` ใหม่ใน v2.2.3: บังคับ scope ใน retrieval, แยก scope ใน graph, export/import แบบ round-trip, ป้องกัน symlink ด้วย realpath, ตรวจ import เข้มงวด, เลิก N+1 query, benchmark cold/ablation และสวิตช์ `MEMORY_RETRIEVAL_MODE` ใหม่ใน v2.2.7: ซิงค์ secret filter ระหว่าง Claude hook กับ capture-core (redact 6 patterns แทนทิ้งบรรทัด), แก้ `err()` ให้ส่ง `isError:true` ตาม MCP spec, แก้ backup rotation (backup เฉพาะเมื่อมี migration pending + prune เก็บ 5 ไฟล์ล่าสุด) และเพิ่ม hook error logging สำหรับ SessionEnd distill ใหม่ใน v2.2.8: แก้บัค scope contamination 0.75→0 (ร้ายแรง), แก้ conflict false 0→1, ปรับ benchmark rescale 5K/20K/100K/500K/1M และ viewer เทียบ profiles ภายใน version, workflow ก่อน commit เหลือ 2 profiles (quick 5K + normal 20K)
|
|
11
11
|
|
|
12
12
|
> English: [README.md](README.md)
|
|
13
13
|
|
package/design.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
This document is up-to-date with the actual codebase state (after completing all future feature plans except AI-assisted extraction, which was removed). The full specification is at `ARCHITECTURE_v2.md` (canonical spec); this file summarizes the overview and current status for convenience.
|
|
4
4
|
|
|
5
5
|
## Current Status
|
|
6
|
-
- **Version:** `package.json` = `2.2.
|
|
6
|
+
- **Version:** `package.json` = `2.2.8`
|
|
7
7
|
- **MCP tools:** 16 tools (`remember`, `recall`, `get_context`, `link_memory`, `merge_memory`, `update_memory`, `import_memory`, `extract_memories`, `consolidate`, `forget`, `history`, `recent_interactions`, `profile`, `lesson`, `memory_stats`, `export_memory`)
|
|
8
8
|
- **Test suites:** 25 suites passing (0 fail) — run via `npm test` (CI on GitHub Actions)
|
|
9
9
|
|
|
@@ -62,3 +62,5 @@ This document is up-to-date with the actual codebase state (after completing all
|
|
|
62
62
|
- v2.2.4 — docs: tidy README badge layout
|
|
63
63
|
- v2.2.5 — docs: sync README/design/ARCHITECTURE/PUBLISH to project (lexical fuzzy matching, 25 suites, benchmark viewer for 3 versions, result versioning) + SECURITY.md + package files; keep v2.2.3 security hardening
|
|
64
64
|
- v2.2.6 — docs: translate all docs to English except readme.th.md (benchmark/README, benchmark/METHODOLOGY, repro/README, viewer HTML, PUBLISH checklist)
|
|
65
|
+
- v2.2.7 — bugfix: synced secret filter (6-pattern redact) between Claude hook and capture-core, fixed `err()` to return `isError:true` per MCP spec, fixed backup rotation (backup only when migrations pending + prune to 5), added hook error logging for SessionEnd distill; benchmark: upgraded to v2.3 spec (semantic-hard dataset, scope safety, graph effectiveness, token efficiency @128 budget, scalability profiles quick→extreme up to 10M, ablation with scope+graph, resumable generation, resource reporting)
|
|
66
|
+
- v2.2.8 — bugfix: fixed scope contamination 0.75→0 (critical) and conflict false 0→1 (GLOBAL leak), fixed graph hop1 0.52→1.0 via includeGraph; benchmark: rescaled profiles 5K/20K/100K/500K/1M (from 10K/100K/1M/5M/10M) for dev machine, viewer profiles within version upgraded to 5K/20K/100K
|
package/dist/db/index.js
CHANGED
|
@@ -197,5 +197,5 @@ export function ok(text) {
|
|
|
197
197
|
return { content: [{ type: "text", text }] };
|
|
198
198
|
}
|
|
199
199
|
export function err(text) {
|
|
200
|
-
return
|
|
200
|
+
return { content: [{ type: "text", text: `error: ${truncate(text, 300)}` }], isError: true };
|
|
201
201
|
}
|
package/dist/db/migrations.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { copyFileSync } from "node:fs";
|
|
1
|
+
import { copyFileSync, readdirSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
2
3
|
import { embed, serialize } from "../lib/embed.js";
|
|
3
4
|
const M001_schema_meta = {
|
|
4
5
|
id: "001_schema_meta",
|
|
@@ -169,28 +170,62 @@ export const MIGRATIONS = [
|
|
|
169
170
|
M006_scope,
|
|
170
171
|
M007_user,
|
|
171
172
|
];
|
|
172
|
-
|
|
173
|
+
const KEEP_BACKUPS = 5;
|
|
174
|
+
function pruneOldBackups(dbPath) {
|
|
173
175
|
try {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
176
|
+
const dir = dirname(dbPath);
|
|
177
|
+
const base = basename(dbPath);
|
|
178
|
+
const prefix = `${base}.backup-`;
|
|
179
|
+
const entries = readdirSync(dir);
|
|
180
|
+
const backups = entries.filter((n) => n.startsWith(prefix)).sort();
|
|
181
|
+
if (backups.length > KEEP_BACKUPS) {
|
|
182
|
+
for (const name of backups.slice(0, backups.length - KEEP_BACKUPS)) {
|
|
183
|
+
try {
|
|
184
|
+
unlinkSync(join(dir, name));
|
|
185
|
+
}
|
|
186
|
+
catch { }
|
|
183
187
|
}
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
catch { }
|
|
191
|
+
}
|
|
192
|
+
export function runMigrations(db) {
|
|
187
193
|
db.exec(`CREATE TABLE IF NOT EXISTS schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);`);
|
|
188
|
-
|
|
194
|
+
const pending = MIGRATIONS.filter((m) => {
|
|
189
195
|
const applied = db
|
|
190
196
|
.prepare("SELECT value FROM schema_meta WHERE key = ?")
|
|
191
197
|
.get(m.id);
|
|
192
|
-
|
|
193
|
-
|
|
198
|
+
return !applied;
|
|
199
|
+
});
|
|
200
|
+
if (pending.length === 0) {
|
|
201
|
+
try {
|
|
202
|
+
const dbPath = db.name;
|
|
203
|
+
if (dbPath && dbPath !== ":memory:")
|
|
204
|
+
pruneOldBackups(dbPath);
|
|
205
|
+
}
|
|
206
|
+
catch { }
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const shouldBackup = process.env.MEMORY_BACKUP_ON_MIGRATE !== "0" &&
|
|
210
|
+
process.env.MEMORY_BACKUP_ON_MIGRATE !== "false";
|
|
211
|
+
if (shouldBackup) {
|
|
212
|
+
try {
|
|
213
|
+
const dbPath = db.name;
|
|
214
|
+
if (dbPath && dbPath !== ":memory:") {
|
|
215
|
+
const backupPath = `${dbPath}.backup-${Date.now()}`;
|
|
216
|
+
try {
|
|
217
|
+
copyFileSync(dbPath, backupPath);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
console.error("[migrations] failed to create backup, aborting");
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
pruneOldBackups(dbPath);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch { }
|
|
227
|
+
}
|
|
228
|
+
for (const m of pending) {
|
|
194
229
|
const tx = db.transaction(() => {
|
|
195
230
|
m.up(db);
|
|
196
231
|
db.prepare("INSERT INTO schema_meta (key, value) VALUES (?, '1') ON CONFLICT(key) DO UPDATE SET value = '1'").run(m.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "th-memory-mcp",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.8",
|
|
4
4
|
"mcpName": "io.github.worakorn-prince/th-memory-mcp",
|
|
5
5
|
"description": "Adaptive Memory MCP server - SQLite-backed memory for OpenCode",
|
|
6
6
|
"author": "worakorn-prince",
|
|
@@ -35,7 +35,11 @@
|
|
|
35
35
|
"distill": "node dist/distill.js",
|
|
36
36
|
"quickstart": "npm run build && node scripts/quickstart.mjs",
|
|
37
37
|
"benchmark": "node repro/run.mjs",
|
|
38
|
-
"benchmark:
|
|
38
|
+
"benchmark:quick": "node benchmark/run.mjs --profile quick --out benchmark/results",
|
|
39
|
+
"benchmark:normal": "node benchmark/run.mjs --profile normal --out benchmark/results",
|
|
40
|
+
"benchmark:heavy": "node benchmark/run.mjs --profile heavy --out benchmark/results",
|
|
41
|
+
"benchmark:all-profiles": "npm run benchmark:quick && npm run benchmark:normal && npm run benchmark:heavy",
|
|
42
|
+
"benchmark:pre-release": "npm run build && npm test && npm run benchmark:all-profiles",
|
|
39
43
|
"benchmark:viewer": "npx serve . -l 3000"
|
|
40
44
|
},
|
|
41
45
|
"engines": {
|