memshelf-mcp
memshelf-mcp gives an AI agent a private, local, git-backed memory shelf: it offloads closed topics as indexed Markdown episodes and recalls them on demand via INDEX → episode → section, instead of keeping everything in context.
memshelf_init— create or top up a memory shelf (layout, categories, POLICY.md, shelf.yml) as plain, local-git, or private-remote storage; idempotent, never overwrites.memshelf_shelve— offload one closed topic as a durable, indexed episode with LLM-written digest (contract-validated), redaction, optional sections/tags/retention date, and a git commit; supportsamend(rewrite in place),sync/push/publish(PR branch), and explicit
Supports cross-writing and reading the same memory shelf with Google's Gemini CLI, enabling vendor-portable memory across different AI platforms.
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., "@memshelf-mcp Shelve this conversation as 'design-review'"
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.
memshelf-mcp
Put your agent's memory on a shelf, hand it the index.
_ _ __
_ __ ___ ___ _ __ ___ ___| |__ ___| |/ _|
| '_ ` _ \ / _ \ '_ ` _ \/ __| '_ \ / _ \ | |_
| | | | | | __/ | | | | \__ \ | | | __/ | _|
|_| |_| |_|\___|_| |_| |_|___/_| |_|\___|_|_|
____________________________________________
| INDEX >> | E-01 | E-02 | E-03 | E-04 | ... |
|__________|______|______|______|______|_____|
memory shelves for AI agentsWhat this is
Long-running agent sessions burn tokens re-sending history and lose detail to lossy auto-compaction. memshelf applies the docshelf pattern — tiny index in context, bodies fetched on demand — to the agent's own working memory:
Closed conversation topics, research dumps, and bulky tool output are offloaded to a local shelf as Markdown episodes.
Each episode carries an LLM-written, contract-validated digest that preserves decisions, rejected alternatives, artifacts, and open threads.
The agent keeps only
INDEX.md(kilobytes) + digests in context and recalls exact sections via INDEX → episode → section slice over MCP.
Positioning in one sentence: claude-mem's loop, git's substrate, docshelf's navigation — episodic memory you can grep, diff, review, and carry between hosts. Private and local by default: the standard storage mode is a local git repo with no remote configured. The tool is public; the memory never is.
Related MCP server: hmem
Measured, not promised
One week of dogfooding on the live shelf — full numbers and methodology in
docs/demo.md:
Measure | Result |
Episodes on the shelf | 34 |
Standing cost in every session (INDEX + digests) | ~8.6K tokens |
Shelved mass those episodes replace | ~1.9M tokens — ≈220 : 1 |
One question answered from memory | ~1.8K tokens (INDEX + one episode) |
Recall test: fresh agent, INDEX path only | 5 / 5 — zero misses, zero over-fetch |
Tokens are counted as chars/4 everywhere, so the ratios are estimator-independent; absolute counts move with the tokenizer.
Quick start
As an MCP server:
# Claude Code
claude mcp add memshelf -- uvx memshelf-mcp// Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"memshelf": { "command": "uvx", "args": ["memshelf-mcp"] }
}
}As a Claude Desktop extension — adapters/claude-desktop/:
an .mcpb bundle installed from Settings → Extensions, with a Default
shelf setting so calls need not repeat the path. Nothing has to be installed
alongside it — not even Python.
As a Claude Code plugin — adapters/claude-code/:
a /shelve skill plus SessionStart / SessionEnd / PreCompact hooks.
Or from the shell (pip install memshelf-mcp, Python ≥ 3.10) — the same
loop, no MCP:
memshelf init --shelf ~/my-shelf --name "My working memory"
memshelf shelve --shelf ~/my-shelf --slug 2026-07-23-topic --kind topic \
--digest "What was decided, what was rejected and why, what stays open." \
--section "Decisions=What was decided, and what was rejected instead — one line each."
memshelf recall --shelf ~/my-shelf --id 2026-07-23-topic --section Decisions --log
memshelf rebuild --shelf ~/my-shelf # render ledger/INDEX from the episode (a bot's job on shelves that have one)
memshelf stats --shelf ~/my-shelf # claimed + realized savings
memshelf doctor --shelf ~/my-shelf # exit 1 on integrity errorspip install 'memshelf-mcp[semantic]' adds an embedding sidecar so search
also finds paraphrases and the other language (memshelf semantic build --shelf ~/my-shelf once; MEMSHELF_SEMANTIC=off turns it off). Optional:
the base install stays grep-only and as light as it is.
Tool surface
One verb per job; the same names over MCP (memshelf_*) and in the CLI
(long-form descriptions: docs/tools.md):
Tool | What it does |
| Create (or top up) a memory shelf: docshelf layout, fixed categories |
| Offload one closed topic as a durable, indexed episode; |
| Validate a digest against the contract without touching the shelf |
| Retro-shelve a whole exported dialog without pulling it through context |
| Return the shelf INDEX — the small recall entry point |
| Fetch an episode by id, or a single |
| Grep the shelf; returns matching episodes |
| The shelf's token economy: standing cost vs shelved mass, claimed vs realized |
| What your context is made of and what you could put down — proposals only |
| Regenerate every derived file from the episodes |
| Archive a period behind one digest-of-digests |
| Drop episodes past |
| Settle multi-writer conflicts: regenerate derived, union the recall log |
| Diagnose: episode schema, digest contract at rest, secret shapes, index bloat |
| CLI only — which installed consumer (pipx, Desktop extension) answers calls, and what is merged but unreleased (#125) |
| CLI only — fetch and fast-forward the shelf clone to its remote (#157) |
| CLI only — remove H2 split directories git never got (migration for #109) |
| CLI only — episodes grouped by frontmatter tag (#18) |
| CLI only — who mentions whom: cross-episode id references as JSON or Mermaid (#18) |
| CLI only — one quarter of the shelf as a Markdown retrospective (#18) |
| CLI only — bootstrap a fresh session from INDEX + selected episodes or sections (#18) |
| CLI only — INDEX (± episodes) as one self-contained HTML page for phone-side reading (#18) |
| CLI only — |
| CLI only — hit@1 / hit@k / MRR of grep vs hybrid on a |
The rules the tools enforce
The digest is a contract, not a convention. It is the only thing read at
recall before fetching a body, so a weak one devalues the whole episode.
lint_digest runs the same validator as shelve with no side effects
(--strict turns warnings into failures); errors block a shelve, warnings do
not — a pure reference digest legitimately carries no decision marker. A
rejected digest is a feature: the tool prints exactly what to fix and writes
nothing.
--amend re-runs the whole pipeline — redaction, the digest contract,
composition — so an amended episode is exactly as guarded as a fresh one,
which a hand-edit of the file never is. Amending a slug that is not on the
shelf is an error, not a create.
The episode is the source; everything else is output. ledger.tsv,
INDEX.md, stats.svg and each category's .meta.json are derived:
shelve writes and commits the episode alone, rebuild renders the rest —
delete all four and rebuild restores them byte-identically. That is what
makes two sessions shelving in parallel a non-event: the merge is clean by
construction. On a shared shelf, let a bot own the derived files on main —
ready-to-copy workflows in adapters/shelf-repo/;
rebuild --adopt migrates an older shelf once, rebuild --check is the
CI guard.
Two consequences worth stating plainly, because getting them wrong costs a merge conflict:
doctorreportsno-ledger-rowandstale-indeximmediately after a correctshelve— on every branch,mainincluded. Nothing is broken: the episode is written, the derived files are not rendered yet. They clear on the nextrebuild— the bot's run, on a shelf that has one.Do not rebuild and commit the derived files by hand to silence them. That is exactly the conflict class the split removes: a hand-regenerated
ledger.tsv/INDEX.md/stats.svgmeets the bot's, and the merge stops being clean by construction. Wait for the renderer; on a shelf without a bot, runmemshelf rebuild --shelf .as its own step.
If those warnings persist for a day while episodes keep arriving, that is a
different state — the renderer is not lagging, it is stopped — and doctor
says so separately, as derived-stale at error severity. The day is counted
from when the renderer could first see the work, not from the ledger's last
commit: an episode pushed minutes ago onto a shelf whose ledger has not moved
since yesterday says nothing about the renderer, and saying otherwise sent
readers to a manual rebuild, which is the conflict this whole split exists
to avoid.
That arrival is read from this clone's reflog for the tracked upstream — the
one local record of when the ref moved here. A commit date is not a
substitute: it says when the episode was written, and «shelve now, push when
confirmed» is a documented way to work, so the two can be a working day apart.
Where the reflog cannot say — a fresh clone starts an empty one, which is what
CI and ephemeral agent sessions run in — doctor reports
renderer-wait-unknown at the unknown level instead of picking a verdict:
from there, «stopped» and «handed the work a minute ago» look the same, and
the renderer has to be judged where it can be observed, on its own job's run.
On a shelf with no upstream there is no renderer to be fair to, and the
ledger's own age stays the clock.
The ref the renderer is judged by is the render branch, not whatever the
checkout happens to track (#180). The bot renders one branch — the remote's
default one, read as origin/HEAD when the clone has it, else origin/main,
else origin/master — so a clone standing on a PR branch is not told
derived-stale for an episode that sits on origin/<pr-branch> only. That
episode waits for a merge, not for the renderer, and doctor says so with the
warning upstream-not-rendered; one that did reach the render branch is judged
there even from the PR branch, and a local commit on either stays
episode-unpushed.
There was a third way to hold stale-index forever, and it is fixed rather
than documented: docshelf split any episode past 50 KiB into section files
beside it, shelve committed the episode alone, and from then on this working
copy rendered an INDEX no other checkout could produce — no rebuild could
clear it, and search answered with addresses that existed on one machine
(#109). shelve no longer splits. A shelf that already carries such
directories keeps reporting them as local-split-dir until
memshelf prune-splits --shelf . --apply removes them; the episode file holds
every section, so nothing is lost. It is a dry run without --apply, and a
split directory that is committed is reported and left alone.
advise proposes, never writes. It answers the question the project was
founded on — a dead topic has been occupying 30K tokens for forty minutes.
The tool cannot see your window, so you tell it what is in there:
memshelf advise --shelf ~/my-shelf \
--occupant 'auth refactor=42000,closed' \
--occupant 'search dump=9000,idle=18' \
--occupant 'Case B verdict=12000,live,episode=2026-07-22-case-b-verdict'Three things keep it honest: it counts itself (INDEX + digests are in the
report, not left out of it), it verifies episode= claims before
proposing a drop, and it reports net — a topic too small to pay for its
own digest is not proposed at all.
Rollup shrinks navigation and nothing else. When INDEX grows into a real
share of your window, rollup collapses a period into one digest-of-digests
and moves the originals to archive/ — still reachable by recall and
search, every ledger row intact. The rollup digest is yours, not the tool's:
synthesizing a quarter is the part a tool cannot do.
index-bloat is not what a rollup is for. INDEX lists your episodes, so
its size grows with the shelf by design; its budget grows with the shelf too
(INDEX_BASE_TOKENS + INDEX_TOKENS_PER_ENTRY × listed). Over budget therefore
means entries are overpriced, never that there are too many of them — so
doctor reports the cost of one line, and the fix is to trim it and
rebuild. A rollup would remove entries and their allowance together and
leave the price where it was. Having the two paired the other way is what made
"archive a third of your memory" the standard way to silence a formatting
problem.
purge deletes the working tree, not history. Retention is opt-in per
episode (--retain-until); purge is a dry run until --apply — and even
then git history still has the file. Real erasure is a deliberate
filter-repo pass over the whole repository, never a side effect of a tool
call, and the purge report says so.
resolve regenerates derived paths, never merges them — a derived file
has no history, only a current correct value. The one file it unions is
recall-log.tsv, because a recall is an event, not a fact about the
episodes. Conflicting episodes are content, not mechanics: resolve
reports them and steps aside.
A tool answer says when the code behind it is stale. The same code ships
five ways, the version number does not move between releases, and twice a
merged fix was not acting because the copy answering the calls was behind
main (#125, #158) — found hours later, sideways, as what looked like a tool
bug. doctor reports that gap as served-code-differs; since a doctor finding
is read only by whoever runs doctor, the MCP server now says it in the response
itself: when the served package's hash differs from the memshelf-mcp checkout
next to the shelf (or $MEMSHELF_CHECKOUT), every envelope — success or error
— opens with a warning carrying that code, both short hashes and where each
lives. Identical code adds nothing. No checkout to compare with adds nothing
per call either, but the initialize instructions say the freshness is unknown
and how to make it known. MEMSHELF_FRESHNESS_WARNING=0 switches the per-call
warning off where the difference is intended.
The design rationale behind each rule lives in
docs/DECISIONS.md and
docs/ARCHITECTURE.md.
One memory, multiple AIs
The memory is vendor-portable, and that is a measured fact, not a design
intention: the same live shelf has been read and cross-written by Claude Code
(Anthropic) and Gemini CLI (Google) through one shelf-spec server —
protocol and field notes in docs/portability.md.
Status
M0 complete: the pattern was validated with zero code on a live shelf —
retro-import of months of material, then a week of shelve-at-close
(docs/M0.md). M1 shipped the server/CLI that enforces it,
plus the Claude Code plugin. Next milestones with exit criteria:
docs/ROADMAP.md; release history:
CHANGELOG.md.
Documents
Rendered site: https://ignatenkofi.github.io/memshelf-mcp/ — including the week-report infographic from the dogfood shelf.
Doc | What it covers |
Problem, the bet, hero scenarios, principles, non-goals | |
Tool reference: the long-form description of every | |
Episode format, digest contract, storage modes, triggers, MCP tool surface, portability model, privacy, failure modes | |
Prior-art survey (2026-07), platform built-ins, positioning, risks | |
Milestones M0–M3 with exit criteria | |
Decision log | |
M0 experiment protocol and results: cases, token ledger, recall test | |
Measured numbers from the dogfood shelf: compression, recall test, doctor findings | |
One memory, multiple AIs: the cross-vendor experiment | |
Claude Desktop and claude.ai projects without hooks: setup, recall, shelve, keeping the index current | |
A worked episode file and a memory-shelf INDEX | |
Claude Code plugin: | |
Claude Desktop | |
Mirror of record for the claude.ai account skill — not installable, exists to be compared |
Project memory shelf
shelf/ is this project's own memory shelf, dogfooding the tool it ships:
session episodes with decisions, rejected options and their reasons. Before
asserting anything about past work here, read shelf/INDEX.md
and fetch the one episode it points at — a guess about a past decision is a
defect, not an estimate. Derived files (ledger.tsv, INDEX.md, stats.svg,
.meta.json) are written by the same commit as the episode
(memshelf rebuild --shelf shelf); the shelf-pr-guard workflow fails a PR or
a push to main where they drifted.
Origin
Designed as RFC-0001 in the docshelf-mcp repo (#42, #43, #44); this repo is the project's home from 2026-07-13 on. The docshelf copy is frozen as a historical snapshot.
Related projects
docshelf-mcp — the sibling project and storage layer: PDFs/Markdown → chat-project-friendly document shelves with the same index-and-fetch economics (measured: ~3.7K tokens vs 1.2M per question). memshelf was born as RFC-0001 in its repo and reuses its splitter/indexer/read/search verbatim.
The dogfood memory shelf is a private repo — by design (MANIFEST principle 5).
License
MIT — see LICENSE.
mcp-name: io.github.ignatenkofi/memshelf-mcp
Available Tools
14 toolsmemshelf_adviseARead-onlyIdempotent
Report what your context window is made of and what you could put down — breakdown plus ranked proposals; writes nothing. Pass your window's occupants (label, size, live or not); with none it reports the shelf alone.
Inputs: params (required). Returns: result
memshelf_doctorARead-onlyIdempotent
Diagnose shelf integrity: episode schema, digest contract at rest,
leaked secrets, ledger consistency, INDEX budget. Read-only; fixes
nothing. check_remote adds the network probe for a public remote.
Inputs: params (required). Returns: result
memshelf_importAIdempotent
Retro-shelve an exported dialog without pulling it through context:
discover lists conversations by content markers, extract writes one
cleaned conversation to a working file for segmenting and shelving.
Inputs: params (required). Returns: result
memshelf_indexARead-onlyIdempotent
Return the shelf INDEX — the small recall entry point. Read it before answering anything about past work, then recall only what you need.
Inputs: params (required). Returns: result
memshelf_initBIdempotent
Create (or top up) a memory shelf: layout, categories, INDEX preamble, POLICY.md, shelf.yml. Idempotent; never overwrites existing files.
Inputs: params (required). Returns: result
memshelf_lint_digestARead-onlyIdempotent
Check a digest against the contract while it is still being written —
the same validator memshelf_shelve runs, writing nothing. strict turns
warnings into failures.
Inputs: params (required). Returns: result
memshelf_purgeADestructiveIdempotent
Drop episodes past retain_until, then reindex. Dry run unless
apply=true; removes working-tree files only, git history keeps them.
Inputs: params (required). Returns: result
memshelf_rebuildAIdempotent
Regenerate the derived files (ledger.tsv, .meta.json, INDEX.md,
stats.svg) from the episodes; check=true only reports drift. Hand-run it
only on a shelf whose bot is not rendering.
Inputs: params (required). Returns: result
memshelf_recallARead-onlyIdempotent
Fetch a shelved episode by id, or one ## Section of it, as a data
envelope. Prefer the section when it answers the question.
Inputs: params (required). Returns: result
memshelf_resolveAIdempotent
Settle the two-writers-on-parallel-branches conflict: union derived rows, rebuild INDEX, run doctor. Episode conflicts are reported, never auto-merged; safe outside a conflict too.
Inputs: params (required). Returns: result
memshelf_rollupB
Archive a period's episodes behind one digest-of-digests you write,
shrinking INDEX; nothing is deleted. Not the answer to index-bloat.
Inputs: params (required). Returns: result
memshelf_searchARead-onlyIdempotent
Grep the shelf for episodes matching every query token; returns addresses and snippets. Use when the episode id is unknown.
Inputs: params (required). Returns: result
memshelf_shelveAIdempotent
Offload one closed topic to the shelf as a durable, indexed episode —
redaction, digest contract and git commit included. Derived files are not
written here — they are rendered by memshelf_rebuild or the shelf's bot.
Details: docs/tools.md.
Inputs: params (required). Returns: result
memshelf_statsARead-onlyIdempotent
Report the shelf's token economy: standing cost vs shelved mass, claimed compression, realized savings from logged recalls.
Inputs: params (required). Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 14 tools
Each tool targets a clearly distinct operation within the memory-shelf lifecycle: reading (index, recall, search), writing (shelve, import), diagnostics (lint_digest, doctor), maintenance (rebuild, rollup, purge, resolve), and insights (stats, advise). Potential overlaps like lint_digest vs. doctor or rebuild vs. resolve are explicitly differentiated by scope and timing in the descriptions.
All 14 tools consistently use the memshelf_ prefix and lower snake_case. The action/artifact naming is predictable across the set, even when a few names are noun-like (stats, doctor) rather than purely verb_noun.
With 14 tools, the server sits in the well-scoped 3-15 range and covers a full lifecycle without obvious filler. Each tool maps to a distinct phase or concern of managing a memory shelf.
The surface covers initialization, creation, import, retrieval, search, metrics, validation, rebuild, rollup, retention purge, conflict resolution, and diagnosis. Minor gaps remain, such as no direct per-episode edit, manual delete, or export operation, though retention and rollup mitigate deletion and archival needs.
Maintenance
Related MCP Connectors
Persistent memory for AI agents: save, search and connect context across tools and sessions.
Persistent memory for AI agents. Search, store, and recall across sessions.
Shared long-term memory for AI agents: save and recall context as a searchable knowledge graph.
Memory system for AI agents with semantic search. Store and recall memories with ease.
Related MCP Servers
- AlicenseAqualityCmaintenanceProvides persistent, searchable memory for AI agents, enabling them to retain, recall, and reflect on information across conversations.1925 PyPI1MIT
- AlicenseNot gradedqualityFmaintenanceEnables persistent, portable memory for AI agents across sessions, devices, and providers with token-efficient 5-level lazy loading and automatic session capture.56 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to persist and recall episodic memories across sessions, consolidating experiences into reusable rules and lessons to reduce repeated mistakes and improve task performance.1MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to automatically capture durable knowledge and retrieve only relevant, token-bounded context from a secure local-first long-term memory, with support for progressive disclosure, snapshots, health diagnostics, and background tasks.Apache 2.0