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-MCPRemember 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: kepta
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.
| Name | Required | Description | Default |
|---|---|---|---|
| purge | No | ||
| node_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It does disclose what soft mode does ('drop from recall index, keep history'), but leaves 'purge' completely undefined — no statement that it is irreversible, what else is deleted, or what is returned. For a destructive mutation tool with zero annotation coverage this is a significant gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence, front-loaded with the verb and resource, and the parenthetical efficiently encodes both modes with zero filler. It is terse rather than padded, though that terseness edges into under-specification.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
A two-parameter node-deletion tool with no annotations, no output schema, and 0% parameter coverage needs more than this. Nothing is said about reversibility of purge, error behavior for unknown node ids, or the effect on related history — all of which an agent must know before calling it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It does map the two branches to the behavior (soft / purge) and implies node_id selects the target, but it never states that purge is a boolean flag defaulting to false, nor gives any format or scoping for node_id. Partial compensation only.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (forget) and resource (a node), and splits it into two named modes (soft vs purge), which an agent can distinguish from siblings like recall/remember. It never names those siblings explicitly, so sibling differentiation is only implied by the semantics of the words.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'soft (drop from recall index, keep history) or purge' implies which mode to pick but gives no rule for choosing between them in practice and no when-not guidance. It also does not state prerequisites (ownership/permissions) or warn that purge should be used sparingly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_nodeC
Read one MemoryNode by id.
| Name | Required | Description | Default |
|---|---|---|---|
| node_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full disclosure burden and does not meet it. It does not say whether this is read-only (inferred from "Read"), what happens when the id is unknown (error vs. empty), whether any permissions or scoping apply, or what shape the MemoryNode comes back in.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The single sentence is front-loaded and free of padding, so nothing is wasted. Brevity here is under-specification rather than efficiency, though, since the sentence could carry a clause on return value or failure behavior at no structural cost.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema and no annotations, so the description is the only source of information about behavior. For a fetch-by-id tool the agent still lacks the id format, the not-found behavior, and the returned structure, leaving a meaningful gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for the single `node_id` parameter, so the schema offers only a name and a string type. The description adds little beyond reiterating "by id" — it does not say whether ids are UUIDs, opaque strings, or where to obtain one, which is the one piece of guidance a lookup tool most needs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ("Read"), a specific resource ("MemoryNode"), and the lookup key ("by id"), which is enough to understand the operation at a glance. It does not, however, distinguish this by-id fetch from the sibling `recall` tool, which also reads memory content, leaving the agent to infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Nothing tells the agent when to use this rather than `recall`, which is the obvious alternative for retrieving memory. The only implicit guidance is "by id", which hints at direct lookup but never states the precondition that an id must already be known.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| k | No | ||
| query | Yes | ||
| history_depth | No | ||
| include_history | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full disclosure burden, and it does add genuine behavior: include_history replaces each hit with its supersedes chain, and history_depth caps how many versions back are traversed to keep the response small. It says nothing about ranking/scoring method (semantic vs keyword), permissions, or the shape of a returned MemoryNode.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose is front-loaded in a single sentence, and the follow-up paragraph is scoped to one behavior. The '(CoMeT-style)' citation is jargon that earns no place for an invoking agent, a minor cost against otherwise tight prose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-annotation, no-output-schema search tool, the definition covers result-shaping behavior but omits what comes back (MemoryNode structure, ordering/ranking of hits) and the meaning of k. An agent can call it, but cannot predict the response well enough to plan downstream steps confidently.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate, and it does for two of four parameters: include_history and history_depth both receive real semantic content (chain expansion and version budget). 'query' is only described as free-text relevance search, and 'k' — the hit count, default 5 — is left entirely unexplained by both schema and description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names a specific verb (find) plus the resource (MemoryNodes) and the retrieval method (relevance to a free-text query), which distinguishes it from the mutation siblings remember/forget and from the id-based get_node. It does not explicitly state the contrast with get_node or stats, so the differentiation is inferred rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit 'use this when... instead of X' guidance against the four siblings. The one real usage rule given is conditional and tied to a flag: setting include_history=True when the agent wants the story rather than the latest state. That is useful but covers only one mode of invocation, leaving the choice between recall, get_node and stats to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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).
| Name | Required | Description | Default |
|---|---|---|---|
| tags | No | ||
| session | No | ||
| raw_text | Yes | ||
| relations | No | ||
| supersedes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses that extraction happens via the current cronista, storage is local, the return includes node id and wall latency, and critically that superseded nodes are retained as history with status=superseded. It omits failure/idempotency behavior and permission or concurrency assumptions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Short, front-loaded, and each line earns its place: what it produces, how relations and supersedes work. The internal benchmark reference is slightly extraneous but harmless context for the returned latency.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a five-parameter mutation with no annotations and no output schema, the description covers the core write and supersede mechanics but leaves two parameters undefined and says nothing about error or duplicate handling. Adequate, not complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It explains the two hardest parameters well ('relations' shape with an example, 'supersedes' chain semantics including auto-supersede), but 'tags' and 'session' are documented nowhere in either the schema or the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb+resource pair ('Turn ... text into a MemoryNode') and enumerates the internal steps: extract narrative structure, store locally, return the node id. Against siblings recall/forget/get_node/stats it is clearly the write path, though it never names a sibling to contrast with.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the mechanics rather than stated: it explains when to supply 'relations' and 'supersedes', but gives no explicit 'use this instead of recall when...' routing or prerequisites. An agent can infer this is the ingestion tool, but must infer it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
statsA
Storage stats: node counts by status, raw T3 text count.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the behavioral burden; it does signal a read-only inspection operation and names the returned metrics, which is real value. However, it says nothing about cost or scope (does it scan the whole store?), whether 'T3' is a namespace/storage tier, or how the counts are keyed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded line with no filler; the resource name leads and the payload follows. Minor deduction because 'raw T3 text count' is unexplained jargon that a reader cannot resolve from the description alone.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, no-output-schema tool, the description usefully enumerates the returned fields, which partially substitutes for the missing output schema. It falls short only on the meaning of the 'raw T3 text' metric and on result shape details.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing to disambiguate; baseline 4 applies. The description correctly does not waste space on a parameter discussion.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource (storage stats) and enumerates what it reports: node counts by status and raw T3 text count. It is unmistakably distinct from the sibling memory operations (recall/forget/remember/get_node), though it never explicitly contrasts itself with them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent can infer this is the tool for inspection/aggregate counts rather than retrieval. There is no explicit 'use this when' statement, no mention of when not to use it, and no pointer to alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.2.1- First observed
forget - First observed
get_node - First observed
recall - First observed
remember - First observed
stats
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.
Persistent memory for AI agents, for code, research and everyday life. Stale facts retire.
- memnodeOAuthdev.memnode
Persistent, inspectable memory for AI agents with lineage, correction, and a hosted MCP endpoint.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceProvides a decision memory layer for codebases, enabling AI agents to search, retrieve, and record business decisions with metadata filters and full-text search.1MIT
- AlicenseNot gradedqualityAmaintenanceLocal 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.2Business Source 1.1
- AlicenseNot gradedqualityBmaintenanceEnables an AI agent to recall past approvals, edits and rejections that are similar to the task at hand before drafting, and to record each new decision along with the human's reason. Memory lives in a local Postgres (PGlite + pgvector) store, so corrections carry across sessions without hosting anything.1MIT
- AlicenseBqualityAmaintenanceProvides 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.1139 PyPI2MIT