total-agent-memory

MCP serverDocs & knowledge

Persistent local memory for coding agents: temporal knowledge graph, procedural and episodic recall

Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.

Connect ahel once, and every AI you use reads what you have installed.

From the project's README

As published by vbcherepanov/total-agent-memory in README.md.

The only memory layer that learns how you work — not just what you said. Persistent, local memory for AI coding agents: Claude Code, Codex CLI, Cursor, any MCP client. Temporal knowledge graph · procedural memory · AST codebase ingest · cross-project analogy · 3D WebGL visualization.

Why this, not mem0 / Letta / Zep / Supermemory / Cognee?docs/vs-competitors.md


v13.0.0 — MCP 2026-07-28, and honest benchmarks (2026-08-27)

Upgrade if you installed after the MCP Python SDK went 2.0. The 2.x line dropped the @Server.list_tools() / @Server.call_tool() decorators this server was built on, and the dependency was floored at mcp[cli]>=1.0.0 — so every fresh pip / uvx / npx / brew / docker install resolved 2.x and died at import. Existing installs kept working only because their pinned 1.x never moved.

Protocol. Tools now register through whichever API the installed SDK exposes, and the server serves both protocol eras from one process: the stateless 2026-07-28 revision — tools/list, server/discover and tools/call with no initialize handshake, protocol metadata per request — alongside the legacy handshake for clients on older SDKs. JSON-answering tools return structuredContent, so clients stop re-parsing strings, and all 74 tools carry readOnlyHint / destructiveHint / idempotentHint annotations that clients use to decide what runs without a confirmation prompt.

Claude Code plugin. The MCP server, the memory-protocol skill and the seven capture hooks now install in one step:

/plugin marketplace add vbcherepanov/total-agent-memory
/plugin install total-agent-memory@vbcherepanov

LongMemEval now measures the product. The runner had its own self-contained BM25 / RRF / MMR / CrossEncoder stack, so the published 96.2% described an algorithm rather than this software. A new --modes store — now the default — ingests each haystack into a real Store and queries Recall.search. Re-measured: 95.1% R@5, 27.6 ms per query.

Benchmarks that no longer measure themselves. Recall.search bumps recall_count on every row it returns, and the scorer adds recall_boost = min(0.3, recall_count * 0.05). Spaced repetition is wanted in normal use and fatal for measurement: successive runs against one database scored 0.547 → 0.565 → 0.588 → 0.607 R@5 without a line of retrieval code changing. Both runners now pass record_usage=False, a clean run and a re-run are byte-identical, and every number below was re-measured on that basis. The LoCoMo runner had also been printing categories 2 and 3 under each other's labels.

BEAM (ICLR 2026) is now part of the suite — retrieval across its ten memory abilities at the 100K / 500K / 1M scales, graded against each probe's source_chat_ids with no LLM in the loop.

And ~3 GB it could not reach (13.0.2). The base install resolved sentence-transformers, transformers, FlagEmbedding and peft, each of which resolves torch, which on Linux resolves the entire nvidia-cu* set: 147 packages and ~3,108 MB of wheels against 97 and ~113 MB without them. The Glama build sandbox simply ran out of disk unpacking nvidia-cudnn-cu13. Yet the default configuration cannot touch any of it — MEMORY_MODE=fast disables the reranker, and the same mode's MEMORY_ALLOW_OLLAMA_IN_HOT_PATH=false is the flag that gates the SentenceTransformer fall-through in Recall._compute. The stack now lives in a rerank extra, and the mirror of the dependency-drift test keeps it there. Every installer was also warming all-MiniLM-L6-v2 — the fallback model, not the one the server embeds with — into a cache nothing reads.

The server was carrying ~450 MB it never used. chromadb and sentence_transformers were imported at module scope, both are fallback paths, and the second pulls in torch — so every user paid for a stack that fastembed made unnecessary. Deferring them took import server from 558 MB to 116 MB and a serving process from 1367 MB to 909 MB. Reported by d.snezhinskiy. A failed fastembed init also stops being a single log line: it now names the cache and the memory cost, because a macOS-purged model cache is the usual reason a memory server suddenly wants 1.5 GB.

Bugs worth naming — all of the "works in a checkout, silently dead when installed" kind. tree-sitter-language-pack was in no requirements file, so "AST codebase ingest, 9 languages" degraded to whole-file chunks for everyone. vocabularies/ and filters/ never made it into the wheel or the image, so canonical tag normalisation ran against an empty vocabulary and every memory_save(filter=…) was a no-op. The enrichment worker shared the Store's sqlite connection — safe for reads, not for writes — and long ingests died on cannot start a transaction within a transaction. Migration 028 failed on every fresh database and could never record itself, so it retried on every startup forever (root cause spotted by @juicetin in #12: two owners for one schema change). And ai_layer/verifier.py looked for NLI calibrations at the pre-.tam path.

Full notes in CHANGELOG.md. Earlier releases: v12.4.0 · v12.0.0 · v11.0.


Table of contents

  • v13.0.0 — what changed
  • The problem it solves
  • 60-second demo
  • Benchmarks — how it compares
    • LoCoMo · BEAM · LongMemEval
  • Competitor comparison
  • What you get
  • Architecture
  • Install
  • Quick start
  • CLI: lookup-memory for sub-agents
  • MCP tools reference
  • TypeScript SDK
  • Dashboard
  • Update
  • Upgrading from v8.x to v9.0
  • Upgrading from v7.x to v8.0
  • Ollama setup
  • Configuration
  • Performance tuning
  • Roadmap
  • Support the project
  • Philosophy & license

The problem it solves

AI coding agents have amnesia. Every new Claude Code / Codex / Cursor session starts from zero. Yesterday's architectural decisions, bug fixes, stack choices, and hard-won lessons vanish the moment you close the terminal. You re-explain the same things, re-discover the same solutions, paste the same context into every new chat.

total-agent-memory gives the agent a persistent brain — on your machine, not in someone else's cloud.

Every decision, solution, error, fact, file change, and session summary is:

  • Captured — explicitly via memory_save or implicitly via hooks on file edits / bash errors / session end
  • Linked — automatically extracted into a knowledge graph (entities, relations, temporal facts)
  • Searchable — 6-stage hybrid retrieval (BM25 + dense + graph + CrossEncoder + MMR + RRF fusion), 95.1% R@5 on public LongMemEval
  • Private — 100% local. SQLite + FastEmbed + optional Ollama. No data leaves your machine.

60-second demo

You:     "remember we picked pgvector over ChromaDB because of multi-tenant RLS"
Claude:  ✓ memory_save(type=decision, content="Chose pgvector over ChromaDB",
                       context="WHY: single Postgres, per-tenant RLS")

[3 days later, different session, possibly different project directory:]

You:     "why did we pick pgvector again?"
Claude:  ✓ memory_recall(query="vector database choice")
         → "Chose pgvector over ChromaDB for multi-tenant RLS. Single DB
            instance, row-level security per tenant."

It's not just retrieval. It's procedural too:

You:     "migrate auth middleware to JWT-only session tokens"
Claude:  ✓ workflow_predict(task_description="migrate auth middleware...")
         → confidence 0.82, predicted steps:
             1. read src/auth/middleware.go + tests
             2. update session fixtures in tests/
             3. run migration 0042
             4. regenerate OpenAPI spec
           similar past: wf#118 (success), wf#93 (success)

Benchmarks — how it compares

Everything below is retrieval: does the memory surface the passage that contains the answer, in the top-K? That is the part this project owns — answer quality is bounded above by it, and it can be graded with no LLM in the loop, which makes the numbers deterministic, free, and reproducible on your machine.

Two things to read them honestly:

  • These are the default fast profile — FastEmbed, no reranker, no LLM anywhere in the path. That is what you get after install.sh, not a tuned configuration.
  • Every runner passes record_usage=False. Recall.search normally bumps recall_count, and the scorer adds recall_boost = min(0.3, recall_count × 0.05) — so before v13, each re-run against the same database scored higher than the last, partly measuring its own history. A clean run and a re-run are now byte-identical.

LoCoMo — snap-research/locomo

1,536 gradable questions across 10 long-running conversations (5,882 turns ingested), plus 446 adversarial questions scored separately.

CategoryNR@1R@5R@10MRR
single-hop2820.2020.5000.6380.332
temporal3210.4110.6890.7350.524
multi-hop920.1630.4130.4350.256
open-domain8410.3630.6330.7120.479
overall1,5360.3310.6070.6870.448

Latency p50 18.2 ms, p95 55.4 ms. Temporal is the strongest category — the bi-temporal knowledge graph earns its keep. Multi-hop is the weakest and is the v13.1 target.

Reproduce: python benchmarks/locomo_bench.py --wipebenchmarks/results/v13-locomo-retrieval.json

BEAM — Beyond a Million Tokens, ICLR 2026

BEAM is the benchmark that starts where context windows stop: conversations of 100K / 500K / 1M tokens (a separate 10M set goes further), probed across ten distinct memory abilities. Scored here against each probe's source_chat_ids.

Scale 100K — 20 conversations, 5,732 messages, 355 gradable probes:

AbilityNR@1R@5R@10MRR
contradiction_resolution400.7001.0001.0000.824
temporal_reasoning400.4750.9751.0000.689
knowledge_update400.5500.9250.9500.719
multi_session_reasoning400.3750.6750.8500.486
information_extraction400.4000.6250.7250.503
summarization360.1670.4440.5560.267
preference_following390.0770.2820.4100.169
event_ordering400.0250.1500.2000.074
instruction_following400.0250.0750.1500.054
overall3550.3130.5750.6510.423

Latency p50 17.7 ms. The shape is the useful part: contradiction resolution, temporal reasoning and knowledge update are effectively solved, while instruction_following and event_ordering are near-zero — those probes ask whether a stated instruction was followed or in what order things happened, and semantic similarity to the question does not find the message where the instruction was given. Retrieval is the wrong primitive there, and that is the roadmap item.

Scale 500K — 35 conversations, 38,058 messages, 629 gradable probes:

AbilityNR@1R@5R@10MRR
contradiction_resolution700.7140.9430.9710.828
knowledge_update690.4640.8550.8990.617
temporal_reasoning700.5000.7860.8710.625
multi_session_reasoning700.3570.6140.7290.470
information_extraction700.2710.4430.5710.354
preference_following700.0710.3000.4710.168
summarization700.1000.2860.4140.174
instruction_following700.0290.1570.2570.086
event_ordering700.0140.0290.1860.042
overall6290.2800.4900.5960.373

Scale 1M — 35 conversations, 74,630 messages, 625 gradable probes:

AbilityNR@1R@5R@10MRR
knowledge_update700.5290.8860.9290.677
contradiction_resolution700.6860.8710.9140.772
temporal_reasoning700.3710.6860.8000.508
multi_session_reasoning700.2140.4290.6000.315
information_extraction700.1570.3710.5000.250
summarization660.0150.2880.5150.147
preference_following690.0290.2460.4060.134
event_ordering700.0000.1570.3290.069
instruction_following700.0290.0860.2000.061
overall6250.2270.4480.5780.327

How it scales, and what that exposed

ScaleMessagesR@5search p50ingest
100K5,7320.57517.7 ms25.6 msg/s
500K38,0580.49058.5 ms10.8 msg/s
1M74,6300.448411.5 ms5.0 msg/s

Recall decays gracefully — 13× the haystack costs 12.7 points of R@5, and the abilities that hold up (knowledge update, contradiction resolution) hold up at every scale. The two curves that do not decay gracefully are the interesting part, and they have separate causes.

Ingest — found and fixed. Throughput fell 5× across the three scales on identical code. The cause was ours: graph/auto_link.py runs on every save and constructed a fresh ConceptExtractor each time. The node-name cache lives on the instance, so it was thrown away immediately and the whole graph_nodes table was re-read per write — 1,000 saves triggered 1,000 full table reads (~139 million rows at the 139k nodes this ingest reaches). Fixed in v13.0.1; counting reads rather than timing makes the check load-independent, and it is now 1 read per 1,000 saves. The ingest column above was measured before that fix and is kept as the record of the problem.

Search — open. p50 grew 7× between 500K and 1M for 2× the data. Store._binary_search loads the binary vectors of every active record into numpy on each query, so search is linear in store size. That is a different problem from the ingest one and is not fixed; an ANN index over the binary vectors is the obvious answer and has not been built yet. Stated rather than buried, because 411 ms is a real number a user would feel.

Reproduce: python benchmarks/beam_bench.py --scale 100K --wipev13-beam-100K.json · v13-beam-500K.json · v13-beam-1M.json

LongMemEval — xiaowu0162/longmemeval-cleaned

470 questions across six question types, re-measured for v13 through the product: each question's haystack is ingested into a real Store and queried with Recall.search, the same path an agent takes.

Question typeCountR@5 (recall_any)
knowledge-update72100.0%
multi-session12198.3%
single-session-user6495.3%
single-session-assistant5694.6%
temporal-reasoning12792.9%
single-session-preference3080.0%
total47095.1%

Also recall_all@5 85.7% (every required fragment, not just one), NDCG@5 88.9%, 27.6 ms per query.

This replaces the 96.2% we published before, and the difference matters more than the 1.1 points. Until v13 this runner used its own self-contained BM25 / RRF / MMR / CrossEncoder stack, so the number described an algorithm, not this software. --modes store drives the shipping path and is now the default. The old modes remain for ablations.

For reference on the same set, Mastra "Observational" reports 95.0% and Supermemory 85.4% — both cloud services.

Reproduce: python benchmarks/longmemeval_bench.py --modes storeevals/longmemeval-2026-08-27-v13-store.json

On end-to-end accuracy numbers

Systems in this space usually publish LoCoMo accuracy — a generator answers from the retrieved context and an LLM judges it. We publish it too, with the two caveats that make it meaningful.

One LLM-judged run is a sample, not a measurement. Temperature 0 does not make the API deterministic and OpenAI documents seed as best-effort, so the runner takes --seed and we report three runs:

CategoryNmeanminmaxspread
single-hop2820.3660.3580.3720.014
temporal3210.4260.4240.4270.003
multi-hop960.2920.2810.3020.021
open-domain8410.5700.5670.5730.006
adversarial4460.9040.8990.9080.009
overall (no adversarial)1,5400.486 ± 0.0020.4840.4880.005
overall (all)1,9860.579 ± 0.0020.5780.5820.004

gpt-4o generator, gpt-4o-mini judge, seeds 1/2/3. Retrieval was byte-identical across all three — only generation and judging vary.

The judge needed two guards, and they point opposite ways.

Refusals scored as correct answers. On ~100 of the 1,540 non-adversarial questions per run, the judge answered YES to "Not mentioned in the conversation." against golds like Sweden, June 2023, Single — F1 exactly 0.00. Almost certainly the adversarial rule bleeding across, since the judge is told to accept a refusal when the gold also indicates no information. Per category the inflation runs 3.2 pp (open-domain) to 14.3 pp (temporal).

Hallucinations scored as correct abstentions. 99.6% of LoCoMo's adversarial golds are the empty string. The judge accepts almost any fluent answer against an empty reference, so 27–30 invented answers per run scored correct — inflating the one category we used to lead on.

Both are rules rather than judgements — on categories 1–4 the gold is a fact, so a refusal cannot be right; with an empty gold, only a refusal can be — so both now run deterministically at judging time. Effect: no-adv 0.551 → 0.486, adversarial 0.966 → 0.904, all 0.645 → 0.579. The table above is corrected.

How noisy is the rest? Aligning all 1,986 questions across the three seeds:

share
generator's answer differed between seeds12.5%
judge's verdict differed5.1%
judge flipped on an identical answer2.7%

The aggregate holds within ±0.005 because those flips roughly cancel, not because the instrument is precise. Quoting one run to three decimals — as we did before — is not supported by the data.

Not comparable to the 90%+ figures some competitors publish: different generators, judges, prompts and question subsets. And on this evidence, an unguarded LLM judge can be worth six points on its own. The retrieval numbers above remain our primary metric because they are checkable without an API key.

benchmarks/results/v13-locomo-llm-3seeds.json · Runner: benchmarks/locomo_bench_llm.py

Do the retrieval numbers mean anything? — negative controls

A retrieval score with no floor under it is not a claim. Every LoCoMo run now scores three degenerate baselines on the same questions:

BaselineR@1R@5R@10
random — ten turns from the same conversation0.0010.0120.023
first — the ten earliest turns0.0000.0230.039
recency — the ten most recent turns0.0010.0030.011
the pipeline0.3310.6070.687

27× the best degenerate baseline. The controls run in the same pass as the metric, so the floor ships with the number rather than living in a script somebody stops running.

Latency profile

  p50 (warm)   ▌ 0.065 ms
  p95 (warm)   ▌▌ 2.97 ms
  LoCoMo       ▌▌▌ 18.2 ms/query    ← full hybrid retrieval over 5,882 records
  BEAM 100K    ▌▌▌ 17.7 ms/query    ← over 5,732 messages
  LongMemEval  ▌▌▌▌▌ 38.8 ms/query  ← includes embedding + CrossEncoder rerank
  p50 (cold)   ▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌ 1333 ms  ← first query after process start

Warm / cold reproducible from evals/results-2026-04-17.json.


Competitor comparison

We're not replacing chatbot memory — we're occupying the coding-agent + MCP + local niche.

Shortened here. Read the whole README on GitHub.

Signals

GitHub stars
67
Forks
17
Last commit
Sep 2026
Advanced
Delivery
total-agent-memory MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
Catalog kind
mcp-server
Gateway key
io-github-vbcherepanov-total-agent-memory
Source
github.com/vbcherepanov/total-agent-memory