fix(mem0-plugin): resolve API key from userConfig, fix #4876

Root cause: .mcp.json required ${MEM0_API_KEY} shell env var which
OAuth flow never populates — MCP server never started, only auth
stubs were exposed.

Fix: switch .mcp.json to ${user_config.MEM0_API_KEY} so Claude Code
reads the key from plugin userConfig (system keychain). Add
resolve_api_key() fallback chain (MEM0_API_KEY -> CLAUDE_PLUGIN_OPTION_MEM0_API_KEY)
to all hook scripts. Update onboard skill and setup_coding_categories.py
to handle keychain-only users. Add 3 tests for key resolution.
This commit is contained in:
kartik-mem0
2026-05-21 17:10:17 +05:30
parent 66600565ed
commit ff2f6c281a
11 changed files with 83 additions and 29 deletions
+29 -1
View File
@@ -1,9 +1,10 @@
"""Tests for write-path app_id migration.
"""Tests for write-path app_id migration and API key resolution.
Verifies that all scripts writing to the Mem0 API:
1. Pass app_id as a top-level parameter (not in metadata)
2. Do NOT include project_id in metadata
3. Include branch in metadata when available
4. Use resolve_api_key() for key resolution with userConfig fallback
"""
from __future__ import annotations
@@ -164,3 +165,30 @@ def test_no_metadata_project_id_anywhere():
metadata = body.get("metadata", {})
assert "project_id" not in metadata, f"Write function #{i} still has metadata.project_id"
assert body.get("app_id") == "proj", f"Write function #{i} missing app_id top-level"
def test_resolve_api_key_prefers_env_var(monkeypatch):
"""resolve_api_key returns MEM0_API_KEY when both are set."""
from _identity import resolve_api_key
monkeypatch.setenv("MEM0_API_KEY", "direct-key")
monkeypatch.setenv("CLAUDE_PLUGIN_OPTION_MEM0_API_KEY", "fallback-key")
assert resolve_api_key() == "direct-key"
def test_resolve_api_key_falls_back_to_plugin_option(monkeypatch):
"""resolve_api_key falls back to CLAUDE_PLUGIN_OPTION_MEM0_API_KEY."""
from _identity import resolve_api_key
monkeypatch.delenv("MEM0_API_KEY", raising=False)
monkeypatch.setenv("CLAUDE_PLUGIN_OPTION_MEM0_API_KEY", "fallback-key")
assert resolve_api_key() == "fallback-key"
def test_resolve_api_key_returns_empty_when_neither_set(monkeypatch):
"""resolve_api_key returns empty string when no key is available."""
from _identity import resolve_api_key
monkeypatch.delenv("MEM0_API_KEY", raising=False)
monkeypatch.delenv("CLAUDE_PLUGIN_OPTION_MEM0_API_KEY", raising=False)
assert resolve_api_key() == ""