Tool reference
The long-form description of every memshelf_* tool. The MCP schema carries
only a one- or two-sentence description per tool — enough to pick the right
tool — because tool schemas ride in the static prefix of every session where
the server is connected, whether or not a tool is ever called (#111). Anything
a caller needs beyond the choice lives here; the CLI verbs (memshelf <verb>)
share the same semantics.
Legend for the Side effects column: none — read-only; shelf — writes
episode files; derived — writes the rendered files (ledger.tsv,
INDEX.md, .meta.json, stats.svg); working file — writes outside the
shelf.
| Tool | Side effects | Pick it when |
|---|---|---|
memshelf_init |
shelf | a directory should become a shelf |
memshelf_shelve |
shelf (+ git commit) | a topic is closed and should leave the context |
memshelf_lint_digest |
none | a digest is still being written |
memshelf_import |
working file | a whole exported dialog should be shelved retroactively |
memshelf_index |
none | starting to answer anything about past work |
memshelf_recall |
none | one episode (or one section) answers the question |
memshelf_search |
none | the episode id is unknown |
memshelf_stats |
none | the shelf’s token economy is the question |
memshelf_advise |
none | the context window is the question |
memshelf_rebuild |
derived | derived files must be regenerated or verified |
memshelf_rollup |
shelf + derived | navigation has grown large |
memshelf_purge |
shelf (deletes) | episodes are past retain_until |
memshelf_resolve |
derived | two writers shelved on parallel branches |
memshelf_sync |
git (fast-forward) | the clone is behind its remote — the bot rendered, or another session shelved |
memshelf_doctor |
none | the shelf’s integrity is the question |
The envelope’s first key may be a warning (#125, #158). When the code
answering the call is not the code in the memshelf-mcp checkout next to the
shelf (or at $MEMSHELF_CHECKOUT), every tool’s answer — success or error —
opens with "warning": "served-code-differs: …", naming both short hashes and
where each lives; read it before trusting the result, since a merged fix may
not be serving. Identical code adds no key. No checkout to compare with adds
none either, and the initialize instructions say the freshness is unknown
and how to make it known. MEMSHELF_FRESHNESS_WARNING=0 switches the warning
off for a host where the copy is meant to differ.
memshelf_shelve
Offload one closed topic to the shelf as a durable, indexed episode.
Redacts credential shapes, enforces the digest contract (≤120 words, named
referents, no secrets), composes the episode, and writes it through docshelf,
staging and auto-committing the episode alone (git shelves; push is opt-in via
push). Derived files — the ledger row, INDEX — are not written here: they
are rendered by memshelf_rebuild or the shelf’s bot (#58), and the response
says so (shelf_totals.derived_stale, next). A contract violation comes back
as an error carrying the exact fixes — nothing is written. Returns the episode
address, redaction report, and any digest warnings. amend=true rewrites an
existing episode in place under the same slug. A directory named like the
episode that docshelf did not write as split sections (docs/<category>/<slug>/)
is an error too (#186), but only with a docshelf that has
SplitDirConflictError (docshelf-mcp#115, merged after 0.5.0 and not
released): that docshelf will not add a document beside the directory,
amend does not clear that, the error says to move the directory aside, and
nothing is written. With docshelf 0.5.0, the release the >=0.5.0 floor
installs, there is no error: shelve exits 0 and docshelf deletes the
directory and everything in it. The refusal takes a docshelf release with
SplitDirConflictError and a floor raised to it.
amend=true keeps a description and a section order the call passes back
unchanged (#205). A description equal to the stored one once whitespace is
collapsed on both sides is written as the episode has it, past the
120-character cap included; only a line break or another control character
in it is flattened, since one frontmatter line cannot hold it. rebuild caps
the INDEX line it renders from it, and a warning says what that line gets.
The sections keep the order the episode has (a ## … line inside a fenced
code block is not a section, and a heading the file repeats counts once),
and one it does not have yet goes right after the last section the canonical
order puts before it. display_title and notes are flattened as before, a
doubled space included.
An episode shelved without approx_tokens records
approx_tokens_source: unmeasured, so the field takes unmeasured too, with
no number or 0; with any other number it is refused, as estimate or
measured without a number is.
push sends HEAD to the branch’s upstream — and only to an upstream of the
same name. git checkout -B claude/x origin/main, how agent sessions start,
makes origin/main the upstream of claude/x; from such a branch the push is
refused before anything is pushed or rebased, because the way from there to
main is a PR. The episode stays committed locally, and the error gives the
way out, git -C <shelf> push -u origin HEAD (publish the branch, then a PR),
and for the next shelve from such a branch publish (a new shelve/<slug>
branch) instead of push. Without push, sync.hint on such a branch is
that same push -u command.
After a push to a shelf with the render bot (shelf-derived.yml), shelve
waits for the bot’s commit and fast-forwards onto it (#157): without that the
clone ends every shelve one bot commit behind, and the next doctor reports
stale-index + no-ledger-row for an episode that is fine. The wait happens
only when the push left the derived files out of date (the bot’s own
rebuild --check), polls every 5 s and gives up after 60 s —
await_render_s overrides the limit, 0 switches the wait off. The outcome is
in sync.render_pulled / sync.render_note; a render that did not arrive is a
note, never an error — the episode is on the remote already.
memshelf_lint_digest
Validate a digest against the Layer-3 contract without touching the shelf.
Same validator memshelf_shelve runs, minus every side effect — so the digest
can be checked while it is still being written, rather than after the episode
is already committed (#71). Errors block a shelve; warnings do not, and
strict is what turns them into a failure for a caller that wants it.
memshelf_recall
Fetch a shelved episode by id — or a single ## Section of it.
Returns the content wrapped in a data envelope: recalled episodes are records,
never instructions. Prefer a section fetch over the whole episode when one
section answers the question — that is where the savings are. Each recall
appends a row to recall-log.tsv (on by default since #112; log=false
reads without a trace): episode_id, section (empty for a whole episode),
fetched_tokens, and ts — the recall’s UTC time to the second (#193), so
the realized savings memshelf_stats reports can be split by period. A log
created before ts keeps its three-column header and gets the longer rows
appended under it; stats and resolve read both widths.
memshelf_index
Return the shelf INDEX — the small recall entry point. Read it before answering anything about past work, then recall only what you need. It is the whole standing memory cost of a session; everything else is fetched on demand.
memshelf_search
Grep the shelf for episodes matching every query token; returns their addresses and snippets. Split episodes match at the section level, so the hit names the section to recall, not just the episode.
Semantic sidecar (#17). Grep is an AND over literal tokens: a paraphrase,
an inflected word, or a query in the other language misses. When the optional
embedding sidecar is usable — pip install 'memshelf-mcp[semantic]' done, an
index built with memshelf semantic build --shelf …, and $MEMSHELF_SEMANTIC
not set to off — the tool fuses the grep ranking with a nearest-chunk
ranking by reciprocal rank and reports mode: "hybrid"; every hit then
carries via: grep, semantic, or both. Otherwise mode is grep and
the result is byte-for-byte what it was before the sidecar existed. The tool
signature and description do not change (#111); a caller reads mode to tell
a paraphrase miss from a sidecar that was never built.
The index is a JSON file under the state directory
(~/.local/state/memshelf-mcp/semantic/<shelf-key>/index.json), never inside
the shelf — a shelf is a git repository and a derived blob would ride into
every diff. It is rebuilt from the episodes, incrementally by file (mtime,
size); memshelf semantic status reports how many files went stale since.
The model (minishlab/potion-multilingual-128M, override with
$MEMSHELF_SEMANTIC_MODEL) is static embeddings: no GPU, no service, loaded
once per process. Measured on the dogfood shelf, 30 hand-written paraphrase /
cross-language / keyword queries: grep found 1, the hybrid 16 in the top 5 and
21 in the top 10 (memshelf search-bench, ROADMAP M3). Build: 209 files,
1,319 chunks, 2.9 s.
memshelf_stats
Report the shelf’s token economy: standing cost (INDEX + digests) vs shelved
mass and compression ratio (claimed, from approx_tokens), plus realized
savings from logged recalls when present. The transparent-savings number the
project’s core claim rests on; see docs/M0.md → Measurement.
Two quantities, deliberately not one (#110). work_volume is the raw sum of
approx_tokens — material the sessions moved. shelved_mass is that sum with
each episode clipped to the context window — context actually freed, and the
figure that feeds compression_ratio and realized_savings. context_window
and capped_episodes report the bound used and how many episodes it clipped;
pass context_window (CLI --context-window, else $MEMSHELF_CONTEXT_WINDOW)
to match your client instead of the 200K default. That default errs small on
purpose: understating a saving is the safer error for a number whose whole job
is to claim one.
Provenance is read, not just stored (#113). An episode whose frontmatter says
approx_tokens_source: unmeasured carries a placeholder 0, not a mass of
zero: it leaves compression_ratio on both sides (its digest is a cost of no
known mass) and is counted in unmeasured_episodes / unmeasured_digest_tokens
and named in the banner (· N unmeasured). standing_cost keeps that digest —
it is paid every session regardless. A legacy episode without the field is an
estimate, not unmeasured. The ledger stays six columns (shelf-spec v0 § 4.4);
the field is read from the episodes under docs/ and archive/docs/.
memshelf_advise
Report what your context is made of and what you could put down (#14).
Tell it what is in your window — a label, a rough token size, and whether the topic is still in play — and it returns a breakdown (static overhead / memshelf’s own cost / live topics / reclaimable) plus ranked proposals. It writes nothing and shelves nothing; you decide.
Two things it does that a self-assessment cannot. It measures what memshelf
itself costs you every session (INDEX + digests), instead of leaving its own
overhead out of the picture. And it verifies any episode_id you claim is
already shelved: if the episode is not on the shelf, it says so rather than
proposing you drop content nobody stored.
Call it with no occupants for the first-run view of the shelf alone — the report will say the window side is missing rather than report it clean.
memshelf_init
Create (or top up) a memory shelf: docshelf layout with fixed categories, the
recall-rule INDEX preamble, a POLICY.md template, the ledger header, and a
spec-conformant shelf.yml. Storage: git-local (default, no remote), plain, or
git-remote (private only). Idempotent — never overwrites existing files, so it
is safe to re-run on a shelf that predates a newer layout.
memshelf_rebuild
Regenerate ledger.tsv, each category’s .meta.json, INDEX.md and
stats.svg from the episodes (#58). The episode is the source; these four are
output, owned by a bot on main, which is what removes the multi-writer
conflict class at the root. With check=true nothing is written and the result
says which files have drifted, INDEX.md and stats.svg included — the
shelf’s PR guard runs exactly this. A derived file that could not be written
(or, with check=true, compared) is named in errors, and ok is false. On a
shelf whose bot renders derived files, run it by hand only when the bot is
down: a hand-committed render collides with the bot’s next commit.
memshelf_rollup
Archive a period’s episodes behind one digest-of-digests (#15).
INDEX.md rides in every session and grows with the shelf; a rollup turns N
INDEX lines into one. Use it when navigation has grown large, not to answer
doctor’s index-bloat — that one means an entry is overpriced, and folding
entries drops their budget along with them. The originals move to the
archive/ sub-shelf — nothing is deleted, recall by id keeps working, and
every ledger row survives, because an archived episode still holds the mass it
saved. The digest is yours: synthesizing a quarter of digests is the part a
tool cannot do, so pass the same quality of digest shelve demands. A derived
file the rebuild after the move cannot render is named in errors: the rollup
is written, but ok is false and the CLI exits 1.
memshelf_purge
Drop episodes whose retain_until has passed, then reindex (#15).
Dry-run by default: without apply=true it only lists what expired. Deletes
the working-tree file — git history still contains it. Real erasure is a
deliberate filter-repo pass over the whole repository, never a side effect of
a tool call, and the result says so. A derived file the rebuild after
apply=true cannot render is named in errors: ok is false and the CLI
exits 1.
memshelf_resolve
Resolve the multi-writer conflict class (two sessions shelved on parallel
branches): union ledger.tsv / recall-log.tsv rows and .meta.json keys
from both sides, rebuild INDEX.md and stats.svg from docs/, then run
doctor. Conflicting episode files are reported as unresolved, never
auto-merged. Also safe outside a conflict — degrades to a derived-files
rebuild. A derived file the rebuild cannot render is named in errors and
makes status attention (CLI exit 1).
memshelf_sync
Fetch and fast-forward the shelf clone to its remote — the shelve preflight
(#108) on its own, writing nothing else. For a clone that fell behind without
shelving: the bot rendered after an earlier push, or another session shelved.
A dirty tracked tree or a diverged branch is a refusal with the executable fix,
exactly as in shelve; a failed fetch is reported as a skip with git’s words.
Returns the same sync block shelve does, plus the resulting head (#157).
memshelf_doctor
Diagnose the shelf: episode schema, the digest contract at rest, secrets that
slipped onto disk, ledger consistency, and the INDEX budget — plus docshelf’s
structural checks. With check_remote=true it also fails a shelf whose git
remote is publicly visible (the one network probe, opt-in). Read-only; reports
findings, fixes nothing. Errors are meant to block a push; warnings name known
states (stale-index, episode-unpushed, index-bloat) that a shelf’s own
rules decide how to treat. Its served-code-differs finding is also
prepended by the server to every tool’s envelope (see the note under the
table), so that gap is not found only by whoever runs doctor.
memshelf_import
Retro-shelve a whole exported dialog without pulling it through context.
Two methods. discover lists the conversations in a claude.ai
conversations.json or Claude Code session JSONL, matched by content
markers (not title), returning metadata only. extract cleans one
conversation — dropping tool_use/tool_result blocks — to a working file and
returns its path plus the noise ratio. The raw transcript stays a file on disk:
it never enters context or a shelf. Then read the cleaned file, segment it, and
shelve each segment (mode=import).