@memharness/mcp
Advanced tools
+4
-3
@@ -71,5 +71,6 @@ #!/usr/bin/env node | ||
| } | ||
| // Dogfood-gate instrumentation, same shape as the MCP server's. A distinct | ||
| // op keeps hook injections from inflating agent-initiated recall counts. | ||
| if (dbPath !== ":memory:") { | ||
| // Optional local instrumentation, same shape and gating as the MCP server's | ||
| // (off unless MEMHARNESS_DEBUG=1). A distinct op keeps hook injections from | ||
| // inflating agent-initiated recall counts. | ||
| if (process.env.MEMHARNESS_DEBUG === "1" && dbPath !== ":memory:") { | ||
| try { | ||
@@ -76,0 +77,0 @@ appendFileSync(join(dirname(dbPath), "usage.log"), `${JSON.stringify({ op: "context", at: new Date().toISOString(), hits })}\n`); |
+35
-3
@@ -47,2 +47,20 @@ import { MemharnessError } from "@memharness/core"; | ||
| /** | ||
| * Atomicity smell: a single remember should carry one assertion. A long fact made | ||
| * of several sentences or semicolon-joined clauses is a compound briefing that | ||
| * can't be revised piece by piece later. Returns the strongest nudge or null. | ||
| * Conservative thresholds keep it quiet on normal one-or-two-sentence facts. | ||
| */ | ||
| function atomicitySmell(fact) { | ||
| const sentences = fact.split(/[.!?](?:\s|$)/).filter((s) => s.trim().length > 0).length; | ||
| const semicolons = (fact.match(/;/g) ?? []).length; | ||
| if (fact.length > 320 && (sentences >= 3 || semicolons >= 2)) { | ||
| return ("this asserts several things at once — split compound knowledge into one remember " + | ||
| "call per assertion so each can be revised independently."); | ||
| } | ||
| if (fact.length > 280) { | ||
| return "that fact is long — split compound knowledge into separate remember calls so each piece can be revised independently."; | ||
| } | ||
| return null; | ||
| } | ||
| /** | ||
| * Wraps a Memharness instance as an MCP server. logUsage receives the op name | ||
@@ -192,2 +210,11 @@ * only (dogfood-gate instrumentation; never fact content). | ||
| } | ||
| // Contradiction/dedup gate: surface existing current beliefs in the same | ||
| // subject that this fact closely resembles, BEFORE inserting (so it isn't | ||
| // compared to itself). Advisory only — the write still proceeds; the agent | ||
| // decides whether to revise an existing belief instead of forking it. | ||
| const similar = mem.nearDuplicates({ | ||
| subject: args.subject, | ||
| text: args.fact, | ||
| limit: 3, | ||
| }); | ||
| const r = mem.remember({ | ||
@@ -208,6 +235,11 @@ subject: args.subject, | ||
| const nudges = []; | ||
| if (args.fact.length > 280) { | ||
| nudges.push("that fact is long — next time split compound knowledge into separate remember " + | ||
| "calls so each piece can be revised independently."); | ||
| if (similar.length > 0) { | ||
| const list = similar.map((s) => `#${s.id} (sim ${s.similarity.toFixed(2)})`).join(", "); | ||
| const them = similar.length > 1 ? "one of them" : "it"; | ||
| nudges.push(`this closely resembles existing belief(s) in '${args.subject}': ${list} — if it ` + | ||
| `updates or contradicts ${them}, use revise (not a parallel fact) so history stays clean.`); | ||
| } | ||
| const atomicity = atomicitySmell(args.fact); | ||
| if (atomicity) | ||
| nudges.push(atomicity); | ||
| // Code-map smell: a long, path/symbol-dense fact an Explore agent could | ||
@@ -214,0 +246,0 @@ // reconstruct from the repo. Advisory only — never blocks the write. |
+2
-1
| { | ||
| "name": "@memharness/mcp", | ||
| "version": "0.1.1", | ||
| "version": "0.1.2", | ||
| "mcpName": "io.github.las7/memharness", | ||
| "description": "MCP server exposing memharness bi-temporal agent memory: remember, recall, revise, diff, why, forget, stats.", | ||
@@ -5,0 +6,0 @@ "license": "Apache-2.0", |
56352
3.45%878
3.91%10
11.11%