EvoMem-MCP
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@EvoMem-MCP Remember we switched the ledger from Postgres to SQLite; that supersedes the old choice."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
EvoMem-MCP
Memory for AI agents that knows when you changed your mind.
English · Español
You tell your coding agent: "we're using Postgres for the ledger." Two weeks later: "we moved the ledger to SQLite, Postgres was overkill."
A month after that you ask "what database does the ledger use?" Your agent's memory holds both notes, and the old one matches the question better. So the agent recommends Postgres again.
This is the default failure mode of agent memory: a pile of facts with no idea which
ones are still true. EvoMem-MCP is a local MCP
memory server that stores decisions as a history. Every new decision is linked to
the one it replaces, the old one is kept but marked superseded, and recall always
resolves to the version currently in force.
recall("ledger postgres") # the query matches the OLD note best...
→ change: "We moved the ledger from Postgres to SQLite." status: active
└─ supersedes: "We picked Postgres for the ledger." status: supersededThe result
We built a benchmark to test one hypothesis: does a memory that links decisions to the ones they replace stop an agent from acting on obsolete decisions?
The metric is ODR (Obsolete Decision Rate): the share of questions where the agent acts on a decision that was already replaced. We used 120 frozen scenarios, with 2 to 6 versions of a decision per topic plus distractors. The judge is a local 27B LLM (Bonsai 2 27B, temperature 0). It reads what memory returns and names the option it would act on.
memory | ODR ↓ | 95% CI |
flat (all notes, no dates) | 0.42 | 0.33–0.51 |
flat with dates (the fair baseline) | 0.20 | 0.14–0.28 |
EvoMem: links set by the agent ( | 0.00 | 0.00–0.03 |
EvoMem: links detected automatically, LLM linker | 0.00 | 0.00–0.03 |
EvoMem: links detected automatically, heuristic linker | 0.008 | 0.00–0.05 |
Paired per scenario against flat with dates, EvoMem wins 24 scenarios and loses 0 (exact McNemar, p ≈ 1e-7).
The surprising part: dates are not enough
The memory was flat with dates, and the current version was right there in the
context with its date in 111 of 120 scenarios. The 27B model still acted on an
obsolete decision in 15% of them. Once the old note was explicitly marked
status: superseded, that error went to 0.
So "just show timestamps and let the model figure it out" doesn't work reliably. An explicit "this was replaced" marker does.
Related MCP server: jevmem-mcp
How it works
flowchart LR
A[Agent] -- "remember(text, supersedes?)" --> S[EvoMem server]
S --> C["Cronista<br/>extracts trigger · change · consequences"]
S --> L["Linker (optional)<br/>which active decision does this replace?"]
C --> DB[("SQLite<br/>nodes + raw text + FTS5/BM25")]
L --> DB
A -- "recall(query)" --> S
DB -- "every hit resolved to the<br/>head of its chain" --> AEvery note becomes a
MemoryNode. Its narrative fields aretrigger(why),change(what was decided),alternatives_tried,consequencesandsummary. The raw text is always kept and indexed, so recall never depends on the extraction being perfect.supersedenever deletes. The old node is kept withstatus: supersededand a link to its replacement.recall(include_history=True)returns the whole chain, newest to oldest, so the agent can see why things changed.recallresolves to the chain head. A query that only matches an old version still returns the current one. This is what the benchmark measures.Who creates the links?
The agent:
remember(text, supersedes=id). This is the most reliable option.The server, with
--auto-supersede, when the agent doesn't pass an id:llmuses any OpenAI-compatible endpoint, local or remote;heuristicneeds a change word plus overlapping vocabulary. It has no extra cost, but it is not safe on its own (see below).
Everything is local: one SQLite file, no cloud, no embeddings service.
Quickstart
git clone https://github.com/agunet/evomem-mcp.git && cd evomem-mcp
pip install -e . # or: uvx --from . evomem-mcp --db ~/.evomem/evomem.db
evomem-mcp --db ~/.evomem/evomem.db # lite mode: no GPU, ~7 ms per rememberClaude Code:
claude mcp add evomem -- evomem-mcp --db ~/.evomem/evomem.dbClaude Desktop / any MCP client:
{ "mcpServers": { "evomem": {
"command": "evomem-mcp",
"args": ["--db", "/home/you/.evomem/evomem.db"] } } }Automatic supersession with a local model (here llama.cpp; any OpenAI-compatible API works):
EVOMEM_LINKER_API_KEY=... evomem-mcp --db ~/.evomem/evomem.db \
--auto-supersede llm --linker-model <model> --linker-base-url http://localhost:8080/v1Tools
tool | what it does |
| Stores a note as a |
| BM25 search over the fields and raw text. Each hit is resolved to its current version, optionally with its history. |
| Fetches one node. |
| Removes a node from recall but keeps its history. |
| Counts nodes by status. |
Reproduce it
The scenarios are generated from a fixed seed, so you get exactly the same 120. The deterministic judge runs in about a minute on CPU:
python make_odr_scenarios.py --out data/odr_scenarios.jsonl --n 120
python benchmark_decisiones.py --scenarios data/odr_scenarios.jsonl --auto heuristicThe LLM-judge numbers above need a judge model behind an OpenAI-compatible endpoint:
python benchmark_decisiones.py --scenarios data/odr_scenarios.jsonl --judge llm \
--judge-model <model> --judge-base-url http://localhost:8080/v1 --auto heuristic,llmThere is also a second benchmark, VDR, which measures the opposite risk: does automatic linking hide decisions that are still valid?
python make_odr_scenarios.py --vdr --out data/odr_vdr.jsonl --n 80 --seed 11The cost of automatic linking: VDR
A linker that is too eager is dangerous. Suppose you note "we switched the staging ledger to SQLite". If the linker treats that as replacing the production ledger decision, the production decision disappears from recall, even though it is still valid.
The VDR set has 80 scenarios where each component has a decision in another scope (staging, analytics, EU region…) that stays valid the whole time. Half of them use change words on purpose to fool the linker.
linker | link precision | still-valid decisions it hides (out of 80) |
perfect links (reference) | 1.00 | — |
LLM (Bonsai 2 27B) | 0.95 | 1 |
heuristic | 0.70 (0.56 with change words) | 8 |
Verdict: in these scenarios the LLM linker works without a human in the loop, at
the cost of about 1.5 s per remember with a local 27B. The heuristic linker should
not run unsupervised.
What this does not prove (yet)
We'd rather you hear the limits from us:
The scenarios are synthetic, with one judge model and one seed. Real agent logs are messier. Evaluating on real agent history is on the roadmap.
The "story" part is still untested. With
history_depth=0(only the current version) ODR is already 0, so the benefit measured here comes from filtering what is obsolete. It does not yet show that seeing the why behind each change helps. That needs scenarios where the reason a decision was abandoned matters ("should we try Kafka?" when Kafka was already dropped for a reason that still holds).Scope confusion is a separate problem. Even with perfect links, the judge applies a production decision to analytics in 21–30% of VDR queries. An explicit
scopefield is planned.The heuristic linker misses paraphrases. It relies on overlapping words. Pass
supersedesyourself, or use the LLM linker.
Side quest: a tiny model as the note-taker
The "cronista" (note-taker) that turns text into fields comes in three versions:
cronista | how | latency (p50, RTX 3060) |
| sentence heuristics, no GPU | ~7 ms |
| NCM 88M base model writes the summary | 207 ms |
| 88M model + a 2.4M-parameter LoRA we trained on 120k synthetic examples | 359 ms |
What we learned, including the negative results:
Fine-tuning for generic summarization doesn't help. On XSum the LoRA does not beat the base model (ROUGE-1 34.3 vs 34.9). For general summaries the model is already at its ceiling, so fine-tuning is only worth it for structured extraction.
Structured extraction works. The trained LoRA reaches ROUGE 88–93 on trigger / change / consequences. On a validation set with different wording that it never saw it scores almost the same, so it generalizes.
The generated summary invents things. It gets the service and the numbers right only 26–37% of the time. We fixed two dataset bugs and retrained, and it still invents. The likely cause is that the model's discrete decoding corrupts literal names and numbers.
So we stopped generating the summary. By default it is assembled from the extracted fields, after matching each one to the closest sentence in the source. That summary is correct 88–93% of the time and is also the fastest option.
The goal of under 100 ms per
rememberon the GPU path is not met yet (359 ms).
The full experiment log, with every table, ablation, dead end and dataset bug, is in
docs/LAB_NOTEBOOK.md (in Spanish).
Roadmap
Linker that can abstain: when unsure, return the candidates to the agent instead of guessing.
A cheaper linker: a small model, called only when the note contains a change word.
An explicit
scopefield, to fix production vs staging vs analytics mix-ups.Hybrid search (BM25 + embeddings) for notes that say the same thing in other words.
Trajectory scenarios, to test whether seeing why a decision was dropped helps, beyond filtering what is obsolete.
Evaluation on real agent histories.
PyPI release.
Status
v0.2.1, a research prototype. The MCP server works end to end and the tests pass
(python test_smoke.py, python smoke_mcp.py). The API may still change.
License
Credits
The NCM (Next-Concept Modeling) base model is by HazyResearch. EvoMem's history design
is inspired by hierarchical, versioned memory (CoMeT-style supersede / merge).
Available Tools
5 toolsforgetB
Forget a node: soft (drop from recall index, keep history) or purge.
Inputs: node_id (required), purge.
get_nodeC
Read one MemoryNode by id.
Inputs: node_id (required).
recallB
Find the most relevant MemoryNodes for a free-text query.
include_history=True replaces each hit by its supersedes chain so the agent sees the story, not just the latest state (CoMeT-style), going at most history_depth versions back to keep the response small.
Inputs: query (required), k, history_depth, include_history.
rememberA
Turn a chunk of agent history / interaction text into a MemoryNode.
Extracts the narrative structure (trigger, change, consequences) with the current cronista, stores it locally, and returns the node id plus the wall latency (to verify the <100 ms target of objective 2.1). relations: optional typed edges to other node ids, e.g. {"caused_by": [id]}. supersedes: id of the node this one replaces (e.g. a decision that changed); the old node is kept as history with status=superseded. If that node was already superseded, the new one replaces the current head of its chain. Without it, the server may detect the replaced node itself (--auto-supersede).
Inputs: raw_text (required), tags, session, relations, +1 more.
statsA
Storage stats: node counts by status, raw T3 text count.
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
Each tool has a clearly distinct purpose: remember writes, recall searches by free-text, get_node fetches by id, forget removes, and stats reports storage. The recall/get_node boundary is well explained (query-based with history chain vs single-id lookup), leaving no realistic misselection.
All names are lowercase snake_case and the core operations use bare verbs (remember, recall, forget), but get_node mixes verb_noun and stats is a bare noun. The style is slightly mixed yet entirely predictable and readable.
Five tools is well-scoped for a memory server: one per core operation (write, search, read, delete, inspect). Nothing appears redundant or missing at this size.
The surface covers the full memory lifecycle—create, query, read, soft-delete/purge, and stats—with updates modeled through supersedes chains rather than a separate update tool. Minor gaps remain, such as no listing/enumeration or relation-traversal operation, but agents can work around these via recall and get_node.
Maintenance
Related MCP Connectors
Persistent memory for AI agents. Search and store durable facts, preferences and decisions.
Persistent memory for AI agents. Search, store, and recall across sessions.
Shared memory for your AI agents. Every fact shows its source, and stale facts get replaced.
Persistent memory for AI agents, for code, research and everyday life. Stale facts retire.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceLocal memory for AI agents in one SQLite file on your own machine. Facts carry a validity window and a confidence score, so an outdated one is superseded and downweighted rather than deleted, and retrieval fuses BM25, vector similarity and a knowledge graph.4Business Source 1.1
- AlicenseAqualityBmaintenanceProvides AI agents with a persistent long-term memory over a single SQLite file, letting them write, screen, consolidate, and recall notes across sessions without any generative LLM in the loop. Recall judges relevance, follows links between notes, and signals when memory is insufficient, so agents get one to three useful facts per question instead of a generic top-k list.1165 PyPI4MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI coding agents to store decisions, preferences, and lessons in a local SQLite store, then recall the most relevant ones on demand. Runs fully local — no cloud, API keys, or per-query cost — and also exposes forget and consolidate operations.2Apache 2.0

Make0 Brainofficial
AlicenseAqualityBmaintenanceGives every AI agent you use one shared, versioned memory in a local SQLite file, so a fact saved from one app is found by all the others. Runs fully offline with no API key, records which agent wrote each change, and keeps every prior version so nothing is silently overwritten.7468 npm15MIT