Skip to main content
Glama
arm00pv
by arm00pv

retained

An MCP memory server whose writes are provable.

A memory server answers ok and you move on. That answer is a promise about bytes, and almost nobody tests it. retained makes the promise checkable: every write is appended, fsynced, read back from the file and hash-chained to its predecessor before it is acknowledged; every read says how much it did not return; and the search index can be reconciled against the log it came from, in both directions.

$ python retained.py --selftest
retained selftest: 14 ok, 0 failed

Three invariants

invariant

what it means

I1

no acknowledgement without proof

append → fsync → byte-exact read-back → chain check; the reply carries durable and the four checks

I2

no silent loss on read

every search returns total_matched, returned, truncated

I3

immutable records, explicit supersede

a correction is a new record; the superseded one is marked, never overwritten

The store is the truth: an append-only JSONL log, one hash-chained record per line. The search index is a SQLite FTS5 table that can be deleted and rebuilt at any time. memory_verify compares them and reports drift both ways, because "the index is ahead of the store" is how a memory system loses things while reporting health.

Related MCP server: mcp-evidence-ledger

Tools

tool

arguments

returns

memory_write

text, kind?, scope?, source?, supersedes?, client_op_id?, fields?

id, seq, sha256, durable, checks{append,fsync,readback_match,chain}, warnings

memory_search

query, scope?, k?, max_tokens?

hits, total_matched, returned, truncated

memory_get

ids?, limit?

full records, missing, corrupt_lines, chain_ok

memory_verify

none

records, chain_ok, corrupt_lines, missing_from_index, index_only, coverage, intact

memory_checkpoint

label, summary?, scope?

a checkpoint record, same proof as a write

Unknown fields are stored verbatim at the top level of the record. Nothing is ever rewritten, so a field this server does not model cannot be lost by it.

Measured against the same probes as everybody else

memory-conformance runs six falsifiable retention probes against a server over MCP stdio. Results:

subject

result

@modelcontextprotocol/server-memory@1.0.0

5/6 — fails foreign_field (a field written from outside is silently dropped)

a minimal control server

6/6

retained

6/6 — including 50/50 concurrent writes across two server processes

$ python harness.py --adapter=retained --cmd="python /path/to/retained.py"
ack_readback     PASS   restart        PASS   foreign_field  PASS
foreign_record   PASS   concurrent     PASS   large_record   PASS
0 probe(s) failed

Hooks: the difference between a memory and a library

An MCP server cannot initiate a request - that is in the protocol, not in this implementation. A store reachable only through a tool call is memory the model has to remember to ask for, which means it usually will not. The fix is a host hook that injects a brief at session start:

$ RETAINED_DIR=~/.retained python retained.py --brief --budget=120
memory: 3 record(s)
- [checkpoint] shipped retained 0.2.0 with optional embeddings; retrieval 11/12 h1, 12/12 h3
- [note] conformance: reference 5/6 retention, 0/12 retrieval
- [note] silentfail issue 1: dead-guard is quadratic, 58s of a 134s scan

Newest first, checkpoints before notes, and every brief says how much it left out - a brief that silently truncates would be this project's own defect one level up. --json gives the same content with included, dropped and tokens for a host that wants to budget.

Claude Code (.claude/settings.json):

{ "hooks": { "SessionStart": [ { "hooks": [ { "type": "command",
  "command": "RETAINED_DIR=$HOME/.retained python3 -m retained --brief --budget=800" } ] } ] } }
}

hooks/session-start.sh is a portable version for any harness that can run a command per session. Without a hook, retained is still a correct store - it is just a store nobody consults.

Retrieval: lexical, and optionally semantic

By default the index is SQLite FTS5 and nothing else. Set one environment variable and it becomes a hybrid:

RETAINED_EMBED_URL=http://127.0.0.1:11434/api/embeddings   # any Ollama-style endpoint
RETAINED_EMBED_MODEL=all-minilm                           # 384 dimensions, ~46 MB

Vectors are stored in the index (never in the log - the log is the truth and stays dependency-free), and search fuses the lexical and vector rankings with reciprocal rank fusion. If the embedder is down, writes still land and search still answers lexically: an optional feature must not become a single point of failure.

Measured on the twelve-question retrieval eval in memory-conformance - six questions sharing vocabulary with the memory they want, six paraphrases:

mode

hit@1

hit@3

lexical only

10/12

11/12

hybrid (all-minilm)

11/12

12/12

The one lexical miss - "how many machines do we run" against "three hosts: throne, watchtower and pi" - is found at rank 3 with vectors and still not at rank 1, which is a fair picture of a 384- dimension model on a twelve-item corpus: it closes the gap, it does not erase it.

The bug the selftest caught on its first run

The read-back check failed immediately: the file was 183 bytes where 182 were written. Without O_BINARY, Windows opens the file in text mode and turns every LF into CRLF, so the bytes on disk are not the bytes written. One flag, found in the first minute - and it is the same carriage-return family that has broken appends in the estate this came from twice already.

Install and run

pip install git+https://github.com/arm00pv/retained
RETAINED_DIR=~/.retained retained        # MCP server on stdio

Register it in any MCP client as a stdio server:

{ "mcpServers": { "retained": { "command": "retained", "env": { "RETAINED_DIR": "~/.retained" } } } }
}

Limits, stated

  • Retrieval is lexical. SQLite FTS5 with bm25 ranking. No embeddings, no semantic search; a memory that needs "find the thing I meant" needs more than this.

  • One writer at a time. The append takes an OS lock (flock / msvcrt.locking); concurrent processes serialise rather than merge.

  • No hooks. An MCP server cannot initiate a request, so injection has to be driven by the client. Without a hook in your harness, this is a tool the model must remember to call.

  • No encryption, no auth, no multi-user scopes beyond the scope field.

  • Fourteen self-test checks and six conformance probes are not a proof of correctness. They are the failures that were thought of, and the list above is the rest.

MIT licensed.

Available Tools

5 tools
memory_checkpointD

memory_checkpoint

ParametersJSON Schema
NameRequiredDescriptionDefault
labelNo
scopeNo
summaryNo

TDQS

D1.1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure, and it discloses nothing. It is unclear whether a checkpoint mutates state, requires persistence, is reversible, or returns anything.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The text is short, but this is under-specification rather than genuine conciseness—there is no content to be concise about. Front-loading is moot when nothing is conveyed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a three-parameter tool with no annotations and no output schema, the description is completely inadequate, omitting purpose, usage, behavior, and every parameter's meaning.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Three parameters (label, scope, summary) exist at 0% schema description coverage, and the description adds zero meaning about their purpose, format, interactions, or defaults. This is the worst case the rubric anticipates.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tautological: description restates name/title.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance whatsoever on when to use this tool, when not to, or which sibling (memory_write, memory_search, memory_get, memory_verify) it replaces. An agent is left entirely to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_getD

memory_get

ParametersJSON Schema
NameRequiredDescriptionDefault
idsNo
limitNo

TDQS

D1.3/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the entire burden of behavioral disclosure, and it discloses nothing. It does not say whether this is a read (implied by 'get' but unconfirmed), whether missing ids error or return empty, or what limit does.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

There is no structure to evaluate — a single bare token. This is under-specification rather than conciseness, so it earns no credit for brevity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

A two-parameter retrieval tool with no annotations, no output schema, and 0% schema coverage needs the description to explain ids semantics, limit behavior, and return shape. None of that is present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%: neither 'ids' nor 'limit' is documented in the schema, and the description adds no meaning for either. The agent cannot tell whether ids is a required filter or optional, nor what limit caps.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose2/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tautological: description restates name/title.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No when-to-use, when-not-to-use, or alternative is mentioned. With four sibling tools (memory_search, memory_verify, memory_checkpoint, memory_write) the agent has no basis for choosing memory_get over memory_search.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_verifyD

memory_verify

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

D1.6/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure, and it discloses nothing: not whether verification is read-only or mutating, what a passing/failing result means, what side effects occur, or whether auth or state is required.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is a single token with zero waste, but this is under-specification rather than conciseness; brevity here comes at the cost of meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a memory-integrity tool with no annotations and no output schema, the description should at minimum say what is verified and what the result signifies. None of that is present, leaving the agent unable to know if or when to call it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so per the baseline there is no parameter semantics to document and nothing the description must compensate for. The empty schema is fully self-describing.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tautological: description restates name/title.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use guidance, no conditions, and no mention of the sibling tools (memory_write, memory_search, memory_get, memory_checkpoint) that could plausibly cover the same need. Nothing routes the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_writeD

memory_write

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNo
textYes
scopeNo
fieldsNo
sourceNo
supersedesNo
client_op_idNo

TDQS

D1.1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden, yet it discloses nothing: not whether the write is durable, upserting, idempotent, permission-gated, or what supersedes/client_op_id imply. Nothing about mutation semantics is communicated.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is short, but only because it is under-specified rather than concise; there is no front-loaded intent to speak of. A bare identifier is not a structured description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter mutation tool with nested objects, no annotations, and no output schema, the definition is completely inadequate. An agent has no basis for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Seven parameters with 0% schema description coverage and no parameter explanation in the description. Nested objects (fields, source) and ambiguous strings (kind, scope, supersedes, client_op_id) are entirely undocumented, so an agent cannot populate them correctly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tautological: description restates name/title.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No when-to-use guidance, no exclusions, and no mention of any alternative among the four sibling tools. The agent must infer everything about selection from the name alone.

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.

  1. 5 tool updatesv0.1.0
    • First observedmemory_checkpoint
    • First observedmemory_get
    • First observedmemory_search
    • First observedmemory_verify
    • First observedmemory_write

TDQS

C2.2/5.0

Scored across 5 tools

Disambiguation4/5

The five tools map to mostly distinct memory operations: write, search, get, verify, and checkpoint. memory_get vs memory_search could overlap if retrieval is query-based, and checkpoint vs write could be confused, but the verbs suggest different intents.

Naming Consistency5/5

All tools use a uniform memory_<verb> snake_case pattern. The naming is predictable and consistent throughout.

Tool Count5/5

Five tools are well-scoped for a memory service, covering core operations without bloat.

Completeness4/5

The set covers write, search, get, verify, and checkpoint, which addresses a core memory lifecycle. Missing explicit delete/update/list operations are minor gaps that may be handled by write upserts or checkpoints.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server providing agent memory with deterministic deletion guarantees, enabling compliant event storage, context retrieval, and audit-proof data management.
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server providing an append-only, hash-chained evidence ledger for agent actions, where every record is a tamper-evident receipt cryptographically bound to all prior records and persisted as human-readable JSONL local state. It exposes tools to append records, verify chain integrity (pinpointing tampering), query records by actor/action/target/time, and fetch ledger stats—with no update or delete capabilities by design.
    1
    Apache 2.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables agents to read and write a durable, append-only memory with verified provenance, supporting search, record capture, and context preparation across sessions and machines.
    MIT