{
  "markdown": "# memharness\n\n<!-- mcp-name: io.github.las7/memharness -->\n\n**A bi-temporal, provenance-carrying memory primitive for AI agents.** One\nSQLite file. No LLM or network calls in the storage layer. Exposed to any agent\nvia MCP.\n\nMost agent memory is a bag of strings. memharness stores **facts**, and\ncombines three semantics that incumbents tend to split apart:\n\n1. **Bi-temporal**: every fact records *when it became true in the world*\n   (`valid_from`/`valid_to`) separately from *when the agent learned it*\n   (`tx_at`). So you can ask: *\"what did you believe on March 1st?\"*\n2. **Supersession, never deletion**: corrections close the old fact and link it\n   to its successor. *\"What did you think before I corrected you?\"* has an\n   answer.\n3. **Provenance per fact**: every memory cites who said it, where, and when.\n   *\"Why do you believe that?\"* has an answer. So does *\"forget everything from\n   that session.\"*\n\nThe storage layer is deterministic: no LLM, no network, no background daemon.\nIt's plain SQLite, so you can open the file with any client.\n\n<p align=\"center\">\n  <img src=\"assets/demo.gif\" alt=\"An agent learns a deploy target, the user corrects it weeks later, and recall / as_of / why / diff explain what was believed when.\" width=\"840\">\n</p>\n\n<p align=\"center\"><em>Run it yourself: <code>cd examples &amp;&amp; npm install &amp;&amp; npm run demo</code></em></p>\n\n## When to use this (and when not to)\n\nmemharness is not a magic accuracy upgrade, and it is honest about that. If your\nagent's memory is small and static and comfortably fits the context window, a\n`CLAUDE.md` file (or just stuffing the history into the prompt) is simpler, and\non short histories full context will match or beat any external memory system.\n\nReach for memharness when:\n\n- **History outgrows the window**: months of facts, many subjects, more than you\n  want to (or can) paste into every prompt.\n- **You need an audit trail**: *\"what did the agent believe when it made this\n  decision?\"* (`as_of`), *\"what changed since Monday?\"* (`diff`), *\"why does it\n  believe this?\"* (`why`). These are queries a bag of strings cannot answer.\n- **You need provenance-scoped deletion**: *\"forget everything from that\n  session/file/source\"* in one call (GDPR-shaped, not a string search).\n- **Beliefs change over time**: corrections should supersede, not silently\n  overwrite, so old reasoning stays explainable.\n\n## How it compares\n\nHonest, and pointed at the thing memharness actually does differently: it is a\ndeterministic, auditable storage layer rather than an extraction service.\n\n| | Storage | LLM calls to **write** | `as_of` / `diff` / `why` | Embeddable / self-host |\n|---|---|---|---|---|\n| **memharness** | one SQLite file | none | yes: bi-temporal + provenance | yes, it's a library |\n| mem0 | hosted / OSS service | yes (extraction pipeline) | partial / no | partial |\n| Zep / Graphiti | hosted graph | yes (LLM ingestion) | bi-temporal, but LLM-built | partial |\n| Letta / MemGPT | agent framework + DB | yes (agent-managed) | no | yes |\n| Anthropic memory tool | client-side files | model edits files | no (model picks) | yes |\n| plain `CLAUDE.md` / files | text files | none | no | yes |\n\nWhere the others win, plainly: mem0 and Zep do **automatic fact extraction**\nfrom raw conversation, which memharness deliberately does not (the write path\nstays model-free; a client or skill decides what is worth remembering). Plain\n`CLAUDE.md` needs no install at all. memharness earns its place when you need the\ntemporal and provenance queries the others don't offer.\n\n## Packages\n\n| Package | What it is |\n|---|---|\n| `@memharness/core` | TypeScript library: schema, migrations, write path, recall ranking. No model, no network. |\n| `@memharness/mcp` | MCP server (stdio) exposing the seven tools to any MCP client. |\n| `@memharness/embed` | Optional. A local embedding model for hybrid (semantic) recall. Not installed by default. |\n\n## Quick start (MCP)\n\nThe default install is small (SQLite plus the MCP SDK); the embedding model is\nopt-in, see [Hybrid recall](#optional-hybrid-recall).\n\n**Claude Code:**\n\n```bash\nclaude mcp add memharness -- npx -y @memharness/mcp\n```\n\n**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`)\n**and Cursor** (`~/.cursor/mcp.json`) use the same JSON shape:\n\n```json\n{\n  \"mcpServers\": {\n    \"memharness\": { \"command\": \"npx\", \"args\": [\"-y\", \"@memharness/mcp\"] }\n  }\n}\n```\n\n**Codex** (`~/.codex/config.toml`) uses TOML, not JSON:\n\n```toml\n[mcp_servers.memharness]\ncommand = \"npx\"\nargs = [\"-y\", \"@memharness/mcp\"]\n```\n\nThe database lives at `~/.memharness/memory.db` (override with `MEMHARNESS_DB`;\n`XDG_DATA_HOME` is honored on Linux). Nothing else is written unless you turn on\nthe optional [debug log](#optional-local-usage-log).\n\n### First run\n\n1. Add the server with one of the commands above, then **restart your client**\n   so it picks up the new MCP server.\n2. In a conversation, hand the agent a durable fact, e.g. *\"remember that I\n   deploy this project with Fly.io.\"* It calls `remember`.\n3. Later (or in a fresh session) ask *\"what do you know about how I deploy?\"* It\n   calls `recall` and answers from memory. Correct it and it calls `revise`;\n   the old belief becomes history, queryable with `as_of` / `why` / `diff`.\n\nNo API key, no signup, no network. The first `remember` creates the SQLite file\nand that's the whole setup. To watch the tools work end to end without an agent,\nrun the demo: `cd examples && npm install && npm run demo`.\n\n### Optional: make recall automatic\n\nBy default the agent decides when to call `recall`. To *push* relevant memory in\nat the start of every session instead (more reliable than hoping the model\nremembers to look), add a Claude Code **SessionStart hook** that runs the\nbundled `memharness-context` tool, whose stdout is injected into context:\n\n```json\n{\n  \"hooks\": {\n    \"SessionStart\": [\n      { \"hooks\": [ { \"type\": \"command\",\n        \"command\": \"npx -y -p @memharness/mcp memharness-context --subject user\" } ] }\n    ]\n  }\n}\n```\n\nIt prints a compact dump of the most relevant current beliefs (and exits quietly\nif there's nothing yet), so the agent starts each session already knowing the\ndurable facts. Pass `--subject` more than once to inject several entities.\n\n## The seven tools\n\n| Tool | What it does | The thesis it tests |\n|---|---|---|\n| `remember` | store an atomic fact with confidence + provenance | facts > blobs |\n| `recall` | ranked current beliefs; `as_of` returns beliefs at a past instant | bi-temporal |\n| `revise` | supersede a belief, keep history | supersession > deletion |\n| `diff` | what changed since a date (learned/revised/retracted) | the audit demo |\n| `why` | provenance + full revision chain for a fact | trust / audit |\n| `forget` | tombstone by id or by source (provenance-based deletion) | GDPR-shaped |\n| `stats` | counts, subjects, schema version | — |\n\n## Library use\n\n```ts\nimport { Memharness } from \"@memharness/core\";\n\nconst mem = Memharness.open(); // ~/.memharness/memory.db\n\n// Learn something now, then learn it was actually true earlier.\nconst { id } = mem.remember({\n  subject: \"user\",\n  fact: \"lives in Osaka\",\n  sourceRef: \"session-2026-06-09\",\n});\nmem.revise({ oldFactId: id, newFact: \"lives in Tokyo\", validFrom: \"2026-05-01\" });\n\nmem.recall({ query: \"lives\" }).facts[0].fact;   // \"lives in Tokyo\" (current belief)\nmem.diff({ since: \"2026-06-01\" });               // { learned, revised, retracted }\nmem.why(id);                                     // { fact, ancestors, descendants }\n```\n\n`recall` returns a `RecallResult` (`{ facts: ScoredFact[]; asOf; truncated;\nusedFallback }`), not a bare string. `asOf` time-travels: `mem.recall({ query:\n\"lives\", asOf: \"2026-04-15\" })` returns what was believed *as held on that date*.\nThat honors transaction time, so a fact learned today is not visible to a query\nabout the past.\n\nRecall ranking is reciprocal-rank fusion over FTS5 BM25 (plus a vector rank when\n[hybrid recall](#optional-hybrid-recall) is enabled), times confidence, times\nrecency decay (90-day half-life, configurable), scored in SQL. An optional\n`maxTokens` budget caps output for context windows. A substring fallback catches\npartial words and typos, in both FTS-only and hybrid modes.\n\n## Optional: hybrid recall\n\nBy default, recall is FTS5 keyword search plus recency/confidence ranking: no\nmodel, fully offline. Hybrid recall adds a **semantic** leg via a local\nembedding model (BGE-small, ~130MB, downloaded once from the HuggingFace hub\nthen fully offline: no API key, no per-query network). Enable it in two steps:\n\n1. Install the optional embedding package alongside the server. With `npx`:\n\n   ```bash\n   npx -y -p @memharness/mcp -p @memharness/embed memharness-mcp\n   ```\n\n   (or `npm i -g @memharness/embed` for a global install).\n\n2. Set `MEMHARNESS_HYBRID=1` in the server's environment.\n\nThe server then keeps stored facts embedded automatically: facts you `remember`\nbecome semantically searchable on the next `recall`, with no separate backfill\nstep. The first hybrid recall prints download progress to stderr while the model\nloads. If the package isn't installed, the server says so and stays FTS-only; it\nnever fails closed.\n\nAt the library level, recall is embedding-provider-agnostic: pass your own query\nvector to `recall({ queryVector })` and attach document vectors with\n`setEmbedding(...)`, from any model you like.\n\n## A worked example\n\nTwo sessions, weeks apart. The agent learns a preference, the user later\ncorrects it, and a downstream question asks what the agent believed *at the\ntime*:\n\n```ts\n// June 9: the agent learns a deploy target and acts on it.\nconst { id } = mem.remember({\n  subject: \"project:acme\",\n  fact: \"deploys via Heroku\",\n  sourceRef: \"session-2026-06-09\",\n});\n\n// June 16: turns out the team moved to Fly back on June 1.\nmem.revise({\n  oldFactId: id,\n  newFact: \"deploys via Fly.io\",\n  validFrom: \"2026-06-01\",\n  sourceRef: \"session-2026-06-16\",\n});\n\nmem.recall({ subject: \"project:acme\" }).facts[0].fact; // \"deploys via Fly.io\"\n\n// \"Why did the CI config you wrote on June 9 target Heroku?\"\nmem.recall({ subject: \"project:acme\", asOf: \"2026-06-09\" }).facts[0].fact;\n//   \"deploys via Heroku\": what the agent honestly believed that day.\n\nmem.why(id);   // the full chain: Heroku, superseded by Fly.io, with sources.\nmem.diff({ since: \"2026-06-15\" });  // surfaces the Heroku -> Fly.io revision.\n```\n\nNo bag-of-strings memory can answer the `as_of` question, because it overwrote\nHeroku the moment it learned Fly.io.\n\n## Correctness\n\nThe property suite is the heart of the project: for randomized sequences of\nremember/revise/forget, `recall({asOf: T})` must equal the belief set produced\nby a naive, SQL-free replay of the event log, probed at every event\ntimestamp ±1ms. 10,000 cases run on every push to main.\n\nBenchmarked at 100k facts (10% revision chains, 2% retractions) on a developer\nlaptop (Apple Silicon): overall recall p95 **~1.3ms** against a 10ms budget,\nacross four query shapes (two-term keyword, keyword + subject, subject-only, and\n`as_of` + keyword). `pnpm bench` seeds the database and asserts the budget, so\nthe number is reproducible rather than quoted.\n\nOne deliberate divergence from the original prototype: retraction stores a\ntimestamp (`retracted_at`), not a flag, so `as_of` queries *before* the\nretraction still see history, which is what the prototype's docs promised but\nits SQL didn't deliver.\n\n## Development\n\n```bash\npnpm install\npnpm test            # unit + behavior suites (property tests at 200 runs)\npnpm test:property   # 10k randomized property cases\npnpm bench           # seed 100k facts, assert recall p95 < 10ms\n```\n\nSchema migrations are forward-only, driven by `PRAGMA user_version`. Rows are\nnever deleted (`forget` tombstones), so `facts.id` doubles as the insert\nsequence. All timestamps are canonical fixed-width UTC ISO 8601, making\nlexicographic comparison chronological.\n\n### Optional: local usage log\n\nFor debugging or measuring your own usage, set `MEMHARNESS_DEBUG=1` and the\nserver appends an op-name and timestamp line (never fact content) to a\n`usage.log` next to the database. It is off by default, fully local, and never\nnetworked.\n\n## License\n\nApache-2.0\n",
  "bytes": 12254,
  "sha": "c007ea2ebbc630286f7ab7a8b877d91f215eaa98b61734cb1b7f751503f3c684",
  "repo_slug": "las7/memharness",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_las7_memharness_a6e42857/readme"
}