diff --git a/mem0-plugin/README.md b/mem0-plugin/README.md index 4c9891921..732c44681 100644 --- a/mem0-plugin/README.md +++ b/mem0-plugin/README.md @@ -157,6 +157,18 @@ After installing, confirm the MCP server is connected: - **Mem0 SDK Skill** — Guides the AI on how to integrate the Mem0 SDK (Python & TypeScript) into your applications. - **Memory Protocol Skill** — Codex-specific skill that instructs the agent to retrieve relevant memories at task start, store learnings on completion, and capture session state before context loss. Complements the lifecycle hooks on Codex. +## 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 Claude Code / Cursor / Codex 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. + +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. + +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). + ## Optional: tune categories for coding workflows mem0 auto-tags every memory with one or more `categories` from a project-level list. The default list is consumer-oriented (`food`, `hobbies`, `music` …) — useful for chat assistants, less so for code. A one-shot script in this plugin replaces it with a coding-focused taxonomy: diff --git a/mem0-plugin/scripts/capture_compact_summary.py b/mem0-plugin/scripts/capture_compact_summary.py index 263c1521f..b6df7c42e 100644 --- a/mem0-plugin/scripts/capture_compact_summary.py +++ b/mem0-plugin/scripts/capture_compact_summary.py @@ -22,6 +22,7 @@ import os import sys import urllib.error import urllib.request +from datetime import date, timedelta sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from _identity import resolve_user_id @@ -45,6 +46,8 @@ if os.environ.get("MEM0_DEBUG"): API_URL = "https://api.mem0.ai" MAX_TAIL_LINES = 2000 MAX_SUMMARY_CHARS = 50000 +# Compact summaries describe a single session's state -- stale after a quarter. +COMPACT_SUMMARY_EXPIRY_DAYS = 90 def tail_lines(filepath: str, n: int) -> list[str]: @@ -92,6 +95,7 @@ def find_compact_summary(lines: list[str]) -> str: def store_summary(api_key: str, summary: str, user_id: str, session_id: str) -> bool: + expires = (date.today() + timedelta(days=COMPACT_SUMMARY_EXPIRY_DAYS)).isoformat() body = { "messages": [{"role": "user", "content": summary}], "user_id": user_id, @@ -101,6 +105,7 @@ def store_summary(api_key: str, summary: str, user_id: str, session_id: str) -> "session_id": session_id, }, "infer": False, + "expiration_date": expires, } data = json.dumps(body).encode("utf-8") diff --git a/mem0-plugin/scripts/on_pre_compact.py b/mem0-plugin/scripts/on_pre_compact.py index cf4c47dc7..6f280842e 100755 --- a/mem0-plugin/scripts/on_pre_compact.py +++ b/mem0-plugin/scripts/on_pre_compact.py @@ -20,6 +20,7 @@ import os import sys import urllib.error import urllib.request +from datetime import date, timedelta sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from _identity import resolve_user_id @@ -45,6 +46,10 @@ MAX_TAIL_LINES = 500 MAX_USER_MESSAGES = 30 MAX_BASH_COMMANDS = 20 MAX_ASSISTANT_TEXT = 10000 +# session_state captures churn fast (active codebase, files in flight). Past +# ~3 months they're stale noise. Durable facts (decisions, conventions) are +# stored separately by the agent without an expiration_date. +SESSION_STATE_EXPIRY_DAYS = 90 def tail_lines(filepath: str, n: int) -> list[str]: @@ -164,6 +169,7 @@ def build_content(state: dict, source: str) -> str: def store_memory(api_key: str, content: str, user_id: str, source: str, session_id: str = "") -> bool: """Store session state as a memory via the Mem0 REST API.""" + expires = (date.today() + timedelta(days=SESSION_STATE_EXPIRY_DAYS)).isoformat() body = { "messages": [ {"role": "user", "content": content} @@ -174,6 +180,7 @@ def store_memory(api_key: str, content: str, user_id: str, source: str, session_ "source": source, "session_id": session_id, }, + "expiration_date": expires, } data = json.dumps(body).encode("utf-8") diff --git a/mem0-plugin/skills/mem0-mcp/SKILL.md b/mem0-plugin/skills/mem0-mcp/SKILL.md index bed63cfc1..bfdd15789 100644 --- a/mem0-plugin/skills/mem0-mcp/SKILL.md +++ b/mem0-plugin/skills/mem0-mcp/SKILL.md @@ -99,6 +99,28 @@ Extract key learnings and store them using the `add_memory` tool: > `metadata.type` (which you set explicitly) and `categories` (which the platform auto-tags after the project's custom-category list — see `scripts/setup_coding_categories.py`) are complementary. Always set `metadata.type` for explicit filtering; the platform fills in `categories` on its own. Don't try to set `categories` on `add_memory` calls — per-request overrides aren't supported on the managed API. +### Expiration: high-churn vs durable + +Some memory types are state snapshots that go stale fast; others are durable facts that should outlive the session that created them. Mark the difference with `expiration_date` on writes. + +| Type | Expiration | Why | +|---|---|---| +| `session_state`, `compact_summary` | `expiration_date` ≈ today + 90 days | Describe a single moment of project state. Useless after a quarter; clutter the recall surface. | +| `decision`, `anti_pattern`, `convention`, `user_preference`, `task_learning`, `environmental` | omit `expiration_date` | Durable facts. A decision made last year is still a decision; same for a convention or a user preference. | + +`add_memory` accepts `expiration_date` as a string (`"YYYY-MM-DD"`). The two server-side hooks (`on_pre_compact.py`, `capture_compact_summary.py`) already set this for the types they write. When you write directly via the MCP tool, follow the same rule. + +### Recency filter on recall + +When the user is asking about *current* state ("where were we", "what's the active task", "the latest decision on X"), filter recall to recent memories so stale snapshots don't surface: + +```python +# Last 90 days only +{"AND": [{"user_id": ""}, {"metadata": {"type": "session_state"}}, {"created_at": {"gte": "<90 days ago, YYYY-MM-DD>"}}]} +``` + +Skip the recency filter when the user is asking about durable facts ("what conventions does this project use", "have we hit this bug before") — those are timeless and recency would hide them. + Memories can be as detailed as needed -- include full context, reasoning, code snippets, file paths, and examples. Longer, searchable memories are more valuable than vague one-liners. ### Use `infer=False` for already-structured content