PowerMem
July 1, 2026 · View on GitHub
This file is a prompt for Claude Code. Open Claude Code in your terminal and say:
Read and follow
apps/claude-code-plugin/SETUP.mdto set up PowerMem memory for Claude Code.
Claude Code will then run the steps below: detect whether you are in the PowerMem
source tree or not, ask you for the few required secrets, and wire PowerMem up as a
globally enabled plugin so every claude session (interactive AND non-interactive
claude -p) uses it automatically — no per-session --plugin-dir flag.
Installed plugin initialization
Use this section when the memory-powermem plugin is already installed from a
Claude Code marketplace and the user runs:
/memory-powermem:init
In this mode, do not run the source/developer install flow below: do not build
hook binaries, do not stage the plugin, do not run claude plugin marketplace add,
do not run claude plugin install, and do not build the dashboard. The plugin is
already installed; this section only prepares the PowerMem backend that the plugin
connects to.
Installed-plugin init ensures uv is available, then starts the backend with
the uvx-style launcher. The package spec depends on the storage backend: the
default SQLite path uses powermem[server,extras] (pulls sentence-transformers
for the local huggingface embedder), while the OceanBase path uses
powermem[server,seekdb]. It does not create a plugin-local venv. Therefore the
PyPI release used by this flow must already contain the backend capabilities
required by the plugin, including the local embedding dependencies. If the user
is validating a plugin change that depends on unpublished backend code, use
POWERMEM_INIT_PACKAGE to pass that exact Git branch or commit to uvx --from
instead of the PyPI package.
Installed-plugin init is idempotent and uses plugin-local state:
$HOME/.powermem/
.env
runtime.env
powermem.pid
powermem-server.log
seekdb_data/
Follow these steps:
Always use a two-step invocation: discover or reuse CLAUDE_PLUGIN_ROOT
first, then run the script. Never write VAR=val sh "$VAR/..." on one line —
the shell expands $VAR before the assignment, producing an empty path.
# If CLAUDE_PLUGIN_ROOT is not already set, find the plugin root:
if [ -z "${CLAUDE_PLUGIN_ROOT:-}" ]; then
export CLAUDE_PLUGIN_ROOT=$(find ~/.claude/plugins/cache/powermem/memory-powermem -maxdepth 2 -name scripts -type d 2>/dev/null | head -1 | xargs dirname)
fi
sh "$CLAUDE_PLUGIN_ROOT/scripts/..."
-
If the skill was just installed or updated, ask the user to run
/reload-pluginsfirst, then retry/memory-powermem:init. -
Run
sh "$CLAUDE_PLUGIN_ROOT/scripts/status.sh"and inspect whether config, uv, managed PID, Python versions, and health are present. -
If the server is healthy and
.envexists — tell the user the current storage backend (readDATABASE_PROVIDERfrom.env). Do not re-runinit.sh. If the user wants to reconfigure, stop the server first, then proceed. -
If
.envis missing, run init with auto-detection first:sh "$CLAUDE_PLUGIN_ROOT/scripts/init.sh"The script reads the current process environment first and attempts to derive the supported Anthropic configuration. It uses
ANTHROPIC_API_KEYfirst; only when that is absent does it useANTHROPIC_AUTH_TOKENtogether withANTHROPIC_BASE_URL. It also readsANTHROPIC_MODELfrom the environment. If the environment does not provide a complete config, it falls back to~/.claude/settings.jsonusing the same Anthropic keys. It writes the plugin-local.envwith the full PowerMem backend defaults: SQLite storage (default for coding agent; setPOWERMEM_INIT_DATABASE_PROVIDER=oceanbasefor OceanBase/seekdb production use), local HuggingFace embedding (no API key,sentence-transformersfrompowermem[extras]), server settings, and logging settings. -
If init reports missing values, ask the user only for those missing values. Do not invent credentials. Re-run init with the matching environment variables:
POWERMEM_INIT_LLM_PROVIDER=anthropic \ POWERMEM_INIT_LLM_MODEL=anthropic/claude-sonnet-4.6 \ POWERMEM_INIT_LLM_API_KEY=... \ sh "$CLAUDE_PLUGIN_ROOT/scripts/init.sh"For a bearer-token gateway, use:
POWERMEM_INIT_LLM_PROVIDER=anthropic \ POWERMEM_INIT_LLM_MODEL=anthropic/claude-sonnet-4.6 \ POWERMEM_INIT_LLM_AUTH_TOKEN=... \ POWERMEM_INIT_LLM_BASE_URL=https://your-gateway.example.com \ sh "$CLAUDE_PLUGIN_ROOT/scripts/init.sh"Optional variables:
POWERMEM_INIT_DATABASE_PROVIDER: storage backend —sqlite(default, coding agent) oroceanbase(production/cluster). Invalid values fall back tosqlite.POWERMEM_INIT_LLM_BASE_URLfor a custom provider gateway.POWERMEM_INIT_PACKAGEto test unpublished backend code throughuvx --frominstead of PyPIpowermem. Match the extras to the storage backend:powermem[server,extras]for SQLite (default, includessentence-transformers),powermem[server,seekdb]for OceanBase. Example:powermem[server,extras] @ git+https://github.com/oceanbase/powermem.git@<branch-or-sha>.POWERMEM_INIT_PYTHONto force a specific Python >= 3.11.POWERMEM_INIT_PORTto force the managed server port.
-
Never print API keys, auth tokens, or other credentials. Mask any secret in summaries.
-
After init succeeds, run
sh "$CLAUDE_PLUGIN_ROOT/scripts/status.sh"again and report the base URL. -
The hook launcher reads
runtime.env, so once init writes a base URL, prompt recall and session-save hooks use that backend automatically.
The default local embedding model (all-MiniLM-L6-v2) is downloaded
automatically by PowerMem at startup — cache hit loads from disk via
SentenceTransformer(local_files_only=True); cache miss on CN networks
downloads through ModelScope and bridges into the HuggingFace hub cache;
cache miss elsewhere downloads from HuggingFace with a 30s timeout.
POWERMEM_INIT_PRELOAD_MODEL is deprecated and now a no-op; init prints a
deprecation message if it is set.
If startup fails with No module named 'sentence_transformers', the backend
package resolved by uvx --from does not include the local embedding dependency.
Publish a backend build that includes it, or set POWERMEM_INIT_PACKAGE to a Git
branch/commit that does.
To test connectivity:
curl -s -m 10 -o /dev/null -w "ModelScope: HTTP %{http_code}\n" \
https://www.modelscope.cn/api/v1/models/AI-ModelScope/all-MiniLM-L6-v2
curl -s -m 10 -o /dev/null -w "HuggingFace: HTTP %{http_code}\n" \
https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2
Set up PowerMem memory for Claude Code on this machine globally. Do the whole integration autonomously and ask me for any secret you need — never invent credentials.
🔒 DATA SAFETY — API Key Masking (MANDATORY):
When displaying ANY .env content — current values, proposed changes, confirmation
summaries, or any other output — you MUST mask LLM_API_KEY, LLM_AUTH_TOKEN,
and any other secret
values (passwords, tokens, keys):
- Key ≥ 10 chars: show only first 4 + last 4 characters (e.g.
sk-a…b12x) - Key < 10 chars: show
***Non-secret values (provider, model, base URLs, storage type, etc.) may be shown in full.
🔒 Shell commands MUST also avoid printing secrets. Terminal output is visible to the user and Claude Code cannot retroactively redact it. Follow these rules:
- When reading env vars or
.env, use inline masking so the shell never prints plaintext:echo "${VAR:0:4}...${VAR: -4}"to show first 4 + last 4, or[ -n "$VAR" ] && echo "set" || echo "empty"just to check existence. - When you need to
cat .envorgrepfor secrets, pipe through sed to mask before printing:cat .env | sed -E 's/(API_KEY=).*/\1***REDACTED***/' - Never run
echo $LLM_API_KEY,echo $LLM_AUTH_TOKEN,env | grep KEY,cat .env(unmasked), or any command that would print a secret value directly to stdout. - Use
readwith-s(silent) when prompting for secrets interactively.
⚠️ .env changes always require user approval. Before modifying .env for any
reason (LLM config, embedder settings, storage switches, ...), show the user the
masked current values (using the rules above), propose the exact change, and WAIT
for confirmation. Never patch .env silently.
This procedure is idempotent: it is safe to re-run. Each step must detect existing state and either skip, reuse, or refresh it instead of failing or duplicating work.
- DETECT CONTEXT. The current directory is the PowerMem source tree if a
pyproject.toml here has name = "powermem" (or src/powermem/ and
apps/claude-code-plugin/ both exist). Tell me which path you will take:
- SOURCE -> deploy from this checkout and install the Claude Code plugin GLOBALLY in HTTP mode (hooks -> REST; rebuild hook binaries only when refreshing them from source changes).
- PYPI/MCP -> install PowerMem from PyPI with uv and connect via the powermem-mcp server (the plugin itself is NOT on PyPI).
⚠️ RULE: Every time you need to modify .env — for any reason, even a
single variable — you MUST stop and ask the user what value to use. Show
the masked current content of the relevant lines (per the 🔒 DATA SAFETY
rules above), propose the change, and WAIT for the user's confirmation before
writing. Never silently patch .env.
1a. DETECT REGION (run before any network operations). Detect whether this machine is in China — this determines uv install source, model download source, and Python package index:
CC=$(curl -s -m 5 https://ipinfo.io/country 2>/dev/null || echo "UNKNOWN")
echo "Region: $CC"
- If
CC=CN: install uv through the USTC GitHub Release mirror, use ModelScope for model downloads, and set the Python package index tohttps://pypi.tuna.tsinghua.edu.cn/simple/. - If
CC != CN(orUNKNOWN): install uv through the official Astral installer, use HuggingFace for model downloads, and use the default PyPI index. - Store
CCin a shell variable — every uv install, package install, and model download step below branches on this value. Re-check on every re-run (region may change if the machine moves or VPN state changes).
uv installation (run before any Python package install):
if ! command -v uv >/dev/null 2>&1; then
if [ "$CC" = "CN" ]; then
export UV_DOWNLOAD_URL=https://mirrors.ustc.edu.cn/github-release/astral-sh/uv/LatestRelease/
curl -sL https://mirrors.ustc.edu.cn/github-release/astral-sh/uv/LatestRelease/uv-installer.sh | sh
else
curl -LsSf https://astral.sh/uv/install.sh | sh
fi
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
fi
uv --version
uv pip conventions (apply to ALL package installs in this file):
a. CN mirror — if CC=CN, add
--default-index https://pypi.tuna.tsinghua.edu.cn/simple to every
uv pip install command. Non-CN (or UNKNOWN) uses the default PyPI index
with no extra flags.
b. Retry — on failure, retry up to 3 times. If a uv pip install command
fails, do NOT immediately escalate to the user. Retry the EXACT same command
up to 2 more times (3 total attempts). If all 3 fail, only then report the
error. Between retries, wait 2 seconds:
bash for i in 1 2 3; do uv pip install --python "$POWERMEM_PYTHON" ... && break echo "uv pip install attempt $i failed, retrying in 2s..." sleep 2 done
This applies to editable installs, dependency installs, model download
dependencies, etc.
c. All four package install locations affected by (a)-(b):
1. uv pip install --python "$POWERMEM_PYTHON" -e '.[server,seekdb]'
2. uv pip install --python "$POWERMEM_PYTHON" -q modelscope
3. uv pip install --python "$POWERMEM_PYTHON" "powermem[server,seekdb]"
4. uv pip install --python "$POWERMEM_PYTHON" -q huggingface_hub
-
COLLECT CONFIG (idempotent). If a .env already exists in the working directory with LLM_PROVIDER / LLM_API_KEY or LLM_AUTH_TOKEN / LLM_MODEL set to real values (not placeholders like
your_api_key_here), REUSE it — skip directly to step 3a/3b. Only collect what is missing. Use zero-config defaults for everything else (storage = platform-aware: embedded SeekDB on supported Linux installs, otherwise SQLite basic mode; embedder = local all-MiniLM-L6-v2) unless I say otherwise.2a. Auto-detect or manual? Use AskUserQuestion (single-select):
Option Description Yes, auto-detect Auto-detect LLM config from the current process environment, then ~/.claude/settings.json, then ask only for missing fieldsIf the user selects "Yes, auto-detect" (or "Other" and types "yes"/"auto"):
Auto-detection priority chain:
- Current process environment variables — check these first:
ANTHROPIC_API_KEYANTHROPIC_AUTH_TOKENANTHROPIC_BASE_URLANTHROPIC_MODEL
~/.claude/settings.json— useenv.ANTHROPIC_*,env.LLM_*, and top-levelmodelas fallback sources- Manual input — ask only for fields that are still missing
PowerMem supports Claude Code's Anthropic API-key path and bearer-token gateway path. Do not treat
ANTHROPIC_AUTH_TOKENasLLM_API_KEY; copy it toLLM_AUTH_TOKENand requireANTHROPIC_BASE_URL. A token without a base URL is incomplete; fall back to API-key mode or ask for the base URL instead of sending the token to Anthropic's default endpoint. Do not migrateCLAUDE_CODE_OAUTH_TOKEN,/logincredentials,apiKeyHelper, Bedrock, Vertex, or Foundry as eitherLLM_API_KEYorLLM_AUTH_TOKEN.Step 1 — Check OS environment variables. Run these checks silently (do not print the values, only note whether each field was found):
Field Check LLM_PROVIDER If ANTHROPIC_API_KEYorANTHROPIC_AUTH_TOKENis set →anthropic; otherwise infer fromANTHROPIC_MODELprefix if presentLLM_MODEL $ANTHROPIC_MODELLLM_API_KEY $ANTHROPIC_API_KEYLLM_AUTH_TOKEN $ANTHROPIC_AUTH_TOKEN, only whenANTHROPIC_API_KEYis absentLLM_BASE_URL $ANTHROPIC_BASE_URLIf environment model detection fails, read
env.ANTHROPIC_MODEL,env.LLM_MODEL, or top-levelmodelfrom~/.claude/settings.json. Keep the model exactly as configured. Do not strip a<provider>/prefix and do not rewrite dotted versions:"anthropic/claude-opus-4.6"→LLM_PROVIDER=anthropic,LLM_MODEL=anthropic/claude-opus-4.6"anthropic/claude-sonnet-4.6"→LLM_PROVIDER=anthropic,LLM_MODEL=anthropic/claude-sonnet-4.6
Credentials — preserve PowerMem's environment precedence: API key first, bearer-token gateway second. If environment credentials are incomplete, fall back to the old
~/.claude/settings.jsoncredential flow. Read:ANTHROPIC_API_KEYANTHROPIC_AUTH_TOKEN, only ifANTHROPIC_API_KEYis absent- fallback
env.ANTHROPIC_AUTH_TOKEN/env.ANTHROPIC_API_KEYfrom~/.claude/settings.json
Base URL — read directly:
ANTHROPIC_BASE_URL- fallback
env.ANTHROPIC_BASE_URLorenv.LLM_BASE_URLfrom~/.claude/settings.json - If
ANTHROPIC_AUTH_TOKENis used,ANTHROPIC_BASE_URLis required. - If
ANTHROPIC_API_KEYis used and the base URL is absent, leave it blank — PowerMem will use the provider's default endpoint.
Step 3 — For any fields still missing after environment and settings detection, ask as a plain chat question (one at a time, per 2b–2e below). Only ask for what is actually missing.
After detection/manual input, show a masked summary of the merged result (per 🔒 DATA SAFETY rules), noting the source of each field (env / settings.json / manual). Then jump to 2f.
If the user does NOT select auto-detect, fall back to the manual flow:
2b. Ask: "Any custom base URL? (paste it, or say
noto use the default)"2c. Ask: "What provider id? (e.g. openai, anthropic, qwen, deepseek, ollama)"
2d. Ask for the credential: API key for direct provider access, or auth token for an Anthropic-compatible gateway. Skip if provider is
ollamaorvllm.2e. Ask: "Which model? (e.g. gpt-4o-mini, claude-sonnet-4-6, qwen-plus)"
For 2b–2e, ask each question as a plain chat message, one at a time. Do NOT use AskUserQuestion for these free-text inputs.
2f. Confirm and write. Show a masked summary of what will be written (per 🔒 DATA SAFETY rules above), then WAIT for explicit "yes" before writing. Copy
.env.exampleif.envdoes not exist, then fillLLM_PROVIDER/LLM_API_KEYorLLM_AUTH_TOKEN/LLM_MODEL. If a base URL was given, write it to the provider-prefixed*_LLM_BASE_URL(e.g.OPENAI_LLM_BASE_URL) — verify spelling against.env.example.full; a typo is silently ignored. - Current process environment variables — check these first:
3a. SOURCE path (global install):
- Ensure uv is installed using Step 1a, then create/reuse an explicit Python
environment and install PowerMem with uv:
bash uv venv venv --python python3.11 POWERMEM_PYTHON="$(pwd)/venv/bin/python" export PATH="$(pwd)/venv/bin:$PATH" uv pip install --python "$POWERMEM_PYTHON" -e '.[server,seekdb]'
⚠️ Both extras are required: [server] adds fastapi/uvicorn and
fastmcp for the HTTP API plus MCP transports;
[seekdb] adds the embedded seekdb storage backend. Platforms where
embedded SeekDB is unavailable default to SQLite basic mode; use
OceanBase/SeekDB for the full capability stack.
- Immediately after uv pip install, detect which Python interpreter was used. Read
the shebang from the freshly-installed powermem-server entry point — this is
the only reliable way to guarantee that the model-download script, the uv call
inside it, and the server all use the exact same interpreter and site-packages.
On many Linux systems the default python points to a version below 3.11 and python3 may point to a different minor
version than what uv used; relying on bare python will silently use
the wrong environment and the download will fail with an ImportError:
POWERMEM_PYTHON=(command -v powermem-server)"
| sed 's|#!||;s| .*||')
echo "Using interpreter: $POWERMEM_PYTHON" # e.g. /usr/bin/python3.11
- Start the embedding model download in the background immediately after the
uv package install, so it runs in parallel with the remaining setup steps (hook build,
plugin stage/install). Branch on the region detected in Step 1a:
**CN region (ModelScope path):**
uv pip install --python "$POWERMEM_PYTHON" -q modelscope && $POWERMEM_PYTHON -c "
from modelscope import snapshot_download
snapshot_download('AI-ModelScope/all-MiniLM-L6-v2')
import os, shutil, urllib.request, json
src = os.path.expanduser('~/.cache/modelscope/hub/models/AI-ModelScope/all-MiniLM-L6-v2')
hub = os.path.expanduser('~/.cache/huggingface/hub/models--sentence-transformers--all-MiniLM-L6-v2')
try:
resp = urllib.request.urlopen('https://huggingface.co/api/models/sentence-transformers/all-MiniLM-L6-v2', timeout=5)
rev = json.load(resp)['sha']
except Exception:
rev = 'fa97f6e7cb1a59073dff9e9d8ba1c7c1591cc08d'
snap = os.path.join(hub, 'snapshots', rev)
os.makedirs(snap, exist_ok=True)
os.makedirs(os.path.join(hub, 'refs'), exist_ok=True)
open(os.path.join(hub, 'refs', 'main'), 'w').write(rev)
skip = {'configuration.json', 'data_config.json'}
[shutil.copytree(os.path.join(src,n), os.path.join(snap,n))
if os.path.isdir(os.path.join(src,n))
else shutil.copy2(os.path.join(src,n), os.path.join(snap,n))
for n in os.listdir(src)
if n not in skip and not os.path.exists(os.path.join(snap,n))]
print('Model download and cache bridge complete.')
" >> /tmp/powermem-model-download.log 2>&1 &
**Non-CN region (HuggingFace path):**
uv pip install --python "$POWERMEM_PYTHON" -q huggingface_hub && $POWERMEM_PYTHON -c "
from huggingface_hub import snapshot_download
snapshot_download('sentence-transformers/all-MiniLM-L6-v2')
print('Model download complete.')
" >> /tmp/powermem-model-download.log 2>&1 &
Store the background PID:
POWERMEM_MODEL_DL_PID=$!
- Ask whether to build the Web Dashboard EARLY — before starting the server —
so the build runs in parallel with model download and server startup. Use
AskUserQuestion (single-select):
| Option | Description |
|--------|-------------|
| Yes, build dashboard | Run `make build-dashboard` in background, parallel with model download and server startup |
| No, skip | Continue without building the dashboard |
If the user selects "Yes, build dashboard", launch it in the BACKGROUND
immediately (do NOT wait for it — model download starts in parallel too):
make build-dashboard >> /tmp/powermem-dashboard-build.log 2>&1 &
DASHBOARD_BUILD_PID=$!
- Confirm the hook binaries are present before install, because they get copied
into Claude's plugin cache at install time:
normal Git/marketplace install: use the committed hooks/bin/ binaries.
if hook source changed and Go 1.22+ is present: make build-claude-hook
if refreshed binaries are needed but Go is absent: offer to install Go or
fall back to the PYPI/MCP path below.
- Ensure the plugin's root .mcp.json stays empty ({}) — default HTTP mode.
- STAGE the plugin into a stable, Claude-owned location so the marketplace does
NOT depend on this checkout — you can move or delete the repo afterwards and
memory keeps working. Copy the whole plugin dir (built binaries included) into
~/.claude/marketplaces/powermem:
DEST="$HOME/.claude/marketplaces/powermem"
mkdir -p "$DEST"
rsync -a --delete "<ABS_PATH>/apps/claude-code-plugin/" "$DEST/"
# no rsync? rm -rf "$DEST" && cp -a "<ABS_PATH>/apps/claude-code-plugin/." "$DEST/"
The committed `hooks/bin/` binaries are already on disk before this copy.
Re-copy on every re-run so the staged dir tracks your latest build.
- Register the marketplace from the STAGED dir (it ships
.claude-plugin/marketplace.json) — never from the repo:
claude plugin marketplace add "$DEST"
If it reports "already on disk", refresh it instead:
claude plugin marketplace update powermem
- Install + enable the plugin globally (user scope). Install auto-enables it:
claude plugin install memory-powermem@powermem --scope user
IMPORTANT idempotency rule: a plain re-install is a no-op and does NOT refresh
the cached copy. If you just rebuilt the binaries or changed the plugin, force
a refresh regardless of whether the plugin was previously installed:
claude plugin uninstall memory-powermem@powermem 2>/dev/null || true
claude plugin install memory-powermem@powermem --scope user
The `|| true` swallows the "not found" error when the plugin was never installed
— uninstall fails with exit code 1 in that case, which would abort a script.
(Enablement is preserved across uninstall+reinstall.)
- Wait for the background model download (started above) to finish before
starting the server. If it is already done this is a no-op:
if [ -n "${POWERMEM_MODEL_DL_PID:-}" ] && kill -0 "$POWERMEM_MODEL_DL_PID" 2>/dev/null; then
echo "Waiting for model download to finish..."
wait "$POWERMEM_MODEL_DL_PID"
fi
grep -q "complete" /tmp/powermem-model-download.log 2>/dev/null \
|| { echo "Model download failed. Check /tmp/powermem-model-download.log"; exit 1; }
- Start the API server only if it is not already healthy (idempotent).
⚠️ STARTUP TIME: first launch can take 60–120s (local embedder load/download + uvicorn bind).
Exit code 7 means the port is not yet bound — do NOT kill and restart.
curl -s http://localhost:8848/api/v1/system/health | grep -q healthy \
|| { powermem-server --host 0.0.0.0 --port 8848 &
echo "Waiting for server (first launch can take 60–120s)..."
for i in $(seq 1 30); do
sleep 5
curl -s -m 3 http://localhost:8848/api/v1/system/health \
| grep -q healthy \
&& echo "Server ready after $((i*5))s." && break
echo " still starting... ($((i*5))s)"
done; }
- **After the server is healthy**, auto-detect dashboard availability:
```bash
# Wait for dashboard build to finish (if started):
if [ -n "${DASHBOARD_BUILD_PID:-}" ] && kill -0 "$DASHBOARD_BUILD_PID" 2>/dev/null; then
echo "Waiting for dashboard build to finish..."
wait "$DASHBOARD_BUILD_PID"
fi
# Auto-detect whether dashboard is available:
if curl -s -m 3 http://localhost:8848/dashboard/ | grep -q '<title>PowerMem'; then
echo "Dashboard is available at http://localhost:8848/dashboard/"
# open in browser if possible
command -v xdg-open >/dev/null 2>&1 && xdg-open http://localhost:8848/dashboard/ &
command -v open >/dev/null 2>&1 && open http://localhost:8848/dashboard/ &
else
echo "Dashboard build may have failed. Check /tmp/powermem-dashboard-build.log"
fi
```
The running server is not restarted. Dashboard assets are served from the
static files directory configured in the server.
- Confirm the plugin is enabled: claude plugin list (look for
memory-powermem@powermem). Do NOT print a --plugin-dir command — it is global
now; every `claude` and `claude -p` loads it automatically.
3b. PYPI/MCP path:
- Install the server extra in the environment that Claude will use, then:
uv venv venv --python python3.11
POWERMEM_PYTHON="(pwd)/venv/bin:POWERMEM_PYTHON" "powermem[server,seekdb]"
powermem-mcp --help
- Register the MCP server globally so it persists across sessions (stdio = no
port), run from the directory holding the .env. Idempotent: if claude mcp get powermem already exists, remove it first, then add:
claude mcp remove powermem 2>/dev/null; claude mcp add powermem -- powermem-mcp stdio
- If registration succeeds but Claude reports the MCP server as failed, debug the
MCP process before retrying setup:
claude mcp list
claude mcp get powermem
command -v powermem-mcp
powermem-mcp --help
Then run the MCP server directly from the same directory that contains .env
and capture stderr separately:
powermem-mcp stdio >/tmp/powermem-mcp.stdout 2>/tmp/powermem-mcp.stderr &
MCP_PID=MCP_PID" 2>/dev/null || true; wait "$MCP_PID" 2>/dev/null || true
sed -n '1,120p' /tmp/powermem-mcp.stderr
sed -n '1,40p' /tmp/powermem-mcp.stdout
Expected: stderr may contain normal startup diagnostics; stdout must be empty
until a JSON-RPC request arrives. If stdout contains banners, warnings, stack
traces, or any non-JSON text, Claude cannot parse the MCP stream. Fix that
output pollution before claiming the MCP path works.
-
VERIFY with a real round-trip — do not claim success without data. Run the exact commands below and substitute nothing except the noted placeholder. Do NOT mark this step done until you have seen a non-empty search result actually come back.
SOURCE/HTTP path — run a/b/c/d/e in order:
a. Confirm the server answers (output must contain "status":"healthy"): curl -s -m 5 http://localhost:8848/api/v1/system/health
b. WRITE a probe memory. CRITICAL SCHEMA: the request body is a single "content" STRING field — NOT a mem0-style "messages" array. Sending {"messages":[...]} returns HTTP 422
{"detail":[{"type":"missing","loc":["body","content"]...}]}. Use a unique user_id so the probe is isolated from real data: curl -s -m 60 -X POST http://localhost:8848/api/v1/memories
-H 'Content-Type: application/json'
-d '{"content":"PowerMem setup probe: my favorite test fruit is dragonfruit-zx9.","user_id":"powermem_setup_probe"}' Expected: JSON with "success": true and a data[0].memory_id (a long numeric string). The call can take 10-30s because the LLM extracts facts — KEEP the -m 60 timeout and WAIT; do not background it or abort early. Save the returned data[0].memory_id (a.k.a. data[0].id) — you need it for cleanup in (e).c. SEARCH it back. CRITICAL SCHEMA: the body field is "query" (not "question" or "text"), with the SAME user_id you wrote with: curl -s -m 30 -X POST http://localhost:8848/api/v1/memories/search
-H 'Content-Type: application/json'
-d '{"query":"what is my favorite test fruit","user_id":"powermem_setup_probe","limit":5}' Expected: data.total >= 1 and data.results[0].content mentions dragonfruit-zx9. If data.total is 0 the round-trip FAILED — do NOT report success. Re-check the server log and the embedder, retry the write in (b) once, then escalate to me.d. SHOW me both the write JSON and the search JSON (this is the proof of success).
e. CLEAN UP the probe — delete by the id from (b), then confirm it is gone: curl -s -m 10 -X DELETE http://localhost:8848/api/v1/memories/<MEMORY_ID> Re-run the search from (c): data.total must now be 0.
f. BONUS (proves global + headless wiring; do it if you can). Run a headless prompt from an UNRELATED dir with NO --plugin-dir, then check the logs for the two hook-driven calls it triggers: ( cd /tmp && claude -p "Reply with exactly: probe ok" ) Then in
server.log(powermem-server), AFTER that run, you MUST see both: POST /api/v1/memories/search <- UserPromptSubmit hook (auto-recall) POST /api/v1/memories <- SessionEnd hook (auto-save) Seeing both proves PowerMem loads automatically in everyclaude/claude -p.PYPI/MCP path: confirm
claude mcp listshows powermem as "connected" (not "failed"). If it shows failed, run the MCP diagnostics below and do not report success until the direct MCP process starts cleanly and Claude shows it connected.a. Inspect Claude's registered MCP config and status: claude mcp list claude mcp get powermem Verify the command is exactly
powermem-mcp stdiounless you intentionally chose a different binary. Also verifycommand -v powermem-mcpsucceeds in the same shell whereclauderuns.b. Run the MCP server directly and inspect both streams: powermem-mcp stdio >/tmp/powermem-mcp.stdout 2>/tmp/powermem-mcp.stderr & MCP_PID=MCP_PID" 2>/dev/null || true; wait "$MCP_PID" 2>/dev/null || true sed -n '1,120p' /tmp/powermem-mcp.stderr sed -n '1,40p' /tmp/powermem-mcp.stdout Interpret the output: - stderr has import errors: install/repair the package or virtual environment. - stderr has missing LLM/API key/auth token/config errors: fix
.envafter asking the user for approval; never silently patch.env. - stderr has model download or network timeouts: pre-download the model or switch to a configured remote embedder. - stdout has non-JSON text before any request: remove noisy prints/logging from stdout; MCP stdio stdout is protocol-only. - both files are empty after timeout: the process likely started and waited for JSON-RPC input; continue with Claude-side connection checks.c. If direct startup is clean but Claude still says failed, refresh the registration: claude mcp remove powermem claude mcp add powermem -- powermem-mcp stdio claude mcp list If it still fails, start Claude with debug logging if available in the installed Claude Code version, then look for the first MCP spawn or JSON-RPC parse error.
-
SUMMARIZE: path taken, where .env lives, where the staged marketplace lives (~/.claude/marketplaces/powermem — independent of this repo), the server URL, how memory is wired (HTTP hooks vs MCP tools — recall is auto-injected on UserPromptSubmit, not a tool the model calls; writes happen on configured hook events), confirmation that it is enabled globally, and the fact that I just run
claude(orclaude -p) with nothing extra. Note: the background server does not survive a reboot — offer to set up a systemd user service for autostart.
Re-running / refreshing later
This file is safe to re-run end to end. The only manual-feeling case is refreshing
the cached plugin after you change the plugin or rebuild the Go hooks at the SAME
version: rebuild (make build-claude-hook), re-copy the result into the staged
marketplace (rsync -a --delete <ABS_PATH>/apps/claude-code-plugin/ ~/.claude/marketplaces/powermem/),
then force-refresh the cache with claude plugin uninstall memory-powermem@powermem
followed by claude plugin install memory-powermem@powermem --scope user (or bump the
version in .claude-plugin/plugin.json so claude plugin update memory-powermem picks it up).
To turn it off without uninstalling: claude plugin disable memory-powermem@powermem
(re-enable with claude plugin enable ...). To disable only prompt-time search
injection, set POWERMEM_PROMPT_SEARCH=0. The hook talks to POWERMEM_BASE_URL
(default http://localhost:8848).
For the full manual reference, see ../../docs/integrations/claude_code.md
🚨 COMPREHENSIVE ERROR HANDLING GUIDE
Every real-world setup encounters issues. This guide documents specific error scenarios and their resolutions discovered during actual setup attempts:
Log File Locations
- powermem-server:
server.log(RotatingFileHandler, 10MB max, 5 backups) - seekdb:
seekdb_data/log/seekdb.log(native C++ engine log)
Error Resolution Checklist
[E001] PEP 668 System Protection
Problem: system Python blocks package installation with "externally-managed-environment" Fix: use uv with a virtual environment
uv venv venv --python python3.11
source venv/bin/activate
# Use the extras matching your storage backend:
# SQLite (default): .[server,extras] (pulls sentence-transformers)
# OceanBase: .[server,seekdb]
uv pip install --python "$VIRTUAL_ENV/bin/python" -e '.[server,extras]'
[E002] Missing Server Dependencies
Problem: Server startup fails with missing packages Fix: Install missing dependencies with the extras matching your backend
# SQLite (default)
uv pip install --python "$POWERMEM_PYTHON" 'powermem[server,extras]'
# OceanBase
uv pip install --python "$POWERMEM_PYTHON" 'powermem[server,seekdb]'
[E003] SeekDB File Locking
Problem: "open seekdb failed OB_ERROR(4000)" or "db opened by other process" Fix: Stop duplicate servers, then remove stale seekdb data only if data loss is acceptable
pkill -f powermem-server
rm -rf seekdb_data
powermem-server --host 0.0.0.0 --port 8848 &
[E004] Missing Go for Hooks
Problem: make build-claude-hook fails
Fix: Install Go 1.22+
brew install go
make build-claude-hook
[E005] Storage Backend Initialization
Problem: 503 errors on API calls despite server health Fix: the Claude Code plugin defaults to local SQLite. Stop the managed server, remove stale SQLite data only if you accept deleting local memories, and restart init:
sh "$CLAUDE_PLUGIN_ROOT/scripts/stop.sh"
rm -f "$HOME/.powermem/powermem.db" "$HOME/.powermem/powermem.db-"*
sh "$CLAUDE_PLUGIN_ROOT/scripts/init.sh"
If you explicitly set POWERMEM_INIT_DATABASE_PROVIDER=oceanbase, use the
OceanBase/seekdb troubleshooting path instead and remove seekdb_data only when
data loss is acceptable.
[E006] Model Download Timeout
Problem: Server hangs or reports "timed out thrown while requesting HEAD" on startup. Cause: The embedding model is not cached and the network is unreachable. Fix: Follow the model pre-download step in Step 3a (branch on region detected in Step 1a). Quick reference:
CN region (ModelScope → HF hub bridge):
# Detect the correct interpreter first (same one powermem uses):
POWERMEM_PYTHON=$(head -1 "$(command -v powermem-server)" | sed 's|#!||;s| .*||')
uv pip install --python "$POWERMEM_PYTHON" modelscope
$POWERMEM_PYTHON -c "from modelscope import snapshot_download; \
snapshot_download('AI-ModelScope/all-MiniLM-L6-v2')"
# Verify (note: models/ subdirectory is required):
ls ~/.cache/modelscope/hub/models/AI-ModelScope/all-MiniLM-L6-v2/
Non-CN region (HuggingFace direct):
POWERMEM_PYTHON=$(head -1 "$(command -v powermem-server)" | sed 's|#!||;s| .*||')
uv pip install --python "$POWERMEM_PYTHON" huggingface_hub
$POWERMEM_PYTHON -c "from huggingface_hub import snapshot_download; \
snapshot_download('sentence-transformers/all-MiniLM-L6-v2')"
# Verify:
ls ~/.cache/huggingface/hub/models--sentence-transformers--all-MiniLM-L6-v2/snapshots/
⚠️ Do NOT use bare python here — on many systems the default python version is below 3.11.
Using the shebang from powermem-server guarantees all three steps (uv install, download,
bridge) run in the same environment.
Then run the bridge script from Step 3a to populate the HuggingFace hub cache
structure — the embedder's cache-detection function checks ~/.cache/huggingface/hub/,
not the ModelScope layout.
⚠️ DO NOT use sentence_transformers.SentenceTransformer(...) to download — it can
hang on networks where HuggingFace is unreachable. On CN region, use ModelScope +
HF hub bridge. On non-CN region, use huggingface_hub.snapshot_download. Always
check region first (Step 1a).
To confirm which sources are reachable:
curl -s -m 10 -o /dev/null -w "ModelScope: HTTP %{http_code}\n" \
https://www.modelscope.cn/api/v1/models/AI-ModelScope/all-MiniLM-L6-v2
curl -s -m 10 -o /dev/null -w "HuggingFace: HTTP %{http_code}\n" \
https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2
[E008] curl Exit Code 7 After Server Start
Problem: curl returns exit code 7 ("Failed to connect") right after launching
powermem-server.
Cause: The server process is running but not yet listening on port 8848. First
launch can take 60–120s (local embedder load/download + uvicorn bind).
Fix: Use the polling loop from Step 3a. Do NOT kill and restart — restarting
resets initialization and makes startup take even longer.
[E009] Server Exits Immediately — fastapi, uvicorn, or fastmcp Missing
Problem: Server exits immediately after launch with "Missing dependencies" error.
Cause: installing only the base project skips optional server dependencies.
powermem[server] installs fastapi, uvicorn, and fastmcp for the HTTP API plus
MCP transports.
Fix:
uv pip install --python "$POWERMEM_PYTHON" -e '.[server,seekdb]'
[E010] Anthropic temperature + top_p both sent → 400
Problem: Memory writes return success:true but data:[]; server.log reports
Error code: 400 - temperature and top_p cannot both be specified for this model.
Cause: base.py._get_common_params populates both temperature and top_p by
default. The Anthropic API (and most Anthropic-compatible proxies) rejects requests
that include both parameters simultaneously.
Fix: In src/powermem/integrations/llm/anthropic.py, drop top_p from params
before calling the API:
params = self._get_supported_params(messages=messages, **kwargs)
params.pop("top_p", None) # Anthropic rejects requests with both temperature and top_p
[E007] Claude MCP Server Shows Failed
Problem: claude mcp list shows powermem as failed or disconnected.
Fix: Inspect Claude's registered command, then run the MCP server directly and
check stdout/stderr separately:
claude mcp list
claude mcp get powermem
command -v powermem-mcp
powermem-mcp --help
powermem-mcp stdio >/tmp/powermem-mcp.stdout 2>/tmp/powermem-mcp.stderr &
MCP_PID=$!; sleep 10; kill "$MCP_PID" 2>/dev/null || true; wait "$MCP_PID" 2>/dev/null || true
sed -n '1,120p' /tmp/powermem-mcp.stderr
sed -n '1,40p' /tmp/powermem-mcp.stdout
Diagnosis:
- Import errors in stderr mean the package or virtual environment is broken.
- Missing key/config errors mean
.envmust be corrected after user approval. - Network/model timeout errors mean the embedder or model cache needs attention.
- Any banner, warning, stack trace, or non-JSON text in stdout breaks MCP stdio.
- Empty stdout/stderr after 10 seconds usually means the process started and is waiting for JSON-RPC input; re-check Claude-side registration next.
[E011] Python Version Below 3.11
Problem: package installation fails or venv created with wrong Python version.
PowerMem requires Python >= 3.11 (pyproject.toml: requires-python = ">=3.11").
Fix: Verify and use Python 3.11+:
python3 --version # must be >= 3.11
# If too old, find and use a newer Python:
python3.11 --version
uv venv venv --python python3.11
source venv/bin/activate
[E012] uv Missing or Not on PATH
Problem: uv: command not found
Fix: Install uv for your region, then add the install directory to PATH:
# Non-CN:
curl -LsSf https://astral.sh/uv/install.sh | sh
# CN:
export UV_DOWNLOAD_URL=https://mirrors.ustc.edu.cn/github-release/astral-sh/uv/LatestRelease/
curl -sL https://mirrors.ustc.edu.cn/github-release/astral-sh/uv/LatestRelease/uv-installer.sh | sh
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
uv --version
[E013] Internal PyPI Mirror Missing Packages
Problem: uv pip install fails with "No matching distribution found for pyobvector"
(or pyseekdb, onnxruntime, etc.).
Cause: The configured Python package index (e.g. internal mirror at yum.tbsite.net) does not
mirror all required packages.
Fix: If the package is already installed elsewhere (e.g. system Python 3.11), copy
it into the venv:
source venv/bin/activate
SITE_PKGS=$(python -c "import site; print(site.getsitepackages()[0])")
# Find the missing package in another Python installation:
find ~/.local/lib/python3.11/site-packages -maxdepth 1 -name "pyobvector*" -type d
# Copy both the package and its .dist-info:
cp -r ~/.local/lib/python3.11/site-packages/pyobvector "$SITE_PKGS/"
cp -r ~/.local/lib/python3.11/site-packages/pyobvector-*.dist-info "$SITE_PKGS/"
⚠️ After copying packages manually, you must also install their transitive dependencies — see [E014].
[E014] Missing Transitive Dependencies After Manual Copy
Problem: Server starts but returns "Memory service unavailable: storage backend
initialization failed" with ModuleNotFoundError: No module named 'onnxruntime'
(or tenacity, tokenizers).
Cause: When packages like pyobvector/pyseekdb are copied manually into the venv,
their transitive dependencies (onnxruntime, tenacity, tokenizers, etc.) are not
automatically resolved by the package installer.
Fix: Install the missing transitive dependencies:
source venv/bin/activate
uv pip install --python "$VIRTUAL_ENV/bin/python" onnxruntime tenacity tokenizers
To verify no other dependencies are missing after manual copying:
python -c "import pyobvector" 2>&1 # should produce no output
python -c "import pyseekdb" 2>&1 # should produce no output
PRE-CHECK & PREREQUISITES
- Verify Python version:
python3 --version(must be >= 3.11, see [E011]) - Verify uv:
uv --version(install it with [E012] if missing) - Verify hook binaries: committed
hooks/bin/binaries should already be present; Go 1.22+ is only needed when refreshing them from hook source changes. - Check mirror access: if using an internal mirror, verify
it has
pyobvector,pyseekdb, andonnxruntime; see [E013] if not.
STEP BY STEP PROVEN PATH
Given the encountered errors, here are the tested workarounds for each approach:
Method A: SOURCE Path (Current Directory Build)
# Create virtual environment to avoid PEP 668 issues.
uv venv venv --python python3.11
source venv/bin/activate
POWERMEM_PYTHON="$VIRTUAL_ENV/bin/python"
# Install everything with ALL required extras
uv pip install --python "$POWERMEM_PYTHON" -e '.[server,seekdb]'
# Git/marketplace installs use committed hook binaries.
# Optional after hook source changes:
# make build-claude-hook
# Register marketplace
DEST="$HOME/.claude/marketplaces/powermem"
mkdir -p "$DEST"
rsync -a --delete "$(pwd)/apps/claude-code-plugin/" "$DEST/"
claude plugin marketplace add "$DEST"
claude plugin install memory-powermem@powermem --scope user
# Start server (logs go to server.log automatically)
powermem-server --host 0.0.0.0 --port 8848 &
Method B: PYPI/MCP Path (Recommended for Stability)
# Clean virtual environment approach
uv venv venv --python python3.11
source venv/bin/activate
POWERMEM_PYTHON="$VIRTUAL_ENV/bin/python"
uv pip install --python "$POWERMEM_PYTHON" 'powermem[server,seekdb]'
claude mcp remove powermem 2>/dev/null
claude mcp add powermem -- powermem-mcp stdio
Method C: Troubleshooting Installation
# Common troubleshooting commands
lsof -i :8848 # Check if port is in use
pkill -f powermem-server # Kill any running server
rm -rf seekdb_data # Reset SeekDB if corrupted and data loss is acceptable
# Check logs
tail -f server.log # PowerMem server errors
tail -f seekdb_data/log/seekdb.log # SeekDB engine errors
FINAL VALIDATION STEPS
After setup, verify the complete round-trip:
# 1. Health check
curl -s http://localhost:8848/api/v1/system/health
# 2. Write test memory
curl -s -X POST http://localhost:8848/api/v1/memories \
-H 'Content-Type: application/json' \
-d '{"content":"PowerMem setup verification complete","user_id":"setup_test"}'
# 3. Search test
curl -s -X POST http://localhost:8848/api/v1/memories/search \
-H 'Content-Type: application/json' \
-d '{"query":"setup verification","user_id":"setup_test","limit":1}'
SYSTEMD AUTOSTART (Optional)
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/powermem.service << EOF
[Unit]
Description=PowerMem Memory Server
After=network.target
[Service]
Type=simple
WorkingDirectory=$(pwd)
ExecStart=/bin/bash -c 'source venv/bin/activate && powermem-server --host 0.0.0.0 --port 8848'
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable powermem.service
systemctl --user start powermem.service
SUMMARY
Path taken: [Based on observed errors, recommend PYPI/MCP approach for stability]
- .env location: $(pwd)/.env
- Virtual environment: $(pwd)/venv
- Plugin marketplace: ~/.claude/marketplaces/powermem
- Server URL: http://localhost:8848
- Memory system: seekdb storage with HTTP hooks
- Global enablement: Complete via
claude plugin install - Usage: Run
claudecommand with no extra flags needed
Quick commands for daily use:
# Start server
source venv/bin/activate
powermem-server --host 0.0.0.0 --port 8848
# Check status
systemctl --user status powermem.service
# Quick restart
ps aux | grep powrmem
Your Claude Code is now configured with automatic memory recall and persistence worldwide.