feat(claude-code-plugin): move the Claude Code plugin to its own integration and ship it as 0.3.0 (#7106)

This commit is contained in:
Kartik
2026-09-01 02:34:45 +05:30
committed by GitHub
parent 19cb89aff4
commit 71fba8d464
39 changed files with 9770 additions and 369 deletions
@@ -1,22 +0,0 @@
{
"name": "mem0",
"version": "0.2.15",
"description": "Persistent memory for Claude Code. Remembers decisions, patterns, and preferences across sessions.",
"author": {
"name": "Mem0",
"email": "support@mem0.ai"
},
"homepage": "https://mem0.ai",
"repository": "https://github.com/mem0ai/mem0",
"license": "Apache-2.0",
"keywords": ["memory", "personalization", "mcp", "semantic-search"],
"userConfig": {
"api_key": {
"type": "string",
"title": "Mem0 API Key",
"description": "Your Mem0 Platform API key (starts with m0-). Get one at https://app.mem0.ai/dashboard/api-keys",
"sensitive": true,
"required": true
}
}
}
-11
View File
@@ -1,11 +0,0 @@
{
"mcpServers": {
"mem0": {
"type": "http",
"url": "https://mcp.mem0.ai/mcp/",
"headers": {
"Authorization": "Token ${MEM0_API_KEY}"
}
}
}
}
+19 -26
View File
@@ -1,6 +1,15 @@
# Mem0 Plugin for Claude Code, Claude Cowork, Cursor, Codex, OpenCode & Antigravity
> **Claude Code users:** version 0.3.0 of the Claude Code plugin now lives at
> [`integrations/claude-code-plugin`](../claude-code-plugin/) and is what `mem0@mem0-plugins` installs.
> Update with `claude plugin marketplace update mem0-plugins` then
> `claude plugin update mem0@mem0-plugins --scope user` — your memories carry over
> automatically. This directory continues to serve the Cursor, Codex, Kimi,
> Antigravity, and OpenCode integrations until they are ported. The Claude Code
> manifest, hooks, and MCP config have been removed from this directory, so
> there is nothing here left to install into Claude Code.
Add persistent memory to your AI workflows. Store, retrieve, and manage memories across sessions using the Mem0 Platform. Works with **Claude Code** (CLI), **Claude Cowork** (desktop app), **Cursor**, **Codex**, **OpenCode**, and **Antigravity**.
# Mem0 Plugin for Cursor, Codex, Kimi, OpenCode & Antigravity
Add persistent memory to your AI workflows. Store, retrieve, and manage memories across sessions using the Mem0 Platform. Works with **Cursor**, **Codex**, **Kimi**, **OpenCode**, and **Antigravity**. For Claude Code, use [`integrations/claude-code-plugin`](../claude-code-plugin/).
## Quick path for agents
@@ -52,21 +61,6 @@ Humans setting up Mem0 by hand should continue with Step 1 below.
Choose one of the options below. All require `MEM0_API_KEY` to be set first (see above).
### Claude Code (CLI) / Claude Cowork (Desktop)
Claude Code and Claude Cowork share the same plugin system.
**CLI:**
```
/plugin marketplace add mem0ai/mem0
/plugin install mem0@mem0-plugins
```
**Cowork desktop app:** Open the Cowork tab, click **Customize** in the sidebar, click **Browse plugins**, and install Mem0.
This installs the full plugin including the MCP server, lifecycle hooks (automatic memory capture), and the Mem0 SDK skill.
### Codex
**Option A — Direct MCP** (fastest, MCP only):
@@ -191,7 +185,7 @@ This runs the setup wizard which:
3. Installs coding-optimized memory categories
4. Shows your identity (user ID, project scope, branch)
The onboarding is idempotent — safe to re-run anytime. On first session in a new project (0 memories), Claude is prompted to run it automatically.
The onboarding is idempotent — safe to re-run anytime. On first session in a new project (0 memories), the agent is prompted to run it automatically.
## Verify it works
@@ -224,27 +218,26 @@ The plugin includes 17 skills accessible via `/mem0:` commands:
## What's included
| Component | Claude Code / Cowork | Cursor (MCP) | Codex (Sideload) | Codex (Direct MCP) | OpenCode (Full) | OpenCode (MCP) | Antigravity |
|-----------|:--------------------:|:------------:|:----------------:|:------------------:|:---------------:|:--------------:|:-----------:|
| MCP Server | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Lifecycle Hooks | Yes | No | Opt-in | No | Yes | No | Yes |
| Mem0 SDK Skill | Yes | No | Yes | No | Yes | No | Yes |
| Component | Cursor (MCP) | Codex (Sideload) | Codex (Direct MCP) | OpenCode (Full) | OpenCode (MCP) | Antigravity |
|-----------|:------------:|:----------------:|:------------------:|:---------------:|:--------------:|:-----------:|
| MCP Server | Yes | Yes | Yes | Yes | Yes | Yes |
| Lifecycle Hooks | No | Opt-in | No | Yes | No | Yes |
| Mem0 SDK Skill | No | Yes | No | Yes | No | Yes |
- **MCP Server** — Connects to the Mem0 remote MCP server (`mcp.mem0.ai`), providing tools to add, search, update, and delete memories. No local dependencies required.
- **Lifecycle Hooks** — Automatic memory capture at key points. Claude Code, OpenCode, and Antigravity wire hooks natively when the full plugin is installed. Codex hooks are opt-in via a one-time installer (`scripts/install_codex_hooks.py`).
- **Lifecycle Hooks** — Automatic memory capture at key points. OpenCode and Antigravity wire hooks natively when the full plugin is installed. Codex hooks are opt-in via a one-time installer (`scripts/install_codex_hooks.py`).
- **Mem0 SDK Skill** — Guides the AI on how to integrate the Mem0 SDK (Python & TypeScript) into your applications.
## Updating the plugin
When the plugin updates (new version pulled from the marketplace, or a fresh local install), the MCP server connection in your existing session is left holding a stale handle and stops responding. **Restart your client to reconnect:**
- **Claude Code:** run `/restart` in the prompt, or close and reopen the CLI.
- **Cursor:** quit and relaunch.
- **Codex:** restart the editor session.
- **OpenCode:** restart the session.
- **Antigravity:** restart the session.
Your `MEM0_API_KEY` doesn't need to be re-entered — the auth header is re-read from your environment on the new session. The plugin's MCP config uses `${MEM0_API_KEY}` interpolation at session start, not at install time, so as long as the env var is set persistently (in your shell profile or `~/.claude/settings.json` `env` block), reconnection is automatic on restart.
Your `MEM0_API_KEY` doesn't need to be re-entered — the auth header is re-read from your environment on the new session. The plugin's MCP config uses `${MEM0_API_KEY}` interpolation at session start, not at install time, so as long as the env var is set persistently (in your shell profile), reconnection is automatic on restart.
If reconnection still fails after a restart, check that `MEM0_API_KEY` is reachable in the new shell (`echo $MEM0_API_KEY`) and confirm you're using a key that starts with `m0-` (from https://app.mem0.ai/dashboard/api-keys, not a legacy token).
-126
View File
@@ -1,126 +0,0 @@
{
"hooks": {
"Setup": [
{
"matcher": "init|maintenance",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/ensure_deps.sh",
"statusMessage": "Installing mem0 SDK...",
"timeout": 120
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/requirements.txt\" \"${CLAUDE_PLUGIN_DATA:-$HOME/.mem0/plugin-data}/requirements.txt\" >/dev/null 2>&1 || \"${CLAUDE_PLUGIN_ROOT}/scripts/ensure_deps.sh\"",
"statusMessage": "Installing mem0 SDK...",
"timeout": 60
}
]
},
{
"matcher": "startup|resume|compact",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_session_start.sh",
"statusMessage": "Loading mem0 context..."
}
]
}
],
"PreToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/block_memory_write.sh"
}
]
},
{
"matcher": "mcp__mem0__add_memory|mcp__plugin_mem0_mem0__add_memory|mcp__mem0__search_memories|mcp__plugin_mem0_mem0__search_memories|mcp__mem0__get_memories|mcp__plugin_mem0_mem0__get_memories|mcp__mem0__delete_all_memories|mcp__plugin_mem0_mem0__delete_all_memories",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/enforce_metadata_defaults.sh",
"timeout": 3
}
]
},
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_file_read.sh",
"timeout": 5
}
]
}
],
"PostToolUse": [
{
"matcher": "mcp__mem0__.*|mcp__plugin_mem0_mem0__.*",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_post_tool_use.sh",
"timeout": 3
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_bash_output.sh",
"timeout": 5
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_stop.sh",
"timeout": 30
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_pre_compact.sh",
"statusMessage": "Preparing pre-compaction summary..."
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/on_user_prompt.sh",
"statusMessage": "Checking memory relevance...",
"timeout": 8
}
]
}
]
}
}
@@ -27,20 +27,20 @@ import urllib.error
import urllib.request
# Each editor surface ships its own manifest with its own version line
# (Antigravity is on 0.1.x while Claude/Cursor/Codex are on 0.2.x), so we read
# the manifest matching the detected platform rather than a single shared one.
# (Antigravity is on 0.1.x while Cursor/Codex are on 0.2.x), so we read the
# manifest matching the detected platform rather than a single shared one.
_PLATFORM_MANIFESTS = {
"antigravity": ("..", "plugin.json"),
"claude-code": ("..", ".claude-plugin", "plugin.json"),
"cursor": ("..", ".cursor-plugin", "plugin.json"),
"codex": ("..", ".codex-plugin", "plugin.json"),
"kimi": ("..", ".kimi-plugin", "plugin.json"),
}
_DEFAULT_MANIFEST = ("..", ".claude-plugin", "plugin.json")
def _load_plugin_version(platform_name: str = "") -> str:
parts = _PLATFORM_MANIFESTS.get(platform_name, _DEFAULT_MANIFEST)
parts = _PLATFORM_MANIFESTS.get(platform_name)
if parts is None:
return "unknown"
try:
plugin_json = os.path.join(os.path.dirname(__file__), *parts)
with open(plugin_json) as f:
@@ -79,8 +79,6 @@ def detect_platform() -> str:
return "kimi"
if os.environ.get("PLUGIN_ROOT"):
return "codex"
if os.environ.get("CLAUDECODE") or os.environ.get("CLAUDE_PLUGIN_ROOT"):
return "claude-code"
if os.environ.get("CURSOR_PLUGIN_ROOT"):
return "cursor"
if os.environ.get("WINDSURF_PLUGIN_ROOT"):
@@ -122,24 +122,12 @@ def test_hash_deterministic():
assert h1 != telemetry._sha256("different-value")
def test_platform_claude_code(monkeypatch):
import telemetry
monkeypatch.delenv("MEM0_PLATFORM", raising=False)
monkeypatch.delenv("ANTIGRAVITY_PLUGIN_ROOT", raising=False)
monkeypatch.delenv("PLUGIN_ROOT", raising=False)
monkeypatch.delenv("CURSOR_PLUGIN_ROOT", raising=False)
monkeypatch.setenv("CLAUDECODE", "1")
assert telemetry.detect_platform() == "claude-code"
def test_platform_cursor(monkeypatch):
import telemetry
monkeypatch.delenv("MEM0_PLATFORM", raising=False)
monkeypatch.delenv("ANTIGRAVITY_PLUGIN_ROOT", raising=False)
monkeypatch.delenv("PLUGIN_ROOT", raising=False)
monkeypatch.delenv("CLAUDECODE", raising=False)
monkeypatch.delenv("CLAUDE_PLUGIN_ROOT", raising=False)
monkeypatch.setenv("CURSOR_PLUGIN_ROOT", "/path")
assert telemetry.detect_platform() == "cursor"
@@ -150,7 +138,6 @@ def test_platform_codex(monkeypatch):
monkeypatch.delenv("MEM0_PLATFORM", raising=False)
monkeypatch.delenv("ANTIGRAVITY_PLUGIN_ROOT", raising=False)
monkeypatch.delenv("CLAUDECODE", raising=False)
monkeypatch.delenv("CLAUDE_PLUGIN_ROOT", raising=False)
monkeypatch.delenv("CURSOR_PLUGIN_ROOT", raising=False)
monkeypatch.setenv("PLUGIN_ROOT", "/path")
@@ -165,7 +152,6 @@ def test_platform_kimi(monkeypatch):
monkeypatch.delenv("MEM0_PLATFORM", raising=False)
monkeypatch.delenv("ANTIGRAVITY_PLUGIN_ROOT", raising=False)
monkeypatch.delenv("CLAUDECODE", raising=False)
monkeypatch.delenv("CLAUDE_PLUGIN_ROOT", raising=False)
monkeypatch.delenv("CURSOR_PLUGIN_ROOT", raising=False)
monkeypatch.delenv("PLUGIN_ROOT", raising=False)
@@ -184,8 +170,8 @@ def test_platform_explicit_override(monkeypatch):
def test_platform_antigravity(monkeypatch):
"""Antigravity sets CLAUDE_PLUGIN_ROOT for compatibility but must be
attributed to its own platform, not claude-code."""
"""Antigravity sets CLAUDE_PLUGIN_ROOT so the shared scripts resolve their
paths. That must not change how it is attributed."""
import telemetry
monkeypatch.delenv("MEM0_PLATFORM", raising=False)
@@ -196,14 +182,14 @@ def test_platform_antigravity(monkeypatch):
def test_plugin_version_is_per_editor(monkeypatch):
"""Each editor reports the version from its OWN manifest. Antigravity is on
a 0.1.x line while Claude/Cursor/Codex are on 0.2.x, so they must not all
report the same shared version."""
a 0.1.x line while Cursor/Codex are on 0.2.x, so they must not all report
the same shared version. An unsupported surface reports "unknown" rather
than borrowing a version it does not ship."""
import telemetry
plugin_dir = os.path.join(os.path.dirname(__file__), "..")
manifests = {
"antigravity": "plugin.json",
"claude-code": os.path.join(".claude-plugin", "plugin.json"),
"cursor": os.path.join(".cursor-plugin", "plugin.json"),
"codex": os.path.join(".codex-plugin", "plugin.json"),
"kimi": os.path.join(".kimi-plugin", "plugin.json"),
@@ -215,6 +201,10 @@ def test_plugin_version_is_per_editor(monkeypatch):
payload = telemetry.build_posthog_payload("plugin.test")
assert payload["properties"]["plugin_version"] == expected, f"{plat} should report {expected} from {rel}"
monkeypatch.setenv("MEM0_PLATFORM", "claude-code")
payload = telemetry.build_posthog_payload("plugin.test")
assert payload["properties"]["plugin_version"] == "unknown"
def test_send_fails_silently(monkeypatch):
import telemetry