← All posts

Claude Code

Claude Code keeps forgetting your project? How to add persistent memory

Claude Code understands your codebase brilliantly and remembers almost none of it. Here's the precise shape of the gap, why the obvious fixes stop working, and the setup that actually holds.

You spend an hour with Claude Code working out why a background job intermittently double-charges customers. It's subtle: a retry wrapper interacting badly with an idempotency key that's generated per attempt rather than per request. You fix it, you ship it, you close the terminal.

Three weeks later a new session touches the same file, sees the idempotency key being threaded awkwardly through four functions, and helpfully offers to simplify it.

This is the thing people mean when they say the agent "forgot". It isn't a reasoning failure — the model is perfectly capable of understanding the constraint. It's a storage failure. Nothing wrote the constraint anywhere the next session could find it.

The short answer

Claude Code persists instructions and does not persist knowledge. Add an MCP memory server and it can do both:

claude mcp add --transport http knownbase https://knownbase.dev/mcp

Then tell it when to use that server, with a short section in CLAUDE.md (the exact wording is at the bottom of this post). That's the whole setup. The rest of this explains why the simpler answers don't hold, so you can tell whether you actually need this yet.

What Claude Code keeps between sessions today

Three things genuinely persist, and it's worth being exact about their limits.

Your repository

Obviously. Claude Code re-reads your code every session, which is why it can be immediately useful on a project it has "never seen". But your code records what the system does, never why it does it that way. The idempotency key above looks like clumsy code, not like a hard-won fix.

CLAUDE.md

Loaded into every session, so anything in it is reliably present. This is genuinely valuable and is the right home for standing rules: how to run tests, what conventions to follow, what never to touch.

Built-in memory

Claude Code can record a small number of durable memories — preferences, standing facts. Useful, and deliberately small. It's designed for "remember that I use pnpm", not for "here are 300 findings about a two-year-old codebase".

Why the obvious fix stops working

The natural response to all this is: put it in CLAUDE.md. And that works, for a while, and then it doesn't — for a reason that isn't about discipline.

CLAUDE.md is loaded in full, on every turn. That's exactly what makes it reliable, and exactly what caps its size. Consider what happens as it grows:

SizeWhat happens
~100 linesIdeal. Cheap, entirely relevant, read carefully.
~500 linesStill fine, but most of it is irrelevant to any given task.
~1,500 linesMeaningful context cost on every message, and the three lines that matter today are buried in 1,497 that don't.
~3,000 linesYou're paying for the whole file constantly, crowding out the code the agent needs to read, and nobody maintains it any more.

The mismatch is structural. Instruction files work by staying small. Project knowledge only ever grows. You can't fix a growth problem with a format that requires the opposite.

(This is a trade-off, not a flaw. The full comparison, and where each one wins.)

The second loss: compaction

Even within a single session, knowledge leaks. When a long session fills its context window, earlier turns get summarized to make room. Summarization is lossy by design, and it's lossy in the direction that hurts most: it preserves the narrative shape and drops the specifics.

“We investigated the double-charge issue and fixed it” survives compaction. “The retry wrapper regenerates the idempotency key per attempt, so a retried request looks new to the provider” frequently doesn't. The sentence that took an hour to earn is exactly the sentence a summarizer treats as detail. More on what compaction takes.

What persistent memory actually looks like

The fix is to put knowledge somewhere outside the context window that the agent can search rather than load. That difference is the whole design:

Instruction fileLoaded entirely, every turn. Must stay small.
Memory serverSearched on demand. Only matching notes enter context. Can hold anything.

Claude Code reaches these over MCP, the same mechanism it uses for every other external tool. The agent gets a handful of tools — search, read, write — and the store lives on the other side of them.

Setting it up

Create a free workspace (3 projects, 300 notes, 2 MCP keys, no card), then:

claude mcp add --transport http knownbase https://knownbase.dev/mcp

Run /mcp to confirm the server is listed. The first tool call opens a browser for OAuth sign-in — there's no key to paste. Full details, including the API-key path for CI, are on the Claude Code integration page.

The part everyone skips

Connecting the server is necessary and not sufficient. An agent with tools available won't reliably use them at the right moments unless you say when. This paragraph in CLAUDE.md is what turns a connected server into a working habit:

## Project memory

This project has persistent memory via the `knownbase` MCP server,
under project "<your-project>".

Before starting work on an unfamiliar area, `search_notes` for prior
decisions, constraints and debugging findings about it.

When you finish something durable, `upsert_note` it:
- an architecture decision, with the reasoning and the alternatives
- a non-obvious root cause (the explanation, not the patch)
- an environment or deployment constraint
- an approach that was tried and rejected, and why
- a handoff, if you're stopping mid-task

Code lives in git. Store the reasoning here. Don't store transcripts,
generated output, or anything unconfirmed.

Note that this section is small — a rule about where knowledge goes, not the knowledge itself. That's the CLAUDE.md-shaped part of the problem, and it stays that size forever.

Back to the double-charge bug

With this in place, the end of that first session looks like:

upsert_note({
  project: "billing",
  title: "Idempotency key must be generated per request, not per retry",
  tags: ["debugging", "constraint", "payments"],
  status: "active",
  body: "Double-charge root cause: retryWithBackoff() regenerated the\n" +
        "idempotency key on each attempt, so the provider saw each retry\n" +
        "as a new charge. The key is now threaded from the caller.\n" +
        "It looks over-engineered in src/billing/charge.js:64 — it isn't.\n" +
        "Do not move key generation back inside the retry wrapper."
})

Three weeks later, before touching that file, the agent runs search_notes({ project: "billing", query: "idempotency retry" }), gets one result back, and doesn't offer to simplify it. The hour was spent once.

When you don't need this yet

Honestly: if your project is a few weeks old, fits in a short CLAUDE.md, and you're the only person on it using one tool, built-in memory is fine and this is overhead. The threshold is usually one of:

  • You've watched an agent re-solve something you know it already solved.
  • Your CLAUDE.md is past a few hundred lines and still growing.
  • More than one agent, or more than one person, works on the codebase.
  • Sessions routinely run long enough to compact.

Below that line, skip it. Above it, the compounding is real: the store is nearly useless in week one and answers three questions a day by month two.

Related reading

FAQ

Does Claude Code have memory between sessions?

Partly. CLAUDE.md is reloaded every session and Claude Code can record a small set of durable memories, so instructions and standing facts persist. What doesn't persist is what a session discovers — reasoning, root causes, rejected approaches. That needs an external store.

Will a bigger context window fix this?

No. A larger window helps within a single session. It does nothing for a session that has ended, because the window starts empty every time. Capacity and persistence are different properties.

How much does this slow the agent down?

One search at the start of a task, one write at the end. Search results are snippets rather than full note bodies, so a search typically costs less context than reading a single source file.

What if the stored notes go out of date?

They will, so the store needs to make that visible: timestamps, per-note status, and revision history, plus the habit of updating a note rather than adding a contradicting one. A memory layer with no notion of time will eventually hand an agent last year's answer with today's confidence.

Give Claude Code a memory that outlives the session

One command to connect, OAuth sign-in, and every later session can search what the last one learned. Free plan, no card required.

Create free workspace