Skip to content
claude code plugin

Teach Claude a rule today.
Watch a session next week apply it — and show you the message it came from.

Nine steps, about 10 minutes, and a few cents of model usage. The service runs in Docker; the only thing you install is the plugin.

Docker + Compose v2 runs postgres, the API, and the worker docker compose version
Claude Code the plugin host claude --version
python3 3.10+ on the host Claude Code hooks are host processes, not containers python3 -V
An LLM API key with credit embeddings and the memory ladder are real model calls step 1

No fake providers. The stack has an LLM_FAKE=1 mode for CI. It can prove transport and exact message capture, but it cannot build the memory this test checks. Leave it off and use a real key.

the service

Steps 1–5 — stand up memory and prove it works

All of this happens before Claude Code is involved. That order is deliberate: when something is wrong here, no plugin, hook, or prompt can fix it.

  1. step 01 Get a model API key and put it in .env

    The shipped design calls OpenAI: a chat model for the memory passes and text-embedding-3-small for every stored record. Create a key at platform.openai.com/api-keys. Add it as OPENAI_API_KEY to .env in the repository root. Compose reads that file automatically, and Git ignores it.

    your terminal
    $ git clone https://github.com/memseekai/memseek && cd memseek

    Another provider? Point examples/agent_memory_catalog/conf/models.yaml at any OpenAI-compatible endpoint that also offers embeddings, name its key variable in api_key_env, and put that variable in .env.

  2. step 02 Start the stack with one command

    --wait returns only once the API is healthy and the one-shot steps have exited 0. First build: a few minutes. After that: seconds.

    your terminal
    $ docker compose up -d --build --wait
    $ docker compose logs setup --no-log-prefix
    
    workspace 'local' created; key written to /state/api_key
    published agent_memory@0.3.0 (20 files) from examples/agent_memory_catalog
    MCP interface ready — 7 tools: context, recall, standing_rules,
      replay_session, remember, record, answer

    migrate and setup are supposed to show as exited in docker compose ps -a: they apply the schema and publish the memory design, then have nothing left to do. If setup still shows as running, it is mid-publish — give it a few seconds.

  3. step 03 Read the workspace key

    The setup step writes it to a bind-mounted file instead of printing it into interleaved logs. This is the key the plugin will ask for.

    your terminal
    $ cat .memseek/api_key
  4. step 04 Check the service answers

    Liveness including the database, then the exact tool contract the plugin's MCP connection depends on — checked from inside the container, so Docker stays the only requirement.

    your terminal
    $ curl -s http://127.0.0.1:8000/health
    {"ok":true,"db":true}
    
    $ docker compose exec \
        -e MEMSEEK_URL=http://127.0.0.1:8000 \
        -e MEMSEEK_API_KEY="$(cat .memseek/api_key)" \
        api memseek mcp --check
    
    # expect: package agent_memory 0.3.0, seven tools, and
    #   "streamable_http": "http://127.0.0.1:8000/mcp"
  5. step 05 Prove your model credentials work — before Claude is involved

    This is the check that separates “my API key is wrong” from “the plugin is broken”, and it takes 30 seconds. Write one message over HTTP, then watch the worker do the real model work.

    your terminal
    $ curl -sS -X POST http://127.0.0.1:8000/records \
        -H "Authorization: Bearer $(cat .memseek/api_key)" \
        -H 'Content-Type: application/json' \
        -d '{"records":[{"collection":"messages","type":"message",
             "entity":"project:preflight",
             "text":"Every distributed-cache key must start with orbit:.",
             "content":{"text":"Every distributed-cache key must start with orbit:.",
                         "role":"user","session_id":"preflight","ordinal":0},
             "dedupe_key":"preflight:0"}]}'
    
    {"inserted":[{"index":0,"id":"4882d928-…","ready":false}],"duplicates":[]}
    
    $ docker compose logs -f worker
    
    # within ~30s, one line per stage:
    "processor":"embedding_v1","provider":"openai","status":"ok"
    "derivation":"l1_extract","status":"ok","output_count":1
    "derivation":"scene_synthesis","status":"ok","output_count":1

    That is the ladder forming from a single message: evidence embedded, a reusable memory extracted, a topic summary written. ready:false in the first response is expected — the record is stored, its embedding is pending. A "status":"error" line naming authentication or a model is your key or your model name, and it is much cheaper to see it here.

    your terminal
    # is the derived memory retrievable?
    $ curl -sS -X POST http://127.0.0.1:8000/views/memory_recall/query \
        -H "Authorization: Bearer $(cat .memseek/api_key)" \
        -H 'Content-Type: application/json' \
        -d '{"entity":"project:preflight","task":"distributed cache key prefix"}'
    
    # then delete the throwaway entity so it cannot pollute the real test
    $ curl -sS -X POST http://127.0.0.1:8000/erase \
        -H "Authorization: Bearer $(cat .memseek/api_key)" \
        -H 'Content-Type: application/json' \
        -d '{"entity":"project:preflight"}'
the plugin

Steps 6–7 — install it, then try to catch it forgetting

Step 7 is the actual product test. Everything before it exists so that a failure there means something.

  1. step 06 Install the plugin from the checkout

    The plugin is not on a marketplace yet — and it does not need to be. The repository you cloned is one: it carries .claude-plugin/marketplace.json. --config supplies the three values the interactive flow would ask for, so there is nothing to type into a prompt.

    in the repo you want Claude to remember
    $ claude plugin marketplace add ./
    $ claude plugin install memseek-memory@memseek --scope local \
        --config MEMSEEK_URL=http://127.0.0.1:8000 \
        --config MEMSEEK_API_KEY="$(cat .memseek/api_key)" \
        --config MEMSEEK_CAPTURE_MODE=conversation
    
    ✔ Successfully added marketplace: memseek (declared in user settings)
    ✔ Successfully installed plugin: memseek-memory@memseek (scope: local)
    
    $ claude plugin details memseek-memory
    
    Skills (5)  memseek-explain, memseek-feedback, memseek-remember,
                memseek-search, memseek-status
    Hooks (5)   SessionStart, UserPromptSubmit, Stop, PreCompact, SessionEnd
    MCP servers (1)  memseek

    Start a session in that repository. The plugin loads on startup:

    your terminal
    $ claude
    
    Memseek connected for project:memseek:2f77b8026b767ade.
    ValueWhat it is
    MEMSEEK_URLhttp://127.0.0.1:8000 — the base URL, no /mcp, no trailing slash
    MEMSEEK_API_KEYthe workspace key from step 3; stored as a sensitive value, not written into settings.json
    MEMSEEK_CAPTURE_MODEconversation, explicit, or off — all three keep recall on

    ./, not . — a bare dot is rejected. --scope local keeps the plugin to this project; --scope user puts it in every project. Installing copies the plugin at its declared version, so if you are editing its hooks or skills, run claude --plugin-dir ./integrations/claude-code instead and it loads your working tree.

    Then confirm the host side:

    claude code
    > /memseek-memory:memseek-status
    
    Memseek is ready.
      Service: http://127.0.0.1:8000
      Project memory: project:memseek:2f77b8026b767ade
      Conversation capture: conversation
      Memory tools: 7/7 available
      Retry queue: 0 pending, 0 need inspection

    Note the project memory name. It is derived from the repository, and it is what makes a later session find the same memory instead of a blank one.

  2. step 07 Teach one rule, restart, and ask cold
    1. 01 · say it
      For this test project, every distributed-cache key must start with orbit:.
      Treat this as a priority-90 coding rule until I revoke it.
    2. 02 · watch it become memory
      $ docker compose logs -f worker
      # "derivation":"l1_extract","status":"ok" — from your actual
      # conversation, with no memory tool called
    3. 03 · confirm it is retrievable
      > /memseek-memory:memseek-search distributed-cache key prefix
      Repeat every few seconds until the orbit: rule comes back. Derivation is asynchronous. If it never appears, the worker or the model key is at fault — not the plugin.
    4. 04 · quit claude code, reopen it in the same repo, and forbid tools
      Do not call a memory tool. Based only on context supplied before this
      request, what prefix must distributed-cache keys use here? Cite the
      memory evidence.
      An answer of orbit: with a citation proves the whole chain at once: captured, derived, project identity stable across a restart, relevant memory selected, brief delivered before Claude answered.
    5. 05 · ask why it believes that
      > /memseek-memory:memseek-explain
      Claude should separate the derived rule from the literal message you typed, with record ids. Memory you cannot audit is not the feature.

    Step 3 passes but step 4 fails → memory is healthy, the automatic brief is the suspect. Step 3 fails → look at the worker first.

steps 8–9

Go deeper, then delete everything

step 08 · optional checks
  • Exact capture, in orderGET /timeline?entity=<project memory> returns your message and Claude's reply as separate rows.
  • Fail-open under outagedocker compose stop api, send a prompt: Claude keeps working and the write waits in a local queue. Start it again and flush.
  • Retention policySwitch capture to explicit or off and confirm ordinary chat stops producing records while recall keeps working.
  • Feedback lands on a real render/memseek-memory:memseek-feedback task_success … reports an artifact-use id and zero queued writes.
step 09 · clean up
# stop, keep the memory and the key
$ docker compose down

# delete everything this test created
$ docker compose down -v && rm -rf .memseek

A fresh volume means a fresh key. down -v destroys the workspace, so the next up mints a new key and the plugin's stored one stops working. Update it in /pluginmemseek-memory@memseek and start a new session.

# and the plugin itself
$ claude plugin uninstall memseek-memory@memseek --scope local
$ claude plugin marketplace remove memseek

When it does not work

SymptomWhat to do
Marketplace file not found at …/marketplaces/memseekai-memseek/…The plugin is not published to GitHub yet, so there is nothing to clone. Install from your checkout: claude plugin marketplace add ./.
Plugin "memseek-memory" not found in marketplace "memseek"The marketplace entry points at a copy with no plugin — usually a failed remote add. claude plugin marketplace remove memseek, then add ./ from the repository root.
Invalid marketplace source formatA bare . is not accepted. Use ./ or an absolute path.
Plugin source edits have no effectInstall copies the plugin at its declared version. Use claude --plugin-dir ./integrations/claude-code while you are editing it.
Port 8000 already in useAdd MEMSEEK_PORT=8100 to .env, bring the stack up again, and give the plugin http://127.0.0.1:8100.
Records stay ready: falseThe embedding call is failing. docker compose logs worker — usually a missing or unfunded key in .env.
Worker logs "status":"error" with a model nameYour account cannot call that model. Change the alias targets in examples/agent_memory_catalog/conf/models.yaml, then docker compose run --rm setup && docker compose restart api worker.
/mcp does not list memseekThe URL must be the base — http://127.0.0.1:8000, with no /mcp and no trailing slash.
Search finds the rule, a new session does notStorage and retrieval are fine; the automatic brief is the suspect. Compare the project memory name memseek-status reports in both sessions.
.memseek/api_key is missingA workspace key is disclosed once. docker compose down -v and start over.

The repo carries the same procedure with every expected output in full, plus the capture-mode matrix and the complete troubleshooting table: docs/claude-code-plugin.md.

What you just tested is not a chat log.

The plugin stores your exact messages as evidence, derives small reusable rules and decisions from them, keeps a summary per area of work, and hands Claude a bounded brief for the task in front of it — with links back to what you actually said. It is one memory design declared in YAML, running on a Postgres you own.