Chapter 13 of 14 · Part 4: Give Your Agent Memory

Chapter 13: The 6 Memory Tools and How to Use Them

By the end of this chapter, you will understand what each of the six memory tools does, know how to use the full memory API for reading, writing, updating, and versioning memories, and be able to build safe concurrent memory updates using preconditions.


The Big Idea

Memory stores give Claude a persistent knowledge layer. The six memory tools are the interface through which Claude interacts with that layer — and through which you, as the developer, can read, write, and audit memory content directly via the API.

These tools are automatic: when you attach a memory store to a session, Claude gains access to them without any additional configuration. You don't have to tell Claude to use them. But understanding what each tool does lets you design better memory architectures and debug memory behavior when something unexpected happens.

The six tools, as defined in the memory documentation:

Tool Description
memory_list List memories in a store, optionally filtered by path prefix
memory_search Full-text search across memory contents
memory_read Read a memory's contents
memory_write Create or overwrite a memory at a path
memory_edit Modify an existing memory
memory_delete Remove a memory

Two tools for finding memories (list and search). One for reading. Two for writing (write creates/overwrites; edit modifies). One for deleting.

Memory Read and Write — Knowledge Persists Across Sessions A central Memory Store cylinder. On the left, a session writes to it with an arrow labeled write/edit. On the right, a new session reads from it with an arrow labeled read/search. The six memory tool names (memory_list, memory_search, memory_read, memory_write, memory_edit, memory_delete) orbit the cylinder pulsing gently. Memory Store Knowledge persists across sessions — attach memory stores to carry learnings forward Memory Store memstore_01Hx… workspace-scoped text documents Session A task: analyze data /prefs/formatting.md /context/project.md /notes/mistakes.md memory_write Writes learnings when done Session B new session, same store /prefs/formatting.md ✓ /context/project.md ✓ /notes/mistakes.md ✓ memory_search Reads before starting task memory_list memory_search memory_read memory_write memory_edit memory_delete Memory is automatic: agent checks stores before starting · writes durable learnings when done · no extra prompting needed
Six-tool wheel diagram. Center: "Memory Store." Six spokes radiating outward, each labeled with a tool name and a brief action phrase. Color coding: blue for read operations (list, search, read), green for write operations (write, edit), red for delete. The three read operations are grouped. Caption: "Automatic when a memory store is attached. No configuration needed."

The Analogy

Think of memory tools as the operations in a well-organized filing system.

memory_list is looking through the file index: "show me all files in the /preferences/ drawer."

memory_search is the search box: "find every document that mentions 'Python formatting' anywhere in its content."

memory_read is pulling a specific file from the cabinet and reading it.

memory_write is creating a new file or replacing one that already exists at that location.

memory_edit is opening an existing file and making a targeted change — like correcting one sentence without rewriting the whole document.

memory_delete is removing a file from the cabinet.

The API you use to manage memory from your code mirrors these same operations — the same actions Claude takes automatically are available to you programmatically for seeding, auditing, and correcting memory content.

DiagramFiling cabinet illustration. Five drawers visible, each labeled with a path prefix (/preferences/, /project/, /user/, /decisions/, /archive/). Overlaid: tool icons pointing at relevant operations — magnifying glass (list, search) at the cabinet overview, an open folder (read) at an individual file, a pencil (write, edit) at a file being updated, an X (delete) at a file being removed.

How It Actually Works

memory_list — Browse by Path

Lists memories in a store, with an optional filter by path prefix. Returns metadata (path, size, sha256 hash) but not content.

page = client.beta.memory_stores.memories.list(
    store.id,
    path_prefix="/",
)
for memory in page.data:
    print(
        f"{memory.path} ({memory.size_bytes} bytes, sha={memory.content_sha256[:8]})"
    )

(Memory)

The path prefix filter uses trailing slash convention: /notes/ matches /notes/a.md but NOT /notes_backup/old.md. This lets you browse a specific "directory" in the memory store without accidentally matching sibling paths.

Use memory_list to:

  • Audit what memories exist in a store
  • Check which topics have been documented
  • Get file sizes before deciding whether to read them

memory_search — Find by Content

Full-text search across memory contents. Returns memories where the content matches the query. This is what Claude uses to find relevant memories before starting a task — it searches for terms related to the current work.

From the developer side, you can call the API equivalent to audit what Claude would find for a given query:

# Via the API, similar to how Claude uses memory_search internally
results = client.beta.memory_stores.memories.list(
    store.id,
    path_prefix="/",
)
# The actual search tool is available to Claude during sessions

The memory_search tool is the reason small, focused memories outperform large monolithic ones: search returns documents, not excerpts. A precise, focused memory document on one topic is more reliably retrieved than a sprawling document that covers many topics loosely.

memory_read — Get the Full Content

Reads a specific memory's full content by ID:

mem = client.beta.memory_stores.memories.retrieve(
    memory_id,
    memory_store_id=store.id,
)
print(mem.content)

(Memory)

memory_read is how Claude loads the full text of a memory it identified via memory_list or memory_search. Use this in your code to read and verify memory content — to confirm what the agent learned, check for errors, or export memory content.

memory_write — Create or Overwrite

Creates a new memory at a path, or overwrites an existing one. This is the primary write operation:

mem = client.beta.memory_stores.memories.write(
    memory_store_id=store.id,
    path="/preferences/formatting.md",
    content="Always use tabs, not spaces.",
)

(Memory)

If a memory already exists at the path, it's overwritten. If not, it's created. The path acts as the unique identifier within the store.

Safe writes with the not_exists precondition:

To only write if nothing exists at that path yet (preventing accidental overwrites):

client.beta.memory_stores.memories.write(
    memory_store_id=store.id,
    path="/preferences/formatting.md",
    content="Always use 2-space indentation.",
    precondition={"type": "not_exists"},
)

Returns 409 memory_precondition_failed if a memory already exists at the path. (Memory)

This is the safe-initialization pattern: seed memory only once, don't clobber what already exists.

memory_edit — Targeted Updates

Modifies an existing memory without overwriting it. Use this for updates rather than rewrites:

client.beta.memory_stores.memories.update(
    mem.id,
    memory_store_id=store.id,
    path="/archive/2026_q1_formatting.md",  # rename it
)

For content changes with concurrency safety, use the content_sha256 precondition — this ensures you're editing a specific known version of the content:

client.beta.memory_stores.memories.update(
    memory_id=mem.id,
    memory_store_id=store.id,
    content="CORRECTED: Always use 2-space indentation.",
    precondition={"type": "content_sha256", "content_sha256": mem.content_sha256},
)

Returns 409 memory_precondition_failed on hash mismatch. (Memory)

The content_sha256 precondition is the optimistic concurrency mechanism: if someone (or another session) changed the memory between when you read it and when you tried to update it, the SHA256 won't match and the update fails safely. This prevents race conditions in multi-agent or multi-session scenarios.

memory_delete — Remove a Memory

Removes a memory from the store:

client.beta.memory_stores.memories.delete(
    mem.id,
    memory_store_id=store.id,
)

Optionally, pass expected_content_sha256 for a conditional delete — only delete if the content is what you expect:

client.beta.memory_stores.memories.delete(
    mem.id,
    memory_store_id=store.id,
    expected_content_sha256=mem.content_sha256,
)

(Memory)

The hash-conditional delete prevents accidentally deleting a memory that was updated since you last read it.

Memory Versioning — The Audit Trail

Every mutation creates an immutable version record (memver_...):

for v in client.beta.memory_stores.memory_versions.list(
    store.id,
    memory_id=mem.id,
):
    print(f"{v.id}: {v.operation}")

Operation types: "created", "modified", "deleted". (Memory)

Redacting sensitive content from version history:

If a memory version contains content that should be expunged — PII, an API key that was accidentally written, outdated credentials — you can redact it:

client.beta.memory_stores.memory_versions.redact(
    version_id,
    memory_store_id=store.id,
)

"Redact hard clears content, content_sha256, content_size_bytes, and path; all other fields, including the actor and timestamps, are preserved." (Memory)

Redaction is non-destructive to the audit trail: you can see that a version existed and who created it, but the sensitive content is gone.

Memory Events in the Event Stream

When a session has memory stores attached, the agent's memory operations appear as agent.tool_use events in the event stream. (Memory) You'll see events like:

agent.tool_use { name: "memory_search", input: { query: "formatting preferences" } }
agent.tool_use { name: "memory_read", input: { memory_id: "mem_01..." } }
agent.tool_use { name: "memory_write", input: { path: "/session/summary.md", content: "..." } }

This visibility into memory operations lets you:

  • Confirm the agent is consulting memory before starting work
  • Verify it's writing the right things at the end of a session
  • Debug unexpected behavior by tracing what was read vs. what was written
DiagramMemory tool sequence for a typical session. Timeline showing: Session starts → agent.tool_use [memory_search "user preferences"] → agent.tool_use [memory_read /preferences/formatting.md] → agent does its work → agent.tool_use [memory_write /session/2026-04-20-summary.md] → session.status_idle. Annotated to show: "Before task: reads. After task: writes."

Try it yourself

Try It Yourself

  1. Create a memory store and write several memories:

    store = client.beta.memory_stores.create(
        name="Project Context",
        description="Context for my coding project.",
    )
    
    # Write formatting preferences
    client.beta.memory_stores.memories.write(
        memory_store_id=store.id,
        path="/preferences/code-style.md",
        content="Use 2-space indentation. Prefer functional patterns. Write tests for all functions.",
    )
    
    # Write project conventions
    client.beta.memory_stores.memories.write(
        memory_store_id=store.id,
        path="/project/conventions.md",
        content="All API endpoints follow REST conventions. Use snake_case for variables, PascalCase for classes.",
    )
    
  2. List all memories in the store:

    page = client.beta.memory_stores.memories.list(store.id, path_prefix="/")
    for memory in page.data:
        print(f"{memory.path} ({memory.size_bytes} bytes)")
    
  3. Read a specific memory:

    mem = client.beta.memory_stores.memories.retrieve(
        page.data[0].id,
        memory_store_id=store.id,
    )
    print(mem.content)
    
  4. Update a memory with a safe precondition:

    # First, get the current SHA
    current_sha = mem.content_sha256
    
    # Safe update — only proceeds if content hasn't changed
    client.beta.memory_stores.memories.update(
        memory_id=mem.id,
        memory_store_id=store.id,
        content="Use 4-space indentation. Prefer functional patterns. Write tests for all functions.",
        precondition={"type": "content_sha256", "content_sha256": current_sha},
    )
    
  5. View the version history:

    for v in client.beta.memory_stores.memory_versions.list(store.id, memory_id=mem.id):
        print(f"{v.id}: {v.operation} at {v.created_at}")
    

    You should see both the created and modified versions.

  6. Attach the store to a session and observe memory tool usage in the event stream (as covered in Chapter 10). Watch for agent.tool_use events with memory tool names.

DiagramBefore/after comparison for the safe update flow. Left panel: "Read memory → get SHA256." Center: "SHA256 = fingerprint of current content." Right panel: "Update with precondition → only writes if SHA256 matches → prevents race conditions." Failed case shown below: "Someone else updated → SHA256 mismatch → 409 returned → read again → try again."

Common pitfalls

Common Pitfalls

  • Writing large memories instead of many small ones. The 100 KB limit per memory is the hard constraint, but the practical constraint is even tighter: large memories are less precisely retrieved by memory_search. A 90 KB monolithic memory on "all project context" is harder to navigate than 20 focused 4 KB memories on specific topics.

  • Not using preconditions for concurrent updates. If multiple sessions run simultaneously and both might write to the same memory path, you can get lost-update problems without preconditions. Use content_sha256 preconditions for any memory that might be written from multiple places.

  • Forgetting to use read_only for shared reference memory. If you have a knowledge base that multiple agents should read but only administrators should update, attach it with access: "read_only". Without this, any agent session can overwrite your carefully curated knowledge base.

  • Never reviewing what the agent wrote. Claude writes memories autonomously. After a few sessions, check what was actually written. Look for incorrect conclusions, outdated information, or sensitive content that shouldn't be persisted. Regular memory audits are part of responsible memory management.

  • Ignoring version history for debugging. When an agent's behavior changes unexpectedly, check the memory version history. A recent modified entry might explain the behavioral change — something was written in a previous session that's now shaping current behavior.


Toolkit

Toolkit

  • Memory Store CRUD Cheat Sheet — Quick-reference code snippets for all six operations (list, search, read, write, edit, delete) with the precondition variants for write and edit. One snippet per operation.

  • Memory Audit Template — A structured checklist for reviewing memory store contents: list all memories, spot large or stale files, check version history for unexpected modifications, identify content that should be redacted.


Chapter Recap

  • The six memory tools divide into: discovery (memory_list, memory_search), reading (memory_read), writing (memory_write, memory_edit), and deletion (memory_delete). Claude uses them automatically when a memory store is attached — no configuration needed.
  • Use preconditions for safe concurrent writes: not_exists to initialize safely, content_sha256 to update without racing. Both return 409 memory_precondition_failed on conflict.
  • Every mutation creates an immutable version record. Use memory_versions.list to audit changes, and memory_versions.redact to expunge sensitive content from history while preserving the audit trail.