Using a Snippet Manager as a Memory Bank for Claude Code
There are a few hundred memory systems for coding agents now. I haven't built one. I had a snippet manager open, it already had an MCP server inside it, and one evening I pointed Claude Code at it to see what would happen.
That's the whole idea, and I'd understand if you stopped reading here.
The reason I bothered: my memory bank for Claude Code had grown to 168 KB of Markdown across six files, all of it pulled into every session through @-imports, and the file describing the current state of the app still said version 3.1.1 while I was shipping 3.4.2. Every session began by loading fifty kilobytes of confident, wrong summary, and I only noticed because I went looking for something else.
Why not just Markdown files and grep
This is the first thing anyone asks, and for a lot of projects the answer is that you should.
If your context memory fits in one file that you read occasionally, files win. No server, no dependency, works everywhere, and you can diff it. Cline's original Memory Bank pattern (six files, read at session start) is still the sensible default, and Cline themselves say it's probably overkill for anything under a day's work. Ian Paterson has a good writeup of pushing plain files much further than I did, with an index and topic files, no MCP involved.
Files stopped working for me at the point where I had enough entries that loading them all was wasteful and enough churn that some of them were quietly wrong. Grep doesn't help with the second problem. Nothing greps a file to check whether it's still true.
What I wanted was to load two entries instead of forty, and to be able to see the forty without opening a text editor. The second part is the bit I'd underrated.
What I actually did
SnipperApp has workspaces, which I'd built for people who keep work and personal snippets apart. A workspace holds storages, storages hold folders, folders hold snippets. That turns out to be a reasonable shape for project memory too, so each project gets a workspace and each workspace has four folders:
Decisions closed questions, with the reasoning
Gotchas things that cost me a day
State what's in flight
Reference dashboards, tickets, runbooks
Every entry is one fact. The body has a what, a why, and a how-to-apply, because entries without the why get ignored by me and misapplied by the agent. Here's a real one, lightly trimmed:
Pending queue drains to the wrong storage if you hardcode the default
What: folder-less snippets queued under a hardcoded default storage sat at
pending_upload forever while sync reported success. The per-storage drain
only fetches rows for storages it actually syncs, so misrouted rows were
never even attempted.
Why: the queue has no invariant enforcement. A row's storage has to match a
drained storage or it's invisible, permanently.
How to apply: any new enqueue path resolves storage from the entity's own
workspace. Never a hardcoded id.
That one took most of a day to find. It's about four lines of consequence and none of it is visible in the code, which looks fine.
The retrieval trick, which is the only clever part
The MCP server exposes the library as tools, which is the obvious path, and also as resources, which is the one that matters here.
A resource listing gives back one line per snippet: the title, a snippet://<uuid> address, and the language. No bodies. So the agent sees an index of what exists, picks the two entries whose titles match what we're about to touch, and reads only those. Forty entries cost forty lines of index, not forty entries of content.
I'll admit I arrived at this by accident and then found out Anthropic had shipped the same shape deliberately. Agent Skills load a name and description up front, then the full skill only when something triggers it. Same three tiers. Mine is a scruffier version of it applied to project notes.
The argument isn't really token cost, though that's the easy version to explain. It's that retrieval quality degrades as you stuff the window, well before you hit any limit. Chroma's context rot work measured this across eighteen models and found reliability falling off on trivial retrieval tasks long before overflow. Loading fifty kilobytes of mostly-irrelevant memory isn't free even when it fits.
Two caveats I hit. The listing returns the hundred most recently updated snippets, newest first, so past that size you're seeing what you've touched lately rather than everything. And it isn't scoped to one workspace, so it spans every project at once. Both push in the same direction: titles have to stand on their own.
Titles are the whole index
This is the part I got wrong first, and nobody seems to write about it.
When the agent reads titles and decides from titles, the title is the retrieval index. I wrote a pile of entries called things like "Sync bug" and "Toolbar fix" and then watched it skip straight past the one it needed, because nothing in "Sync bug" tells you it's about CloudKit rejecting writes to fields that were never deployed.
The ones that work read like a claim you could disagree with:
cloudkit-production-schema-deployment
dedup-breaks-after-cloudkit-roundtrip
pending-queue-storage-misroute
docs-written-from-prd-not-app
toolbar-search-appkit-core
verify-ui-changes-yourself
Not beautiful. But you can tell from that list alone roughly where this codebase is sharp, and so can the agent, which is the point. I rename entries fairly often now, usually right after it fails to find one.
Reading your agent's memory like a normal person
I didn't expect to care about this part.
Because the store is an app I already use, the memory has a window. I can open it, scroll, see syntax highlighting, fix a wrong entry in about four seconds. When the agent writes something mid-session it appears in the sidebar while I'm working, which is how I catch it writing junk.
That sounds minor and it's the main reason I stopped using files. Not because files are slow. Because I never opened them. A directory of Markdown you never look at rots quietly and tells you nothing about it, and the 50 KB file that spent twelve builds claiming the wrong version wasn't really a technical failure. It was a nobody-ever-looks-at-this failure. Moving it somewhere I actually look fixed more than any retrieval improvement did.
I don't think this argues for my app in particular. It argues for keeping agent memory somewhere you already spend time. If that's Obsidian for you, obsidian-mind wires a vault up the same way and you should use that instead.
Wiring, briefly
Setup is one line and it's in the MCP docs, so I won't repeat it here. The server is a stdio binary inside the app. Nothing to install separately, no port, no daemon, and the app doesn't need to be running for the agent to reach the library.
Writing is create_snippet with a title, body, workspace and folder. It hands back a sentence with the new id in it. Tags, if you want them, are a second step, because there's no tags parameter on create: you make the tag, then attach it by id, which means an agent doing this in bulk should list tags once and reuse them. Slightly annoying. I keep meaning to fix it.
The standing instruction in my CLAUDE.md is roughly this. Check the memory index at the start, read what's relevant, write a new entry when we learn something expensive, and correct entries that turn out to be wrong. That last clause matters more than the rest put together and it's the one everybody leaves out.
What I haven't measured
I don't know how often it fetches the wrong entries. It feels like not often, which is exactly the sort of thing people say right before somebody asks for numbers, so treat it as unverified. I also don't know what fraction of what I've written has ever been read again. My guess is that it's low and that the Gotchas folder carries nearly all the value, but I haven't instrumented any of it and I'd rather say so than invent a figure.
The other limits are the obvious ones. It's a local database, so it's mine alone and this does nothing for team memory. It needs me in the loop, since an agent writing its own memory unsupervised produces volume rather than signal. And nothing here stops entries going stale. It makes a stale one visible and cheap to fix, which in my case was the entire problem, but that's a smaller claim than "solves memory".
If you want the background on connecting an assistant to a library at all, this walkthrough covers it. SnipperApp 3 is a one-time purchase with a trial if you want the exact setup, though the more portable takeaway is the index-of-titles shape, which works in whatever you already have open.
I'd like to hear how other people write their entries. The storage question feels settled to me. What belongs inside an entry doesn't.