·

Memory MCP With Claude Code CLI and VS Code

Set up Memory MCP in Claude Code CLI and VS Code so your AI agent can persist and recall context across sessions right from your editor.

Claude Code is the tool most likely to already have a CLAUDE.md doing part of this job, so this topic spends real time on where that file stops being enough and where Memory MCP picks up. Setup itself is quick — Claude Code has first-class MCP server management via claude mcp, and the same config works whether you're driving it from the terminal or from the VS Code extension, since both read the same project-level .mcp.json.


Installing and Connecting Memory MCP to Claude Code

Add the server at project scope so it's checked into .mcp.json and shared with your team, or at local scope if you want it private to your machine:

claude mcp add memory --scope project \
  -e MEMORY_FILE_PATH=/Users/dat/agent-memory/english-grammar-platform.json \
  -- npx -y @modelcontextprotocol/server-memory

Because MEMORY_FILE_PATH is a machine-local absolute path, committing it verbatim to a shared .mcp.json breaks for teammates on different machines. Two ways to handle it in practice:

{
  "mcpServers": {
    "memory": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-memory"],
      "env": {
        "MEMORY_FILE_PATH": "${AGENT_MEMORY_DIR}/english-grammar-platform.json"
      }
    }
  }
}

Claude Code expands ${VAR} references from the shell environment at launch time, so each developer sets AGENT_MEMORY_DIR once in their shell profile and the committed .mcp.json stays portable. The alternative — simpler for solo projects — is --scope local, which writes to a git-ignored local config instead:

claude mcp add memory --scope local \
  -e MEMORY_FILE_PATH=/Users/dat/agent-memory/english-grammar-platform.json \
  -- npx -y @modelcontextprotocol/server-memory

Verify the connection:

claude mcp list

The VS Code extension reads the exact same .mcp.json — there's no separate configuration surface. Open the repo in VS Code with the Claude Code extension installed, and the memory server shows up already connected in the extension's MCP panel with no additional setup. This is worth confirming explicitly the first time: open the panel, check the tool list includes create_entities, search_nodes, read_graph, and the rest of the nine tools from the previous topic.

Tips
- Use --scope project with an environment-variable path when the team shares memory; use --scope local for a solo, machine-specific graph.
- Run claude mcp list after adding — a silently-failed npx resolution (no internet, wrong Node version) shows up as a disconnected server, not a loud error.
- The VS Code extension and the CLI share one .mcp.json — you never configure memory twice for the same repo.


Designing a Memory Schema for Project Conventions and Decisions

Before Claude Code writes a single entity, decide on an entity-type vocabulary. Skipping this step is how teams end up with a graph where entityType is inconsistently "service", "microservice", and "backend-service" for the same category of thing, which quietly breaks search_nodes filtering by convention (the tool doesn't filter by type server-side, but your prompts and your own mental model do).

A schema that's worked well for mid-sized web/API projects:

entityType: "decision"     — an architectural or process choice, with reasoning
entityType: "constraint"   — a hard limit (infra, API, business rule)
entityType: "convention"   — a naming/style rule specific to this repo
entityType: "component"    — a service, module, or major file/directory
entityType: "incident"     — a bug or outage and what fixed it

Seed it once, explicitly, in a prompt at the start of a project:

Prompt to Claude Code:

  Store these as memory entities before we start:

  1. entityType "convention", name "api-error-format": all API error
     responses follow {"error": {"code": str, "message": str}} — defined
     in middleware/errors.py, do not use plain string error bodies.

  2. entityType "constraint", name "db-connection-limit": postgres-primary
     on the current RDS tier caps at 100 connections; any new worker pool
     sizing must account for this.

  3. entityType "decision", name "no-orm": we use raw SQL via sqlx instead
     of an ORM, decided 2026-02, because migrations needed hand-tuned
     query plans the ORM couldn't produce.

Claude Code translates that into three create_entities calls. Confirm the shape it actually wrote with read_graph once, early on, so you catch schema drift before it compounds:

{
  "type": "entity",
  "name": "no-orm",
  "entityType": "decision",
  "observations": [
    "We use raw SQL via sqlx instead of an ORM, decided 2026-02",
    "Migrations needed hand-tuned query plans the ORM couldn't produce"
  ]
}

Notice it split one sentence into two observations — that's normal and fine; search_nodes matches against the concatenated observation text either way.

Tips
- Fix your entityType vocabulary before the first real session — retrofitting consistency into an existing graph means manual delete_entities + create_entities passes.
- Run read_graph right after seeding to confirm Claude Code interpreted your prompt the way you meant it, not a paraphrase that drifted.
- Keep the seed prompt itself in a checked-in file (e.g. docs/memory-seed.md) so a fresh graph can be rebuilt if the memory file is ever lost.


Writing and Recalling Memories During Long Development Sessions

The pattern that works in practice: prompt Claude Code to check memory before starting non-trivial work, and to write memory after resolving something non-obvious — don't leave either direction implicit.

Start-of-task prompt:

  Before touching the rate limiter, search memory for anything
  related to "rate limit" or "throttle". Summarize what you find,
  then propose your approach.
Claude Code's tool call and response:

  search_nodes("rate limit")
  → 1 entity found: "rate-limiter" (component)
    - "Uses token bucket, not sliding window — sliding window was
       reverted in commit a3f21c9 due to burst rejections under
       mobile app load-test traffic"

  Based on this, I'll extend the existing token bucket implementation
  rather than reintroducing a sliding window.

That single lookup just prevented a regression an agent with no memory would have no way to know about — the commit history technically has the answer, but nothing prompts an agent to go looking for a revert three months old unless it already suspects one exists.

End-of-session capture is the half people skip. Make it a habit, not an afterthought:

End-of-task prompt:

  We just fixed the connection pool exhaustion bug. Store what we
  learned as a memory entity: entityType "incident", name
  "conn-pool-exhaustion-2026-08", observations covering the root
  cause, the fix, and the constraint it revealed about db-connection-limit.
  Also add a relation: conn-pool-exhaustion-2026-08 -> db-connection-limit,
  relationType "revealed".

That relation is what makes the graph model earn its keep over a flat log — a future session searching db-connection-limit now surfaces the incident too, without anyone having cross-referenced it by hand.

For very long sessions (multi-hour refactors), checkpoint memory mid-session rather than only at the end — if the session crashes or you run out of context and have to /compact, mid-session observations already on disk survive; anything still only in the conversation transcript doesn't.

Tips
- Make "search memory first" and "write memory after" explicit instructions in your prompts — Claude Code won't reliably infer when a fact is memory-worthy on its own.
- Add relations between new incidents/decisions and the components or constraints they touch — this is what makes future searches surface related context automatically.
- Checkpoint memory writes mid-session on long tasks; don't wait until the very end when a crash or /compact could lose the transcript first.


Memory MCP vs CLAUDE.md: When to Use Which

These aren't competitors so much as tools for different decay rates and different retrieval shapes. CLAUDE.md is read in full, every session, unconditionally — that's its strength and its cost. Memory MCP is queried selectively — that's its strength and its cost (you only get back what you thought to search for).

CLAUDE.md Memory MCP
Read pattern Always, in full, every session On-demand, via search/open
Best for Stable, universally-relevant rules (commit format, test command, repo layout) Facts relevant to some tasks, not all; things that accumulate over time
Context cost Fixed, paid every session regardless of task Near-zero until queried; scales with how much you search
Editing Manual, versioned in git, reviewed like code Agent-writable at runtime, no review step by default
Structure Free-form markdown Typed entities + relations — supports multi-hop queries
Failure mode Grows unbounded, everyone stops reading it carefully Stale/wrong facts silently persist and get retrieved as if current

Concretely: "commits must be prefixed [English] for changes under product-english/" belongs in CLAUDE.md — it's universally relevant, rarely changes, and costs nothing extra to have the agent read every time. "We reverted the sliding-window rate limiter because of burst rejections in March" belongs in memory — it's relevant only when someone touches the rate limiter, and stuffing every such incident into CLAUDE.md would make the file unreadable within a quarter.

The failure mode worth naming honestly: CLAUDE.md is git-reviewed, so a bad edit gets caught in PR review. Memory MCP writes typically don't go through any review at all — the agent decides what's memory-worthy and writes it directly. That's convenient and also how you get context poisoning: an incorrect inference stored as fact has no code-review gate to catch it before the next session inherits it as truth. Section "Memory MCP vs CLAUDE.md" isn't really "pick one" — most real projects use CLAUDE.md for the fixed rules and memory for the accumulating, task-conditional facts, and periodically audit the memory file the way you'd audit any unreviewed input (see Module 17's final topic on auditing memory quality).

Tips
- Put universally-relevant, rarely-changing rules in CLAUDE.md; put task-conditional, accumulating facts in memory.
- Remember CLAUDE.md is git-reviewed and memory typically isn't — treat memory writes with the same skepticism you'd apply to an unreviewed PR.
- If a memory entity's fact becomes universally relevant (every session needs it), promote it into CLAUDE.md and delete the redundant memory entity — don't keep it in both places where they can drift apart.


Tips

Tips
- Add memory MCP once per repo at project scope with an environment-variable path — both the CLI and the VS Code extension pick it up from the same .mcp.json.
- Fix your entity-type vocabulary before the first seed prompt; retrofitting consistency later means manual graph surgery.
- Make memory search-before and write-after an explicit part of your prompting pattern, not an assumption about agent behavior.
- Use CLAUDE.md for fixed, universal rules and memory for accumulating, task-conditional facts — and audit memory periodically since it skips code review by default.