# memshelf-mcp [Health: Active]

**Category:** 🧠 Knowledge & Memory  
**Repository:** https://github.com/ignatenkofi/memshelf-mcp  
**GitHub Stars:** 0  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/memshelf-mcp

## Description
Working memory for AI agents: digest-indexed episodes on a git shelf, recalled a section at a time.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `uvx` (confidence: high):

```json
"mcpServers": {
  "memshelf-mcp": {
    "command": "uvx",
    "args": ["memshelf-mcp"]
  }
}
```

## Documentation & README

# memshelf-mcp

> Put your agent's memory on a shelf, hand it the index.

[![PyPI](https://img.shields.io/pypi/v/memshelf-mcp)](https://pypi.org/project/memshelf-mcp/)
[![Python](https://img.shields.io/pypi/pyversions/memshelf-mcp)](https://pypi.org/project/memshelf-mcp/)
[![Glama](https://glama.ai/mcp/servers/ignatenkofi/memshelf-mcp/badges/score.svg)](https://glama.ai/mcp/servers/ignatenkofi/memshelf-mcp)
[![ci](https://github.com/ignatenkofi/memshelf-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/ignatenkofi/memshelf-mcp/actions/workflows/ci.yml)
[![security](https://github.com/ignatenkofi/memshelf-mcp/actions/workflows/security.yml/badge.svg)](https://github.com/ignatenkofi/memshelf-mcp/actions/workflows/security.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Docs](https://img.shields.io/badge/docs-ignatenkofi.github.io%2Fmemshelf--mcp-blue.svg)](https://ignatenkofi.github.io/memshelf-mcp/)
[![Sibling: docshelf](https://img.shields.io/badge/sibling-docshelf--mcp-green.svg)](https://github.com/ignatenkofi/docshelf-mcp)

```text
                              _          _  __
 _ __ ___   ___ _ __ ___  ___| |__   ___| |/ _|
| '_ ` _ \ / _ \ '_ ` _ \/ __| '_ \ / _ \ | |_
| | | | | |  __/ | | | | \__ \ | | |  __/ |  _|
|_| |_| |_|\___|_| |_| |_|___/_| |_|\___|_|_|
  ____________________________________________
 | INDEX >> | E-01 | E-02 | E-03 | E-04 | ... |
 |__________|______|______|______|______|_____|
        memory shelves for AI agents
```

## What this is

Long-running agent sessions burn tokens re-sending history and lose detail
to lossy auto-compaction. **memshelf** applies the
[docshelf](https://github.com/ignatenkofi/docshelf-mcp) pattern — tiny index
in context, bodies fetched on demand — to the agent's own working memory:

1. Closed conversation topics, research dumps, and bulky tool output are
   offloaded to a local shelf as Markdown **episodes**.
2. Each episode carries an LLM-written, contract-validated **digest** that
   preserves decisions, rejected alternatives, artifacts, and open threads.
3. 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.

## Measured, not promised

One week of dogfooding on the live shelf — full numbers and methodology in
[`docs/demo.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/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**:

```bash
# Claude Code
claude mcp add memshelf -- uvx memshelf-mcp
```

```jsonc
// Claude Desktop (claude_desktop_config.json)
{
  "mcpServers": {
    "memshelf": { "command": "uvx", "args": ["memshelf-mcp"] }
  }
}
```

As a **Claude Desktop extension** — [`adapters/claude-desktop/`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/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/`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/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:

```bash
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 stats  --shelf ~/my-shelf   # claimed + realized savings
memshelf doctor --shelf ~/my-shelf   # exit 1 on integrity errors
```

`pip 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`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/tools.md)):

| Tool | What it does |
|---|---|
| `init` | Create (or top up) a memory shelf: docshelf layout, fixed categories |
| `shelve` | Offload one closed topic as a durable, indexed episode; `--amend` rewrites in place |
| `lint_digest` | Validate a digest against the contract without touching the shelf |
| `import` | Retro-shelve a whole exported dialog without pulling it through context |
| `index` | Return the shelf INDEX — the small recall entry point |
| `recall` | Fetch an episode by id, or a single `## Section` of it |
| `search` | Grep the shelf; returns matching episodes |
| `stats` | The shelf's token economy: standing cost vs shelved mass, claimed vs realized |
| `advise` | What your context is made of and what you could put down — proposals only |
| `rebuild` | Regenerate every derived file from the episodes |
| `rollup` | Archive a period behind one digest-of-digests |
| `purge` | Drop episodes past `retain_until`, then reindex — dry run by default |
| `resolve` | Settle multi-writer conflicts: regenerate derived, union the recall log |
| `doctor` | Diagnose: episode schema, digest contract at rest, secret shapes, index bloat |
| `prune-splits` | CLI only — remove H2 split directories git never got (migration for #109) |
| `tags` | CLI only — episodes grouped by frontmatter tag (#18) |
| `graph` | CLI only — who mentions whom: cross-episode id references as JSON or Mermaid (#18) |
| `retro` | CLI only — one quarter of the shelf as a Markdown retrospective (#18) |
| `fork` | CLI only — bootstrap a fresh session from INDEX + selected episodes or sections (#18) |
| `mirror` | CLI only — INDEX (± episodes) as one self-contained HTML page for phone-side reading (#18) |
| `semantic` | CLI only — `build` / `status` / `drop` the embedding sidecar that turns `search` hybrid; lives outside the shelf, needs `pip install 'memshelf-mcp[semantic]'` (#17) |
| `search-bench` | CLI only — hit@1 / hit@k / MRR of grep vs hybrid on a `query<TAB>expected-id` file (#17) |

## 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/`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/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:

* **`doctor` reports `no-ledger-row` and `stale-index` immediately after a
  correct `shelve` — on every branch, `main` included.** Nothing is broken:
  the episode is written, the derived files are not rendered yet. They clear on
  the next `rebuild` — 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.svg` meets the bot's, and the merge stops
  being clean by construction. Wait for the renderer; on a shelf without a bot,
  run `memshelf 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.

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:

```bash
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.

The design rationale behind each rule lives in
[`docs/DECISIONS.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/DECISIONS.md) and
[`docs/ARCHITECTURE.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/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`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/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`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/M0.md)). M1 shipped the server/CLI that enforces it,
plus the Claude Code plugin. Next milestones with exit criteria:
[`docs/ROADMAP.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/ROADMAP.md); release history:
[`CHANGELOG.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/CHANGELOG.md).

## Documents

Rendered site: <https://ignatenkofi.github.io/memshelf-mcp/> — including the
week-report infographic from the dogfood shelf.

| Doc | What it covers |
|---|---|
| [`docs/MANIFEST.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/MANIFEST.md) | Problem, the bet, hero scenarios, principles, non-goals |
| [`docs/tools.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/tools.md) | Tool reference: the long-form description of every `memshelf_*` tool (the MCP schema carries only the short one) |
| [`docs/ARCHITECTURE.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/ARCHITECTURE.md) | Episode format, digest contract, storage modes, triggers, MCP tool surface, portability model, privacy, failure modes |
| [`docs/LANDSCAPE.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/LANDSCAPE.md) | Prior-art survey (2026-07), platform built-ins, positioning, risks |
| [`docs/ROADMAP.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/ROADMAP.md) | Milestones M0–M3 with exit criteria |
| [`docs/DECISIONS.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/DECISIONS.md) | Decision log |
| [`docs/M0.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/M0.md) | M0 experiment protocol and results: cases, token ledger, recall test |
| [`docs/demo.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/demo.md) | Measured numbers from the dogfood shelf: compression, recall test, doctor findings |
| [`docs/portability.md`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/portability.md) | One memory, multiple AIs: the cross-vendor experiment |
| [`docs/examples/`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/examples/) | A worked episode file and a memory-shelf INDEX |
| [`adapters/claude-code/`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/adapters/claude-code/) | Claude Code plugin: `/shelve` skill + SessionStart/SessionEnd/PreCompact hooks |
| [`adapters/claude-desktop/`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/adapters/claude-desktop/) | Claude Desktop `.mcpb` extension: builder, bundle checker, default-shelf setting |

## 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`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/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](https://github.com/ignatenkofi/docshelf-mcp/pull/42),
[#43](https://github.com/ignatenkofi/docshelf-mcp/pull/43),
[#44](https://github.com/ignatenkofi/docshelf-mcp/pull/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](https://github.com/ignatenkofi/docshelf-mcp)** — the
  sibling project and storage layer: PDFs/Markdown → chat-project-friendly
  document shelves with the same index-and-fetch economics
  ([measured](https://github.com/ignatenkofi/docshelf-mcp/blob/main/docs/demo.md):
  ~3.7K tokens vs 1.2M per question). memshelf was born as
  [RFC-0001](https://github.com/ignatenkofi/docshelf-mcp/tree/main/docs/rfc/0001-memshelf)
  in its repo and reuses its splitter/indexer/read/search verbatim.
- The dogfood memory shelf is a private repo — by design
  ([MANIFEST](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/docs/MANIFEST.md) principle 5).

## License

MIT — see [`LICENSE`](https://github.com/ignatenkofi/memshelf-mcp/blob/HEAD/LICENSE).

---

mcp-name: io.github.ignatenkofi/memshelf-mcp

