fix(plugins): give the MCP server the hooks' data dir, release 0.3.4
mcp_server.py never calls configure_harness, so it resolved ~/.mem0/mem0-plugin while the Cursor, Kimi and Antigravity hooks use ~/.mem0/<host>-plugin. A key set in Cursor's plugin settings and cached by the sessionStart hook was invisible to search_memories. build.py now writes DATA_DIR_NAME into each bundle's _harness_id.py and memory_core seeds its default from it, like the PLATFORM_* values. An end-to-end test launches the real session-start hook and MCP server for Claude Code, Cursor and Codex the way each host does, with the key in plugin settings and in the mem0 init config, and checks the search reaches the API with that key. Bumps the Python plugins to 0.3.4, OpenCode to 0.4.2, Pi and DeepSeek to 0.3.3, with changelog entries for each. Fixes #7346
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
"name": "mem0",
|
||||
"source": "./integrations/claude-code-plugin",
|
||||
"description": "Cross-session memory and token savings for coding agents.",
|
||||
"version": "0.3.3"
|
||||
"version": "0.3.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"name": "mem0",
|
||||
"source": "./integrations/cursor-plugin",
|
||||
"description": "Cross-session memory and token savings for coding agents.",
|
||||
"version": "0.3.3"
|
||||
"version": "0.3.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{
|
||||
"id": "mem0",
|
||||
"displayName": "Mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"description": "Cross-session memory and token savings for coding agents.",
|
||||
"homepage": "https://mem0.ai",
|
||||
"keywords": ["memory", "personalization", "mcp", "semantic-search"],
|
||||
|
||||
@@ -2100,6 +2100,16 @@ A full-featured command-line interface for Mem0, available in both Python and No
|
||||
<Tabs>
|
||||
<Tab title="Mem0 Plugin">
|
||||
|
||||
<Update label="2026-09-25" description="Portable agent plugin v0.3.4">
|
||||
|
||||
**Fixes:**
|
||||
- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when no key is set in the plugin settings or `MEM0_API_KEY`, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Auth:** A plugin setting that reaches the plugin as an unexpanded `${api_key}` or `${user_id}` placeholder is treated as unset. It is no longer sent as the API key, cached to disk, or used as the user ID ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **State:** The `search_memories` MCP server now keeps its state in `~/.mem0/coding-agent-plugin`, the same directory the status, pause, resume and forget skills use, instead of `~/.mem0/mem0-plugin` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/<version>` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-08" description="Shared agent plugin runtime">
|
||||
|
||||
**Changed:**
|
||||
@@ -2411,6 +2421,15 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex
|
||||
|
||||
<Tab title="Claude Code">
|
||||
|
||||
<Update label="2026-09-25" description="Claude Code plugin v0.3.4">
|
||||
|
||||
**Fixes:**
|
||||
- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when no key is set in the plugin settings or `MEM0_API_KEY`, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Auth:** A plugin setting that reaches the plugin as an unexpanded `${api_key}` or `${user_id}` placeholder is treated as unset. It is no longer sent as the API key, cached to disk, or used as the user ID ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/<version>` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-23" description="Claude Code plugin v0.3.3">
|
||||
|
||||
**Improvements:**
|
||||
@@ -2452,6 +2471,16 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex
|
||||
|
||||
<Tab title="Cursor">
|
||||
|
||||
<Update label="2026-09-25" description="Cursor plugin v0.3.4">
|
||||
|
||||
**Fixes:**
|
||||
- **Auth:** An API key set in the Cursor plugin settings now reaches the `search_memories` MCP server. The server looked for the cached key in `~/.mem0/mem0-plugin` while the hooks cached it in `~/.mem0/cursor-plugin`, so search asked for auth whenever Cursor did not pass the setting to the MCP server directly ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when no key is set in the plugin settings or `MEM0_API_KEY`, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Auth:** A plugin setting that reaches the plugin as an unexpanded `${api_key}` or `${user_id}` placeholder is treated as unset. It is no longer sent as the API key, cached to disk, or used as the user ID ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/<version>` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-23" description="Cursor plugin v0.3.3">
|
||||
|
||||
**Improvements:**
|
||||
@@ -2492,6 +2521,14 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex
|
||||
|
||||
<Tab title="Codex">
|
||||
|
||||
<Update label="2026-09-25" description="Codex plugin v0.3.4">
|
||||
|
||||
**Fixes:**
|
||||
- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when `MEM0_API_KEY` is not set, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/<version>` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-23" description="Codex plugin v0.3.3">
|
||||
|
||||
**Improvements:**
|
||||
@@ -2531,6 +2568,13 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex
|
||||
|
||||
<Tab title="OpenCode">
|
||||
|
||||
<Update label="2026-09-25" description="OpenCode plugin v0.4.2">
|
||||
|
||||
**Fixes:**
|
||||
- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when neither `MEM0_API_KEY` nor a shell profile sets one, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-23" description="OpenCode plugin v0.4.1">
|
||||
|
||||
**Improvements:**
|
||||
@@ -2646,6 +2690,15 @@ Initial release of the Mem0 plugin for Claude Code and Cursor, followed by Codex
|
||||
|
||||
<Tab title="Antigravity">
|
||||
|
||||
<Update label="2026-09-25" description="Antigravity plugin v0.3.4">
|
||||
|
||||
**Fixes:**
|
||||
- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when `MEM0_API_KEY` is not set, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Data directory:** The `search_memories` MCP server uses `~/.mem0/antigravity-plugin`, the data directory the hooks use, instead of `~/.mem0/mem0-plugin` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/<version>` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-23" description="Antigravity plugin v0.3.3">
|
||||
|
||||
**Improvements:**
|
||||
@@ -2759,6 +2812,15 @@ Existing memories written by the previous versions are not rewritten. If your me
|
||||
|
||||
<Tab title="Kimi">
|
||||
|
||||
<Update label="2026-09-25" description="Kimi Code plugin v0.3.4">
|
||||
|
||||
**Fixes:**
|
||||
- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when `MEM0_API_KEY` is not set, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Data directory:** The `search_memories` MCP server uses `~/.mem0/kimi-plugin`, the data directory the hooks use, instead of `~/.mem0/mem0-plugin` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Packaging:** `PLUGIN_VERSION` bumped to `0.3.4`, so the `mem0-plugin/<version>` wire header and `plugin_version` telemetry field identify builds with this fix ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-23" description="Kimi Code plugin v0.3.3">
|
||||
|
||||
**Improvements:**
|
||||
@@ -3117,6 +3179,13 @@ Existing memories written by the previous versions are not rewritten. If your me
|
||||
|
||||
<Tab title="Pi Agent">
|
||||
|
||||
<Update label="2026-09-25" description="Pi Agent plugin v0.3.3">
|
||||
|
||||
**Fixes:**
|
||||
- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when neither `MEM0_API_KEY` nor `~/.pi/agent/mem0-config.json` sets one, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-23" description="Pi Agent plugin v0.3.2">
|
||||
|
||||
**Improvements:**
|
||||
@@ -3246,6 +3315,14 @@ Existing memories written by the previous versions are not rewritten. If your me
|
||||
|
||||
<Tab title="DeepSeek Harness">
|
||||
|
||||
<Update label="2026-09-25" description="deepseek-plugin v0.3.3">
|
||||
|
||||
**Fixes:**
|
||||
- **Auth:** Falls back to the API key `mem0 init` saved in `~/.mem0/config.json` when neither `config.apiKey` nor `MEM0_API_KEY` is set, so search and the status check no longer report a missing key after `mem0 init` ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
- **Config:** An empty `apiKey` now counts as unset and falls through to `MEM0_API_KEY` and the `mem0 init` key, instead of failing validation ([#7449](https://github.com/mem0ai/mem0/pull/7449))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-09-23" description="deepseek-plugin v0.3.2">
|
||||
|
||||
**Improvements:**
|
||||
|
||||
@@ -103,6 +103,7 @@ def _render_harness_id(host: str, *, portable: bool = False) -> str:
|
||||
"\n"
|
||||
f'HARNESS_ID = "{host}"\n'
|
||||
f'SOURCE_TAG = "{tag}"\n'
|
||||
f'DATA_DIR_NAME = "{host}-plugin"\n'
|
||||
"\n"
|
||||
"# Platform-side vocabulary (mem0_event.source + X-Application). The whole\n"
|
||||
"# plugin family is one source; which editor it runs in is the application.\n"
|
||||
|
||||
@@ -28,12 +28,23 @@ from typing import Any, Iterable
|
||||
|
||||
import telemetry
|
||||
|
||||
# Read from the generated per-host module so a new entrypoint is correct without
|
||||
# remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_DATA_DIR_NAME = "mem0-plugin"
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
DEFAULT_API_URL = "https://api.mem0.ai"
|
||||
PLUGIN_VERSION = "0.3.3"
|
||||
PLUGIN_VERSION = "0.3.4"
|
||||
|
||||
_harness_name: str = "generic"
|
||||
_harness_env_prefix: str = "MEM0_PLUGIN"
|
||||
_harness_data_dir_name: str = "mem0-plugin"
|
||||
_harness_data_dir_name: str = _DATA_DIR_NAME
|
||||
_harness_source_tag: str = "mem0_plugin"
|
||||
|
||||
|
||||
@@ -1802,16 +1813,6 @@ def extraction_message_batches(
|
||||
return batches
|
||||
|
||||
|
||||
# Platform surface attribution. Read from the generated per-host module so a new
|
||||
# entrypoint is correct without remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
|
||||
def platform_headers(key: str) -> dict[str, str]:
|
||||
"""Auth plus the three surface-identity headers.
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
"""Launch each host's real hooks and MCP server the way the host does, and check the search is authenticated."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
from string import Template
|
||||
|
||||
import pytest
|
||||
|
||||
INTEGRATIONS = Path(__file__).resolve().parents[2]
|
||||
KEY = "m0-plugin-setting-key"
|
||||
CODEX_INHERITED_ENV = ("HOME", "PATH", "LANG", "TMPDIR")
|
||||
|
||||
|
||||
class _Mem0Api(BaseHTTPRequestHandler):
|
||||
authorizations: list[str]
|
||||
|
||||
def do_POST(self) -> None:
|
||||
self.rfile.read(int(self.headers.get("Content-Length") or 0))
|
||||
self.authorizations.append(self.headers.get("Authorization", ""))
|
||||
body = b'{"results": []}'
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
def log_message(self, *args: object) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mem0_api():
|
||||
handler = type("Handler", (_Mem0Api,), {"authorizations": []})
|
||||
server = ThreadingHTTPServer(("127.0.0.1", 0), handler)
|
||||
threading.Thread(target=server.serve_forever, daemon=True).start()
|
||||
yield f"http://127.0.0.1:{server.server_port}", handler.authorizations
|
||||
server.shutdown()
|
||||
|
||||
|
||||
def _json(path: Path) -> dict:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def _expand(value: str, variables: dict[str, str]) -> str:
|
||||
return Template(value).safe_substitute(variables)
|
||||
|
||||
|
||||
def _run_hook(command: str, cwd: Path, env: dict[str, str], payload: dict) -> None:
|
||||
result = subprocess.run(
|
||||
["sh", "-c", command], cwd=cwd, env=env, input=json.dumps(payload), text=True, capture_output=True
|
||||
)
|
||||
assert result.returncode == 0, result.stderr
|
||||
|
||||
|
||||
def _search(argv: list[str], cwd: Path, env: dict[str, str]) -> dict:
|
||||
requests = [
|
||||
{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05"}},
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "tools/call",
|
||||
"params": {"name": "search_memories", "arguments": {"query": "earlier fixes"}},
|
||||
},
|
||||
]
|
||||
result = subprocess.run(
|
||||
argv,
|
||||
cwd=cwd,
|
||||
env=env,
|
||||
input="\n".join(json.dumps(request) for request in requests) + "\n",
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=30,
|
||||
)
|
||||
assert result.returncode == 0, result.stderr
|
||||
return json.loads(result.stdout.splitlines()[-1])
|
||||
|
||||
|
||||
def _claude_code(host_env: dict[str, str], tmp_path: Path, repo: Path, setting: bool) -> tuple:
|
||||
root = INTEGRATIONS / "claude-code-plugin"
|
||||
plugin_env = {"CLAUDE_PLUGIN_ROOT": str(root), "CLAUDE_PLUGIN_DATA": str(tmp_path / "claude-data")}
|
||||
hook_env = {**host_env, **plugin_env, **({"CLAUDE_PLUGIN_OPTION_API_KEY": KEY} if setting else {})}
|
||||
command = _json(root / "hooks" / "hooks.json")["hooks"]["SessionStart"][0]["hooks"][0]["command"]
|
||||
_run_hook(command, repo, hook_env, {"session_id": "s1", "cwd": str(repo)})
|
||||
|
||||
server = _json(root / ".mcp.json")["mcpServers"]["mem0"]
|
||||
env = {**host_env, **{name: _expand(value, plugin_env) for name, value in server["env"].items()}}
|
||||
return [sys.executable, *(_expand(argument, plugin_env) for argument in server["args"])], repo, env
|
||||
|
||||
|
||||
def _cursor(host_env: dict[str, str], tmp_path: Path, repo: Path, setting: bool) -> tuple:
|
||||
root = INTEGRATIONS / "cursor-plugin"
|
||||
command = _json(root / "hooks" / "hooks.json")["hooks"]["sessionStart"][0]["command"]
|
||||
if setting:
|
||||
command = command.replace("${api_key}", KEY)
|
||||
hook_env = {**host_env, "CURSOR_PLUGIN_ROOT": str(root)}
|
||||
_run_hook(command, repo, hook_env, {"conversation_id": "c1", "workspace_roots": [str(repo)]})
|
||||
|
||||
server = _json(root / "mcp.json")["mcpServers"]["mem0"]
|
||||
args = [_expand(argument, {"CURSOR_PLUGIN_ROOT": str(root)}) for argument in server["args"]]
|
||||
return [sys.executable, *args], repo, {**host_env, **server["env"]}
|
||||
|
||||
|
||||
def _codex(host_env: dict[str, str], tmp_path: Path, repo: Path, setting: bool) -> tuple:
|
||||
root = INTEGRATIONS / "codex-plugin"
|
||||
codex_env = {**host_env, **({"MEM0_API_KEY": KEY} if setting else {})}
|
||||
data = str(tmp_path / "codex-data")
|
||||
hook_env = {**codex_env, "PLUGIN_ROOT": str(root), "PLUGIN_DATA": data, "CLAUDE_PLUGIN_DATA": data}
|
||||
command = _json(root / "hooks" / "hooks.json")["hooks"]["SessionStart"][0]["hooks"][0]["command"]
|
||||
_run_hook(command, repo, hook_env, {"session_id": "s1", "cwd": str(repo)})
|
||||
|
||||
server = _json(root / ".mcp.json")["mcpServers"]["mem0"]
|
||||
forwarded = [*CODEX_INHERITED_ENV, *server["env_vars"]]
|
||||
env = {name: codex_env[name] for name in forwarded if name in codex_env}
|
||||
return [sys.executable, *server["args"]], root / server["cwd"], env
|
||||
|
||||
|
||||
HOSTS = {"claude-code": _claude_code, "cursor": _cursor, "codex": _codex}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("key_source", ["plugin setting", "mem0 init"])
|
||||
@pytest.mark.parametrize("host", sorted(HOSTS))
|
||||
def test_mcp_server_searches_with_the_key_the_user_configured(host, key_source, tmp_path, mem0_api):
|
||||
"""The key reaches the MCP server however the host delivers it: plugin setting, env, or `mem0 init`."""
|
||||
api_url, authorizations = mem0_api
|
||||
home = tmp_path / "home"
|
||||
repo = tmp_path / "repo"
|
||||
repo.mkdir()
|
||||
if key_source == "mem0 init":
|
||||
(home / ".mem0").mkdir(parents=True)
|
||||
(home / ".mem0" / "config.json").write_text(json.dumps({"platform": {"api_key": KEY}}), encoding="utf-8")
|
||||
host_env = {
|
||||
"HOME": str(home),
|
||||
"PATH": os.environ["PATH"],
|
||||
"MEM0_API_URL": api_url,
|
||||
"MEM0_TELEMETRY": "false",
|
||||
"MEM0_CODE_USER_ID": "test-user",
|
||||
}
|
||||
|
||||
argv, cwd, env = HOSTS[host](host_env, tmp_path, repo, key_source == "plugin setting")
|
||||
response = _search(argv, cwd, env)
|
||||
|
||||
assert not response["result"].get("isError"), response
|
||||
assert authorizations == [f"Token {KEY}"]
|
||||
@@ -83,6 +83,26 @@ def test_identity_resolves_without_init(harness, spec):
|
||||
assert out == f"{harness} {source_tag}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("harness,spec", sorted(HOSTS.items()))
|
||||
def test_data_dir_resolves_without_init(harness, spec):
|
||||
"""mcp_server never configures the harness, so its default must match configure_harness(harness) in hooks and skills."""
|
||||
directory, _ = spec
|
||||
core = _core_dir(directory)
|
||||
if not core.exists():
|
||||
pytest.skip(f"{directory} is not built in this tree")
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
out = _run(
|
||||
core,
|
||||
Path(tmp),
|
||||
"import os; os.environ.pop('MEM0_CODE_DATA_DIR')\n"
|
||||
"import mcp_server, memory_core; print(memory_core.data_dir())\n"
|
||||
f"memory_core.configure_harness({harness!r}); print(memory_core.data_dir())",
|
||||
)
|
||||
expected = str(Path(tmp) / ".mem0" / f"{harness}-plugin")
|
||||
assert out.splitlines() == [expected, expected]
|
||||
|
||||
|
||||
def test_mcp_server_records_the_real_harness():
|
||||
"""mcp_server imports telemetry and never initialises it (server.py has no init).
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
HARNESS_ID = "antigravity"
|
||||
SOURCE_TAG = "ANTIGRAVITY_PLUGIN"
|
||||
DATA_DIR_NAME = "antigravity-plugin"
|
||||
|
||||
# Platform-side vocabulary (mem0_event.source + X-Application). The whole
|
||||
# plugin family is one source; which editor it runs in is the application.
|
||||
|
||||
@@ -28,12 +28,23 @@ from typing import Any, Iterable
|
||||
|
||||
import telemetry
|
||||
|
||||
# Read from the generated per-host module so a new entrypoint is correct without
|
||||
# remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_DATA_DIR_NAME = "mem0-plugin"
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
DEFAULT_API_URL = "https://api.mem0.ai"
|
||||
PLUGIN_VERSION = "0.3.3"
|
||||
PLUGIN_VERSION = "0.3.4"
|
||||
|
||||
_harness_name: str = "generic"
|
||||
_harness_env_prefix: str = "MEM0_PLUGIN"
|
||||
_harness_data_dir_name: str = "mem0-plugin"
|
||||
_harness_data_dir_name: str = _DATA_DIR_NAME
|
||||
_harness_source_tag: str = "mem0_plugin"
|
||||
|
||||
|
||||
@@ -1802,16 +1813,6 @@ def extraction_message_batches(
|
||||
return batches
|
||||
|
||||
|
||||
# Platform surface attribution. Read from the generated per-host module so a new
|
||||
# entrypoint is correct without remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
|
||||
def platform_headers(key: str) -> dict[str, str]:
|
||||
"""Auth plus the three surface-identity headers.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"homepage": "https://docs.mem0.ai/integrations/antigravity",
|
||||
"native": {
|
||||
"pluginRoot": "${ANTIGRAVITY_PLUGIN_ROOT}",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"description": "Cross-session memory and token savings for coding agents.",
|
||||
"author": {
|
||||
"name": "Mem0"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
HARNESS_ID = "claude-code"
|
||||
SOURCE_TAG = "CLAUDE_CODE_PLUGIN"
|
||||
DATA_DIR_NAME = "claude-code-plugin"
|
||||
|
||||
# Platform-side vocabulary (mem0_event.source + X-Application). The whole
|
||||
# plugin family is one source; which editor it runs in is the application.
|
||||
|
||||
@@ -28,12 +28,23 @@ from typing import Any, Iterable
|
||||
|
||||
import telemetry
|
||||
|
||||
# Read from the generated per-host module so a new entrypoint is correct without
|
||||
# remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_DATA_DIR_NAME = "mem0-plugin"
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
DEFAULT_API_URL = "https://api.mem0.ai"
|
||||
PLUGIN_VERSION = "0.3.3"
|
||||
PLUGIN_VERSION = "0.3.4"
|
||||
|
||||
_harness_name: str = "generic"
|
||||
_harness_env_prefix: str = "MEM0_PLUGIN"
|
||||
_harness_data_dir_name: str = "mem0-plugin"
|
||||
_harness_data_dir_name: str = _DATA_DIR_NAME
|
||||
_harness_source_tag: str = "mem0_plugin"
|
||||
|
||||
|
||||
@@ -1802,16 +1813,6 @@ def extraction_message_batches(
|
||||
return batches
|
||||
|
||||
|
||||
# Platform surface attribution. Read from the generated per-host module so a new
|
||||
# entrypoint is correct without remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
|
||||
def platform_headers(key: str) -> dict[str, str]:
|
||||
"""Auth plus the three surface-identity headers.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"homepage": "https://docs.mem0.ai/integrations/claude-code",
|
||||
"native": {
|
||||
"pluginRoot": "${CLAUDE_PLUGIN_ROOT}",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"description": "Cross-session memory and token savings for coding agents.",
|
||||
"author": { "name": "Mem0", "email": "support@mem0.ai" },
|
||||
"homepage": "https://docs.mem0.ai/integrations/codex",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
HARNESS_ID = "codex"
|
||||
SOURCE_TAG = "CODEX_PLUGIN"
|
||||
DATA_DIR_NAME = "codex-plugin"
|
||||
|
||||
# Platform-side vocabulary (mem0_event.source + X-Application). The whole
|
||||
# plugin family is one source; which editor it runs in is the application.
|
||||
|
||||
@@ -28,12 +28,23 @@ from typing import Any, Iterable
|
||||
|
||||
import telemetry
|
||||
|
||||
# Read from the generated per-host module so a new entrypoint is correct without
|
||||
# remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_DATA_DIR_NAME = "mem0-plugin"
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
DEFAULT_API_URL = "https://api.mem0.ai"
|
||||
PLUGIN_VERSION = "0.3.3"
|
||||
PLUGIN_VERSION = "0.3.4"
|
||||
|
||||
_harness_name: str = "generic"
|
||||
_harness_env_prefix: str = "MEM0_PLUGIN"
|
||||
_harness_data_dir_name: str = "mem0-plugin"
|
||||
_harness_data_dir_name: str = _DATA_DIR_NAME
|
||||
_harness_source_tag: str = "mem0_plugin"
|
||||
|
||||
|
||||
@@ -1802,16 +1813,6 @@ def extraction_message_batches(
|
||||
return batches
|
||||
|
||||
|
||||
# Platform surface attribution. Read from the generated per-host module so a new
|
||||
# entrypoint is correct without remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
|
||||
def platform_headers(key: str) -> dict[str, str]:
|
||||
"""Auth plus the three surface-identity headers.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"homepage": "https://docs.mem0.ai/integrations/codex",
|
||||
"native": {
|
||||
"pluginRoot": "${PLUGIN_ROOT}",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"description": "Cross-session memory and token savings for coding agents.",
|
||||
"author": { "name": "Mem0", "email": "support@mem0.ai" },
|
||||
"homepage": "https://docs.mem0.ai/integrations/cursor",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
HARNESS_ID = "cursor"
|
||||
SOURCE_TAG = "CURSOR_PLUGIN"
|
||||
DATA_DIR_NAME = "cursor-plugin"
|
||||
|
||||
# Platform-side vocabulary (mem0_event.source + X-Application). The whole
|
||||
# plugin family is one source; which editor it runs in is the application.
|
||||
|
||||
@@ -28,12 +28,23 @@ from typing import Any, Iterable
|
||||
|
||||
import telemetry
|
||||
|
||||
# Read from the generated per-host module so a new entrypoint is correct without
|
||||
# remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_DATA_DIR_NAME = "mem0-plugin"
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
DEFAULT_API_URL = "https://api.mem0.ai"
|
||||
PLUGIN_VERSION = "0.3.3"
|
||||
PLUGIN_VERSION = "0.3.4"
|
||||
|
||||
_harness_name: str = "generic"
|
||||
_harness_env_prefix: str = "MEM0_PLUGIN"
|
||||
_harness_data_dir_name: str = "mem0-plugin"
|
||||
_harness_data_dir_name: str = _DATA_DIR_NAME
|
||||
_harness_source_tag: str = "mem0_plugin"
|
||||
|
||||
|
||||
@@ -1802,16 +1813,6 @@ def extraction_message_batches(
|
||||
return batches
|
||||
|
||||
|
||||
# Platform surface attribution. Read from the generated per-host module so a new
|
||||
# entrypoint is correct without remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
|
||||
def platform_headers(key: str) -> dict[str, str]:
|
||||
"""Auth plus the three surface-identity headers.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"homepage": "https://docs.mem0.ai/integrations/cursor",
|
||||
"native": {
|
||||
"pluginRoot": "${CURSOR_PLUGIN_ROOT}",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mem0/deepseek-plugin",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"description": "Mem0 long-term memory as a native DeepSeek Harness (Cordis) plugin.",
|
||||
"type": "module",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
HARNESS_ID = "kimi"
|
||||
SOURCE_TAG = "KIMI_PLUGIN"
|
||||
DATA_DIR_NAME = "kimi-plugin"
|
||||
|
||||
# Platform-side vocabulary (mem0_event.source + X-Application). The whole
|
||||
# plugin family is one source; which editor it runs in is the application.
|
||||
|
||||
@@ -28,12 +28,23 @@ from typing import Any, Iterable
|
||||
|
||||
import telemetry
|
||||
|
||||
# Read from the generated per-host module so a new entrypoint is correct without
|
||||
# remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_DATA_DIR_NAME = "mem0-plugin"
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
DEFAULT_API_URL = "https://api.mem0.ai"
|
||||
PLUGIN_VERSION = "0.3.3"
|
||||
PLUGIN_VERSION = "0.3.4"
|
||||
|
||||
_harness_name: str = "generic"
|
||||
_harness_env_prefix: str = "MEM0_PLUGIN"
|
||||
_harness_data_dir_name: str = "mem0-plugin"
|
||||
_harness_data_dir_name: str = _DATA_DIR_NAME
|
||||
_harness_source_tag: str = "mem0_plugin"
|
||||
|
||||
|
||||
@@ -1802,16 +1813,6 @@ def extraction_message_batches(
|
||||
return batches
|
||||
|
||||
|
||||
# Platform surface attribution. Read from the generated per-host module so a new
|
||||
# entrypoint is correct without remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
|
||||
def platform_headers(key: str) -> dict[str, str]:
|
||||
"""Auth plus the three surface-identity headers.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"description": "Cross-session memory and token savings for coding agents.",
|
||||
"keywords": ["memory", "coding-agents", "continual-learning", "token-efficiency"],
|
||||
"author": { "name": "Mem0", "email": "support@mem0.ai" },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"homepage": "https://docs.mem0.ai/integrations/kimi",
|
||||
"native": {
|
||||
"pluginRoot": "${KIMI_PLUGIN_ROOT}",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
HARNESS_ID = "coding-agent"
|
||||
SOURCE_TAG = "CODING_AGENT_PLUGIN"
|
||||
DATA_DIR_NAME = "coding-agent-plugin"
|
||||
|
||||
# Platform-side vocabulary (mem0_event.source + X-Application). The whole
|
||||
# plugin family is one source; which editor it runs in is the application.
|
||||
|
||||
@@ -28,12 +28,23 @@ from typing import Any, Iterable
|
||||
|
||||
import telemetry
|
||||
|
||||
# Read from the generated per-host module so a new entrypoint is correct without
|
||||
# remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import DATA_DIR_NAME as _DATA_DIR_NAME
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_DATA_DIR_NAME = "mem0-plugin"
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
DEFAULT_API_URL = "https://api.mem0.ai"
|
||||
PLUGIN_VERSION = "0.3.3"
|
||||
PLUGIN_VERSION = "0.3.4"
|
||||
|
||||
_harness_name: str = "generic"
|
||||
_harness_env_prefix: str = "MEM0_PLUGIN"
|
||||
_harness_data_dir_name: str = "mem0-plugin"
|
||||
_harness_data_dir_name: str = _DATA_DIR_NAME
|
||||
_harness_source_tag: str = "mem0_plugin"
|
||||
|
||||
|
||||
@@ -1802,16 +1813,6 @@ def extraction_message_batches(
|
||||
return batches
|
||||
|
||||
|
||||
# Platform surface attribution. Read from the generated per-host module so a new
|
||||
# entrypoint is correct without remembering to configure anything.
|
||||
try: # pragma: no cover - absent only in the un-built shared source tree
|
||||
from _harness_id import PLATFORM_APPLICATION as _PLATFORM_APPLICATION
|
||||
from _harness_id import PLATFORM_SOURCE as _PLATFORM_SOURCE
|
||||
except ImportError:
|
||||
_PLATFORM_SOURCE = "MEM0_PLUGIN"
|
||||
_PLATFORM_APPLICATION = ""
|
||||
|
||||
|
||||
def platform_headers(key: str) -> dict[str, str]:
|
||||
"""Auth plus the three surface-identity headers.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
||||
"name": "mem0",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"description": "Cross-session memory and token savings for coding agents.",
|
||||
"author": {
|
||||
"name": "Mem0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mem0/opencode-plugin",
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2",
|
||||
"type": "module",
|
||||
"description": "Mem0 persistent memory plugin for OpenCode — add, search, and manage memories across sessions",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mem0/pi-agent-plugin",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"type": "module",
|
||||
"description": "Mem0 memory extension for Pi Agent persistent, scoped, semantic memory across sessions and projects",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"category": "Productivity",
|
||||
"description": "Cross-session memory and token savings for coding agents.",
|
||||
"version": "0.3.3"
|
||||
"version": "0.3.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user