The decision piece argued the case: context editing and the memory tool aren't competitors, they're a division of labor. Context editing evicts the tool output you can afford to lose because it's re-fetchable; the memory tool writes the facts you can't afford to lose to a store outside the window. This is the other half — the request that turns both on, and the four config lines that keep it from quietly wrecking your prompt cache.

The one-line answer: Enable both in one client.beta.messages.create(...) call. The memory tool — {"type": "memory_20250818", "name": "memory"} — is now generally available, no beta header. Context editing needs the context-management-2025-06-27 beta header and a context_management block with a clear_tool_uses_20250919 edit. The one setting that separates "works" from "thrashes the cache" is clear_at_least: clearing invalidates the cached prefix, so set a floor (5k–20k tokens) and add exclude_tools: ["memory"] so the durable layer is never what gets cleared.

Step 1 — register the memory tool (and own its storage)#

The memory tool is client-side by design. The API doesn't store anything. It issues file commands — view, create, str_replace, insert, delete, rename — and your code executes them against storage you control. The /memories path in those commands is just a prefix you map onto a real per-user directory, database, or bucket. A later session only continues from the same memory if you register the same tool and serve the same store.

Two non-negotiables:

STORE: dict[str, str] = {}  # swap for real per-user storage in production

def _validate(path: str) -> None:
    if not path.startswith("/memories") or ".." in path:
        raise ValueError("path escapes /memories")

def execute_memory(cmd: dict) -> tuple[str, bool]:
    """Return (content, is_error). One arm per command; guard every path."""
    c = cmd["command"]
    _validate(cmd.get("path") or cmd.get("old_path", "/memories"))
    if c == "create":
        STORE[cmd["path"]] = cmd["file_text"]
        return f"File created successfully at: {cmd['path']}", False
    if c == "view":
        text = STORE.get(cmd["path"])
        if text is None:
            return f"The path {cmd['path']} does not exist.", True
        body = "\n".join(f"{i+1:6d}\t{l}" for i, l in enumerate(text.split("\n")))
        return f"Content of {cmd['path']}:\n{body}", False
    if c == "str_replace":
        p, old = cmd["path"], cmd["old_str"]
        if p not in STORE or old not in STORE[p]:
            return f"old_str did not appear verbatim in {p}.", True
        STORE[p] = STORE[p].replace(old, cmd.get("new_str", ""), 1)
        return "The memory file has been edited.", False
    # ... insert / delete / rename follow the same shape
    return f"Unknown command {c}", True

The full command semantics (line-numbered view, insert_line offsets, exact return strings) are in the memory-tool docs; each SDK also ships a BetaLocalFilesystemMemoryTool if you'd rather not hand-roll the backend.

Step 2 — turn on context editing in the same request#

Now add the context_management block to the same call — it lives right beside your tools array and needs the beta header flipped on. The whole trick is in four fields, and getting them right is the difference between an agent that quietly manages its own window and one that reprocesses your entire cache every few turns. Read the config below, then the annotation under it explaining what each line actually buys you:

resp = client.beta.messages.create(
    model=MODEL,                          # a current Claude 4+ model id
    max_tokens=4096,
    messages=messages,
    tools=[
        {"type": "memory_20250818", "name": "memory"},
        {"type": "web_search_20250305", "name": "web_search", "max_uses": 10},
    ],
    betas=["context-management-2025-06-27"],   # for context editing
    context_management={
        "edits": [{
            "type": "clear_tool_uses_20250919",
            "trigger": {"type": "input_tokens", "value": 100_000},
            "keep": {"type": "tool_uses", "value": 3},
            "clear_at_least": {"type": "input_tokens", "value": 10_000},
            "exclude_tools": ["memory"],           # never clear the durable layer
        }],
    },
)

What each line buys you:

When a clear fires, the response echoes it: resp.context_management.applied_edits reports cleared_tool_uses and cleared_input_tokens, so you can log exactly what left the window.

Step 3 — run the loop; let each lever do its job#

The agentic loop is ordinary: append the assistant turn, execute any tool calls, feed results back. The only memory-specific branch is routing memory tool calls into execute_memory; server-side tools like web_search return their own results.

while resp.stop_reason == "tool_use":
    messages.append({"role": "assistant", "content": resp.content})
    results = []
    for block in resp.content:
        if block.type == "tool_use" and block.name == "memory":
            content, is_error = execute_memory(block.input)
            results.append({"type": "tool_result", "tool_use_id": block.id,
                            "content": content, "is_error": is_error})
    if results:
        messages.append({"role": "user", "content": results})
    resp = client.beta.messages.create(...)   # same kwargs as above

The division of labor now runs itself: the auto-injected protocol pushes the model to write specifics to memory as it works, so durable facts land before the window fills; context editing then evicts the bulky, re-fetchable tool output around them. Two more knobs if you need them — pair clear_tool_uses_20250919 with clear_thinking_20251015 to also drop old reasoning (list clear_thinking first in edits), and if you'd rather not hand-write the loop at all, client.beta.messages.tool_runner(...) drives it for you.

Why it's worth the wiring#

Anthropic's own numbers: context editing alone lifts their agentic benchmark ~29%; context editing plus memory, ~39%; a 100-turn web-search run cut token consumption 84% and finished workflows that would otherwise have died from context exhaustion. That gap between 29 and 39 is the entire argument for wiring both — the memory tool is what makes a fact survive the very clearing that keeps the agent alive. Turn one on and you manage the window. Turn both on, exclude memory from the clear, and you keep what matters while you do it.