
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
memory3l-mcp
Advanced tools
Auditable three-layer memory over MCP: an append-only fact ledger and verifiable invariants I1-I5, served read-only from SQLite
Auditable memory over MCP: an append-only fact ledger plus verifiable invariants, served read-only from SQLite.
The
mcp-name:string above andserver.json'snamemust stay identical: the MCP Registry verifies PyPI package ownership by matching them. Changing one without the other fails verification.
A memory system claims it never loses a fact. Almost none can prove it, because nothing keeps an independent record to compare against.
memory3l keeps one: an append-only ledger of every (slot, value) ever
extracted, written when each summary is created — including the summaries later
overridden or merged away, which is exactly where facts disappear. This server
exposes the cross-check as tools, so an agent can ask whether a value is still
reachable, why a fact left the working set, or whether a deletion actually
completed, and get an answer derived from persisted state rather than from the
summary's own paraphrase.
The checks are the product. The storage is ordinary SQLite.
uvx memory3l-mcp # no install step, runs from PyPI
The audit core (memory3l) is installed as an ordinary dependency. MEMORY3L_ROOT
is only needed when developing against a source checkout:
MEMORY3L_ROOT=/path/to/Memory uvx memory3l-mcp
The ledger defaults to ~/.memory3l/memory3l.db and is created on demand, so a
fresh install starts and answers episodes: [] instead of failing. Point it
somewhere else with MEMORY3L_DB, or --db PATH.
python3 scripts/seed_demo.py --db /tmp/demo.db # generates a real ledger offline
MEMORY3L_DB=/tmp/demo.db uvx memory3l-mcp
No network, no API key, no Redis.
Read-only by default. Without --allow-write (or MEMORY3L_ALLOW_WRITE=1) the
server is an audit surface: it can inspect memory but not create it.
| Tool | Answers |
|---|---|
memory_context | the memory block to put in your prompt — call before answering |
remember | record a completed turn — call after answering |
store_info | which database is served, whether it is readable, and whether memory is on |
list_episodes | which episode ids exist — call this first |
audit | the invariant report for one episode |
audit_summary | the same, aggregated over every episode |
current | the newest value of every slot, as the model should be told it |
history | every value a slot ever held, with supersession |
fact | why one fact left the working set (live / archived / missing) |
evidence | the original dialogue a fact was extracted from |
tombstones | what was erased, what is provably gone, what prose remains |
temporal | the versioned projection, its anomalies, and its DDL |
append_facts | intake for your own extractor; it cannot make a fact visible |
The memory half is two calls per turn, and the order matters:
memory_context(episode_id) # before you answer
remember(episode_id, user_message, agent_message) # after you answer
memory_context returns memory in a fixed order (recent dialogue, current values,
index titles, summary chain) with no persona attached, so it drops into any prompt.
An empty block is the correct answer for a new conversation.
MCP has no hook, so nothing is automatic: if you skip memory_context the
agent answers without memory even though remember recorded the turns. The
memory3l skill is what teaches an agent to make both calls.
Two operational notes:
remember is where the model call goes. It summarises, so each turn costs a
summariser call. Point --summarizer at a small model.episode_id is one conversation;
the chain and the turn counter are read back from SQLite, so a restarted server
continues rather than starting over.Without a model (--summarizer none) memory still works — turns are stored verbatim
— but nothing is extracted, so the current-value registry stays empty and the audit
tools have nothing to check. store_info reports which mode you are in rather than
letting the difference be discovered later.
An episode with no facts satisfies every invariant trivially, so an unknown episode is an explicit error rather than an empty pass. A tool that answers "no violations" about an episode that does not exist is worse than one that fails.
Any MCP client that can launch a stdio server. For DSH, add one row to
$DSH_HOME/profiles/<name>/cordis.patch.yml:
- insert:
- id: mcp-memory
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: memory
transport: stdio
command: uvx
args: ['memory3l-mcp']
env:
MEMORY3L_DB: /path/to/your/memory3l.db
Tools then appear as mcp__memory__audit, mcp__memory__current, and so on —
the same mcp__<server>__<tool> shape Claude Code and Codex use. DSH passes the
env entries through to the server (credential-shaped variables are scrubbed from
the ambient environment, which is why the ledger path is set explicitly here).
skills/memory3l/SKILL.md is an Agent Skills
bundle that teaches an agent when to reach for these tools and how to report the
answer without overstating it — chiefly that "no violations" is not a guarantee
that nothing was lost.
Install it into the cross-client directory so every compliant agent sees it:
memory3l-mcp-install-skill # → ~/.agents/skills/memory3l
Skills installed there are visible to any client that scans .agents/skills/,
and vice versa. No per-client adaptation. The command refuses to overwrite an
existing skill whose content differs — pass --force if you mean it, or
--target DIR to install somewhere else.
MEMORY3L_ROOT=$PWD PYTHONPATH=$PWD/src python -m unittest discover -s tests
python scripts/seed_demo.py --db /tmp/demo.db
Requires mcp>=2. Note that mcp 2.0 renamed FastMCP to MCPServer and made
mcp.server.fastmcp raise on import; this package targets 2.x.
MIT
FAQs
Auditable three-layer memory over MCP: an append-only fact ledger and verifiable invariants I1-I5, served read-only from SQLite
We found that memory3l-mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.