Files
mem0/docs/integrations/claude-code.mdx
T
Saket Aryan df70d7833f fix(plugins): stamp surface identity at record time, not send time
Six defects in 0.3.x plugin telemetry. Defects 1, 2 and 6 were not three bugs:
they were one spool protocol getting three properties wrong.

Identity was decided by the wrong process. `harness` was stamped in record(),
correctly, but `source` was read from a module global in flush() — so whichever
process drained the spool named every event in it. Two processes never call
init(): mcp_server.py, and the detached `python3 telemetry.py` sender that
spawn_flush() starts. record() now stamps source beside harness, and the build
generates core/_harness_id.py per host so identity resolves with no init() call
at all. That also unifies two defaults that disagreed (`<host>_plugin` vs
`MEM0_<HOST>_PLUGIN`), which could yield three source values for one plugin.

Ownership was inferred, not held. Path.replace is os.rename, which preserves
mtime, so a claim made after a quiet minute inherited the spool's age and was
stealable the instant it existed. Claims are touched on creation and the
per-batch rewrite doubles as a lease heartbeat.

Progress was not durable. flush() returned on the first failed batch without
truncating, so the retry re-posted from index 0 — 150 events delivered 250
times. It now rewrites the claim with the unsent remainder after every batch,
bounding a crash to one repeated batch, and each event carries a uuid.

Parked batches starved. They were only reachable when no spool existed, and
because sessions keep recording there usually was one, so a batch parked by a
failed send waited until the 7-day expiry deleted it unsent — despite its own
presence being what starts the sender. flush() drains them in the same run, and
expiry now applies only after a genuine retry has failed.

code.install counted upgrades and repeat sessions. is_first_run() read the
identity file, which only a successful flush writes, so an offline user recorded
an install every session forever. A dedicated install-state.json is claimed
atomically at record time; a non-empty data directory reads as an upgrade.

The docs called this anonymous. Every event carries the account email, and the
hashes were unsalted SHA-256 over a git remote URL or an absolute path
containing the username. READMEs, the module docstring and a new docs section
now say what the code does, and repo/session digests are salted per install.

A cached email outlived an API key change. It is now re-resolved when the key's
fingerprint differs, and $identify aliases anonymous->email only — aliasing one
account to another merges person profiles irreversibly.

All six shipped green because the shared core's only tests lived under one host,
behind a conftest that calls init() at import. Core behaviour was never
exercised uninitialised. Adds agent-plugin-core/tests with no init, including
subprocess tests and coverage for the portable plugin, which has no flush worker
and would pass a native-only test vacuously.

Also puts the three surface headers on the SDKs, CLIs and integrations, and
corrects a README claiming ZAPIER/STRANDS were already in the platform allowlist.

Verified: 59 core tests, 203 claude-code, 11 cursor, 5 codex, 2 kimi, 6
antigravity. ruff and compileall clean. --check clean for all six hosts.
TypeScript changes are not typechecked locally (deps not installed).

Claude-Session: https://claude.ai/code/session_01C7tEmH86HAr7GoAAKCEHZb
2026-09-15 00:14:11 +05:30

210 lines
12 KiB
Plaintext

---
title: Claude Code
description: "Persistent cross-session memory for Claude Code. Install once, memories are captured automatically and recalled in every future session."
---
Claude Code forgets everything between sessions. This plugin fixes that. Install it, work normally, and Claude remembers what happened across sessions.
<Info>Current plugin version: `0.3.1`.</Info>
## Prerequisites
1. A Mem0 Platform account and API key (starts with `m0-`):
- [Sign up at app.mem0.ai](https://app.mem0.ai?utm_source=oss&utm_medium=integration-claude-code)
- [Get your API key](https://app.mem0.ai/dashboard/api-keys?utm_source=oss&utm_medium=integration-claude-code)
2. A Claude Code version that supports plugin agents, worktree isolation for agents, and the `SubagentStart`, `SubagentStop`, and `PostToolUseFailure` hook events.
3. Python 3.10+ and Git on your machine.
## Quick start
```bash
export MEM0_API_KEY='your-mem0-api-key'
claude plugin marketplace add mem0ai/mem0
claude plugin install mem0@mem0-plugins --scope user --config api_key="$MEM0_API_KEY"
unset MEM0_API_KEY
```
Restart Claude Code (or run `/reload-plugins`), open a Git repository, and work normally. That's it.
### Managing the plugin
```bash
claude plugin marketplace update mem0-plugins # refresh the marketplace catalog
claude plugin update mem0@mem0-plugins --scope user # update the plugin (restart to apply)
claude plugin uninstall mem0@mem0-plugins # uninstall the plugin (keeps the marketplace)
```
## What you can do
### Automatic memory
Once installed, memory works without any action from you:
- **Capture** happens in the background as you work. Hooks save user messages, Claude's answers, changed files, and test/build results locally. Capture records evidence locally; memory extraction runs through Mem0 in a background worker.
- **Recall** happens automatically before Claude's first response in a new session. If the first prompt has at least 20 characters, the plugin searches with that prompt and injects up to five relevant memories. For shorter prompts or later questions, use explicit search.
### Commands
| Command | What it does |
| --- | --- |
| `/mem0:search` | Search memories from earlier sessions. Supports `--top-k <n>`, `--category <name>`, `--scope <repo\|dir\|mine>`, and `--run-id <session-id>`. |
| `/mem0:status` | Check if memory is working: config, capture state, pending flushes, API key validity. |
| `/mem0:forget` | Delete your memories for this repo (shared project memory stays unless you pass `--include-project-memory`). |
| `/mem0:pause` | Pause memory capture. |
| `/mem0:resume` | Resume capture after a pause. |
| `/mem0:remember` | Tell Claude to capture something specific in its reply. |
Categories for `--category`: `project_knowledge`, `decisions_and_constraints`, `workflows`, `problems_and_fixes`, `results`. Without it, all categories are searched.
### Search tool
After the automatic first-prompt search, Claude can also call `search_memories` with a specific question, and you can run `/mem0:search` yourself. Explicit searches return up to 3 results by default (configurable to 20). The combined search output is capped at 4,000 characters by default, configurable with `max_context_chars`. This recall limit does not truncate captured messages sent for extraction.
### Sidekick agent
Sidekick is available only in Claude Code.
`mem0:sidekick` is a Sonnet coding agent that runs in a separate Git worktree. Use it to offload investigation, implementation, testing, or review without burning main-session context.
```text
Ask Mem0's sidekick to investigate and implement this in its separate worktree.
Review its result and send any corrections back to the same sidekick.
```
Changes stay in the sidekick's worktree until the main agent reviews and copies them over. By default the worktree branches from the repo's default branch. Set `worktree.baseRef` to `"head"` in your Claude settings to branch from the current commit instead. Uncommitted changes are not copied into the sidekick's worktree.
At startup, Sidekick receives the memories already recalled for its parent session. It can also call the Mem0 search tool for its assigned task. Start and completion hooks track its work locally; completing a Sidekick task does not independently send a memory-extraction request. Sidekick returns its result, validation, and a local commit when it changes files, so the main agent can review the work before incorporating it.
## How it works
The plugin follows a simple cycle: capture during a session, extract memories in the background, recall in the next session.
<Frame>
<img src="/images/plugin-sequence.svg" alt="Sequence diagram: the first user prompt triggers search, hooks capture activity during the session, a background worker extracts memories after every five exchanges or on idle/exit, and the next session recalls them." />
</Frame>
**Step by step:**
1. **Capture.** Hooks save the main agent's activity locally: user messages, Claude's answers, changed files, and short test/build results. Direct Sidekick lifecycle records stay local. Subagent results included in the main transcript can provide supporting evidence for extraction; the main agent's final response establishes the outcome. Capture does not call a model.
2. **Flush.** After every five completed exchanges, a detached background worker sends that batch to Mem0. Large exchanges flush sooner. Ending or compacting the session flushes anything remaining. If the session sits idle, an auto-flush runs after five minutes (configurable with `MEM0_CODE_IDLE_FLUSH_SECONDS`). The timer resets on each new exchange. The worker survives Claude Code exiting.
3. **Extract.** Each flush sends `add` calls with `agent_id` (the project identity), `user_id` (you), `app_id` (the repository), and `run_id` (the session). Prompts and responses are redacted without a character cutoff. Large conversations are split across requests without dropping message text. Mem0 classifies each extracted memory as shared project knowledge or a personal preference.
4. **Recall.** On the next session's first prompt, the plugin searches automatically and supplies up to five relevant memories. No model is called to write the query.
## Memory scoping
Each `add` call carries separate extraction instructions for project facts and personal facts. Mem0 sorts each memory into one of two buckets:
- **Shared project memory** (keyed by `agent_id`, scoped by `app_id`): one namespace per repository. Stores conventions, decisions, constraints, commands that work, and commands that failed with what fixed them. Everyone on the repo reads and writes the same pool. Project memory never carries a `user_id`, so teammates' searches never mix in your preferences. Directory information is stored in metadata for directory-scoped searches.
- **Personal memory** (keyed by `user_id`, scoped by `app_id`): your preferred tools, style, habits, and anything you asked to be remembered. Scoped to the repository by `app_id`, private to you.
Credentials are redacted before anything leaves your machine.
<Frame>
<img src="/images/memory-scoping-venn.svg" alt="Venn diagram showing one add call producing shared project memory (agent_id, visible to team) and personal memory (user_id, private to you). Search returns the union of both." />
</Frame>
## Search scope
Every memory carries identifiers showing where it came from:
| Identifier | What it is | Example |
| --- | --- | --- |
| `user_id` | You (personal memory only) | Your Mem0 user ID |
| `agent_id` | The project identity (shared memory only) | `acme-payments-api-<hash>` |
| `app_id` | The repository (scopes both lanes) | `acme-payments-api` |
| `run_id` | The Claude Code session | The session ID |
New Git repository memories use an `agent_id` with a hash of the Git remote identity so matching owner/repository names on different hosts stay separate. Searches also include the previous unhashed `agent_id`, scoped by the repository's `app_id`, so existing shared memories remain available after upgrading. Those older memories retain their original namespace, which did not distinguish Git hosts. Local folders continue using a hash of their path.
Explicitly forgetting shared project memory with `--include-project-memory` covers both repository IDs. Without that option, shared memories are preserved.
A search returns the union of shared project memory and your personal preferences. The scope narrows the project part:
| Scope | What you get |
| --- | --- |
| `repo` (default) | All project memory across every subdirectory, plus your preferences |
| `dir` | Project memory from the directory you're in (and its children), plus your preferences |
| `mine` | Your personal preferences only |
The `dir` scope is hierarchical: a parent directory sees everything in its children, but a child never sees the parent's memories.
The search tool accepts an optional `run_id` with every scope (`repo`, `dir`, and `mine`); `/mem0:search` exposes it as `--run-id session-id`. It restricts both shared and personal results to memories saved in that coding-agent session. Omit it to search across sessions. This is a memory filter, not a label for the session making the request; automatically filtering by the current session would hide earlier-session memories. Each memory update still records the session's `run_id`. Set the default scope with the `search_scope` setting or `MEM0_CODE_SEARCH_SCOPE` env var.
## Settings
| Setting | Default | What it controls |
| --- | --- | --- |
| `api_key` | required | Mem0 Platform API key. |
| `user_id` | local account name | User ID for memory storage. Resolved from: setting, `MEM0_CODE_USER_ID`, `MEM0_USER_ID`, `MEM0_RESOLVED_USER_ID`, `$USER`, `%USERNAME%`, then `default`. Set explicitly to share memories across machines. |
| `top_k` | `3` | Max memories per explicit search (1 to 20). |
| `max_context_chars` | `4000` | Max characters returned per search (1,000 to 10,000). |
| `search_scope` | `repo` | Default scope: `repo`, `dir`, or `mine`. Also read from `MEM0_CODE_SEARCH_SCOPE`. |
## Upgrading from 0.2.x
Breaking update. Your memories carry over, most local config does not.
- **Memories carry over.** Same user and repository scoping, including `~/.mem0/project_map.json`.
- **Env vars still work.** `MEM0_API_KEY`, `MEM0_USER_ID`, `MEM0_PROJECT_ID`.
- **Commands replaced.** Old commands replaced by `/mem0:search`, `/mem0:status`, `/mem0:forget`, `/mem0:pause`, `/mem0:resume`, `/mem0:remember`.
- **MCP server replaced.** Nine read/write tools replaced by the single read-only `search_memories` tool.
- **Local config ignored.** `~/.mem0/settings.json` and per-project `mem0.md` files are no longer read.
- **Old memories remain searchable.** Pre-upgrade memories may use different categories. Omit category filters if an older memory is missing from the results.
```bash
claude plugin marketplace update mem0-plugins
claude plugin update mem0@mem0-plugins --scope user
```
## Troubleshooting
| Problem | Fix |
| --- | --- |
| Missing key | Reinstall with `--config api_key="$MEM0_API_KEY"` while the var is set. |
| `401 Unauthorized` | API key is invalid or expired. Run `/mem0:status` to confirm. |
| No memory after ending a session | Extraction runs in the background. Wait a moment, then search again. |
| Sidekick won't start | Must be in a Git repo. Check that your Claude Code version supports plugin agents and worktrees. |
| Remove the plugin | `claude plugin uninstall mem0@mem0-plugins` |
## Telemetry
The plugin sends usage events (which hook ran, timing, result counts, failure
types) so Mem0 can see what's used and what's breaking.
These events are **not anonymous**. When an API key is configured, which
installing the plugin requires, they are sent under your Mem0 account email,
the same way the Python SDK and the CLI attribute theirs. Without a key they
are sent under a random per-machine id.
Each event carries the event name, the plugin version, the harness it ran in,
your OS and Python version, timings, counts, and a coarse failure label.
Repository and session identifiers are hashed with a random salt generated on
your machine and never sent, so they cannot be linked back to a repository name
or path.
Prompts, memory text, queries, file paths, repository names, and API keys are
never sent.
Turn it off:
```bash
export MEM0_TELEMETRY=false
```
<CardGroup cols={2}>
<Card title="Mem0 MCP Setup" icon="puzzle-piece" href="/platform/mem0-mcp">
Detailed MCP configuration for all clients
</Card>
<Card title="Codex Integration" icon="/images/provider-icons/openai.svg" href="/integrations/codex">
Add Mem0 memory to OpenAI Codex workflows
</Card>
</CardGroup>
<Snippet file="star-on-github.mdx" />