Skip to main content
Glama
Pseudogiant-xr

PseudoLife-MCP

Official

Pseudolife-MCP

PyPI CI License: Apache-2.0 Python 3.10+

简体中文 · 日本語 · 한국어 · Português (BR) · Español

Persistent long-term memory for Claude Code, Codex, and other MCP clients.

An MCP server that gives coding agents a long-term memory that persists across sessions — surviving context compactions and fresh tasks. Your coding agent is the intelligence; this server is its memory on disk.

Cortex Console — Observatory view

What you get:

  • Associative memory with honest forgetting — a flat similarity store ranked by hybrid dense-plus-lexical retrieval, with conflict detection that admits potential updates while preserving earlier source notes; whole-note replacement is explicit. (The measured verdict: a preregistered ablation campaign found the previous 8-band continuum tied a flat store on every gate, so the simpler structure ships; the continuum remains one config line away.)

  • Canonical facts, not vibes — one current value per entity.attribute slot (or a member set, for slots that hold many concurrent values); corrections supersede rather than silently overwrite, and the full version history survives.

  • Dreams — a bundled local extractor, or any OpenAI-compatible endpoint (a Claude model on your Max plan, a GPT-5.6 model on a ChatGPT plan, LM Studio, Ollama, vLLM), consolidates the memory stream into facts and a knowledge graph while you're not looking.

  • Lessons from its own work — successes, dead-ends, and your corrections become do/avoid guidance surfaced at the start of every session.

  • A web console to watch it think — the Cortex Console above, plus cited world facts, session episodes, and document RAG.

Measured, with receipts — the full 500-question LongMemEval sweep, all six question types, and every number ships with its committed run artifact:

LongMemEval oracle, 500 questions

naive RAG

commit-gated cascade

accuracy, all six question types

0.688

0.690

context tokens per question

~1210

~883

knowledge-update slice (78 of the 500)

0.859

0.936 (retired — see below)

Equal accuracy to naive RAG across the whole benchmark on ~73% of the context, and better calibrated about what it does not know: on BEAM-100K's abstention questions the fact spine scores 0.950 against naive RAG's 0.775, unchanged under two independent judges. Read that as calibration, not recall — in the budget-matched five-arm run of 2026-09-02 (rag 0.725 there; one replicate, local judge) an arm served no memory at all scores 1.000 on the same questions, because refusing is the right answer there and an empty context always refuses. The fact spine loses where an answer has to be aggregated across sessions. The second claim to survive a judge swap is a win rather than a wash: re-run on 2026-09-04 with the hybrid arm budget-matched to the control at 6 turns, the same 500 questions give hybrid 0.730 against naive RAG's 0.690 under the local judge and 0.736 against 0.694 under claude-opus-5 — paired +0.040 / +0.042, p 0.015 / 0.013 — bought with more context, ~1229 tokens against the control's ~1124, not less, and carried mostly by temporal-reasoning questions. Graded by a local, byte-reproducible judge (the cross-judge check names its second judge) — compare within rows, never against GPT-judged leaderboards.

Retired 2026-08-25 (#188): the 0.936 knowledge-update headline. It was measured on the 2026-07-30 bench stack (Qwen3.6-27B answerer and judge). Re-running the same 78 questions after the 2026-08-17 migration to Qwen3.8-27B puts the cascade at 0.846, below the naive-RAG control — which lands on 0.859 on both stacks. The cascade serves the fact-spine answer unless that channel says "I don't know", so it measures the answerer's abstention behaviour as much as the memory: 32/78 abstentions at 46/46 commit precision on the old stack, 22/78 at 0.839 on the new one. The 500-question table above is on the older judge and has not been re-judged, so read its cascade row as an upper bound.

Full tables, the per-type breakdown, both stacks side by side, and every artifact: Benchmarks.

Quickstart

Install and register the lite tier. No Docker, no database to set up, no container runtime:

pip install "pseudolife-mcp[lite]"
claude mcp add --scope user pseudolife-memory -- pseudolife-mcp

Codex instead of Claude Code — same shape:

pip install "pseudolife-mcp[lite]"
codex mcp add pseudolife-memory --env PSEUDOLIFE_WRITER_ID=codex -- pseudolife-mcp

For Codex, finish setup before starting a fresh task. In the existing [mcp_servers.pseudolife-memory] table in ~/.codex/config.toml, add startup_timeout_sec = 240, tool_timeout_sec = 240, and required = true. The shim can wait up to 180 seconds for a cold daemon; Codex's default startup budget is 10 seconds. required makes missing memory visible at startup and waits for its initial catalog. These are starting budgets, not a promise that a first model download fits. The tool budget leaves time for the shim's 180-second deadline to report a failure before the host cancels it; prewarm with pseudolife-mcp serve in a terminal if needed.

Approve memory_message in that table's tool configuration ([mcp_servers.pseudolife-memory.tools.memory_message] approval_mode = "approve", or allow it once and keep the approval); Codex hook setup (python ops/setup-codex-hooks.py) sets it when you approve the hooks, and keeps any value you chose. Board mail can wake an idle Codex task by default, and the woken task reads its mail with that tool: without the approval it stalls on a prompt until someone answers it. Set PSEUDOLIFE_CODEX_DOORBELL = "0" in the same env table to keep the task from being woken (see Codex doorbell).

The agent board (peer awareness and addressed mail between sessions) is on by default, behind bearer authentication. Without coordination.allowed_principals in the daemon's config.yaml, only the singular PSEUDOLIFE_MCP_TOKEN principal is admitted. A Codex bearer from a PSEUDOLIFE_MCP_TOKENS map stays off the board until an operator lists it (allowed_principals: [default, codex]). Until then a shim on the default setting leaves coordination off without an error; one pinned with --enable shows an attach-unavailable hint instead. python ops/setup-codex-coordination.py --check reports ready (default-on) or names the cause. See Codex CLI and desktop.

The MCP handshake delivers compact recall/capture/reflection instructions. For the complete standing guidance, copy the bundled memory block into your project AGENTS.md or ~/.codex/AGENTS.md. For session briefings and per-turn reminders, follow Codex hooks and verification. Use one MCP registration and one hook source; an installed plugin may already provide either. After the daemon is running, execute pseudolife-mcp doctor from the same environment as the registered command. It checks the handshake and annotations without calling bank tools. When the shell has no PSEUDOLIFE_MCP_TOKEN or PSEUDOLIFE_MCP_TOKEN_FILE, doctor takes the token from the Claude Code registration (~/.claude.json, or under CLAUDE_CONFIG_DIR), else from the Codex one, and names where it came from in credential_source; with none and a daemon that requires a token, it reports BearerMissing.

Then in either coding agent: "remember that my staging box is haze-02" → the agent calls memory_store; next session, "which box is staging?" → memory_search finds it. Browse everything at the Cortex Console: http://127.0.0.1:8765/ui/.

The first session auto-starts the daemon, which provisions an embedded PostgreSQL 18 (pgvector included, via pg0-embedded) under a stable per-user data dir and downloads the embedding model (~1.2 GB, one-time). It is a real Postgres bank, not a cut-down one: pseudolife-mcp backup writes a standard owner-free pg_dump archive (plus a state archive, 7-day rotation) that restores into any PostgreSQL 18 target regardless of role — the Docker tier included — so outgrowing lite is a dump/restore, not a migration project (backups). For a tier- and Postgres-version-independent copy, pseudolife-mcp export / import move the whole bank as portable JSONL (logical export / import). Windows needs an ASCII-only data path (PSEUDOLIFE_MCP_DATA_DIR).

What lite gives you, and the one thing it doesn't

lite (pip)

durable (Docker)

Associative store, hybrid search, supersession, version history

yes

yes

Cortex facts, knowledge graph, lessons, world facts, episodes

yes

yes

Cortex Console, document RAG, pseudolife-mcp backup

yes

yes

Dream consolidation filling the cortex on its own

no extractor ships

yes — bundled local CPU sidecar

External volumes, health-checked services, deploy/rollback tooling

no

yes

The gap, stated plainly. Lite ships no extractor, so the dream pass still runs, prunes, and acknowledges its input batch, but writes no canonical facts: on this path memory_fact_set is the only cortex writer. Everything else above works. Nothing about this is silent — curl http://127.0.0.1:8765/health reports "extractor": "none", and the stdio shim says the same on stderr at session start.

Any OpenAI-compatible endpoint closes it. The daemon inherits the environment it starts from, so two variables are the whole fix — with a local Ollama:

export PSEUDOLIFE_DREAM_BASE_URL=http://localhost:11434/v1
export PSEUDOLIFE_DREAM_MODEL=qwen2.5:7b
pseudolife-mcp serve
$env:PSEUDOLIFE_DREAM_BASE_URL = "http://localhost:11434/v1"
$env:PSEUDOLIFE_DREAM_MODEL    = "qwen2.5:7b"
pseudolife-mcp serve

/health then reports "extractor": "configured". One gotcha: a daemon that is already running keeps the environment it started with, and the shim reattaches to it rather than spawning a new one — stop the old daemon first. A hosted endpoint works too, and costs you the zero-egress property: memory text leaves the machine. Extractor tiers, quality, and the trade-offs: Dreaming.

Related MCP server: agent-mem0

Everything above plus the bundled extractor, external volumes, health-checked services, and backup/rollback tooling. Requires Docker and at least one MCP-capable coding agent — Claude Code, Codex, and Gemini CLI are wired end-to-end; anything else gets paste-ready config (provider matrix). One command from clone to first memory:

git clone https://github.com/Pseudogiant-xr/Pseudolife-MCP.git
cd Pseudolife-MCP
ops/install.sh          # Linux / macOS
ops\install.ps1         # Windows (pwsh 7+)
# Codex: add --client codex / -Client codex
# Codex defaults to automatic hook-source detection and asks once for approval.
# Unattended hook approval: --codex-hook-trust yes / -CodexHookTrust yes
# Instructions only: --codex-hooks skip --instructions append
# PowerShell equivalent: -CodexHooks skip -Instructions append
# Both:  add --client both  / -Client both
# Gemini: add --client gemini — or several: --client claude,codex,gemini
# Other MCP agents (Cursor, Windsurf, Zed, ...): --client generic

The installer first asks where the memory bank lives: on this machine (the default, and everything below), on another machine that already runs it (a client-only install that asks for the daemon's URL and token), or on this machine with other machines connecting to it (see Sharing one bank across machines). It then asks which agents to wire (multi-select, with a capability matrix showing exactly what each one gets — session briefing, per-turn discipline, standing file), runs the preflight (one exact fix line per missing prerequisite), then asks which dream extractor should consolidate memories —

  • sidecar — the bundled local CPU model; no Claude plan needed, works for everyone, and keeps every memory on the box (~11.8 GB image);

  • claude-only — the lightest install: a Claude model via a CLI shim (claude-opus-5-5 by default since 2026-09-29, when it cleared the extraction-ladder gate against claude-opus-5; needs a logged-in Max-plan claude CLI); the sidecar image is never built or pulled (~11.8 GB lighter; dreams pause while the shim is down);

  • claude-fallback — the Claude shim primary, the bundled sidecar as automatic fallback (Max-plan CLI plus the ~11.8 GB image);

  • openai-only / openai-fallback — the same two shapes on an OpenAI subscription: a GPT-5.6 or GPT-6 model (Terra by default) via the Codex CLI shim on a signed-in ChatGPT plan (extraction quality unmeasured — see the dreaming guide);

  • endpoint / endpoint-fallback — any OpenAI-compatible server you name (--extractor-url, --model: LM Studio, Ollama, vLLM, a hosted API), alone or with the sidecar as fallback. The earlier names sonnet-* and codex-* still work as deprecated spellings —

then brings the stack up, installs the selected clients' session hooks (where the client has a hook system), registers the MCP transport (the stdio shim by default, with a per-provider writer id; direct HTTP via --transport http), and health-checks the daemon — finishing with a per-agent ladder of what got wired and what that agent's platform cannot support. Codex setup offers one choice to enable automatic memory briefings, reminders, and session cleanup (plus, where the agent board is on, a board check-in at session start and a new-mail hint per prompt), use standing instructions only, or skip. Automatic setup reuses an enabled PseudoLife plugin or installs the three lifecycle hooks, backs up configuration, approves only their exact current definitions, and verifies execution. If verification fails, the same approval allows the standing memory block as a fallback; setup reports the remaining repair step. Hook-less providers (Gemini CLI and generic agents) are offered the standing block. --instructions append always writes the block from examples/CLAUDE.memory.md into ~/.claude/CLAUDE.md / ~/.codex/AGENTS.md / ~/.gemini/GEMINI.md (useful for subagent visibility even with hooks). Idempotent — re-run any time; --extractor <mode> switches extractor setups. Non-interactive example: ops/install.sh --extractor sidecar --client codex --codex-hook-trust yes. Without explicit hook approval, unattended setup does not grant trust; use --codex-hooks skip --instructions append for instructions only. Explicit --instructions skip prevents fallback edits. The installer also turns the agent board on: it mints a bearer token in ops/.env (owner-only, never printed) and gives each shim client it wires an owner-only token file. Re-running it on an existing install adds that file to the Claude Code registration in place. The ladder's last line, and pseudolife-mcp doctor, say whether the board is on or why it is off. Use doctor --host codex or --host claude-code for a compact coordination snapshot: reachability, bearer admission, registration evidence, tool inventory and configured wake are separate. Default doctor is read-only; health and a configured wake path do not prove delivery. The coordination diagnostic quickstart covers saved-instance checks and an explicitly disposable mail proof. --no-token / -NoToken keeps an open-loopback install with the board dormant (Turning the board on). Linux (Docker Engine): your user must be in the docker group — sudo usermod -aG docker $USER, then log out/in (the preflight checks this).

Image sizes, the Windows WSL2 memory cap, and what the installer automates: the containerized install below.

ops/preflight.sh --client codex    # or ops\preflight.ps1 -Client codex
docker volume create pseudolife-mcp-bank
docker volume create pseudolife-mcp-state
docker compose -f ops/docker-compose.yml up -d --build   # first build, once

# ...or pull the prebuilt images instead of building (releases >= 0.14.0):
docker compose -f ops/docker-compose.yml -f ops/docker-compose.ghcr.yml pull pseudolife-pg pseudolife-daemon
docker compose -f ops/docker-compose.yml -f ops/docker-compose.ghcr.yml up -d

# Verify, then wire the transport into one or both clients.
curl http://127.0.0.1:8765/health

# Stdio shim (the installer's default — per-session episode identity).
# PSEUDOLIFE_MCP_NO_SPAWN=1 makes the shim wait for the container instead
# of spawning a host fallback that can shadow the Docker bank after a
# reboot; set it on Docker-tier registrations like these.
pip install pseudolife-mcp
claude mcp add --scope user pseudolife-memory --env PSEUDOLIFE_MCP_NO_SPAWN=1 -- pseudolife-mcp
codex mcp add pseudolife-memory --env PSEUDOLIFE_MCP_NO_SPAWN=1 -- pseudolife-mcp

# ...or direct HTTP (no pip package needed; fine for single-session setups):
claude mcp add --transport http --scope user pseudolife-memory http://127.0.0.1:8765/mcp
codex mcp add pseudolife-memory --url http://127.0.0.1:8765/mcp

# Reinforce the protocol-level memory loop with a global standing instruction:
cat examples/CLAUDE.memory.md >> ~/.claude/CLAUDE.md
cat examples/CLAUDE.memory.md >> ~/.codex/AGENTS.md
# (PowerShell: Add-Content "$env:USERPROFILE\.claude\CLAUDE.md" (Get-Content examples\CLAUDE.memory.md -Raw))

Optional knobs live in ops/.env (cp ops/.env.example ops/.env — the install/update scripts scaffold it too; every value is commented, a missing file runs entirely on defaults).

What this is

A memory engine exposed over MCP. There's no chat UI and no LLM doing the thinking — your coding agent is the intelligence; these are tools it calls to store and recall what matters. (Models are bundled as plumbing: baked embedding weights for retrieval, and the optional CPU extractor sidecar that consolidates memories into facts while you sleep.)

Where it sits among the common approaches to agent memory — each column is a fair tool for what it's for; this table is about what question each one answers, not who's wrong:

notes file (CLAUDE.md)

auto-journaling plugin

plain vector store

Pseudolife-MCP

Survives sessions and compactions

yes

yes

yes

yes

"What is X now?" has one current answer

if you curate it

no — replays what happened

no — every stored version competes at recall

yes — slot-keyed cortex

A canonical-fact correction replaces the old value

you edit the file

appended beside it

old and new both retrievable, unranked by recency of truth

cortex supersedes, with full version history kept

Facts know their age and go stale

no

no

no

dated, freshness-decayed, quarantined when stale

Distils do/avoid lessons from its own outcomes

no

no

no

yes

Benchmark numbers ship with their raw run artifacts

—

typically no

typically no

every published number, test-enforced

Auto-journaling records what the agent did; Pseudolife curates what it learned. Both are useful — they answer different questions. Named alternatives — Mem0, Zep/Graphiti, Letta, Cognee, memU, Memori — and the cases where one of them is the better pick: Comparison.

It layers several complementary stores: the associative store (a flat embedding store ranked by cosine similarity fused with a BM25 lexical pool (on by default), with conflict-aware admission and explicit source-note replacement; an 8-tier banded layout is available as an opt-in preset); the cortex (slot-keyed canonical facts — one current value per entity.attribute, or a member set for set-valued slots — with provenance tiers and contender parking instead of silent overwrites); a typed knowledge graph over those facts with a closed relation vocabulary and on-read inference; the world cortex (durable cited facts about external reality, age-decayed trust); procedural lessons learned from the agent's own work; and a ChromaDB reference bank for document RAG. The canonical layers in depth: the memory model; the graph and multi-hop recall: retrieval.

State lives in Postgres (the durable source of truth) behind a single long-lived daemon; every session attaches through a thin stdio shim (installer default — per-session identity) or directly over HTTP (single-session setups). The result: Claude can pick up where it left off, correct itself when facts change, and reason over relationships — without you re-explaining context each session.

Documentation

This README is the front door — install, wiring, and the basic loop. The deep material lives in the user guide:

Page

What's in it

Configuration

Env vars, tuned defaults, toolset tiers, stdio shim, LAN sharing, data layout, backups, schema history

Providers

Capability matrix per coding agent, memory instruction layers, AGENTS.md standard, Codex hook setup and verification, writer ids

Retrieval

Reranker, BM25 hybrid, abstention floors, ranking-trace debugging, memory_recall, the knowledge graph

Dreaming

Extractor tiers, the bundled sidecar, upgrading the extractor, Sonnet-fallback, cadence, deep dream, consolidation

Episodes & sessions

Daemon-owned session episodes, the briefing hook, nested sub-episodes, tags

The memory model

Cortex slots, provenance contenders, world cortex, lessons, temporal/HLC stamps

Benchmarks

LongMemEval results; why extraction quality dominates

Comparison

Mem0, Zep/Graphiti, Letta, Cognee, memU, Memori — the axes, and when to use something else

Security posture

Memory poisoning (ASI06): every shipped mitigation, and what is not defended

Sharing one bank across machines

Exposing the daemon over Tailscale, a LAN or a proxy; per-machine principals; client-only installs

Secure MCP Tunnels

Guided ChatGPT developer-app setup, private keys, cloud verification and optional autostart

Agent isolation

The test suite's own Postgres login, and running agent sessions under a separate account that cannot reach the bank's keys

Coordination recovery

Agent mailboxes, maintainer passkeys and the board's audit log after a database restore

Plus evals/README.md (full benchmark methodology) and CONTRIBUTING.

Tools exposed

The surface was consolidated 2026-07-02 (55 → 32 tools; now 38 with memory_toolset, the set-slot pair and coordination): lifecycle families became verb-dispatched tools (memory_dream, memory_forget, memory_graph_review), and dump/introspection views moved to the Cortex Console (REST) — the manifest is agent context every session, so it stays lean.

Tool

Purpose

memory_store(text, source?, tags?, origin?, episode?, authority?, distortion_tolerance?)

Remember one durable fact / decision / observation (canonical facts reach the cortex via the dream pass or memory_fact_set); authority/distortion_tolerance label the speech act and how exactly it must survive — auto (default) is a deterministic form heuristic, no model call, and both labels are inherited through supersession unless restated

memory_search(query, top_k?, filters..., rerank?, bm25?, explain?, verbose?)

Associative retrieval; canonical cortex facts surface ahead of recall hits, each dated (asserted_at / last_confirmed / human age, plus stale when it has rotted); explain=True attaches a ranking trace

memory_recent(n?, sources?, episodes?, tags?, verbose?)

Newest stores, timestamp-ordered (debug + session catch-up)

memory_supersede(old_text?, new_text, entry_id?)

Correct the selected entry by ID, or one unique exact-text match; ambiguous/missing targets fail closed. Keep the old entry as history; derived_flagged names canonical facts built on it (flagged, never rewritten)

memory_reinstate(entry_id, operation_id, expected_..., evidence_packet_sha256, reviewer_ids, reason)

Reinstate one independently reviewed retired entry under its durable ID; Postgres-only, named-principal, exact-preimage, append-only and idempotent. Refuses any trace invalidation and never confirms derived cortex facts

memory_forget(scope, ...)

Forget from one store: memory (by text/substring/source/episode/tag — several filters narrow the match, AND across kinds like memory_search; a match over memory.delete_confirm_threshold entries, default 20, is refused with would_delete until the call repeats with confirm_bulk=true) and fact hard-delete; world and lesson (by entity/attribute) retire the slot with an audit row — reversible via memory_graph_review(action="restore_slot")

memory_stats()

Store occupancy, hit rates, totals

memory_agents(action, project?, task?, status?, lease?, worktree?, repository_id?, path?, expect?, children?, park_reason?, park_needs?, park_clear_by?, park_resume?, park_expires?, name?)

Experimental peer awareness or update of the caller's registered context, on by default for authenticated installs (coordination); lists peers active within the hour (three hours while holding a lease), each status with its age and a stale flag past two hours, and each row by its session's name (the title Claude Code or Codex shows, else one the agent set with name, else its memory_session_title, else its label and short id; board names), and counts the rest as idle_omitted; update sets project, task, status, expect (seconds until the status is overdue), children (the labels of subagents working under the caller's address, which only read the board; the Claude Code plugin's subagent hooks keep their own entries there, and a Codex subagent's own row carries its parent_agent_id) and the park record (why the session stopped, what clears it, who can, what to do then; a plain status clears it), which every listed row carries; claim/release take or free an advisory lease, or an advisory claim on one exact repository file (worktree + relative path through the stdio shim; a hashed repository_id + path over direct HTTP; no globs, directories or edit enforcement — exact repository file claims); unknown episode scope stays unknown, and activity is not a resource reservation

memory_message(action, to?, text?, request_id?, reply_to?, after?, message_id?, clears?, urgent?, limit?, peer?)

Experimental addressed mail: send (needs to, text and a new unique request_id; reuse the id only to retry the identical send after an uncertain result) to one agent (its id, or a unique prefix of 8+ hex characters), to project:<name> or to all (every attached, non-idle peer, at most 50, one request id for the burst, per-recipient receipts), non-destructive receive, explicit recipient ack (one id or several comma-separated, ids or prefixes), or read-only history of this instance's own retained sent and received mail (limit ≤ 50, optional exact peer id; no delivery, acknowledgement or wake); requires authenticated adapter binding, remains outside memory retrieval, and never grants user approval; a subagent's own address (a Codex subagent) does not send (child_send_refused): its parent does. Each receipt carries the daemon's wake decision (hinted, not_needed, rung, withheld with the parked need, no_path, capped): a parked peer rings only for mail that clears what it declared it needs (or urgent mail), a peer parked done only for urgent mail from the maintainer, the maintainer's delegate for the project (pseudolife-mcp lease delegate) or the park's named clearer, an idle peer that has not parked only for urgent mail; plain mail to it waits for its next turn (park records and wake)

memory_get(entry_id) / memory_reinforce(entry_id)

Dereference a memory id to its full episode (+ consolidated_into); reinforce it after finding it useful

memory_fact_get(entity, attribute, verbose?)

The one CURRENT canonical value at a slot (+ parked contenders); on an empty slot returns ranked candidates (same-entity, then similar slots); aged/contested facts carry a ready-made correct_with call (as do memory_search / memory_world_search hits)

memory_fact_set(entity, attribute, value, origin?, confidence?, episode?, freshness_class?, authority?, distortion_tolerance?)

Assert a canonical fact deliberately (insert / confirm / supersede / contest); freshness_class (auto default) says how fast the slot rots — auto infers it from the entity's kind; authority/distortion_tolerance (auto = deterministic form heuristic, no model call) inherit the slot's labels unless restated

memory_fact_resolve(entity, attribute, accept)

Settle a contested slot — adopt (true) or discard (false) the contender

memory_set_add(entity, attribute, member) / memory_set_remove(entity, attribute, member)

Add/confirm or retract one member of a set-valued slot (many concurrent values, e.g. tags — not one NOW value); a scalar there converts to a set one-way on first memory_set_add, except a number-led aggregate scalar ("32", "$1,500"), which is protected — the add parks as a contender instead. Read with memory_fact_get, which returns {kind: "set", members, removed} for these slots

memory_history(entity, attribute?, as_of?)

With attribute: version timeline at a slot, with writer/temporal stamps; as_of (ISO-8601 or epoch seconds) keeps only the versions written by then. Without: the entity's causal chain — dated fact/entry/edge/lesson events ("what led to X")

memory_world_set(entity, attribute, value, source_url?, ...)

Assert a cited WORLD fact (external knowledge; age-decayed trust by freshness class)

memory_world_search(query, top_k?, verbose?)

Search world facts — each carries effective_confidence, a stale flag, and its citation

memory_outcome(task, outcome, about?, detail?, polarity?, episode?, used_ids?)

Record a procedural outcome signal (success/failure/correction); the dream distils signals into lessons. used_ids names the search hits the work actually turned on — each credits every retrieval_events row in the session window that served it with a retrieval_uses label (used_via=outcome), the relevance signal a learned reranker trains on; same session, within use_window_seconds, or nothing is credited

memory_lesson_search(query, top_k?, verbose?)

Recall learned lessons for the task at hand — heed polarity - dead-ends; re_verify flags lessons whose subject facts changed since

memory_dream(action, limit?, commit_token?, apply?, snippets?, run_id?)

Drive the dream: status / pull / commit / run (server-side extractor) / runs (audit trail of recent passes) / rollback (revert the latest committed pass from its pre-image journal) / deep (full-corpus graph consolidation; dry-run unless apply, which snapshots the graph tables first; snippets=false omits candidate evidence; responses carry evidence-enriched merge_proposals for near-duplicate triage, with long lists capped and their full counts under truncated)

memory_graph_review(action, proposal_id?, proposal_ids?, proposals?, scope?, src?, dst?, relation?, store?)

Work the review queue: list / propose / relate (link a pair and dismiss its duplicate proposal in one call) / dismiss_pair / dismiss_slot_pair / restore_slot / accept_link / reject_link / accept_merge / accept_junk / reject_entity (merge/entity decisions are audit-stamped decided_by=agent over MCP, human via Console); list takes scope=<memory source> (the project scope of the Console's Graph and Review views) to keep only analyzer findings whose entities carry that source — queued proposals (proposed_link / merge_candidate / junk_candidate) always list; omit or "all" for everything; it is not a finding-kind filter; proposal_ids settles many id-actions in one call; restore_slot undoes a memory_forget(scope="lesson"/"world") retirement — store + the retired entity|attribute key in src (or a bare entity to restore every retired aspect). In the slot keys restore_slot and dismiss_slot_pair take, a literal | inside an entity or attribute is spelled %7C; the one bare | joins the two halves, and a key with more than one bare | is refused

memory_session_title(title, episode?)

Name THIS session's auto-opened episode (default titles are generic); episode is your session handle from the briefing — concurrent sessions share one HTTP connection, so pass it to land the rename on your own episode

memory_episode_start(title, hint?, episode?) / memory_episode_end(episode?)

Open/close a nested sub-episode for a substantial task; entries stored while open carry its id; episode is your session handle so the nest/pop lands in your own tree when several sessions run concurrently

memory_episode_summary(id)

Stats + tag/source distribution + recent entries within an episode

memory_consolidation_candidates(query?, episode?, ...)

Cluster near-duplicate memories ripe for consolidation

memory_consolidate(replaces?, new_text, source?, tags?, entry_ids?)

Replace selected entries with one canonical note; validate every ID (or unique exact text) before any changes

memory_graph_relate(src, relation, dst, ...)

Assert a typed edge (closed relation vocabulary; re-assertion bumps confidence)

memory_graph_unrelate(src, relation, dst)

Retract an edge (superseded, kept for audit)

memory_alias(entity, alias)

Bind an alternative name — lookups resolve aliases first

memory_graph(entity, depth?, include_facts?, to?, relation_filter?)

Entity neighborhood (≤3 hops) with derived transitive/inverse edges and per-edge EXTRACTED/INFERRED/AMBIGUOUS provenance tags; to returns the shortest path between two entities

memory_recall(query, hops?, top_k?, verbose?)

Multi-hop retrieval for relational questions; low_confidence: true → fall back to memory_search

memory_relation_define(name, description, ...)

Grow the closed relation vocabulary (deliberate, rare act)

document_ingest(path, source?)

Index a file (txt/md/pdf/html) verbatim in the reference bank — the lossless complement to agent-side distillation (division of labor)

document_search(query, top_k?)

RAG search over the reference bank only

memory_toolset(action)

Check or change this principal's visibility tier: status / expand / collapse

Each tool returns plain JSON. See pseudolife_memory/mcp_server.py for docstrings — those are what Claude reads to decide when to call which tool. The five recall-path tools return compact entries by default (result payloads are agent context on every retrieval; search and recent entries keep their write date), and memory_fact_get likewise drops bookkeeping keys from its record; pass verbose=true for full metadata. Full-table dumps and topology views live in the Cortex Console (/api/*) and the pseudolife-mcp briefing CLI.

Toolset tiers. Three visibility tiers — minimal (10 tools), core (24), full (38) — filtered per principal at tools/list; a principal (the named bearer-token identity, or the writer id for single-token installs) steps its own tier up or down with memory_toolset before calling a hidden tool. Defaults, per-client mapping, and weak-model deployments: Configuration — toolset tiers.

Architecture

One memory daemon owns the bank and serves MCP over streamable HTTP at /mcp; every Claude Code session (and any LAN agent) attaches to it. Postgres 18 + pgvector (in Docker on the durable tier; the lite tier runs the same Postgres embedded, no container) is the durable source of truth — the in-memory store is a write-through cache hydrated at startup (a small weights.pt persists only counters — there are no MLP weights).

The daemon runs either containerized (recommended — portable, no host Python) or as a host process. Claude Code attaches through a thin torch-free stdio shim (the installer default — per-session identity, needed for concurrent sessions) or directly over HTTP (simpler for a single session):

Claude session A ─┐  stdio shim (installer default) or HTTP
Claude session B ─┼───────────────────► pseudolife-mcp daemon ─► Postgres (Docker)
LAN agent ────────┘  or stdio shim         (single writer)        pgvector
                     (per session)         host proc OR Docker

This kills two v0.1 hazards by construction: a single writer means concurrent sessions can't clobber each other, and entries are transactional so a crash can't wipe the bank. The single writer is enforced: the daemon holds a Postgres advisory-lock writer lease on its bank. A second daemon, or a script or eval that opens the live bank through the service, refuses to start and names the process that holds it. Stop the daemon before offline maintenance such as ops/dedup_cortex.py. If the bank fails to load at startup, the daemon serves nothing rather than a partly loaded bank, and /health reports degraded with the reason until a retry succeeds. On top of the associative store sit the canonical layers — cortex, world facts, lessons, temporal/HLC stamps (the memory model) — joined to a typed knowledge graph walkable via memory_graph and multi-hop memory_recall (retrieval & the graph).

Install — containerized (any OS)

What the durable tier installer above does, by hand. The whole stack — Postgres and the memory daemon — runs in Docker. No host Python, no torch install, no version skew; the daemon image bakes in CPU-only torch and the embedding weights — Qwen/Qwen3-Embedding-0.6B (the default retrieval backbone since schema v25) plus all-MiniLM-L6-v2 (kept baked for the ONNX-parity test path) — so it runs identically on Windows / macOS / Linux. Requires only Docker; built once: ~5.0 GB daemon image (measured 2026-07-29 on the deployed build) + ~0.6 GB Postgres + ~11.8 GB extractor sidecar (measured 2026-08-20 with the v3 multi-task bake; skip the sidecar entirely with the installer's claude-only mode). The ~12.6 GB and ~10.4 GB figures published before 2026-07-29 are retired: both were inflated by a CUDA torch build that a dependency-resolution bug pulled into the image (see the CHANGELOG); the daemon has always been CPU-only.

git clone https://github.com/Pseudogiant-xr/Pseudolife-MCP.git
cd Pseudolife-MCP

# 1. One-time: create the two persistent volumes (bank + daemon state).
docker volume create pseudolife-mcp-bank
docker volume create pseudolife-mcp-state

# 2. Build + start all three services (Postgres, extractor, then the daemon).
docker compose -f ops/docker-compose.yml up -d --build

Or skip the ~5 GB daemon build entirely and pull the prebuilt images (releases ≥ 0.14.0):

docker compose -f ops/docker-compose.yml -f ops/docker-compose.ghcr.yml pull pseudolife-pg pseudolife-daemon
docker compose -f ops/docker-compose.yml -f ops/docker-compose.ghcr.yml up -d

The extractor sidecar is not published and still builds locally; update the pull path with pseudolife-mcp update (backup, pinned image, rollback tag; see Updating), not a bare pull + up -d.

Upgrading from a pre-rename install (volumes ops_pseudolife_pgdata / ops_pseudolife_data)? Don't rename those volumes — keep pointing at them by creating ops/.env with PSEUDOLIFE_BANK_VOLUME=ops_pseudolife_pgdata and PSEUDOLIFE_STATE_VOLUME=ops_pseudolife_data before up. See the compose header.

Windows: cap Docker Desktop's WSL2 VM, which otherwise claims up to ~50% of host RAM — how much the stack actually needs, the ops/wslconfig.example template, and the daemon container's own memory cap: Configuration — Windows / WSL2 memory.

The daemon serves MCP at http://127.0.0.1:8765/mcp and restarts with Docker — no logon task needed. First build downloads the model into the image (once); every container start after that is offline and fast. Wire Claude Code in via the stdio shim (installer default) or directly over HTTP (both below). Where the data actually lives, and how to back it up: Configuration — data layout.

Other machines, one bank: sessions on a laptop, a server or a container can use this daemon too, through a client-only install that runs just the shim. Exposing the daemon (Tailscale Serve, a LAN publish or a reverse proxy), per-machine tokens and board admission: Sharing one bank across machines.

Host-process install (Windows, for GPU / dev): run Postgres in Docker but the daemon on host Python — for hacking on the daemon or running the embedder on a local GPU. Steps, the pseudolife-mcp CLI modes, and the logon autostart task: Configuration — host-process install.

Updating

Upgrading from before the agent board (no bearer token yet): this migration step applies to installer-managed Docker installations. Rerun ops/install.ps1 (Windows) or ops/install.sh (Linux / macOS) with the same client selection (sessions can stay open: the shim installs as a new runtime beside the running one). The installer creates a bearer token for the default shim install and migrates the environment of the installer-managed pseudolife-memory stdio registration for Claude Code in place. Custom registrations are preserved; follow the installer's printed credential warnings for Gemini or custom registrations. -All / --all and ops/update_clients.py do not create the token or migrate the registration environment. Then refresh clients with the Everything at once recipe below and restart them.

A plugin marketplace added as Pseudogiant-xr/Pseudolife-MCP (installers up to 0.16.1) is refreshed over SSH, which fails on a machine with no GitHub SSH key. pseudolife-mcp update and the installers move it to the HTTPS URL themselves, with backups. If that step reports a failure, the manual fallback is: in settings.json ($CLAUDE_CONFIG_DIR, else ~/.claude), set the pseudolife-mcp entry's source under extraKnownMarketplaces to {"source": "git", "url": "https://github.com/Pseudogiant-xr/Pseudolife-MCP.git"}, then run claude plugin marketplace add https://github.com/Pseudogiant-xr/Pseudolife-MCP.git (never marketplace remove it: that uninstalls the plugin).

One command, no checkout: the installed shim updates the whole install from a release:

pseudolife-mcp update            # the newest release on PyPI
pseudolife-mcp update --tag 0.15.1
pseudolife-mcp update --check    # exit 0 when a newer release exists, 3 when current

The first update from 0.15.0 or earlier has no such command (that shim answers "unknown mode", and that daemon never announces a release): from a checkout, git pull, then ops/update.sh --all or ops/update.ps1 -All; for a pipx or pip install, pipx install --force "pseudolife-mcp[lite]==<version>" or pip install --upgrade "pseudolife-mcp[lite]==<version>" (the [lite] extra only for a lite install). After that, pseudolife-mcp update exists. On a client-only machine (the daemon on another host), update and --clients-only move this machine's clients to the daemon's release.

On a Docker-tier install it pulls the pinned GHCR daemon image, backs the bank up (the checkout's backup script when the compose project still has one, else its own pg_dump + state-volume tar into ~/.pseudolife-mcp/ backups), tags the running image for rollback, recreates only the daemon container from the compose files it was created with (or the package's bundled copies when the checkout is gone), waits for /health at the new version, then installs the release as a new shim runtime beside the running one, refreshes the plugin cache and prints the Codex hook step. The backup stays visible and deliberate: nothing here automates it away. --clients-only / --daemon-only take one half; the shim itself runs from wherever it was registered, so use its full path when pseudolife-mcp is not on PATH. On a pip / lite install it upgrades the package in the interpreter that holds it (pip, or pipx) and restarts nothing: it says what to restart.

You are told when it is time: the daemon checks PyPI for the newest release in the background (updates.check_releases, on by default) and every session's briefing then opens with release X is available — run pseudolife-mcp update. With updates.unattended_clients: true in config.yaml (off by default) a Docker-tier shim on the daemon's host installs the daemon's release as its own new runtime and refreshes the plugin cache by itself whenever the daemon is newer than it; the daemon recreate stays a deliberate command. On a headless host, pseudolife-mcp update --schedule 03:30 installs a daily task or timer that applies a new release only while updates.unattended_daemon: true is set and no session is active on the agent board, with the same backup and rollback tag, and posts a board notice either way. Codex keeps its hook approvals across updates (it approves the hook definitions, not the scripts), and an update from a checkout refreshes manual Codex hook copies itself. Where Codex runs the plugin's hooks, the client step refreshes Codex's installed plugin copy through codex plugin marketplace upgrade while the approved hook definitions are unchanged (Codex specifics); only a changed hooks.json makes every update path print the approval steps. See Updating.

Lite tier by hand: one command, bank untouched:

pip install -U "pseudolife-mcp[lite]"

On Windows, first close every Claude Code, Codex and Claude Desktop session using the shim (quit Desktop from the tray): upgrading a package that is running can leave it half-removed.

Docker tier from a checkout: after a git pull (or local code change), redeploy the daemon only — safely, without touching Postgres or the extractor:

.\ops\update.ps1        # Windows
./ops/update.sh         # Linux / macOS

Both are thin wrappers over the same Python deploy pseudolife-mcp update runs (ops/update.py → pseudolife_memory/update_cli.py), so there is one implementation. It backs up the bank (pg_dump + a state-volume tar), tags a rollback image (when a previous one exists — it says so loudly when there isn't), rebuilds + recreates only the daemon, and waits for /health. It never runs down -v. (Host-process install: just restart the daemon — pip install -e . is editable.) Build cache is pruned automatically after every healthy deploy; see Docker disk retention for the weekly Scheduled Task and the manual .vhdx compact. Never run docker system prune --volumes, which deletes volumes.

The image records the commit it was built from, and /health reports it as build (git_sha, dirty, built_at, and source: checkout or release). So the script refuses a tree with uncommitted or untracked files, and lists them. It also refuses a tree git cannot describe (no git, not a clone, or git's safe.directory refusal, which it quotes). Commit or clean up first, or pass -AllowDirty / --allow-dirty to deploy the tree as it is: stamped dirty: true, or unknown when git cannot describe it. Each deploy builds a new image, so the daemon container is recreated even when the commit has not changed.

Everything at once: the daemon is one of three installs. The shim your clients launch and the Claude Code plugin are separate and do not move with it. -All / --all moves them in the same run, after the daemon is healthy:

.\ops\update.ps1 -All   # Windows
./ops/update.sh --all   # Linux / macOS

No session has to be closed for the shim step. Each shim version installs into its own runtime (%LOCALAPPDATA%\pseudolife-mcp\runtimes\NNNNNN on Windows, ~/.local/share/pseudolife-mcp/runtimes/NNNNNN elsewhere) and every client registers one launcher path (%LOCALAPPDATA%\pseudolife-mcp\bin\pseudolife-mcp.exe / ~/.local/share/pseudolife-mcp/bin/pseudolife-mcp) that starts the newest complete runtime. The installer and the update step also make pseudolife-mcp typed in a terminal reach the launcher. On Linux and macOS ~/.local/bin/pseudolife-mcp becomes a link to it; an older pipx or pip --user entry there is moved aside as pseudolife-mcp.<kind>-<stamp> (never deleted), and anything else there is left alone and named. When ~/.local/bin is not on PATH, the step prints the line to add to your shell profile: export PATH="$HOME/.local/bin:$PATH". On Windows the launcher directory goes first on your user PATH, ahead of pipx's and pip's copies; open a new terminal for the name to reach the launcher, because a terminal that was already open keeps its old PATH. pseudolife-mcp doctor reports what the name resolves to (path_resolution) and warns when that is not the launcher; the launcher's full path always works. The step installs the checkout as a new runtime beside the old one, moves any registration that still names a runtime, pipx or virtualenv path to the launcher in place (Claude Code, Codex, Claude Desktop and Gemini CLI; each file is backed up first as <file>.bak-<stamp>), and removes older runtimes once no process runs from them and no registration names them. Sessions already running keep the runtime they started with; the next session start uses the new one. A shim running straight from this checkout's .venv is already live and is named instead. It then refreshes the plugin cache when its bytes differ from the marketplace clone (claude plugin update installs the new copy beside the one running sessions use; nothing is uninstalled), and reports whether Codex's hook copy matches the checkout (that refresh is a consent step: python ops/setup-codex-hooks.py). It ends with a ladder of what moved and which clients need a restart; a client-side step that fails is reported, never a failed deploy. The same helper runs on its own: python ops/update_clients.py, and python ops/shim_runtime.py manages the runtimes by hand (install, list, prune, migrate). Custom registrations are preserved and named; upgrade those in their own interpreter. A daemon-side credential change with an old shim leaves the two out of step: the Claude Desktop registrar refuses a shim that cannot read the selected token file (exit 4), and names the upgrade.

  • Upgrading past 2026-09-28: board mail starts waking idle sessions. Once the plugin cache and the shim move (-All, then a client restart), a Claude Code session and a Codex task with a codex CLI are woken when mail arrives that clears the need they parked on; before this both wake paths were opt-in. Nothing rings for chatter, and rings are capped. To keep the old behaviour, set PSEUDOLIFE_AGENT_WAKE_HOOK=0 in the env block of ~/.claude/settings.json and PSEUDOLIFE_CODEX_DOORBELL = "0" in the Codex server's env table (PSEUDOLIFE_AGENT_COORDINATION=0 in either place turns off the board for that client). pseudolife-mcp doctor shows each client's wake path under wake.

The three tell on each other: /health reports the daemon's version and a digest of the hook scripts it shipped with; the session briefing opens with a one-line notice when the plugin's version differs from the daemon's (naming /plugin marketplace update pseudolife-mcp then /plugin update pseudolife-memory@pseudolife-mcp, or the daemon redeploy, whichever side is behind), or when the version matches but the hooks do not (naming the --all command above); the shim says so on stderr and ahead of its instructions; and pseudolife-mcp doctor reports version_mismatch.

Two upgrades are not automatic, because neither can be done safely in place. Both have a step-by-step runbook — backup, dry run, apply, verify, roll back — and a fresh install needs neither:

  • A bank older than 0.11.0 (schema v25): every embedding column moved from vector(384) to vector(1024), so the daemon refuses to start rather than half-migrate. Re-embed offline with ops/migrate_embeddings.py — the v25 migration runbook.

  • A Docker-tier bank created before 2026-08-14 (PostgreSQL 16 → 18): a Postgres major bump cannot reuse the old data volume. Run pwsh ops/migrate-pg18.ps1 — the PostgreSQL 18 migration runbook.

Wire into your coding agent

Plugin (hooks + commands). The installer adds it whenever Claude Code is a selected client (--claude-plugin skip / -ClaudePlugin skip opts out; an installed plugin is left alone). It wires the session hooks (briefing + episode identity), the memory-loop instructions, and the /dream + /memory-status commands. By hand, the same two commands inside Claude Code do it:

/plugin marketplace add https://github.com/Pseudogiant-xr/Pseudolife-MCP.git
/plugin install pseudolife-memory@pseudolife-mcp

The plugin replaces the settings.json hook, and the daemon serves a compact memory core and a live briefing as session context. By default (memory_policy.variant: compact) the full CLAUDE.md block below is not served; append it if you want the complete guidance. It deliberately does not bundle the MCP server: Claude Code loads a plugin server alongside any user-registered one with no deduplication, which doubled every session's tool namespace next to the installer's registration — so the transport is registered exactly once, by ops/install.* (stdio shim by default — per-session episode identity) or the one-liner below. Hooks an earlier install wrote to ~/.claude/settings.json would duplicate the plugin's; the installer offers to remove them once the plugin runs (--claude-legacy-hooks remove / -ClaudeLegacyHooks remove unattended). Details, non-default ports/tokens, and migration: plugin/README.md.

Manual transport registration. The installer's default (shim mode) registers a thin stdio shim — one shim process per session, so every session carries its own tier-1 identity. The same wiring by hand:

pip install pseudolife-mcp    # daemon in Docker; add [lite] for the pip tier
claude mcp add --scope user pseudolife-memory --env PSEUDOLIFE_MCP_NO_SPAWN=1 -- pseudolife-mcp

PSEUDOLIFE_MCP_NO_SPAWN=1 belongs on Docker-tier registrations: the shim then waits for the container instead of spawning a host-side fallback whose port bind can race a still-booting Docker and shadow the real bank. On the [lite] pip tier drop the --env — there the spawn fallback is the zero-config path.

Direct HTTP works too — the daemon serves MCP over HTTP natively (no shim, no host command, nothing OS-specific; concurrent sessions then share one episode identity, so it fits single-session setups best):

claude mcp add --transport http --scope user pseudolife-memory http://127.0.0.1:8765/mcp

(--scope user registers it for every project; drop it to register for the current project only.) Or write the equivalent JSON yourself — into ~/.claude.json under the top-level mcpServers key for user scope, or into a .mcp.json at a project root for project scope:

{
  "mcpServers": {
    "pseudolife-memory": {
      "type": "http",
      "url": "http://127.0.0.1:8765/mcp"
    }
  }
}

For a token-protected daemon, add a headers key to that Claude JSON entry: "headers": { "Authorization": "Bearer <your-token>" }.

Claude Desktop (the app, including its Cowork and Code sessions) — the installer writes the entry for you:

ops/install.sh --client claude-desktop      # Windows: ops\install.ps1 -Client claude-desktop

Desktop has no mcp add; its servers live in claude_desktop_config.json (macOS ~/Library/Application Support/Claude/, Linux ~/.config/Claude/, Windows %APPDATA%\Claude\ — except the Store/MSIX build, whose real file is under %LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\; the installer prefers that path when it exists). The equivalent entry by hand:

{
  "mcpServers": {
    "pseudolife-desktop": {
      "command": "/absolute/path/to/pseudolife-mcp",
      "env": {
        "PSEUDOLIFE_WRITER_ID": "claude-desktop",
        "PSEUDOLIFE_MCP_NO_SPAWN": "1",
        "PSEUDOLIFE_MCP_DAEMON_URL": "http://127.0.0.1:8765"
      }
    }
  }
}

The entry is named pseudolife-desktop on purpose. Desktop's Code tab runs Claude Code, which starts its own per-session pseudolife-memory server; when an app-level entry has the same name, Desktop sends the session's mcp__pseudolife-memory__* calls to the app-level entry and the session's own server gets none. Re-running the installer renames an entry it wrote under the old name (its env sets PSEUDOLIFE_WRITER_ID to claude-desktop), keeping any env keys you added and backing the config up first. It leaves any other pseudolife-memory entry alone and warns about it. Chat and Cowork then list the tools as mcp__pseudolife-desktop__*.

Two things differ from the CLI clients. Desktop launches MCP servers with a sanitized environment — PATH plus a few system variables, none of your shell's exports — so command must be the shim's absolute path (which pseudolife-mcp / Get-Command pseudolife-mcp), and a token-protected daemon needs "PSEUDOLIFE_MCP_TOKEN_FILE": "/absolute/path/to/a/private/file" in that env block: a bearer exported in your OS environment never reaches the shim. The symptom of forgetting it is every session failing with "Couldn't start for Cowork and Code sessions. Error: unhandled errors in a TaskGroup (1 sub-exception)" — a 401 under the SDK's wrapper, which the shim now names plainly on stderr at startup. When the daemon is token-gated the installer writes that file (owner-only) from PSEUDOLIFE_MCP_TOKEN in its environment or ops/.env, or migrates a literal token already in the entry into it; with no token to write it says so and exits 3. The shim must also be able to read that file: releases through 0.15.0 only read the literal PSEUDOLIFE_MCP_TOKEN, so the registrar probes <command> --help for the file form first and refuses an older shim (exit 4, nothing written) rather than register an entry that would fail with the same TaskGroup error — upgrade the shim (re-run the installer, or python ops/update_clients.py --only shim from the checkout: it installs a new runtime beside the running one, so no session has to close) and re-run. After any edit, fully quit Desktop from the tray or menu-bar icon and relaunch — closing the window does not reload the config.

Codex — the installer's default (shim mode) wires the same stdio shim, so a Codex session gets its own tier-1 identity instead of inheriting a concurrent Claude session's episode:

pip install pseudolife-mcp
codex mcp add pseudolife-memory --env PSEUDOLIFE_MCP_NO_SPAWN=1 -- pseudolife-mcp

(Same Docker-tier note as the Claude wiring above: keep PSEUDOLIFE_MCP_NO_SPAWN=1 when the daemon runs in Docker; drop it on the [lite] pip tier.)

The HTTP one-liner works too (no pip package needed):

codex mcp add pseudolife-memory --url http://127.0.0.1:8765/mcp

Or add the equivalent user-level entry to ~/.codex/config.toml:

[mcp_servers.pseudolife-memory]
url = "http://127.0.0.1:8765/mcp"
bearer_token_env_var = "PSEUDOLIFE_MCP_TOKEN"

For that Codex HTTP configuration, export PSEUDOLIFE_MCP_TOKEN in the environment that launches Codex. The token stays out of config.toml, and Codex reads it when connecting. This is unnecessary for the default stdio shim.

Gemini CLI — same shape (-s user matters: Gemini defaults to project scope; the -e env gives Gemini sessions their own write attribution, and the same Docker-tier PSEUDOLIFE_MCP_NO_SPAWN=1 note as above applies):

pip install pseudolife-mcp
gemini mcp add -s user -e PSEUDOLIFE_WRITER_ID=gemini -e PSEUDOLIFE_MCP_NO_SPAWN=1 pseudolife-memory pseudolife-mcp

Or HTTP, no pip package needed:

gemini mcp add -s user -t http pseudolife-memory http://127.0.0.1:8765/mcp

Note: since 2026-06-18 Google no longer serves individual-tier accounts (free, AI Pro, AI Ultra) through Gemini CLI — OAuth sign-in fails and points at Antigravity. The wiring above stays correct, but individual accounts need API-key auth (GEMINI_API_KEY) to actually run sessions — or use Google Antigravity itself, which connects to the same bank via ~/.gemini/config/mcp_config.json; both are covered in the providers guide.

Any other MCP-capable agent (Cursor, Windsurf, Zed, Copilot CLI, …) — add the generic mcpServers entry to that tool's MCP config (ops/install.sh --client generic prints both shapes ready to paste):

{
  "mcpServers": {
    "pseudolife-memory": {
      "command": "pseudolife-mcp",
      "env": {
        "PSEUDOLIFE_WRITER_ID": "mcp-client",
        "PSEUDOLIFE_MCP_NO_SPAWN": "1"
      }
    }
  }
}

What each agent gets — and what its platform can't support (hooks, per-turn discipline): the provider matrix.

Verify: run claude mcp list, codex mcp list, or gemini mcp list (the server should report connected), then ask the agent to "store a memory that this install works" and check it appears in the Console's Stream view at http://127.0.0.1:8765/ui/.

Preferring stdio (this is what the installer wires by default, for per-session identity)? A thin torch-free shim proxies stdio to the daemon: stdio shim · sharing one bank across machines · backups & restore rehearsal · agent mailbox recovery.

The server's value depends on the agent using it. The MCP server advertises the core loop through protocol-level instructions; the shim adds the messageboard check-in when its coordination adapter is up. The plugin's memory SessionStart hook (also used by verified Codex hooks) delivers a short operating guide and a bounded briefing; without the plugin, the installer's Claude Code settings.json hook (pseudolife-mcp briefing --hook-json) delivers the same two. A separate coordination hook asks the agent to set its project, task and status, discover peers, and read pending messages, but only where the board is on for that credential (it is on by default behind bearer authentication, so an open install or a disabled board adds no check-in). Detailed memory guidance remains in the bundled standing block. Hooks add per-prompt reminders and session bookkeeping; neither delivery method guarantees that the model performs every requested memory operation.

By default (memory_policy.variant: compact) hooks serve at most that short guide, and the detailed block reaches an agent only as a standing copy (the full_separate_hook variant serves it in a separate hook; see Startup memory policy). For the complete guidance, for subagent visibility (subagents read CLAUDE.md but not hook output), or in place of hooks, append it to Claude's global ~/.claude/CLAUDE.md, Codex's global ~/.codex/AGENTS.md, Gemini's global ~/.gemini/GEMINI.md, or a per-project CLAUDE.md / AGENTS.md:

cat examples/CLAUDE.memory.md >> ~/.claude/CLAUDE.md
cat examples/CLAUDE.memory.md >> ~/.codex/AGENTS.md
cat examples/CLAUDE.memory.md >> ~/.gemini/GEMINI.md
Add-Content "$env:USERPROFILE\.claude\CLAUDE.md" (Get-Content examples\CLAUDE.memory.md -Raw)
Add-Content "$env:USERPROFILE\.codex\AGENTS.md" (Get-Content examples\CLAUDE.memory.md -Raw)

For hook-less providers this standing block supplies the full memory policy, but it cannot provide a live briefing or run session cleanup. AGENTS.md is the cross-vendor standard for standing agent instructions (Linux Foundation-governed; read by Codex, Copilot, Cursor, Gemini CLI, Zed, and 30+ others), so a per-project AGENTS.md carrying the block reaches almost every agent at once. Claude Code is the holdout — it reads CLAUDE.md — but a CLAUDE.md whose first line is @AGENTS.md imports the shared file, so one copy serves every tool.

The block (examples/CLAUDE.memory.md) teaches the loop: RECALL at the start (memory_search / memory_lesson_search / memory_fact_get / memory_world_search), CAPTURE as you go (memory_store with an honest origin, memory_fact_set for canonical facts, memory_world_set for cited external facts, source="status" for verbose logs so they stay out of the dream), REFLECT at the end (memory_outcome, with used_ids naming the hits you actually used — the dream distils these signals into the lessons surfaced at your next session start).

For an existing Codex installation, run python ops/setup-codex-hooks.py. The helper asks once, detects the hook source, backs up changed configuration, persists scoped trust through Codex, and verifies startup briefing, prompt reminder, and session cleanup, plus the agent-board check-in where the board is on. The Docker installer runs this step for you. See Codex setup options and fallback.

For Claude Code, use the plugin, or the legacy ops/install-hook.ps1 -Client claude / ops/install-hook.sh --client claude for briefing and reminder hooks. The legacy --client codex path remains available but only writes hook definitions; it does not complete trust and verification. Session episodes also work without hooks through the daemon: Episodes & sessions.

Current Codex runtimes enable hooks by default, including Windows. Availability depends on the application/runtime and policy, not the model. If [features] hooks = false is intentional, keep it and use the standing AGENTS.md block. Codex runs the plugin's Windows hooks as native PowerShell 7 commands; Claude Code runs their Bash commands through Git Bash, so it needs Git for Windows installed (see plugin/README.md). See the official hook protocol.

Codex hook trust: setup approval is limited to PseudoLife's current hook definitions: the memory and coordination SessionStart and UserPromptSubmit handlers and SessionEnd, plus, with the plugin, its Stop entry (Claude Code's wake hook, on by default; in Codex it runs only the park gate), its SubagentStart and two SubagentStop handlers (Claude Code's subagent board hooks, a no-op in Codex, plus the park gate) and its PreToolUse guard. It does not approve other plugins or bypass future trust checks. The same consent sets approval_mode = "approve" for the memory_message tool on the pseudolife-memory server, unless you set another value yourself, so a Codex thread can then read and send board mail without asking. Codex approves the definitions, not the scripts they run, so the approval also covers the scripts later PseudoLife updates install (for manual copies the update installs them itself, verified by content). Changed definitions need approval again, and so does a handler a plugin update adds; Codex skips an unapproved one silently in the desktop app, which pseudolife-mcp update (from a checkout, ops/update.ps1 -All or ops/update_clients.py) reports as needs-approval. If automatic setup cannot use the installed runtime's trust interface, it reports the problem and asks you to open /hooks to review and trust the definitions. Approved standing instructions remain available as fallback. Installed files alone do not establish that hooks are working.

Usage patterns

At session start — loads what you've worked on before, persistent across compactions:

memory_search("project context for X")

During work — store real decisions; skip fleeting chatter (the shipped store gate is permissive, so deliberate, durable claims only):

memory_store("Decided to use stdio transport for the MCP because no port conflicts", source="pseudolife")

When corrected — marks the old fact superseded and stores the correction; both surface in future retrieval, the new one ranked higher. Select the entry by the id carried on the search or recent hit:

memory_supersede(
  entry_id=417,
  new_text="Provider interface uses async calls — sync version was the v0.7 prototype only"
)

old_text= still selects by the full stored text when that text is exactly unique among live entries; it is the legacy selector and the only one file mode has. Ambiguous or missing targets change nothing.

Hygiene — memory and fact scopes hard-delete (at least one filter is required for scope memory, preventing accidental wholesale deletion; several filters narrow the match, so text="probe", source="status" is the probe entry in that source, not the whole source; a match over memory.delete_confirm_threshold entries — default 20, 0 disables — is refused with would_delete until the call repeats with confirm_bulk=true; on Postgres, forgetting an entry also retires the current facts, dream edges and lessons it was the last surviving source of, keeping them as history, and queues affected session digests for regeneration); lesson and world scopes retire the slot with an audit row and are reversible with memory_graph_review(action="restore_slot", store=..., src="entity|attribute"); for "keep the history but mark it wrong" use memory_supersede instead:

memory_forget(scope="memory", source="test-noise")              # refused past 20 matches
memory_forget(scope="memory", source="test-noise", confirm_bulk=True)
memory_forget(scope="fact", entity="test-entity")

Discovering what's in the bank: open the Cortex Console — sources, tags, episodes, and full-table views all live there. Going deeper: reranking, BM25, abstention, and trace debugging · episodes + tags · canonical facts, contenders, world facts, lessons · the consolidation workflow.

Dreaming — consolidating memories into facts

A dream distils the recent associative stream into canonical cortex facts while you're not looking: pull unconsolidated memories → extract (entity, attribute, value) → acknowledge those exact entries durably. New entries remain pending regardless of their timestamps; failed acknowledgement can replay claim application. Manual commits use the token returned by pull. Extraction is pluggable:

Tier

How it runs

Needs

Quality

0 — none

no extractor configured — the dream still runs, prunes, and acknowledges input batches, but writes no canonical facts

nothing

none (memory_fact_set is your only cortex writer)

1 — agent-driven

the agent itself is the gateway: the /dream judgment session (its manual-extraction branch fires only when no endpoint is configured)

the agent you already run

highest

2 — shipped default

daemon auto-sweep → the bundled CPU sidecar, or any OpenAI-compatible endpoint

nothing (sidecar)

high; free if local

The stack ships tier 2 preconfigured (the bespoke Gemma 4 E4B extractor fine-tune in a llama.cpp sidecar, internal-only). The sweep cadence, pointing dreams at a bigger local model or at Claude Sonnet with automatic sidecar fallback, the full-corpus deep dream graph pass, and the privacy/cost trade-offs: Dreaming.

Benchmarks

The headline is the whole benchmark, not a slice: all six LongMemEval question types, 500 questions, oracle variant, run end to end through the memory (qwen-27b extraction under the v25 embedding backbone, BM25-on turn retrieval). Single pass, graded by the local Qwen3.6-27B bench judge (2026-08-03):

arm

accuracy

context tokens/question

naive RAG (top-6 turns)

0.688

~1210

cortex facts only

0.416

~158

hybrid (facts + top-3 turns)

0.664

~842

commit-gated cascade

0.690

~883

The cascade is a serving policy, not a fourth pipeline: answer from the consolidated facts when that channel commits, fall back to raw-turn RAG when it abstains. Overall this is a wash on accuracy at ~73% of the context — 0.690 vs 0.688 is one question in 500 on a single pass, and nobody should read it as a win. The fact spine alone answers at ~13% of RAG's token budget, at a large accuracy cost outside the types it is built for. The structure is per type:

question type

n

naive RAG

commit-gated cascade

knowledge-update (facts change)

78

0.859

0.936 (retired — why)

single-session-user

70

0.929

0.943

single-session-assistant

56

0.911

0.929

single-session-preference

30

0.800

0.700

temporal-reasoning

133

0.526

0.526

multi-session

133

0.504

0.474

The consolidated spine helps where a fact changes and where the answer sits inside one session; it loses where the answer must be aggregated across sessions or ordered in time, because per-fact consolidation is exactly what discards that structure. BEAM-100K reproduces the same shape independently. Its abstention questions are where the spine looks best — the fact-spine arm scores 0.950 against naive RAG's 0.775, identical under the local judge and under an independent Opus-class judge — but the 2026-09-02 five-arm run bounds that reading: a no-memory arm scores 1.000 there, so the edge is calibration (a small fact context refuses where raw turns confabulate), not evidence that the memory recalled anything. Setup, caveats, both bench stacks side by side, and the evidence that extraction quality is the dominant factor: Benchmarks; full methodology: evals/README.md.

Retrieval itself was re-measured on the same corpus before the v25 backbone swap (150 questions, 74,183 haystack turns, 299 gold turns; pure recall — no reader, no judge): Qwen/Qwen3-Embedding-0.6B reaches R@10 0.809 against bge-base-en-v1.5's 0.742 and the previously-shipped all-MiniLM-L6-v2's 0.572, and beats bge-base head-to-head +32/−12 at k=10 (p=0.004). Artifacts: embedder-recall-shootout-20260727.json, embedder-recall-qwen-vs-bge-20260728.json.

Cortex Console (web UI)

An operator dashboard served by the daemon itself — point a browser at http://127.0.0.1:8765/ui/ (the /health and /mcp endpoints are unchanged; the console is additive). It's a read-mostly instrument panel for seeing and steering the memory a human otherwise can't observe: Observatory (health, per-layer counts, the memory store's capacity meter, dream gauges, consolidation review), Cortex (canonical facts by entity with provenance, version-history ladders, Adopt/Discard for contested slots), World / Lessons / Episodes, Stream (live search with rerank/BM25 toggles and a ranking-trace panel), Recall (multi-hop walks and paths between two entities), Graph (a 3D galaxy of entities with an entity page per star, and a table view), Review (the graph review queue: Accept/Reject merges or — for a source file and its own bare concept, band.py ↔ band — record an implements edge instead of forcing merge-or-dismiss; proposals a background dream has already judged carry a verdict chip — accept/reject/leave with confidence, the model's reason in the tooltip — as a lead, never a decision), Insight (hubs, communities and open questions from the graph digest), and Settings (every safe config.yaml scalar with live-vs-restart badges, diff-preview, and atomic save). Bookmarks into the pre-v3 console (#/console, #/atlas, #/coordination) land on the matching view.

The Board is a read-only coordination view: active roster/status age, reported children, park needs/resume, resource holders and FIFO queues. Pending mail counts are visible only for the caller's principal; the latest 100 retained send/read/ack/wake/expiry events omit all message bodies. This view requires a configured bearer on coordination.allowed_principals, even on an otherwise open loopback installation. Refresh neither receives/acknowledges mail nor settles expired leases. Snapshots are marked stale after a minute; use Refresh for current state. The API is GET /api/agents?view=coordination&limit=50 (roster limit 1–50); the default /api/agents awareness response is unchanged. Incoming audit visibility uses current recipient ownership, so incoming events may no longer appear after the recipient address is pruned; audit retention also bounds the timeline. A cold durable tier reports unavailable rather than initializing the bank through this read.

Auth mirrors /mcp: /ui (static shell) and /health are open; /api/* requires the same PSEUDOLIFE_MCP_TOKEN bearer when one is set (the console prompts for it and stores it locally). REST POST bodies are capped in wire bytes: 4 MiB for the text-bearing writes (/api/facts/set, /api/consolidate, /api/supersede), 32 KiB for /api/coordination/*, 16 KiB for the session-end hook, 1 KiB for /api/pair, and 256 KiB for everything else; a larger body gets HTTP 413 {"error": "request_too_large"}. No CDN, fully offline: a Svelte build committed under pseudolife_memory/web/static/ with vendored OFL fonts and a vendored 3D engine, served straight from the daemon, so neither the daemon image nor a pip install needs Node. The source is in frontend/; CI rebuilds it and fails if the committed build differs. Developing the UI? A fixture-backed dev server (no Postgres, no torch) serves the console against canned data: python -m pseudolife_memory.web.devserver → http://127.0.0.1:8770/ui/. Its payloads self-announce ("fixtures": true on /health), and the topbar shows a "Demo data, not a real bank" chip, so a fixture run is never mistaken for a real bank.

Capabilities at a glance

Capability

Status

Transport

Streamable-HTTP MCP daemon (/mcp); stdio shim is the installer default (per-session identity) — HTTP remains for single-session setups

Storage

Postgres 18 + pgvector (source of truth); ChromaDB for the reference bank

Associative store

Flat similarity store (default since the 2026-08-15 measured verdict; the 8-tier banded preset remains opt-in); hybrid dense + BM25 ranking (BM25 on by default); contradiction detection admits potential updates, including a deterministic slot-identity path regardless of embedding similarity, while retaining earlier source notes; whole-note supersession requires an explicit replacement operation

Canonical-fact cortex

Single-writer: LLM dream pass + memory_fact_* (regex auto-promote opt-in, default off)

Set-valued slots

memory_set_add / memory_set_remove for many-current-value slots; one-way scalar→set conversion, aggregate scalars guarded (park as contender); an assistant-origin add cannot convert or join another tier's set, and cannot retract another tier's member

Provenance contenders

Tier-rank guard user > action > agent > assistant; memory_fact_resolve

Fact currency

Every cortex fact is dated (asserted_at / age); freshness_class (evergreen / slow / volatile) decays effective_confidence and flags stale. Left auto, the class is inferred from the entity's kind (schema v24 entity_kinds) — only system entities can rot; artifacts and concepts stay evergreen

Write-time labels

authority (directive / observation / quoted — the speech act, orthogonal to the origin tier) and distortion_tolerance (constraint / procedural / belief / preference / episodic) on entries and facts, set at write time (explicit, or a deterministic heuristic under auto) and inherited through supersession unless restated. A constraint source is carried verbatim through the dream (with a post-dream guard) and pinned ahead of cosine in memory_search's cortex block and memory_recall when the query names its entity; a quoted source is low-trust for the two-man rule (schema v35)

Knowledge graph

Typed entities/edges, closed relation vocab, on-read closure (Postgres + NetworkX, no AGE/Cypher)

World cortex

memory_world_* — cited external facts + age-decayed freshness (manual ingest)

Procedural memory

memory_outcome (signals) → dream-synthesised lessons via memory_lesson_search; prefers/avoids graph edges; single-writer

Sense of time + multi-writer

Per-write stamp (tx/valid time, HLC ordering, writer/session); memory_history; relative age on reads; write_mode seam (snapshot live, occ Phase-2)

Episodes + tags

Session episodes daemon-owned, keyed by a resolved five-tier session identity; hook eager-open or lazy-open on first store + idle reaper + prune-empty + resume-after-reap; nested sub-episodes with subtree-expanded recall; multi-valued tags=[...]

Session briefing

SessionStart hook injects lessons + verified world facts + last-session recap once per conversation (resume/compact get the episode handle only); the plugin's per-turn note speaks only when lessons or other sessions' status notes changed (pseudolife-mcp briefing adds the unsure-graph section)

Consolidation

memory_consolidation_candidates + memory_consolidate

Optional components

Cross-encoder reranker (rerank=True, ~80 MB); ONNX embedding backend (pip install .[onnx] — load-only, and auto-selected when installed and the configured model's artifact is already on disk, ~3x faster CPU encode on MiniLM. The configured artifact must already exist locally: the daemon image provisions MiniLM's while building, while a pip install stays on torch until you provision it yourself. Models whose Transformer module loads from a subfolder use torch on native Windows, and the default Qwen3-Embedding-0.6B has no ONNX export at all); NLI contradiction scorer (pip install .[nli], ~278 MB)

Web console

Cortex Console at /ui/ — health/stats, fact review + history, 3D graph and review queue, search/trace, coordination board, config editor (read-mostly, token-gated like /mcp)

Schema version

v55 (Postgres meta version) — additive ADD COLUMN IF NOT EXISTS migrations on daemon start, except v25: the vector(384)→vector(1024) move is not additive, so the daemon refuses to start against an older-dimensioned bank until you run ops/migrate_embeddings.py; legacy file-mode .pt banks auto-migrate into Postgres; full version history

Troubleshooting

Start with curl http://127.0.0.1:8765/health — it reports the daemon's package version, the schema version, storage backend, auth state, and persist_errors (non-zero means writes are failing to reach Postgres; check docker logs pseudolife-mcp-daemon).

  • The cortex stays empty (canonical facts never appear on their own). /health reporting "extractor": "none" means no extractor is configured, so the dream writes no facts and memory_fact_set is the only cortex writer — expected on the lite tier. Point the daemon at an OpenAI-compatible endpoint (Quickstart) or use the Docker tier's bundled sidecar. "extractor": "disabled" instead means dreaming itself is switched off in config. "extractor": "stalled" means an extractor is configured but live dreams have stopped reaching it; the stall sub-object gives the reason and times. For stall.reason login_expired, the extractor CLI's login lapsed (or a hosted endpoint rejected its key): re-run claude auth login (or codex login) on the daemon host. See When dreaming stalls.

  • Lite daemon refuses to start on Windows with a message about the data path: the embedded Postgres runtime needs an ASCII-only data directory. Set PSEUDOLIFE_MCP_DATA_DIR to one (e.g. C:\pseudolife-data) — Configuration.

  • First build is slow / big. The daemon image (~5.0 GB, several minutes to build) bakes in CPU torch and the embedding weights (Qwen3-Embedding-0.6B plus MiniLM); the extractor sidecar adds a ~5.3 GB model download on its first build. Every start after that is offline and fast — if a rebuild is re-downloading models, the Docker layer cache was pruned.

  • Daemon unreachable after wsl --shutdown (Windows): the host port forward is gone — docker restart pseudolife-mcp-daemon re-establishes it.

  • Docker eating RAM (Windows): the WSL2 VM (Vmmem) claims up to ~50% of host memory by default. Copy ops/wslconfig.example to %USERPROFILE%\.wslconfig, tune memory=, then wsl --shutdown.

  • Port already in use: the stack binds 127.0.0.1:8765 (daemon) and 127.0.0.1:5433 (Postgres). Change the host side in ops/docker-compose.yml if either collides.

  • Console shows "offline" / Unauthorized: "offline" means the daemon isn't reachable (see above); a 401 prompt means it runs with PSEUDOLIFE_MCP_TOKEN — paste that token into the Console's Token dialog.

  • The coding agent doesn't see the tools: claude mcp list or codex mcp list should show pseudolife-memory ✓ connected. If not, re-check the URL (http://127.0.0.1:8765/mcp — the /mcp path matters) and the bearer header when a token is set. The daemon preloads the embedder on a warmup thread at start (~5–10 s); a very early first call can race it and take a few seconds.

  • Tools vanish after an upgrade / the client log says "Connection closed": the shim's registered command can live outside the repo venv, and the MCP SDK v2 migration set an mcp>=2.1 floor — an older SDK in that environment crashes the shim on start. The shim detects this and prints the interpreter path and the exact fix on stderr: pip install -U "mcp>=2.1,<2.2" in that interpreter, or re-run the installer (which registers the project venv's shim).

  • Claude Desktop says "Couldn't start for Cowork and Code sessions. Error: unhandled errors in a TaskGroup (1 sub-exception)": the real exception is at the bottom of mcp-server-pseudolife-desktop.log (mcp-server-pseudolife-memory.log for an entry not yet renamed) in the app's log folder (%LOCALAPPDATA%\Claude\Logs on Windows, ~/Library/Logs/Claude on macOS). If it is a 401, the daemon is token-gated and the Desktop-launched shim holds no credential: Desktop sanitizes the environment, so put PSEUDOLIFE_MCP_TOKEN_FILE in the entry's env (see Claude Desktop under Wire into your coding agent) or re-run ops/install.* --client claude-desktop, then fully quit and relaunch. On the Windows Store build the file lives under %LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\. If the entry already carries PSEUDOLIFE_MCP_TOKEN_FILE and still 401s, the shim predates token-file support (PyPI releases through 0.15.0 read only the literal token): pseudolife-mcp --help from a capable shim lists PSEUDOLIFE_MCP_TOKEN_FILE; upgrade the shim (python ops/update_clients.py --only shim from the checkout installs a new runtime beside the running one, so no session has to close) and re-run the installer, which now refuses to register an older one against a token file.

  • A harness "removed tools" notice is not an outage. A resumed session can carry a larger tool roster in its transcript than the current toolset tier serves — that's a visibility filter, not a disconnect. Make one memory_search call before concluding the MCP is down.

Uninstall

Lite tier: remove the MCP registration (claude mcp remove pseudolife-memory / codex mcp remove pseudolife-memory), then pip uninstall pseudolife-mcp. If you also want the bank gone, delete the per-user data directory (%LOCALAPPDATA%\pseudolife-mcp on Windows, ~/.local/share/pseudolife-mcp on Linux, ~/Library/Application Support/pseudolife-mcp on macOS — or wherever PSEUDOLIFE_MCP_DATA_DIR points). Back it up first: pseudolife-mcp backup works on lite too.

Docker tier — deletion is deliberate at every step:

# 1. Optional: take a final backup first (ops/backup.ps1 or ops/backup.sh).
# 2. Stop and remove the containers (volumes survive this).
docker compose -f ops/docker-compose.yml down
# 3. Remove the MCP registration.
claude mcp remove pseudolife-memory
codex mcp remove pseudolife-memory
gemini mcp remove pseudolife-memory -s user
# 4. Only when you're sure: delete the data volumes (THIS is the memory).
docker volume rm pseudolife-mcp-bank pseudolife-mcp-state

Host-process installs: also unregister the logon task (Unregister-ScheduledTask -TaskName "Pseudolife-MCP Daemon") and remove the SessionStart briefing hook — plus the UserPromptSubmit memory-change hook (pseudolife-mcp prompt-hook; older installs: the discipline echo) — from ~/.claude/settings.json and/or ~/.codex/hooks.json (a timestamped .bak-* sits next to each edited file).

Testing

pip install -e .[dev], then pytest tests/. The suite covers every layer, from the MemoryService surface to the Cortex Console REST API; model-heavy pieces are stubbed so it stays fast and offline. The PG-backed suites each target a throwaway per-run pseudolife_memory_test_<pid> database on the bundled dev container (never your real bank; concurrent runs can't collide), dropped on exit, and skip cleanly without Postgres. The suite logs in with its own test login, which cannot open the bank (pseudolife-mcp test-login create writes ~/.pseudolife-mcp/test-pg.env; see agent isolation), else the password in ops/.env (override with PSEUDOLIFE_TEST_DATABASE_URL or PSEUDOLIFE_TEST_PG_PASSWORD); a server that is reachable but rejects the credentials errors the PG-backed tests rather than skipping them, so a rotated password can never produce a green run by accident. Full dev setup: CONTRIBUTING.

What's not built yet

  • Reflection via MCP sampling — would let the dream borrow Claude itself as the extractor; Claude Code doesn't yet support it.

  • Replication / offline copies — one daemon owns one bank; other machines use it over the network (remote bank), but there is no replica or offline mode.

  • Automated world-knowledge ingestion — populating the world cortex from the live web needs a web-fetch tool the standalone server doesn't ship; an agent with web access can automate the fetch+cite step today via memory_world_set.

Support

Solo-maintained, best-effort. One person builds, tests, and runs this; there is no support contract and no response-time commitment. That said, issues are read and most get an answer.

  • Something is broken → open a bug report. The form asks for your /health output, schema version, install tier, and client, because those four answer most questions before any back-and-forth.

  • Something is missing → open a feature request. Say what you were trying to do, not only what to add.

  • A security problem → do not open a public issue. Use GitHub's private vulnerability reporting — SECURITY.md. Memory integrity specifically: security posture.

  • Sending a patch → CONTRIBUTING and CODE_OF_CONDUCT. The bar is "surgical, tested, and explained", not "big".

If you need someone to call, Comparison — use something else if names vendors who sell support.

License

Apache-2.0 — see LICENSE and NOTICE.

Available Tools

38 tools
document_ingestA
Destructive

Index a file into the reference bank — a separate store for background documents (papers, manuals, codebases) retrieved by pure cosine similarity, kept apart from conversational memory.

Returns: {source, chunks_stored, chunks_total}.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesPath to a .txt / .md / .pdf file, resolved on the SERVER's filesystem — with the Docker daemon, a path visible inside the container (e.g. a mounted volume), not a host path.
sourceNoSource tag for the chunks; defaults to the filename.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare the write/destructive/non-idempotent profile, so the description only needs to add context. It adds meaningful architectural detail (separate reference store, pure cosine-similarity retrieval) but says nothing about what ingestion overwrites or how repeated ingests behave, which matters for a destructiveHint=true tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Short and front-loaded: the core action and store definition come first, then the return shape. The return-value sentence is slightly redundant given an output schema exists, but the description remains efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter ingest tool with full schema coverage and an output schema, the description covers purpose, storage model, and return shape. The only real gap is behavioral detail on the destructive/non-idempotent nature already hinted at in annotations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both parameters (path, source) are already documented in the schema, including the server-filesystem caveat. The description adds no parameter-level meaning beyond that, making the baseline 3 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Index a file into the reference bank') and scopes it to background documents, which separates it from the memory_* cluster. It doesn't explicitly name document_search as the retrieval counterpart, so it's clear but not fully sibling-differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: the examples (papers, manuals, codebases) and 'kept apart from conversational memory' suggest when this store applies, but there is no explicit when-to-use statement and no named alternative such as document_search or memory_store.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_agentsC

Bearer required; list before shared work/resume.

project/task: relevance only. No adapter: unknown scope; idle_omitted counts idle. Adapter auth updates; omissions stay. expect: overdue seconds; children <=8 ([] clears); activity != lock. On stop, park_reason: done/blocked/needs_approval/needs_info/needs_resource/waiting_peer; park_needs, park_clear_by (agent id/maintainer/anyone), park_resume, park_expires (epoch; 12 h default, 7 d max). Ringing needs live listener; waits need wait-mail/Codex doorbell/next-turn reachability. ""/plain status clears. Claim coordinator:/claim:; reclaim renews, release frees, busy queues; status/expect optional. Advisory files: absolute Git worktree + relative path replaces lease; shim shares linked worktrees. HTTP: prepared SHA-256 repository_id/path. No globs/directories/edit enforcement. Park/status grants no approval.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathNo
taskNo
leaseNo
actionNolist
expectNo
statusNo
projectNo
childrenNo
worktreeNo
park_needsNo
park_reasonNo
park_resumeNo
park_expiresNo
park_clear_byNo
repository_idNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

C2.5/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=false, idempotentHint=false, destructiveHint=false, openWorldHint=false, but the description offers little beyond constraints: park expiry defaults (12 h/7 d max), 'children <=8', 'advisory files', 'No globs/directories/edit enforcement', 'Park/status grants no approval'. It never explains idempotency, retry behavior, or the effect of each action, which matters for a non-idempotent, non-read-only tool. The bearer-auth note is useful but leans on annotations for the rest.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The prose is a dense, telegraphic wall of semicolon-separated fragments ('Ringing needs live listener; waits need wait-mail/Codex doorbell/next-turn reachability') that is difficult to parse and not front-loaded with the tool's core purpose. Many sentences do not earn their place in their current form because their referents are unclear, so this fails conciseness and structure despite being short.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be described, but for a 15-parameter, non-read-only, non-idempotent coordination tool the description leaves key behaviors (what 'list' returns, how claims/parks interact, failure/retry semantics, which actions accept which params) unexplained. The scattered hints help but fall well short of what an agent needs to call this correctly across its four actions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must supply meaning, and it partially does: park_reason enum values, park_clear_by options (agent id/maintainer/anyone), children <=8 with '[] clears', expect semantics ('overdue seconds'), and the claim scheme (coordinator:<project>/claim:<path>). But 15 parameters exist and several (path, task, lease, worktree, repository_id, park_needs, park_resume, park_expires) are only mentioned in passing or have their semantics left implicit.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names concrete resources (claims, parks, leases, projects/tasks) and a bearer-auth requirement, and the first line hints at a list-before-shared-work workflow. However, it never opens with a clear statement of what the tool does as a whole (e.g., 'Coordinate multi-agent state: register activity, claim work, park/resume tasks'), so an agent must infer the purpose from scattered fragments. The 'project/task: relevance only' fragment is opaque without context.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It offers implied guidance: 'list before shared work/resume' and 'project/task: relevance only', plus action-ish hints ('reclaim renews, release frees, busy queues'). But there is no explicit statement of when to use this vs. siblings like memory_message or memory_session_title, and no when-not-to-use. The advice is present but fragmentary and requires the agent to piece it together.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_aliasA
Destructive

Bind an alternative name to an entity (e.g. pg → postgres) so facts and graph lookups under either name land on the same node. Returns the entity's full alias list.

ParametersJSON Schema
NameRequiredDescriptionDefault
aliasYesThe alternative name.
entityYesThe canonical entity to bind onto.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, non-idempotent, non-read-only, so the safety profile is covered by structured data. The description usefully adds the downstream effect (lookups resolve to the same node) and the return value, but it never discloses what the 'destructive' behavior actually is — e.g. whether re-binding an alias already owned by another entity overwrites that binding.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences, the operation is front-loaded, and the effect and return value follow immediately. No sentence is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description needn't explain the returned alias list, and annotations cover the mutation safety profile, so the core is complete. The one meaningful omission for a destructive bind is conflict/overwrite behavior, which leaves it slightly short of full.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and both parameters are documented as 'canonical entity' and 'alternative name', so the schema carries the load. The description's example directionally reinforces the alias→entity mapping but adds no format, collision, or validation semantics. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

It uses a specific verb and resource ('Bind an alternative name to an entity') and the `pg` → `postgres` example makes the direction of the binding unambiguous. It is distinguishable from siblings like memory_set_add or memory_graph_relate, though it never names an alternative to contrast against.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The clause 'so facts and graph lookups under either name land on the same node' conveys the rationale and implies when aliasing is useful, but there is no explicit when-to-use, when-not-to-use, or pointer to a sibling such as memory_graph_relate. Usage is implied rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_consolidateA
Destructive

Replace a cluster of near-duplicate memories with one canonical note. Use exactly one selector mode; there is no similarity fallback. All targets must resolve before any changes. Missing, retired or ambiguous targets cause no mutation; search again and resubmit IDs. Selected entries remain as history after replacement.

Returns: {superseded_count, superseded_texts, superseded_ids, new_memory_stored}.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNoLabels for the new note.
sourceNoSource tag for the new note.
new_textYesThe canonical note that replaces them.
replacesNoUnique exact stored texts; omit with entry_ids.
entry_idsNoPositive IDs in this bank; prefer over replaces.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds real behavior beyond the annotations: all-or-nothing atomicity ('All targets must resolve before any changes'), the exact failure modes that suppress mutation ('Missing, retired or ambiguous'), and the post-condition that 'selected entries remain as history after replacement.' These are the facts an agent needs before a destructive call and none are in the hints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the action, then constraint, then failure behavior, then return shape. No sentence is filler; the return-key line is dense and useful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, atomic, multi-target mutation, the description covers what is replaced, how targets are selected, what happens on failure, and what history remains. The output schema exists and the description's return summary is a harmless convenience rather than a gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds selector-mode semantics on top: exactly one of 'replaces'/'entry_ids' must be used and there is no similarity fallback, which is stronger than the schema's 'omit with entry_ids.'

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('replace') and resource ('a cluster of near-duplicate memories') with the outcome ('one canonical note'), so the agent knows exactly what it does. It does not differentiate from the sibling memory_supersede, which arguably overlaps, leaving the agent to infer the boundary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete operating rules: 'Use exactly one selector mode; there is no similarity fallback,' plus the failure path ('search again and resubmit IDs'). It stops short of naming the sibling tool an agent should use instead when it does not already have a resolved cluster.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_consolidation_candidatesA
Read-only

Find clusters of near-duplicate memories ripe for consolidation — the same thing phrased five ways across five sessions. Anchor with a query or an episode; read the clusters, synthesise one canonical note, then commit it via memory_consolidate.

Each member carries its id; commit with memory_consolidate(entry_ids=[...]) rather than the members' texts — IDs survive rewording and duplicate phrasing, exact text does not.

Returns: {count, clusters: [{cohesion, size, members}]}.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNoConsider only entries carrying one of these tags.
queryNoTopic-driven anchor: cluster memories near this description.
top_kNoHow many candidate entries to cluster over.
episodeNoSession-driven anchor: cluster within this episode id.
sourcesNoConsider only entries with one of these source tags.
max_clustersNoMax clusters returned.
min_cohesionNoMinimum intra-cluster cosine — raise it to flag only near-duplicates.
min_cluster_sizeNoDrop clusters with fewer members than this.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds genuine context beyond that: the commit path must use member IDs rather than texts, with the rationale that 'IDs survive rewording and duplicate phrasing, exact text does not.' It also surfaces the anchor requirement, which the annotations do not.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The definition is front-loaded with the core purpose and follows with the workflow, ID guidance, and return shape. Every sentence is substantive. The 'Returns:' line is mildly redundant given an output schema exists, which keeps it from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With annotations covering read-only safety and an output schema documenting the return shape, the description only needs to convey purpose, workflow, and the commit handoff. It does all three. A richer treatment of how clusters are ranked or how many to expect might push it higher, but nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds meaning beyond the schema by requiring the caller to 'anchor with a query or an episode' — the schema shows both as optional and defaulting to null, so this either/or constraint is real added guidance. The rest of the parameter description is redundant with the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource — 'Find clusters of near-duplicate memories ripe for consolidation' — with a concrete example ('the same thing phrased five ways across five sessions'). It distinguishes this step from the commit step by naming memory_consolidate as the follow-up, though it doesn't contrast directly with general search siblings like memory_search or memory_recall.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit workflow: anchor with a query or an episode, read the clusters, synthesise one canonical note, then commit via memory_consolidate. That names the next tool and the condition for using this one. It stops short of saying when NOT to use this tool versus a plain search, so it falls just short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_dreamA
Destructive

Drive the dream — consolidation of recent memories into canonical facts and graph structure.

Actions: status: backlog + whether a sweep would fire. Read-only. pull: unconsolidated memories, oldest first; write facts via memory_fact_set, then commit. run: a server-side dream with the configured extractor (loop to drain). deep: full-corpus graph consolidation; dry run unless apply. Settle candidates via memory_graph_review; duplicate lesson/world slots are listed for hand curation. Lists are capped; truncated holds their full counts. runs: recent dream passes (tallies, status). rollback: revert a committed pass from its journal (facts + events; traces/cursor kept).

Returns: per-action dict; {error} on bad input.

ParametersJSON Schema
NameRequiredDescriptionDefault
applyNodeep: True writes the consolidation (graph tables are snapshotted first); the default is a dry run.
limitNopull/run: how many memories to process (pull defaults to 40). runs: how many passes to list (defaults to 10).
actionYes
cursorNoDeprecated; use commit_token.
run_idNorollback: which pass to revert; defaults to the newest committed pass.
snippetsNodeep: False omits the evidence snippets.
commit_tokenNocommit: token returned by pull.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With annotations already declaring destructiveHint=true and readOnlyHint=false, the description adds real context beyond them: rollback reverts facts + events while traces/cursor are kept, deep snapshots graph tables before writing and defaults to a dry run, duplicate lesson/world slots require hand curation, and lists are capped with 'truncated' holding full counts. It does not discuss auth requirements or cost/rate limits, so it is rich but not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded in one sentence and the action list is organized and scannable. Given six actions and seven parameters the length is defensible, though a few lines (e.g. rollback details) could be tighter without losing meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a multi-action tool with 7 params and an output schema, the description covers nearly everything an agent needs. The one gap is the 'commit' action, which appears in the enum and is referenced inside the pull line but never given its own described entry, leaving its behavior implicit.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 86%, so most parameters are already documented. The description adds meaning beyond the schema for the 'action' enum values, whose schema entries carry only a title, by explaining what each action does and its read/write semantics; it also flags 'commit' as the write step after pull. The remaining param notes largely restate schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening states a specific verb+resource: 'Drive the dream — consolidation of recent memories into canonical facts and graph structure,' and each action (status/pull/run/deep/runs/rollback) is named and described. However, it never explicitly differentiates itself from consolidation-related siblings like memory_consolidate or memory_consolidation_candidates, so the agent must infer the boundary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Per-action guidance is strong: status is labeled read-only, pull is for unconsolidated memories (oldest first) with an explicit follow-up path via memory_fact_set then commit, deep is full-corpus with dry-run default, rollback reverts a committed pass. It stops short of stating when NOT to use this tool versus siblings, so it lacks full alternative routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_episode_endB

Close the current open sub-episode and pop back to its parent.

ParametersJSON Schema
NameRequiredDescriptionDefault
episodeNoYour session handle — pops only within your own session's subtree; the session root is never closed.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=false, idempotentHint=false and destructiveHint=false, so the safety profile is already covered. The description adds the stack-pop semantics (closes and returns to parent), but omits what happens when there is no open sub-episode and does not mention that repeated calls are non-idempotent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no filler; the action and its effect come first. It is efficient, though it could carry one clause of precondition without bloat.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and the tool is simple with one optional parameter. However, for a non-idempotent state-mutating stack operation the description says nothing about error or no-op behavior when there is no open sub-episode.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single 'episode' parameter is already documented in the schema, including the session-scope constraint and the guarantee that the session root is never closed. The description adds nothing beyond the schema, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Close') and resource ('the current open sub-episode') plus the stack effect ('pop back to its parent'), which clearly distinguishes it from memory_episode_start. It does not explicitly name its sibling counterpart, but the operation is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: an agent infers it should call this when a sub-episode is open, and it should use memory_episode_start to open one. There is no explicit when-to-use, no statement about what happens if no sub-episode is open, and no named alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_episode_startA

Open a named sub-episode; it nests under the session episode, and memories stored while it is open carry its id + title for episode-scoped search. memory_episode_end closes it and pops back.

ParametersJSON Schema
NameRequiredDescriptionDefault
hintNoOptional note on what the task is about.
titleYesShort name for the task, used in later recaps.
episodeNoYour session handle (from the session-start briefing) — anchors the sub-episode to YOUR session when several run concurrently.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare the write/idempotency profile (readOnlyHint=false, idempotentHint=false); the description goes beyond them by disclosing the nesting/stacking semantics and the side effect that memories written while open carry the episode id + title. It omits auth or concurrency caveats beyond the reference to several sessions running concurrently.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with zero filler. The core action and the nesting relationship are front-loaded, and the closing counterpart is mentioned last as a natural follow-on.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained. The description adequately covers the state transition and scoping effect for a stateful episode tool, leaving only minor gaps such as what happens on nested or repeated opens.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents title, hint, and episode (session handle). The description adds no format or syntax detail beyond what the schema provides, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Open a named sub-episode') and immediately clarifies the containment model (nests under the session episode). It also names the sibling that undoes it ('memory_episode_end closes it and pops back'), so an agent can distinguish the pair without reading either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains the condition for use: memories stored while the episode is open get tagged with its id and title for episode-scoped search, which implies you open one before storing related memories. It names the closing complement, but does not explicitly contrast against other ways of scoping memories (e.g. memory_store alone).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_episode_summaryA
Read-only

Stats, tag/source distribution and recent entries for one episode ("summarise what we worked on"); {found: false} if unknown.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesAn episode id, as it appears on search/recent results.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, so safety is covered. Beyond that, the description adds a genuinely useful behavioral fact: the tool returns {found: false} when the episode id is unknown, which tells the agent how to detect a miss instead of assuming an exception.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single compact sentence with no filler. The return-value content is front-loaded rather than the action verb, which is a minor ordering quirk, but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be enumerated, and the description nonetheless flags the unknown-id case. For a one-parameter, read-only summary tool this is close to complete; only the absence of routing guidance against similar read tools leaves a small gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is a single parameter with 100% schema description coverage ("An episode id, as it appears on search/recent results"), so the schema already carries the semantics and the description adds nothing further about the id. Baseline 3 applies when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the resource (one episode) and enumerates what is returned: stats, tag/source distribution, and recent entries. It is clearly a read/summary operation, distinct from siblings like memory_episode_start/end, though the verb itself is implied rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The quoted phrase "summarise what we worked on" gives an implied use case, but there is no explicit when-to-use guidance, no exclusions, and no named alternative (e.g. memory_recent or memory_history which could also surface recent activity). Usage must be inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_fact_getA
Read-only

Look up the one CURRENT value at an (entity, attribute) slot. One value per slot. A null record means EMPTY, not unknown — memory_search still finds context. A set-valued slot returns {kind: "set", members, removed} instead — members: [] means EMPTY too.

Returns: {record | null, contenders} (+ entity_ref when the entity has a graph node). Non-empty contenders = unsettled conflict (see memory_fact_resolve); on an empty slot, candidates lists nearby slots — ranked leads, not answers. re_verify = a memory this fact was derived from has since been corrected; the value still stands but check it before acting. Set slots carry it too, at the slot. PostgreSQL keeps this warning after the corrected source memory is evicted or deleted; file mode has no durable retraction warning. verbose=True adds the record's provenance, support and temporal stamp; memory_history shows the slot's version chain.

ParametersJSON Schema
NameRequiredDescriptionDefault
entityYesThe slot's subject; the (entity, attribute) match is case- and separator-insensitive.
verboseNoFull record; the default drops bookkeeping keys.
attributeYesThe slot's attribute.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only safety, so the description is free to add deeper semantics: null means EMPTY (not unknown), set slots return {kind: "set", members, removed}, non-empty contenders signal conflict, and re_verify flags a corrected source memory. It even distinguishes PostgreSQL durable warnings from file-mode behavior — context far beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense but front-loaded: the core lookup and the null-is-empty rule come first, followed by return shape and edge-case flags. Nearly every sentence earns its place, though the return-format detail is heavier than strictly necessary given an output schema exists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter read tool with a rich output schema and full annotation coverage, the description covers the edge cases an agent would otherwise misread: empty vs unknown, set-slot shape, conflict signaling, and staleness warnings. Nothing needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds meaning beyond the schema's terse "Full record; the default drops bookkeeping keys" by specifying that verbose adds provenance, support, and temporal stamp. That is genuine added value over the structured field.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb+resource+scope: "Look up the one CURRENT value at an (entity, attribute) slot." This clearly distinguishes it from memory_fact_set (writes a slot), memory_fact_resolve (settles conflicts), and memory_search (broad retrieval). An agent can identify the exact operation without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Routes the agent to alternatives with conditions: memory_search for context on empty slots, memory_fact_resolve for unsettled conflicts, memory_history for version chains. It lacks an explicit "use this instead of X when..." framing for the primary lookup case, but the sibling routing is concrete and actionable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_fact_resolveA
Destructive

Settle a CONTESTED fact slot after checking with the human.

Returns: {resolved, accepted, action, current, record} or {resolved: false, reason: "no_contender"}.

ParametersJSON Schema
NameRequiredDescriptionDefault
acceptYesTrue adopts the parked contender as the new current value (the old value is kept as history); False discards the contender and keeps the current value.
entityYesThe contested slot's subject.
attributeYesThe contested slot's attribute.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, non-idempotent, non-read-only, so the safety profile is partly covered. The description adds genuinely new context: the human-confirmation precondition and the failure shape ({resolved:false, reason:'no_contender'}). It does not, however, elaborate on the destructive consequences beyond what the schema's 'accept' description already says.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the action and its precondition. The Returns block is somewhat redundant given an output schema exists, but naming the 'no_contender' failure shape is still worth the space, so it stays efficient overall.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-required-param mutation with an output schema and full annotation coverage, the description supplies the purpose, the human-confirmation precondition, and the failure mode. Return-value detail is rightly left to the output schema. A bit more on alternatives and destructive consequence would complete it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already carries the parameter meaning, including the accept True/False semantics. The description adds only the notion of a 'parked contender' and the human-check context. Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: settling a 'CONTESTED fact slot'. This is clearly distinguishable from siblings like memory_fact_set and memory_fact_get, which imply setting/getting rather than resolving a contested state. It stops short of explicitly naming those siblings, so it earns a 4 rather than a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives one implied precondition: this is only for a slot that is already contested, and only 'after checking with the human'. That narrows usage meaningfully. However, it never names the alternative tools (memory_fact_set, memory_supersede) or states when NOT to use this, so the routing guidance remains inferential.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_fact_setA
Destructive

Assert a canonical fact — insert, confirm, or correct a slot.

A new value at an existing slot supersedes the old (history kept). A conflicting write parks as a contender (action="contested", winner under current) — check with the human, settle via memory_fact_resolve.

authority / distortion_tolerance inherit the slot's labels unless restated.

Returns: {action: inserted|confirmed|superseded|contested, ...record}.

ParametersJSON Schema
NameRequiredDescriptionDefault
valueYesThe value that is canonical NOW.
entityYesThe slot's subject; the (entity, attribute) match is case- and separator-insensitive.
originNoWho asserted it: "user" = the human told you; otherwise "action"/"agent". Omitted records "agent".
episodeNoEpisode handle for attribution.
attributeYesThe slot's attribute.
authorityNoSpeech act of the source: "directive" / "quoted" (doc or third party) / "observation"; "auto" infers.auto
confidenceNoHow sure you are, 0..1.
freshness_classNoHow fast the value rots. "auto" infers the decay rate from the entity kind.auto
distortion_toleranceNo"constraint" = verbatim, pinned in recall; "auto" infers only that.auto

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds substantial context beyond the annotations: the old value is superseded but history is kept (softening the destructiveHint), conflicting writes park as contenders rather than overwriting, and authority/distortion_tolerance inherit slot labels unless restated. This is exactly the mutation-safety detail the destructive/non-idempotent annotations flag as risky.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Densely front-loaded — the core action leads, then conflict semantics, then inheritance, then the return shape. No filler sentences, though a few clauses are terse enough to require a second read.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter mutation tool with a contested-state model, the description covers action semantics, conflict handling, inheritance behavior, and even the return shape (despite an output schema existing). Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description earns an extra point by documenting cross-parameter behavior the schema cannot express: authority and distortion_tolerance inherit the slot's labels unless restated, which changes how an agent should reason about defaults on a write.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb and resource: 'Assert a canonical fact — insert, confirm, or correct a slot.' The three modes map cleanly onto the slot model, and it explicitly routes the conflict case to the sibling memory_fact_resolve, so an agent can distinguish it from memory_fact_get and memory_store.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear conditional guidance for the contested case ('check with the human, settle via memory_fact_resolve') and explains supersede vs. confirm behavior. It lacks an explicit statement of when NOT to use this versus memory_store or memory_supersede, so it stops short of the top tier.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_forgetA
Destructive

Forget from one memory store. memory hard-deletes entries (junk/test cleanup), retiring their digests and facts/edges left unsupported; fact hard-deletes (no audit trail); world and lesson RETIRE the slot with an audit row, undone by memory_graph_review( action="restore_slot"). For "now wrong, keep history" use memory_fact_set (facts) or memory_supersede (memories) instead.

scope="memory" needs at least one of text/substring/ source/episode/tag; several NARROW the match (AND, like memory_search): text + source is one entry, not the whole source. Over memory.delete_confirm_threshold (20) matches it refuses with would_delete until confirm_bulk=True. The other scopes need entity.

Returns: {deleted_count | removed, ...}; {error} on bad input.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagNomemory scope: entries carrying this tag.
textNomemory scope: entries with exactly this text.
scopeYesWhich store to delete from.
entityNofact/world scope: the slot's subject. lesson scope: the task.
sourceNomemory scope: entries with this source tag.
episodeNomemory scope: entries stamped with this episode id.
attributeNofact/world scope: one slot's attribute; omit to purge the whole entity. lesson scope: the aspect.
substringNomemory scope: entries whose text contains this.
confirm_bulkNomemory scope: allow over memory.delete_confirm_threshold (20) matches; else refused, reporting would_delete.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint=true, but the description adds material context beyond them: memory/fact are hard-deletes with no audit trail (retiring digests and orphaning facts/edges), while world/lesson retire with an audit row undoable via memory_graph_review(restore_slot), and the bulk-confirm refusal path at 20 matches. This is exactly the behavioral depth the annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The text is front-loaded with the per-scope behavior, then parameter constraints, then the return shape. It is information-dense but a single sentence spans hard-delete vs retire plus the restore path, which slightly crowds the structure. Nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter destructive tool with an output schema, the description covers the per-scope effects, required-parameter alternatives, destroy-vs-retire semantics, the bulk-confirm guard, and the escape hatches (memory_fact_set, memory_supersede, restore_slot). An agent has everything needed to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds real value on top by stating that scope=memory needs at least one of text/substring/source/episode/tag, that several narrow via AND rather than widening, and that the other scopes require entity – conditions the per-property schema descriptions don't capture.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb ('Forget' / hard-delete / retire) and resource (entries across four named scopes), and names the sibling tools (memory_fact_set, memory_supersede, memory_graph_review) with which it could be confused. It distinctly differentiates itself by scope behavior, which no other sibling does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states which alternative to use for the 'now wrong, keep history' case – memory_fact_set for facts and memory_supersede for memories – and carves out when hard-delete is appropriate (junk/test cleanup) versus when retiring is appropriate. When-not guidance is explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_getA

Dereference a memory id to the full stored episode plus consolidated_into — the canonical facts it produced. Reading it gently reinforces it. Superseded: adds replaced_by, as in search. {found: false, faded: true} once forgotten.

ParametersJSON Schema
NameRequiredDescriptionDefault
entry_idYesA memory id, as returned by search results or by a fact's ``source_entries``.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds meaningful behavior beyond the annotations: reading gently reinforces the memory (explaining why readOnlyHint=false), superseded entries include replaced_by, and forgotten entries return {found: false, faded: true}. It omits permission/auth context, but the reinforcement side effect is the key non-obvious trait and it is disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and kept short. Some fragments ('Superseded: adds replaced_by, as in search.') are terse to the point of being cryptic, but there is little wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return shape need not be spelled out, and the description still adds the special-case response for forgotten entries. For a single-parameter read tool it is nearly complete, missing only explicit routing among sibling memory reads.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single entry_id parameter is already documented in the schema, including where the id comes from. The description adds only marginal meaning beyond that, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (dereference) and resource (a memory id to its full stored episode plus consolidated_into), and clarifies what the payload contains. It differentiates the episode payload from the canonical facts it produced, but does not explicitly contrast with near-siblings like memory_fact_get or memory_episode_summary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied: you call it when you hold a memory id from search results or a fact's source_entries and want the full episode. The 'as in search' clause hints at behavioral parity with search, but there is no explicit when-to-use versus alternatives such as memory_fact_get.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_graphA
Read-only

Read an entity's graph neighborhood: nodes, typed edges, and each node's canonical facts. Transitive/inverse edges arrive pre-derived (marked derived: true with rule provenance).

Returns: {found, entity, nodes, edges, paths}.

ParametersJSON Schema
NameRequiredDescriptionDefault
toNoAlso return the shortest path from ``entity`` to this entity under ``paths``; its nodes are folded into the neighborhood.
depthNoHops from the root. Max 3.
entityYesThe root entity to read out from.
include_factsNoFalse omits each node's canonical facts.
relation_filterNoKeep only edges whose relation contains this substring.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds real behavioral value beyond them: transitive/inverse edges arrive pre-derived and are flagged derived:true with rule provenance. It does not explain the non-idempotent hint or cost/limits, but the derived-edge semantics are genuinely useful.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three compact sentences, front-loaded with the core action and followed by the derived-edge caveat. The explicit 'Returns:' line is somewhat redundant given an output schema exists, but it is brief rather than padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only traversal tool with an output schema and safety annotations, the description is largely sufficient: it states scope, the pre-derived edge semantics, and the return keys. Nothing an agent needs to invoke it correctly is missing, though a note on traversal cost or depth limits would round it out.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents entity, depth, to, include_facts, and relation_filter. The description adds no extra parameter semantics (e.g., depth interaction with relation_filter or path folding behavior beyond what the schema states), so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb+resource: 'Read an entity's graph neighborhood: nodes, typed edges, and each node's canonical facts.' This is clearly distinct from the graph mutators (memory_graph_relate/unrelate) and review tools, but the description never names a sibling to route against, so it stops short of explicit differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'read an entity's graph neighborhood', so an agent can infer it is the traversal/expansion tool. However, with ~40 siblings including memory_search, memory_recall, and memory_fact_get, there is no explicit when-to-use-this-vs-that guidance or exclusion criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_graph_relateA
Destructive

Assert a typed relation between two entities, e.g. ("web-app", "runs-on", "host-1"). Entities auto-create and resolve through aliases; re-asserting an edge bumps its confidence. On a rejected relation, pick a suggestion, fall back to related-to, or grow the vocabulary deliberately via memory_relation_define.

Returns: {src, relation, dst, confidence, warnings} or {error: "unknown_relation", suggestions}.

ParametersJSON Schema
NameRequiredDescriptionDefault
dstYesThe object entity.
srcYesThe subject entity.
originNoWho asserted the edge.
dst_typeNoEntity kind for ``dst``: set when the node is created, or filled in on an existing node that has none.
relationYesFrom the closed registry: ``depends-on``, ``part-of``, ``runs-on``, ``hosts``, ``uses``, ``configures``, ``stores-data-in``, ``related-to``. Separator variants normalise; an unknown name is rejected WITH the closest matches.
src_typeNoEntity kind for ``src``: set when the node is created, or filled in on an existing node that has none.
confidenceNoHow sure you are, 0..1.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare the mutation/safety profile (readOnly=false, destructive=true, idempotent=false), and the description adds meaningful behavior on top: entities auto-create and resolve through aliases, re-asserting an edge bumps confidence (which explains the non-idempotent hint), and unknown relations are rejected with suggestions. No contradiction with the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the action and an example, then behavior, then the return shape, in a compact paragraph with no filler. Dense but each sentence carries information; the only minor redundancy is restating the return shape despite an output schema existing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter mutation tool, the description covers the critical unknowns an agent needs: auto-creation side effects, alias resolution, confidence bumping on re-assert, the rejection/suggestion path, and the fallback tool. With full schema coverage and an output schema present, nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents src/dst/relation/confidence/origin/types, including the closed relation registry and its normalisation/rejection behavior. The description reinforces the triplet shape with an example but adds no syntax or constraint detail beyond what the schema provides, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Assert a typed relation between two entities') and grounds it with a concrete triplet example ("web-app", "runs-on", "host-1"). It also distinguishes itself from the vocabulary-management sibling memory_relation_define, so an agent can identify the tool's role without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit branch guidance for the failure case: on a rejected relation, pick a suggestion, fall back to related-to, or call memory_relation_define to grow the vocabulary. This names an alternative tool and the condition that selects it, though it does not state when to prefer this over other writes like memory_store or memory_set_add.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_graph_reviewA
Destructive

Work the graph review queue — deep-dream proposals that need a verdict before they touch the graph.

Actions: list: pending findings/proposals. propose: file link proposals for review. relate: related-not-duplicate verdict — writes the relation edge and dismisses the pair. dismiss_pair: mark src/dst genuinely distinct. dismiss_slot_pair: same for lesson/world duplicate listings. restore_slot: undo a lesson/world forget (forgets retire, never delete) — store + src (the retired key). accept_link/reject_link: settle an edge proposal by id. accept_merge: fold a near-duplicate into its twin. accept_junk: delete an over-extraction artifact. reject_entity: keep the entity, dismiss the proposal.

Returns: per-action dict; {error} on bad input.

ParametersJSON Schema
NameRequiredDescriptionDefault
dstNorelate/dismiss_pair/dismiss_slot_pair: the second entity or key.
srcNorelate/dismiss_pair: the first entity. dismiss_slot_pair: an "entity|attribute" key from the deep response; restore_slot: retired key or entity.
scopeNolist: keep only analyzer findings whose entities carry this memory source (Atlas project); queued proposals always list; omit or "all" for everything. Not a finding kind.
storeNodismiss_slot_pair/restore_slot: "lesson" or "world".
actionNolist
relationNorelate: edge relation to write (graph vocabulary).
proposalsNopropose: ``[{src, relation, dst, similarity?, rationale?}]``.
proposal_idNoId actions: the proposal to settle.
proposal_idsNoId actions: many proposals at once.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, readOnlyHint=false and idempotentHint=false, and the description is consistent with them. It adds meaningful context beyond the annotations: 'forgets retire, never delete', that relate writes the relation edge and dismisses the pair, and what accept_merge/accept_junk actually do to data. It stops short of disclosing auth/permission needs or irreversibility per action.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in one sentence, followed by a scannable action list and a one-line return note. The awkward indentation and the split of some entries across lines cost a little, but there is no filler and each line carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so the brief 'per-action dict; {error} on bad input' note is sufficient. All 11 enum values are described, and with 0 required params the default of `list` is implied by the description. Missing only per-action required-parameter hints, which would help an agent call non-default actions correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 89%, so the schema already documents nearly every parameter and even maps params to actions (e.g. 'relate/dismiss_pair: the first entity'). The description's param mentions ('store + src (the retired key)', 'settle an edge proposal by id') largely restate the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (the graph review queue) and frames it as 'deep-dream proposals that need a verdict before they touch the graph', then enumerates all 11 actions with one-line semantics. It distinguishes itself from siblings like memory_consolidate and memory_dream by tying the queue to dream proposals, but it never disambiguates the `relate` action from the sibling tool memory_graph_relate, which appears to write the same kind of edge.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The per-action lines imply when each action applies ('relate' for related-not-duplicate, 'accept_junk' for over-extraction artifacts), which is real guidance. However, there is no explicit when-to-use-this-tool-vs-alternatives statement, no prerequisites or ordering, and no indication of which actions require which params before calling.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_graph_unrelateA
Destructive

Retract a relation — the edge is marked superseded (kept for audit) and leaves memory_graph results. Re-asserting the same triple later revives it.

ParametersJSON Schema
NameRequiredDescriptionDefault
dstYesThe edge's object entity.
srcYesThe edge's subject entity.
relationYesThe edge's relation.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=false, so the safety profile is covered. The description adds valuable non-obvious behavior: the edge is marked superseded but kept for audit, and re-asserting revives it. This goes beyond annotations by explaining the audit/revival semantics. Minor gap: doesn't explicitly note non-idempotency, but annotations cover that.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the core action (retract a relation) followed by the behavioral detail (audit-kept, revival). No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given annotations cover safety (destructive, not idempotent) and an output schema exists (so return details are not needed), the description provides sufficient context for correct invocation. A slight gap remains: no explicit guidance on when to prefer this over nearby alternatives, but the core behavior is well-specified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents src, relation, and dst. The description adds no additional semantics for these parameters. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (retract) and resource (a relation/edge in the memory graph), and distinguishes itself from the sibling memory_graph_relate by describing the opposite operation. An agent can distinguish this from graph_relate without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clear implied context: use when a relation should be retracted / removed from graph results. The mention of revival on re-assertion gives the agent a hint about reversibility. However, no explicit 'when to use this vs alternatives' (e.g., vs memory_forget, memory_supersede, memory_set_remove) is given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_historyA
Read-only

Read an entity's past. SLOT mode (attribute given): every version of that canonical fact slot, oldest→newest, each with writer/session, tx/valid time, and age ("what did this used to be? who set it?") — compaction thins chains past ~30d. CHAIN mode (attribute omitted): the entity's dated fact/entry/edge/lesson events merged oldest→newest ("what led to X?").

Returns: {entity, attribute, count, versions} (slot mode) or {found, entity, count, events} (chain mode).

ParametersJSON Schema
NameRequiredDescriptionDefault
as_ofNoSlot mode only: ISO-8601 or epoch seconds; returns only versions written by then.
entityYesThe subject whose past to read.
attributeNoThe fact slot to read; omit for chain mode.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, and the description adds real context beyond them: oldest-to-newest ordering, the fields returned per version (writer/session, tx/valid time, age), and the ~30d compaction caveat. The compaction note is a genuinely useful behavioral trait not visible in any structured field.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense but front-loaded: purpose first, then mode breakdown, then return shapes. Each clause carries information (mode, ordering, fields, caveat) rather than filler, though the inline return-shape enumeration is somewhat verbose given an output schema exists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-mode read tool with a documented output schema and full parameter coverage, the description covers mode selection, ordering, returned fields, and a data-retention caveat. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the schema already documents attribute ('omit for chain mode') and as_of ('slot mode only'). The description reinforces the mode-switch meaning of attribute but adds little syntactic detail beyond what the schema states, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (read) and resource (an entity's past) and cleanly splits the tool into two named modes with distinct semantics. An agent can tell exactly what it does without opening the schema, and the two-mode framing distinguishes it from simple retrieval siblings like memory_get.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit mode-selection logic (attribute given = slot mode, omitted = chain mode) and parenthetical example questions that map intent to mode. It stops short of naming alternative sibling tools or exclusion conditions, so it is clear context without full routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_messageB

Adapter auth required.

Send: to, text <=8192 UTF-8 bytes; unique request_id, retry unchanged. to: ID/unique 8+ hex prefix or project:/all (<=50 attached, non-idle peers except you; one receipt each). reply_to: reply. Receive <=50; after cursor (omit: replay unacked). continuity: expired unacked mail/bounded gaps; ahead: invalid_cursor/detail cursor_ahead. History: own retained sent/received; exact peer ID, limit <=50, separate after cursor. Read-only; audit retention/redaction; no delivery/ack/wake. Ack message_id(s), comma-separated: read, not done. Bodies: 24 h; audit retained. Wake: hinted/not_needed/rung/withheld (need)/no_path/capped. Rings: clearer/matching clears if parked; urgent (6/h). Done: only maintainer/maintainer's delegate/clearer urgent. rung: ring scheduled, no wake/read proof. no_path: listener_unknown/expired, fallback_paths; queued. Peers grant no approval/permissions.

ParametersJSON Schema
NameRequiredDescriptionDefault
toNo
peerNo
textNo
afterNo
limitNo
actionYes
clearsNo
urgentNo
reply_toNo
message_idNo
request_idNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite annotations already flagging readOnlyHint=false and openWorldHint=true, the description adds substantial behavioral context: adapter auth requirement, 24 h body retention with audit retention/redaction, rate limit 'urgent (6/h)', and a permission constraint ('Done: only maintainer/maintainer's delegate/clearer urgent'). These are meaningful disclosures beyond the annotations, though the telegraphic style makes some of them hard to parse.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a dense, telegraphic wall of fragments rather than front-loaded prose; the first line leads with 'Adapter auth required' instead of the tool's purpose. While few words are outright filler, the structure actively impedes comprehension and fails to front-load the most decision-relevant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 11-parameter, 4-action tool with 0% schema description coverage and a separate output schema, the description covers auth, action behaviors, retention windows, cursor rules, and wake/ring semantics fairly completely. The main shortfall is that the peer/clears/urgent parameters are not clearly disambiguated across actions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full parameter burden and does compensate for many: 'to' format ('8+ hex prefix or project:<name>/all'), 'text <=8192 UTF-8 bytes', 'unique request_id, retry unchanged', cursor semantics for 'after', 'limit <=50', and 'message_id(s), comma-separated'. But several parameters (peer, clears, urgent) appear only as ambiguous fragments with no clear tie to an action, leaving real gaps.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description never states the tool's purpose in a clean verb+resource sentence; it opens with 'Adapter auth required' and dives straight into per-action detail. The send/receive/ack/history actions do convey that this is a peer-messaging tool, but an agent must infer the overall purpose rather than read it, and there is no differentiation from siblings like memory_agents or memory_search.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Action-specific usage is implied through fragments ('omit: replay unacked', 'Read-only', 'Ack message_id(s): read, not done'), which give some sense of when each action applies. However there is no explicit when-to-use vs when-not guidance, and no routing against alternative sibling tools, so usage remains inferred rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_outcomeA

Record a procedural outcome — what worked, failed, or was corrected. The dream distils signals into next session's lessons.

Returns {recorded, signal_id, task, outcome}; needs Postgres. used_ids adds used_ids_recorded (credited), used_ids_unmatched (nothing served it), used_ids_served_elsewhere (another session did), used_ids_ignored (not ids), used_ids_truncated (over 50), used_ids_errors (write failed), used_ids_reason (why none landed).

ParametersJSON Schema
NameRequiredDescriptionDefault
taskYesKind of task, in stable wording ("deploy engine to host") so signals for the same work group.
aboutNoThe tool or approach concerned; aids traversal.
detailNoWhat worked, or what the dead-end was.
episodeNoEpisode handle for attribution.
outcomeYesWhat happened.
polarityNo"+" do-this or "-" avoid; usually omit — it is inferred from the outcome.
used_idsNoIds of the search hits you actually used, e.g. [1421, 903] — the relevance label only you can write. At most 50. Credits this session's searches from the last hour; log before that lapses.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare the write/non-idempotent/non-destructive profile, so the description's added value is the Postgres dependency ('needs Postgres'), the credit mechanism for used_ids, and the one-hour expiry window. It also explains the used_ids_* result buckets, which clarifies what a partial or failed attribution looks like. It stops short of stating permissions or failure behavior beyond the used_ids_errors bucket.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded in the first sentence, followed by dependency and return information. The long used_ids_* enumeration is informative but dense and partly duplicates the output schema, slightly bloating the definition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter write tool with annotations and an output schema, the description covers purpose, environment dependency, the credit/timing constraint, and partial-failure semantics. Nothing critical is missing, though the relationship to sibling memory tools is unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so every parameter is already documented, including used_ids, about, detail, and polarity. The description mostly restates the used_ids semantics ('the relevance label only you can write', 'at most 50', last-hour crediting) rather than adding new meaning. Baseline 3 applies when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb+resource ('Record a procedural outcome') and enumerates the recorded outcome kinds (worked, failed, corrected). It is clearly distinguishable from storage siblings like memory_store or memory_message in substance, though it never names an alternative to route against.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'what worked, failed, or was corrected' and by the instruction to log used_ids 'before that lapses' (a one-hour window), which is genuinely actionable timing guidance. However, there is no explicit when-to-use/when-not statement and no named alternative among the many memory_* siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_recallA
Read-only

Multi-hop retrieval over the knowledge graph, for RELATIONAL questions whose answer is reached by following links — "what does X ultimately run on?", "how does A reach C?" — which single-shot memory_search can't chain. Read-only. low_confidence: true means no seed entity matched — fall back to memory_search.

Returns: {seeds, entities, edges, paths, texts, iterations}. entities/edges/texts are capped (currently 10/15/6) with a per-hop reservation, so a hub seed's own 1-hop ring can't crowd out the deeper hops the walk exists to reach; edges prefers links between surviving entities; each entity's facts is capped (currently 5). A fact carrying re_verify stands on a memory that has since been corrected — the value still stands, but check it before acting. A seed entity's constraint facts come first, marked pinned. truncated: true (with searches_issued) means a search ceiling or time budget stopped the walk early: some re-queries, and possibly deeper hops, were skipped, so supporting texts and deeper entities may be missing — narrow the question or lower hops. Details: docs/guide/retrieval.md.

ParametersJSON Schema
NameRequiredDescriptionDefault
hopsNoMax graph hops. Clamped to 1..5.
queryYesNatural-language relational question; its top hits seed the graph walk.
top_kNoBounds only the SEED search (the initial hits that name the walk's start entities), not the result, which is capped separately; up to 3 ``texts`` slots go to seed hits.
verboseNoFull fact/edge provenance and untruncated texts. Default facts are ``{attribute, value}``, edges ``{src, relation, dst}``, texts truncated to a preview.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover only the safety profile (readOnlyHint true, destructiveHint false); the description goes well beyond by disclosing capping behavior (10/15/6 with per-hop reservation), fact cap of 5, the meaning of re_verify on corrected memories, pinned constraint facts, and what truncated/searches_issued implies about missing data. This is unusually rich behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in the first sentence and every later sentence carries operational detail (caps, re_verify, truncated). It is dense rather than bloated, though 'Read-only' duplicates the readOnlyHint annotation and the return-field listing slightly overlaps the output schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having an output schema, the description supplies the interpretive layer the schema cannot: what capping, re_verify, pinned facts, and truncated mean for the agent's next action. Combined with the doc pointer, nothing an agent needs to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3 and the schema already documents hops, top_k, and verbose. The description still adds meaning by tying hops to the truncated failure mode ('lower hops') and explaining why results are capped independently of top_k, which the schema does not spell out.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (multi-hop retrieval) and resource (knowledge graph) plus the exact class of question it serves (relational, link-following). It explicitly distinguishes itself from memory_search, which 'can't chain', so an agent can route without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names the alternative (memory_search) and the condition that selects it, gives a fallback rule when low_confidence is true, and tells the agent to narrow the question or lower hops when truncated. When-to-use, when-not, and recovery paths are all explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_recentA
Read-only

List the N most recently stored memories, newest first — timestamp order, not relevance. Useful for "what did I just store?" and for catching up at the start of a session. The sources/episodes/ tags filters AND-combine.

ParametersJSON Schema
NameRequiredDescriptionDefault
nNoHow many entries to return.
tagsNoKeep only entries carrying one of these tags.
sourcesNoKeep only entries with one of these source tags.
verboseNoFull per-entry metadata; default entries are compact.
episodesNoKeep only entries stamped with one of these episode ids.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds non-obvious behavior: the ordering guarantee and that sources/episodes/tags filters AND-combine, which an agent could otherwise assume was OR. It does not need to explain return values since an output schema exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, zero waste, with the core identity (most recent N, newest first) front-loaded before the usage hints and the filter-combination note.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-param, zero-required read tool with full schema coverage, annotations, and an output schema, nothing an agent needs is missing: ordering, scope, filter interaction, and use contexts are all stated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description goes beyond the per-parameter schema text by specifying that the three filter parameters AND-combine, a cross-parameter semantic not derivable from individual field descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (List), resource (memories), quantity (N), and ordering semantics ('newest first — timestamp order, not relevance'). The 'not relevance' clause implicitly contrasts with memory_search, letting an agent distinguish the two without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names two concrete use cases ('what did I just store?' and 'catching up at the start of a session'), which is clear positive guidance. It stops short of explicitly naming memory_search as the alternative for relevance-based retrieval or stating when-not to use it, so it falls just below the top tier.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_reinforceA

Strengthen one memory after reading it via memory_get and finding it genuinely useful — a deliberate "this mattered" signal that helps it resist forgetting. Read first, then reinforce.

ParametersJSON Schema
NameRequiredDescriptionDefault
entry_idYesThe id of the memory to strengthen.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already disclose that this is a non-read-only, non-idempotent, non-destructive operation, so the safety profile is covered. The description adds the useful effect ('resist forgetting') but does not address whether repeated calls compound strength (relevant given idempotentHint=false) or how invalid ids are handled.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One front-loaded sentence that leads with the action and its rationale. 'Read first, then reinforce' mildly restates the earlier 'after reading it via memory_get' clause, a small redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter mutation with an output schema, the description supplies the intent, the trigger, and the ordering. The remaining gap is idempotency/compounding behavior, which is a minor omission given the output schema carries the response.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter is fully described in the schema, so the baseline is 3. The description adds only a light link between the id and the memory_get workflow, without new format or constraint details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Strengthen') and resource ('one memory'), and clarifies the effect ('helps it resist forgetting'). It is clearly distinguishable from siblings like memory_forget, memory_supersede, and memory_reinstate, which imply opposite or different semantics.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit trigger — only after reading via memory_get and finding it 'genuinely useful' — and states the ordering constraint ('Read first, then reinforce'). It does not, however, name specific alternative siblings or state when reinforcement should be skipped in favor of them.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_reinstateA
DestructiveIdempotent

Reinstate one reviewed retired entry by durable ID. PostgreSQL only; requires a named principal and exact retirement preimage. Refuses trace-invalidated entries and never changes derived cortex. Retry an uncertain call with the same UUID and inputs.

ParametersJSON Schema
NameRequiredDescriptionDefault
reasonYesNon-empty reason recorded in the audit.
entry_idYesPositive durable entry ID from this bank.
operation_idYesNew canonical UUID; reuse it unchanged after an uncertain response.
reviewer_idsYesNon-empty reviewer identifiers recorded in the audit.
expected_text_sha256YesLowercase SHA-256 of the reviewed entry text.
evidence_packet_sha256YesLowercase SHA-256 binding the private review packet.
expected_source_sha256YesLowercase SHA-256 of the reviewed source tag.
expected_superseded_atYesExact reviewed superseded_at database value.
expected_superseded_by_text_sha256YesLowercase SHA-256 of the reviewed replacement text.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds substantial context beyond the annotations: backend constraint ('PostgreSQL only'), an auth requirement ('requires a named principal'), a precondition ('exact retirement preimage'), and explicit refusal/scope behavior ('Refuses trace-invalidated entries and never changes derived cortex'). These go well beyond the destructive/idempotent hints the annotations already carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four dense sentences with the core verb+resource front-loaded and each clause carrying distinct information (backend, auth, precondition, refusal behavior, retry). No filler, though it is slightly more compact-packing than a maximal 5 might demand.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter mutation tool with an output schema, the description covers auth needs, preconditions, refusal cases, mutation scope, and retry semantics; return values are appropriately left to the output schema. A minor gap is that the resulting state transition after reinstatement is only implied.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 9 parameters in detail. The description adds only indirect context ('by durable ID', 'same UUID', 'exact retirement preimage'), which reinforces but does not extend what the schema provides. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — 'Reinstate one reviewed retired entry by durable ID' — which distinguishes it from the opposite operation (memory_supersede) and from memory_reinforce/memory_forget. It does not explicitly name the sibling it differs from, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the domain (reinstating a reviewed retired entry) and by the retry clause 'Retry an uncertain call with the same UUID and inputs.' However, there is no explicit when-not or named alternative, so guidance is only at the implied level.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_relation_defineA
Destructive

Add a relation to the closed graph vocabulary — a deliberate, rare act. Prefer the builtins; define one only when a recurring connection genuinely fits none of them.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesThe new relation name, hyphenated like the builtins.
dst_typeNoSoft entity-kind expectation for the object; a mismatch warns, never rejects.
src_typeNoSoft entity-kind expectation for the subject; a mismatch warns, never rejects.
inverse_ofNoPair with an existing relation, as ``runs-on`` is the inverse of ``hosts``.
transitiveNoTrue closes the relation transitively, so chained edges arrive pre-derived.
descriptionYesWhat the relation means, for later readers.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, idempotentHint=false, and readOnlyHint=false, so the safety profile is covered. The description adds that this is a 'deliberate, rare act' on a 'closed' vocabulary, implying weight and permanence, but it doesn't state reversibility, auth requirements, or the effect on existing edges. Adequate context beyond annotations, but not rich.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences that front-load the action and scope, then the caveat. No filler; every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need no explanation, and annotations carry the safety profile. The description covers purpose, rarity guidance, and naming intent, leaving nothing essential missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so every parameter (name, dst_type, src_type, inverse_of, transitive, description) is already documented in-schema. The description adds only the didactic 'prefer the builtins' framing and no extra per-parameter syntax or format detail. Baseline 3 applies since the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: adding a relation to a 'closed graph vocabulary'. The phrase 'closed graph vocabulary' signals a schema-level definition rather than an instance-level edge, which meaningfully separates it from memory_graph_relate/unrelate. It stops short of naming those siblings explicitly, so an agent must still infer the boundary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear when/when-not guidance: 'Prefer the builtins; define one only when a recurring connection genuinely fits none of them.' The rarity bar and the preference for existing relations are explicit. It does not name the concrete sibling tools (e.g., memory_graph_relate) an agent might otherwise reach for.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_session_titleA

Name THIS session's auto-opened episode (default titles are generic). Call once at the start of work so session recaps read meaningfully. Idempotent; call again to rename.

ParametersJSON Schema
NameRequiredDescriptionDefault
titleYesWhat this session is about, e.g. "Pseudolife-MCP" or "auth-refactor".
episodeNoYour session handle — names that session's root directly (the identity a hook-registered client has).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.5/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description asserts "Idempotent," but the annotations declare idempotentHint=false, a direct contradiction on the tool's repeat-call semantics. Beyond that it does disclose useful context (generic defaults, rename behavior), but the explicit conflict with structured metadata is disqualifying.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the action and the reason to call it, with zero filler. The trailing "Idempotent; call again to rename" is compact but is also the clause that introduces the annotation conflict.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be described, and the description covers when to call and what gets named. It is complete enough for an agent to invoke correctly, aside from the idempotency inconsistency.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and both parameters (title, episode) are fully documented in the schema with examples and defaults. The description adds no parameter-level detail beyond the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb ("Name") and a precise target ("THIS session's auto-opened episode"), and explicitly frames the value proposition ("default titles are generic"). An agent can distinguish it from siblings like memory_episode_start or memory_episode_summary without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear timing guidance ("Call once at the start of work so session recaps read meaningfully") and the rename case ("call again to rename"). It does not name an explicit alternative tool or exclusion, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_set_addA

Add/confirm a member of a set-valued slot (many concurrent values, not one NOW value). A scalar there converts to a set on first call — one-way — except number-led scalars ("32", "$1,500"), which are protected: the add parks as a contender (action="contested", settle via memory_fact_resolve). Read with memory_fact_get.

Returns: {action, entity, attribute, member, members_count}.

ParametersJSON Schema
NameRequiredDescriptionDefault
entityYesThe slot's subject.
memberYesThe member to add or re-confirm.
attributeYesThe slot's attribute.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare the safety profile (readOnly=false, destructive=false, idempotent=false, openWorld=false). The description goes well beyond them by disclosing the one-way scalar-to-set conversion, the special protection for number-led scalars, and the contested/contender outcome — non-obvious side effects an agent cannot infer from the schema. It stops short of describing rate limits or failure modes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core purpose, then the scalar-conversion edge case, then the routing hints and return shape. It is dense and jargon-heavy ("parks as a contender"), but every clause carries behavioral information that is not available elsewhere, so little is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so the trailing returns line is largely redundant, but the description covers the operation's key nuance (set semantics, one-way conversion, contested outcome) for a genuinely non-trivial memory tool. It could be more complete about the relationship to memory_set_remove or when to prefer the scalar slot tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it explains that the slot is set-valued (contextualizing entity/attribute) and that a member can be re-confirmed rather than only inserted. The schema itself only offers terse labels ("The slot's subject"), so this framing is genuinely additive.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource (add/confirm a member of a set-valued slot) and immediately disambiguates it from scalar single-value semantics ("many concurrent values, not one NOW value"). It also names the sibling read tool (memory_fact_get) and the resolution tool (memory_fact_resolve), so an agent can place it in the family without opening other schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clearly signals when this tool applies: set-valued slots with many concurrent members. It routes contested adds to memory_fact_resolve and reads to memory_fact_get. It does not explicitly say when NOT to use it (e.g., the scalar alternative memory_fact_set), which keeps it short of a full 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_set_removeA
Destructive

Retract one current set member (audit row kept). Read with memory_fact_get.

Returns: {action, entity, attribute, member, members_count}.

ParametersJSON Schema
NameRequiredDescriptionDefault
entityYesThe slot's subject.
memberYesThe current member to retract.
attributeYesThe slot's attribute.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and readOnlyHint=false, so safety is covered. The description adds real context beyond them: '(audit row kept)' discloses soft-delete/retention semantics, telling the agent the retraction is recoverable and auditable. It does not address idempotency (idempotentHint=false) or what happens on a repeated call.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Very short and front-loaded: the operation and its audit behavior come first. The 'Returns: {...}' line is largely redundant since an output schema exists, which is the only wasted space.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-required-param mutation with annotations and an output schema, the definition covers purpose, retention semantics, and the read path. Gaps are minor: no idempotency note and no statement of what happens when the member is not currently present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so entity/attribute/member are already documented. The phrase 'current set member' adds a mild constraint that the member must be currently present, but no format or error semantics beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: 'Retract one current set member'. The word 'Retract' contrasts implicitly with the sibling memory_set_add, and 'one' bounds the operation. It stops short of naming the add/supersede alternatives outright, so the sibling contrast must be inferred.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The only routing guidance is 'Read with memory_fact_get', which points at the read path but does not say when to retract vs. using memory_supersede, memory_forget, or memory_fact_resolve. Usage is implied by the verb rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_statsA
Read-only

Memory-bank vital signs: store occupancy vs capacity, hit rates, true-drop count, and totals. Use to gauge how much has been remembered or to diagnose why retrieval feels off.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, covering the safety profile for this read-only tool. The description adds only a list of reported metrics, which the output schema already provides, and gives no additional behavioral context such as auth needs, rate limits, or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences: the first front-loads the resource and its metrics, the second gives the use case. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter read-only stats tool with an output schema and comprehensive annotations, the description covers purpose and usage adequately. It could be slightly richer about what the metrics mean or when to prefer other introspection tools, but nothing critical is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so the baseline is 4. The description adds no parameter information, but none is needed because the schema is empty.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource ('Memory-bank vital signs') and enumerates the exact metrics returned (store occupancy vs capacity, hit rates, true-drop count, totals), so an agent knows what the tool does. However, it does not name a sibling or use a clear verb like 'get', so it is clear but not maximally differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: 'Use to gauge how much has been remembered or to diagnose why retrieval feels off.' It does not mention when not to use it or name alternative introspection tools (e.g., memory_agents, memory_toolset), so it gives clear context without exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_storeA
Destructive

Store one durable fact, decision, or observation. Use proactively for anything worth keeping — one claim per call, and decide which kind it is: PERSIST what stays true and will be wanted back; leave task-scoped detail in CONTEXT ONLY; RE-VERIFY a fast-changing value at its source, parking it with memory_fact_set(..., freshness_class="volatile") rather than persisting a stale one; ASK when the claim is ambiguous instead of persisting the guess. Near-duplicates are dropped, not erred (stored=False, reason="below_surprise_threshold"). For canonical NOW use memory_fact_set.

Returns: {stored, surprise, reason, cortex_promoted}.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNoOptional labels, e.g. ["decision", "blocker"].
textYesThe claim to remember.
originNoWho asserted the claim.
sourceNoStable per-project/topic tag for later filtering.agent
episodeNoEpisode handle for attribution.
authorityNoSpeech act: "directive" (an instruction to you) / "quoted" (a doc or third party said it) / "observation"; "auto" infers.auto
distortion_toleranceNo"constraint" = must survive verbatim, pinned in recall; "auto" infers only that.auto

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true and idempotentHint=false, and the description reinforces this by disclosing that near-duplicates are dropped rather than erred, with stored=False and reason='below_surprise_threshold'. It adds real behavioral context beyond the annotations, though it doesn't spell out what a store overwrites or any permission/rate constraints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core purpose in the first sentence and then gives a compact decision procedure. It is dense but every clause carries guidance; the parenthetical return-key detail is slightly redundant given the output schema, which keeps it just short of a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description need not explain return values, and it still summarizes them. For a seven-parameter mutation tool, it covers purpose, routing, dedup behavior, and the volatile-value caveat adequately, though it omits any note on overwrite/permission semantics.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema documents all seven parameters, and the description does not map its PERSIST/RE-VERIFY guidance onto the authority or distortion_tolerance enums. It even references a freshness_class parameter that lives on memory_fact_set, not this schema, so it adds little beyond the structured fields. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Store) and resource (one durable fact, decision, or observation) and immediately distinguishes itself from memory_fact_set, which is named as the destination for canonical NOW. An agent can tell what this tool is for and how it differs from the closest sibling.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use guidance (proactively, one claim per call) and a decision procedure: persist durable claims, leave task-scoped detail in context only, re-verify volatile values, ask when ambiguous. It also names the alternative (memory_fact_set) and the condition that selects it, leaving little to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_supersedeA
Destructive

Mark a stored memory obsolete and record its replacement. The old entry is kept but flagged superseded, so retrieval ranks the correction higher and shows both together.

Use exactly one selector. Missing, retired, or ambiguous targets cause no mutation; search again and resubmit an ID. No similarity fallback.

Returns: {superseded_count, superseded_texts, superseded_ids, new_memory_stored, derived_flagged} — the last being the canonical facts the dream built on the memories just corrected. They are FLAGGED, never rewritten; check each and re-assert the ones that moved. Each row carries has_current_value: false means the slot holds no current fact any more — blast radius worth seeing, but nothing to go re-check. The list is capped, live slots first; derived_flagged_truncated / derived_flagged_total say when a correction reached further than the cap.

ParametersJSON Schema
NameRequiredDescriptionDefault
entry_idNoPositive search/recent ID in this bank; preferred selector.
new_textYesThe replacement claim; stored fresh.
old_textNoUnique exact stored text; omit with entry_id.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations (destructiveHint/readOnlyHint/idempotentHint). It discloses that the old entry is retained-but-flagged, that failed selectors produce no mutation, that there is no similarity fallback, and how derived facts are FLAGGED (never rewritten) with truncation-cap semantics — all material behavior an agent needs before mutating.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads purpose and selector rules before the returns detail; every section is dense with operative content. The Returns block is long and partly overlaps the output schema, but its semantic interpretation (has_current_value, truncation flags) is not pure repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive mutation with an output schema and full annotation coverage, this is essentially complete: purpose, selector rules, failure behavior, and return semantics are all covered. Minor redundancy with the output schema keeps it from a 5.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real semantic value: 'Use exactly one selector,' telling the agent entry_id and old_text are mutually exclusive alternatives and that entry_id is preferred. That constraint is not obvious from the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Mark a stored memory obsolete and record its replacement') and immediately clarifies the distinguishing semantic: the old entry is kept but flagged, so retrieval ranks the correction higher. This separates it from deletion-style siblings like memory_forget without needing to name them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear operating guidance: 'Use exactly one selector,' failure modes (missing/retired/ambiguous targets cause no mutation), and the remediation ('search again and resubmit an ID'). It does not explicitly name which sibling to use instead of this tool (e.g. forget vs supersede vs reinstate), so it stops short of full alternative routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_toolsetA

Adjust YOUR visible toolset, one tier at a time (minimal → core → full; scoped to your credential/writer identity, free, instant). Core adds graph/recall, world facts, lessons, documents; full adds supersede/forget/history, dream and graph-review admin. status reports the ladder. Expand first: clients reject hidden-tool calls.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds behavioral context beyond the annotations: it specifies the tool is free and instant, scoped to credential/writer identity, and that expanding is required because 'clients reject hidden-tool calls.' It does not explicitly state what happens to visible tools when collapsing (e.g., whether they become hidden or inaccessible), which would be useful for a mutation tool. Annotations indicate non-read-only and non-destructive, which is consistent, but the description adds value without fully covering the consequences of collapse.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: it starts with the core purpose, then lists tier contents, then adds critical usage guidance. Every sentence earns its place without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (managing visible toolset tiers) and the presence of an output schema (which likely reports status or tier changes), the description is largely complete for an agent to invoke correctly. It covers actions, tier contents, and the necessity to expand first. It could be improved by noting the exact effect of 'collapse' (e.g., whether tools are removed or just hidden) to fully inform the agent, but it is sufficient for most use cases.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has a single enum parameter 'action' with values expand/collapse/status, but the schema description coverage is 0%. The description explains the expand and collapse actions through the tier ladder and mentions that 'status' reports the ladder. However, it doesn't add syntax or format details beyond what's implied by the enum. With only one parameter and baseline 4 for zero params, but here there is 1 parameter with an enum and the description provides some meaning for each action, a 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Adjust YOUR visible toolset, one tier at a time'), names the spectrum (minimal → core → full), and clearly delineates what each tier adds (core adds graph/recall, world facts, lessons, documents; full adds supersede/forget/history, dream and graph-review admin). This is a precise verb+resource statement that differentiates it from siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use it ('Expand first: clients reject hidden-tool calls'), what the tiers are, and that it is 'scoped to your credential/writer identity, free, instant.' It provides clear operational guidance and a prerequisite (expand before calling hidden tools).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_world_setA
Destructive

Assert a canonical WORLD fact — sourced EXTERNAL knowledge (versions, prices, who-holds-a-role, research findings), kept separate from user/project facts. Route verified web/docs findings here, with the citation. A newer source supersedes an older value at the same slot.

Returns: {action: inserted|confirmed|superseded|rejected, ...record}.

ParametersJSON Schema
NameRequiredDescriptionDefault
valueYesThe externally-sourced value.
entityYesThe slot's subject.
attributeYesThe slot's attribute.
confidenceNoSource confidence, 0..1.
source_urlNohttp(s) citation URL; any other scheme is rejected.
content_hashNoHash of the fetched source, for change detection.
retrieved_atNoEpoch seconds the source was fetched.
source_quoteNoThe 1-2 sentences the claim was extracted from.
freshness_classNoTrust decay applied at read time: ``evergreen`` never decays, ``slow`` is months, ``volatile`` is weeks.volatile

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, readOnlyHint=false, idempotentHint=false and openWorldHint=false, so the safety profile is covered. The description adds genuine behavioral context beyond that: the supersession model ('A newer source supersedes an older value at the same slot'), telling the agent what happens to a prior value on a conflicting write.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core purpose and keeps the description compact. The 'Returns:' sentence is redundant because an output schema already exists, but the waste is minor and the overall structure is efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists, so return values need not be explained, and all parameters are fully documented in the schema. The description still covers the domain distinction, the routing condition, and the supersession behavior, making it adequate for a 9-parameter mutation tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 9 parameters (including confidence semantics, URL scheme rejection, and freshness_class decay). The description adds only the vague phrase 'with the citation', which does not enrich parameter meaning beyond the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Assert a canonical WORLD fact') and enumerates the domain (versions, prices, who-holds-a-role, research findings). It scopes itself apart from user/project facts, but does not name the sibling (e.g. memory_fact_set) that handles those, so differentiation is implicit rather than explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear positive trigger ('Route verified web/docs findings here, with the citation') and implies the complement by keeping world facts separate from user/project facts. However, it never names an alternative tool or states a when-not condition, leaving the routing rule partly to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 11 tool updatesv0.16.0
    • Addedmemory_agents
    • Changedmemory_consolidate7 fields changed
      • addedInput schema / properties / entry_ids
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "type": "integer"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Positive IDs in this bank; prefer over replaces.",
        +  "title": "Entry Ids"
        +}
      • addedInput schema / properties / replaces / anyOf
        Added value: +[
        +  {
        +    "items": {
        +      "type": "string"
        +    },
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / replaces / default
        Added value: +null
      • changedInput schema / properties / replaces / description
        Previous value: -"The memories being folded in; each is matched by exact text or close paraphrase."New value: +"Unique exact stored texts; omit with entry_ids."
      • removedInput schema / properties / replaces / items
        Removed value: -{
        -  "type": "string"
        -}
      • removedInput schema / properties / replaces / type
        Removed value: -"array"
      • changedInput schema / required
        Previous value: -[
        -  "replaces",
        -  "new_text"
        -]New value: +[
        +  "new_text"
        +]
    • Changedmemory_dream2 fields changed
      • addedInput schema / properties / commit_token
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "commit: token returned by pull.",
        +  "title": "Commit Token"
        +}
      • changedInput schema / properties / cursor / description
        Previous value: -"commit: the newest pulled timestamp. Required for that action."New value: +"Deprecated; use commit_token."
    • Changedmemory_fact_get1 field changed
      • addedInput schema / properties / verbose
        Added value: +{
        +  "default": false,
        +  "description": "Full record; the default drops bookkeeping keys.",
        +  "title": "Verbose",
        +  "type": "boolean"
        +}
    • Changedmemory_forget6 fields changed
      • addedInput schema / properties / confirm_bulk
        Added value: +{
        +  "default": false,
        +  "description": "memory scope: allow over memory.delete_confirm_threshold (20) matches; else refused, reporting would_delete.",
        +  "title": "Confirm Bulk",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / episode / description
        Previous value: -"memory scope: delete entries stamped with this episode id."New value: +"memory scope: entries stamped with this episode id."
      • changedInput schema / properties / source / description
        Previous value: -"memory scope: delete entries with this source tag."New value: +"memory scope: entries with this source tag."
      • changedInput schema / properties / substring / description
        Previous value: -"memory scope: delete entries whose text contains this."New value: +"memory scope: entries whose text contains this."
      • changedInput schema / properties / tag / description
        Previous value: -"memory scope: delete entries carrying this tag."New value: +"memory scope: entries carrying this tag."
      • changedInput schema / properties / text / description
        Previous value: -"memory scope: delete entries whose text matches this exactly."New value: +"memory scope: entries with exactly this text."
    • Changedmemory_graph_review6 fields changed
      • changedInput schema / properties / dst / description
        Previous value: -"relate/dismiss_pair: the second entity. dismiss_slot_pair: an \"entity|attribute\" key from the deep response."New value: +"relate/dismiss_pair/dismiss_slot_pair: the second entity or key."
      • changedInput schema / properties / proposal_id / description
        Previous value: -"Id actions: the one proposal to settle."New value: +"Id actions: the proposal to settle."
      • changedInput schema / properties / proposal_ids / description
        Previous value: -"Id actions: settle many proposals in one call, instead of ``proposal_id``."New value: +"Id actions: many proposals at once."
      • changedInput schema / properties / relation / description
        Previous value: -"relate: the edge relation to write, from the graph vocabulary."New value: +"relate: edge relation to write (graph vocabulary)."
      • changedInput schema / properties / scope / description
        Previous value: -"list: keep only findings of this kind."New value: +"list: keep only analyzer findings whose entities carry this memory source (Atlas project); queued proposals always list; omit or \"all\" for everything. Not a finding kind."
      • changedInput schema / properties / store / description
        Previous value: -"dismiss_slot_pair / restore_slot: which store the key belongs to — \"lesson\" or \"world\"."New value: +"dismiss_slot_pair/restore_slot: \"lesson\" or \"world\"."
    • Addedmemory_message
    • Changedmemory_outcome1 field changed
      • addedInput schema / properties / used_ids
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "type": "integer"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Ids of the search hits you actually used, e.g. [1421, 903] — the relevance label only you can write. At most 50. Credits this session's searches from the last hour; log before that lapses.",
        +  "title": "Used Ids"
        +}
    • Addedmemory_reinstate
    • Changedmemory_search3 fields changed
      • changedInput schema / properties / bm25 / description
        Previous value: -"Tri-state override for keyword scoring, which aids exact-term queries; None follows config."New value: +"Tri-state override for keyword scoring (exact terms); None follows config."
      • changedInput schema / properties / top_k / description
        Previous value: -"Max entries returned."New value: +"Max entries; caps cortex facts at min(5, top_k)."
      • changedInput schema / properties / verbose / description
        Previous value: -"Full per-entry metadata; the default is compact ``{id, text, source, tags, score}`` plus supersession when set."New value: +"Full per-entry metadata and untruncated ``text``; default entries are compact."
    • Changedmemory_supersede6 fields changed
      • addedInput schema / properties / entry_id
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Positive search/recent ID in this bank; preferred selector.",
        +  "title": "Entry Id"
        +}
      • addedInput schema / properties / old_text / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / old_text / default
        Added value: +null
      • changedInput schema / properties / old_text / description
        Previous value: -"The memory now obsolete. Matched exact-text first, then by nearest embedding — a close paraphrase works."New value: +"Unique exact stored text; omit with entry_id."
      • removedInput schema / properties / old_text / type
        Removed value: -"string"
      • changedInput schema / required
        Previous value: -[
        -  "old_text",
        -  "new_text"
        -]New value: +[
        +  "new_text"
        +]
  2. 33 tool updatesv0.15.0
    • Changeddocument_ingest2 fields changed
      • addedInput schema / properties / path / description
        Added value: +"Path to a .txt / .md / .pdf file, resolved on the SERVER's filesystem — with the Docker daemon, a path visible inside the container (e.g. a mounted volume), not a host path."
      • addedInput schema / properties / source / description
        Added value: +"Source tag for the chunks; defaults to the filename."
    • Changeddocument_search2 fields changed
      • addedInput schema / properties / query / description
        Added value: +"Natural-language description of the passage wanted."
      • addedInput schema / properties / top_k / description
        Added value: +"Max chunks returned."
    • Changedmemory_alias2 fields changed
      • addedInput schema / properties / alias / description
        Added value: +"The alternative name."
      • addedInput schema / properties / entity / description
        Added value: +"The canonical entity to bind onto."
    • Changedmemory_consolidate4 fields changed
      • addedInput schema / properties / new_text / description
        Added value: +"The canonical note that replaces them."
      • addedInput schema / properties / replaces / description
        Added value: +"The memories being folded in; each is matched by exact text or close paraphrase."
      • addedInput schema / properties / source / description
        Added value: +"Source tag for the new note."
      • addedInput schema / properties / tags / description
        Added value: +"Labels for the new note."
    • Changedmemory_consolidation_candidates8 fields changed
      • addedInput schema / properties / episode / description
        Added value: +"Session-driven anchor: cluster within this episode id."
      • addedInput schema / properties / max_clusters / description
        Added value: +"Max clusters returned."
      • addedInput schema / properties / min_cluster_size / description
        Added value: +"Drop clusters with fewer members than this."
      • addedInput schema / properties / min_cohesion / description
        Added value: +"Minimum intra-cluster cosine — raise it to flag only near-duplicates."
      • addedInput schema / properties / query / description
        Added value: +"Topic-driven anchor: cluster memories near this description."
      • addedInput schema / properties / sources / description
        Added value: +"Consider only entries with one of these source tags."
      • addedInput schema / properties / tags / description
        Added value: +"Consider only entries carrying one of these tags."
      • addedInput schema / properties / top_k / description
        Added value: +"How many candidate entries to cluster over."
    • Changedmemory_dream5 fields changed
      • addedInput schema / properties / apply / description
        Added value: +"deep: True writes the consolidation (graph tables are snapshotted first); the default is a dry run."
      • addedInput schema / properties / cursor / description
        Added value: +"commit: the newest pulled timestamp. Required for that action."
      • addedInput schema / properties / limit / description
        Added value: +"pull/run: how many memories to process (pull defaults to 40). runs: how many passes to list (defaults to 10)."
      • addedInput schema / properties / run_id / description
        Added value: +"rollback: which pass to revert; defaults to the newest committed pass."
      • addedInput schema / properties / snippets / description
        Added value: +"deep: False omits the evidence snippets."
    • Changedmemory_episode_end1 field changed
      • addedInput schema / properties / episode
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Your session handle — pops only within your own session's subtree; the session root is never closed.",
        +  "title": "Episode"
        +}
    • Changedmemory_episode_start3 fields changed
      • addedInput schema / properties / episode
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Your session handle (from the session-start briefing) — anchors the sub-episode to YOUR session when several run concurrently.",
        +  "title": "Episode"
        +}
      • addedInput schema / properties / hint / description
        Added value: +"Optional note on what the task is about."
      • addedInput schema / properties / title / description
        Added value: +"Short name for the task, used in later recaps."
    • Changedmemory_episode_summary1 field changed
      • addedInput schema / properties / id / description
        Added value: +"An episode id, as it appears on search/recent results."
    • Changedmemory_fact_get2 fields changed
      • addedInput schema / properties / attribute / description
        Added value: +"The slot's attribute."
      • addedInput schema / properties / entity / description
        Added value: +"The slot's subject; the (entity, attribute) match is case- and separator-insensitive."
    • Changedmemory_fact_resolve3 fields changed
      • addedInput schema / properties / accept / description
        Added value: +"True adopts the parked contender as the new current value (the old value is kept as history); False discards the contender and keeps the current value."
      • addedInput schema / properties / attribute / description
        Added value: +"The contested slot's attribute."
      • addedInput schema / properties / entity / description
        Added value: +"The contested slot's subject."
    • Changedmemory_fact_set9 fields changed
      • addedInput schema / properties / attribute / description
        Added value: +"The slot's attribute."
      • addedInput schema / properties / authority
        Added value: +{
        +  "default": "auto",
        +  "description": "Speech act of the source: \"directive\" / \"quoted\" (doc or third party) / \"observation\"; \"auto\" infers.",
        +  "enum": [
        +    "auto",
        +    "directive",
        +    "observation",
        +    "quoted"
        +  ],
        +  "title": "Authority",
        +  "type": "string"
        +}
      • addedInput schema / properties / confidence / description
        Added value: +"How sure you are, 0..1."
      • addedInput schema / properties / distortion_tolerance
        Added value: +{
        +  "default": "auto",
        +  "description": "\"constraint\" = verbatim, pinned in recall; \"auto\" infers only that.",
        +  "enum": [
        +    "auto",
        +    "constraint",
        +    "procedural",
        +    "belief",
        +    "preference",
        +    "episodic"
        +  ],
        +  "title": "Distortion Tolerance",
        +  "type": "string"
        +}
      • addedInput schema / properties / entity / description
        Added value: +"The slot's subject; the (entity, attribute) match is case- and separator-insensitive."
      • addedInput schema / properties / episode / description
        Added value: +"Episode handle for attribution."
      • addedInput schema / properties / freshness_class / description
        Added value: +"How fast the value rots. \"auto\" infers the decay rate from the entity kind."
      • addedInput schema / properties / origin / description
        Added value: +"Who asserted it: \"user\" = the human told you; otherwise \"action\"/\"agent\". Omitted records \"agent\"."
      • addedInput schema / properties / value / description
        Added value: +"The value that is canonical NOW."
    • Changedmemory_forget8 fields changed
      • addedInput schema / properties / attribute / description
        Added value: +"fact/world scope: one slot's attribute; omit to purge the whole entity. lesson scope: the aspect."
      • addedInput schema / properties / entity / description
        Added value: +"fact/world scope: the slot's subject. lesson scope: the task."
      • addedInput schema / properties / episode / description
        Added value: +"memory scope: delete entries stamped with this episode id."
      • addedInput schema / properties / scope / description
        Added value: +"Which store to delete from."
      • addedInput schema / properties / source / description
        Added value: +"memory scope: delete entries with this source tag."
      • addedInput schema / properties / substring / description
        Added value: +"memory scope: delete entries whose text contains this."
      • addedInput schema / properties / tag / description
        Added value: +"memory scope: delete entries carrying this tag."
      • addedInput schema / properties / text / description
        Added value: +"memory scope: delete entries whose text matches this exactly."
    • Changedmemory_get1 field changed
      • addedInput schema / properties / entry_id / description
        Added value: +"A memory id, as returned by search results or by a fact's ``source_entries``."
    • Changedmemory_graph5 fields changed
      • addedInput schema / properties / depth / description
        Added value: +"Hops from the root. Max 3."
      • addedInput schema / properties / entity / description
        Added value: +"The root entity to read out from."
      • addedInput schema / properties / include_facts / description
        Added value: +"False omits each node's canonical facts."
      • addedInput schema / properties / relation_filter / description
        Added value: +"Keep only edges whose relation contains this substring."
      • addedInput schema / properties / to / description
        Added value: +"Also return the shortest path from ``entity`` to this entity under ``paths``; its nodes are folded into the neighborhood."
    • Changedmemory_graph_relate7 fields changed
      • addedInput schema / properties / confidence / description
        Added value: +"How sure you are, 0..1."
      • addedInput schema / properties / dst / description
        Added value: +"The object entity."
      • addedInput schema / properties / dst_type / description
        Added value: +"Entity kind for ``dst``: set when the node is created, or filled in on an existing node that has none."
      • addedInput schema / properties / origin / description
        Added value: +"Who asserted the edge."
      • addedInput schema / properties / relation / description
        Added value: +"From the closed registry: ``depends-on``, ``part-of``, ``runs-on``, ``hosts``, ``uses``, ``configures``, ``stores-data-in``, ``related-to``. Separator variants normalise; an unknown name is rejected WITH the closest matches."
      • addedInput schema / properties / src / description
        Added value: +"The subject entity."
      • addedInput schema / properties / src_type / description
        Added value: +"Entity kind for ``src``: set when the node is created, or filled in on an existing node that has none."
    • Changedmemory_graph_review9 fields changed
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "list",
        -  "propose",
        -  "relate",
        -  "dismiss_pair",
        -  "dismiss_slot_pair",
        -  "accept_link",
        -  "reject_link",
        -  "accept_merge",
        -  "accept_junk",
        -  "reject_entity"
        -]New value: +[
        +  "list",
        +  "propose",
        +  "relate",
        +  "dismiss_pair",
        +  "dismiss_slot_pair",
        +  "restore_slot",
        +  "accept_link",
        +  "reject_link",
        +  "accept_merge",
        +  "accept_junk",
        +  "reject_entity"
        +]
      • addedInput schema / properties / dst / description
        Added value: +"relate/dismiss_pair: the second entity. dismiss_slot_pair: an \"entity|attribute\" key from the deep response."
      • addedInput schema / properties / proposal_id / description
        Added value: +"Id actions: the one proposal to settle."
      • addedInput schema / properties / proposal_ids / description
        Added value: +"Id actions: settle many proposals in one call, instead of ``proposal_id``."
      • addedInput schema / properties / proposals / description
        Added value: +"propose: ``[{src, relation, dst, similarity?, rationale?}]``."
      • addedInput schema / properties / relation / description
        Added value: +"relate: the edge relation to write, from the graph vocabulary."
      • addedInput schema / properties / scope / description
        Added value: +"list: keep only findings of this kind."
      • addedInput schema / properties / src / description
        Added value: +"relate/dismiss_pair: the first entity. dismiss_slot_pair: an \"entity|attribute\" key from the deep response; restore_slot: retired key or entity."
      • addedInput schema / properties / store / description
        Added value: +"dismiss_slot_pair / restore_slot: which store the key belongs to — \"lesson\" or \"world\"."
    • Changedmemory_graph_unrelate3 fields changed
      • addedInput schema / properties / dst / description
        Added value: +"The edge's object entity."
      • addedInput schema / properties / relation / description
        Added value: +"The edge's relation."
      • addedInput schema / properties / src / description
        Added value: +"The edge's subject entity."
    • Changedmemory_history3 fields changed
      • addedInput schema / properties / as_of / description
        Added value: +"Slot mode only: ISO-8601 or epoch seconds; returns only versions written by then."
      • addedInput schema / properties / attribute / description
        Added value: +"The fact slot to read; omit for chain mode."
      • addedInput schema / properties / entity / description
        Added value: +"The subject whose past to read."
    • Changedmemory_lesson_search3 fields changed
      • addedInput schema / properties / query / description
        Added value: +"The task at hand, described the way it would have been logged."
      • addedInput schema / properties / top_k / description
        Added value: +"Max entries returned."
      • addedInput schema / properties / verbose / description
        Added value: +"Full provenance metadata; default entries are compact."
    • Changedmemory_outcome6 fields changed
      • addedInput schema / properties / about / description
        Added value: +"The tool or approach concerned; aids traversal."
      • addedInput schema / properties / detail / description
        Added value: +"What worked, or what the dead-end was."
      • addedInput schema / properties / episode / description
        Added value: +"Episode handle for attribution."
      • addedInput schema / properties / outcome / description
        Added value: +"What happened."
      • addedInput schema / properties / polarity / description
        Added value: +"\"+\" do-this or \"-\" avoid; usually omit — it is inferred from the outcome."
      • addedInput schema / properties / task / description
        Added value: +"Kind of task, in stable wording (\"deploy engine to host\") so signals for the same work group."
    • Changedmemory_recall4 fields changed
      • addedInput schema / properties / hops / description
        Added value: +"Max graph hops. Clamped to 1..5."
      • addedInput schema / properties / query / description
        Added value: +"Natural-language relational question; its top hits seed the graph walk."
      • addedInput schema / properties / top_k / description
        Added value: +"Bounds only the SEED search (the initial hits that name the walk's start entities), not the result, which is capped separately; up to 3 ``texts`` slots go to seed hits."
      • addedInput schema / properties / verbose / description
        Added value: +"Full fact/edge provenance and untruncated texts. Default facts are ``{attribute, value}``, edges ``{src, relation, dst}``, texts truncated to a preview."
    • Changedmemory_recent5 fields changed
      • addedInput schema / properties / episodes / description
        Added value: +"Keep only entries stamped with one of these episode ids."
      • addedInput schema / properties / n / description
        Added value: +"How many entries to return."
      • addedInput schema / properties / sources / description
        Added value: +"Keep only entries with one of these source tags."
      • addedInput schema / properties / tags / description
        Added value: +"Keep only entries carrying one of these tags."
      • addedInput schema / properties / verbose / description
        Added value: +"Full per-entry metadata; default entries are compact."
    • Changedmemory_reinforce1 field changed
      • addedInput schema / properties / entry_id / description
        Added value: +"The id of the memory to strengthen."
    • Changedmemory_relation_define6 fields changed
      • addedInput schema / properties / description / description
        Added value: +"What the relation means, for later readers."
      • addedInput schema / properties / dst_type / description
        Added value: +"Soft entity-kind expectation for the object; a mismatch warns, never rejects."
      • addedInput schema / properties / inverse_of / description
        Added value: +"Pair with an existing relation, as ``runs-on`` is the inverse of ``hosts``."
      • addedInput schema / properties / name / description
        Added value: +"The new relation name, hyphenated like the builtins."
      • addedInput schema / properties / src_type / description
        Added value: +"Soft entity-kind expectation for the subject; a mismatch warns, never rejects."
      • addedInput schema / properties / transitive / description
        Added value: +"True closes the relation transitively, so chained edges arrive pre-derived."
    • Changedmemory_search12 fields changed
      • addedInput schema / properties / bands / description
        Added value: +"Keep only entries held by one of these bands."
      • addedInput schema / properties / bm25 / description
        Added value: +"Tri-state override for keyword scoring, which aids exact-term queries; None follows config."
      • addedInput schema / properties / disable_recency_boost / description
        Added value: +"True to score without the recency bias."
      • addedInput schema / properties / episodes / description
        Added value: +"Keep only entries stamped with one of these episode ids."
      • addedInput schema / properties / explain / description
        Added value: +"Attach a ranking ``trace``; implies verbose."
      • addedInput schema / properties / min_score / description
        Added value: +"Override the 0.25 relevance floor."
      • addedInput schema / properties / query / description
        Added value: +"Natural-language description; specific beats vague."
      • addedInput schema / properties / rerank / description
        Added value: +"Tri-state override for cross-encoder reranking (~200ms); None follows config."
      • addedInput schema / properties / sources / description
        Added value: +"Keep only entries with one of these source tags."
      • addedInput schema / properties / tags / description
        Added value: +"Keep only entries carrying one of these tags."
      • addedInput schema / properties / top_k / description
        Added value: +"Max entries returned."
      • addedInput schema / properties / verbose / description
        Added value: +"Full per-entry metadata; the default is compact ``{id, text, source, tags, score}`` plus supersession when set."
    • Changedmemory_session_title2 fields changed
      • addedInput schema / properties / episode
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Your session handle — names that session's root directly (the identity a hook-registered client has).",
        +  "title": "Episode"
        +}
      • addedInput schema / properties / title / description
        Added value: +"What this session is about, e.g. \"Pseudolife-MCP\" or \"auth-refactor\"."
    • Changedmemory_set_add3 fields changed
      • addedInput schema / properties / attribute / description
        Added value: +"The slot's attribute."
      • addedInput schema / properties / entity / description
        Added value: +"The slot's subject."
      • addedInput schema / properties / member / description
        Added value: +"The member to add or re-confirm."
    • Changedmemory_set_remove3 fields changed
      • addedInput schema / properties / attribute / description
        Added value: +"The slot's attribute."
      • addedInput schema / properties / entity / description
        Added value: +"The slot's subject."
      • addedInput schema / properties / member / description
        Added value: +"The current member to retract."
    • Changedmemory_store7 fields changed
      • addedInput schema / properties / authority
        Added value: +{
        +  "default": "auto",
        +  "description": "Speech act: \"directive\" (an instruction to you) / \"quoted\" (a doc or third party said it) / \"observation\"; \"auto\" infers.",
        +  "enum": [
        +    "auto",
        +    "directive",
        +    "observation",
        +    "quoted"
        +  ],
        +  "title": "Authority",
        +  "type": "string"
        +}
      • addedInput schema / properties / distortion_tolerance
        Added value: +{
        +  "default": "auto",
        +  "description": "\"constraint\" = must survive verbatim, pinned in recall; \"auto\" infers only that.",
        +  "enum": [
        +    "auto",
        +    "constraint",
        +    "procedural",
        +    "belief",
        +    "preference",
        +    "episodic"
        +  ],
        +  "title": "Distortion Tolerance",
        +  "type": "string"
        +}
      • addedInput schema / properties / episode / description
        Added value: +"Episode handle for attribution."
      • addedInput schema / properties / origin / description
        Added value: +"Who asserted the claim."
      • addedInput schema / properties / source / description
        Added value: +"Stable per-project/topic tag for later filtering."
      • addedInput schema / properties / tags / description
        Added value: +"Optional labels, e.g. [\"decision\", \"blocker\"]."
      • addedInput schema / properties / text / description
        Added value: +"The claim to remember."
    • Changedmemory_supersede2 fields changed
      • addedInput schema / properties / new_text / description
        Added value: +"The replacement claim; stored fresh."
      • addedInput schema / properties / old_text / description
        Added value: +"The memory now obsolete. Matched exact-text first, then by nearest embedding — a close paraphrase works."
    • Changedmemory_world_search3 fields changed
      • addedInput schema / properties / query / description
        Added value: +"Natural-language description of the external fact needed."
      • addedInput schema / properties / top_k / description
        Added value: +"Max entries returned."
      • addedInput schema / properties / verbose / description
        Added value: +"Full provenance metadata; default entries are compact."
    • Changedmemory_world_set9 fields changed
      • addedInput schema / properties / attribute / description
        Added value: +"The slot's attribute."
      • addedInput schema / properties / confidence / description
        Added value: +"Source confidence, 0..1."
      • addedInput schema / properties / content_hash / description
        Added value: +"Hash of the fetched source, for change detection."
      • addedInput schema / properties / entity / description
        Added value: +"The slot's subject."
      • addedInput schema / properties / freshness_class / description
        Added value: +"Trust decay applied at read time: ``evergreen`` never decays, ``slow`` is months, ``volatile`` is weeks."
      • addedInput schema / properties / retrieved_at / description
        Added value: +"Epoch seconds the source was fetched."
      • addedInput schema / properties / source_quote / description
        Added value: +"The 1-2 sentences the claim was extracted from."
      • addedInput schema / properties / source_url / description
        Added value: +"http(s) citation URL; any other scheme is rejected."
      • addedInput schema / properties / value / description
        Added value: +"The externally-sourced value."
  3. 3 tool updatesv0.14.0
    • Changedmemory_dream2 fields changed
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "status",
        -  "pull",
        -  "commit",
        -  "run",
        -  "deep"
        -]New value: +[
        +  "status",
        +  "pull",
        +  "commit",
        +  "run",
        +  "deep",
        +  "runs",
        +  "rollback"
        +]
      • addedInput schema / properties / run_id
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Run Id"
        +}
    • Changedmemory_graph_review3 fields changed
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "list",
        -  "propose",
        -  "dismiss_pair",
        -  "dismiss_slot_pair",
        -  "accept_link",
        -  "reject_link",
        -  "accept_merge",
        -  "accept_junk",
        -  "reject_entity"
        -]New value: +[
        +  "list",
        +  "propose",
        +  "relate",
        +  "dismiss_pair",
        +  "dismiss_slot_pair",
        +  "accept_link",
        +  "reject_link",
        +  "accept_merge",
        +  "accept_junk",
        +  "reject_entity"
        +]
      • addedInput schema / properties / proposal_ids
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "type": "integer"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Proposal Ids"
        +}
      • addedInput schema / properties / relation
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Relation"
        +}
    • Changedmemory_history1 field changed
      • addedInput schema / properties / as_of
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "number"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "As Of"
        +}
  4. 35 tool updatesv0.11.0
    • First observeddocument_ingest
    • First observeddocument_search
    • First observedmemory_alias
    • First observedmemory_consolidate
    • First observedmemory_consolidation_candidates
    • First observedmemory_dream
    • First observedmemory_episode_end
    • First observedmemory_episode_start
    • First observedmemory_episode_summary
    • First observedmemory_fact_get
    • First observedmemory_fact_resolve
    • First observedmemory_fact_set
    • First observedmemory_forget
    • First observedmemory_get
    • First observedmemory_graph
    • First observedmemory_graph_relate
    • First observedmemory_graph_review
    • First observedmemory_graph_unrelate
    • First observedmemory_history
    • First observedmemory_lesson_search
    • First observedmemory_outcome
    • First observedmemory_recall
    • First observedmemory_recent
    • First observedmemory_reinforce
    • First observedmemory_relation_define
    • First observedmemory_search
    • First observedmemory_session_title
    • First observedmemory_set_add
    • First observedmemory_set_remove
    • First observedmemory_stats
    • First observedmemory_store
    • First observedmemory_supersede
    • First observedmemory_toolset
    • First observedmemory_world_search
    • First observedmemory_world_set

TDQS

A3.5/5.0

Scored across 38 tools

Disambiguation3/5

There is a dense cluster of overlapping retrieval tools (memory_search, memory_recall, memory_lesson_search, memory_world_search, memory_recent, document_search) and several write paths (memory_store, memory_fact_set, memory_set_add, memory_world_set, memory_supersede, memory_consolidate). The extremely detailed descriptions do a lot to separate them, but the boundaries are subtle enough that an agent could easily misselect.

Naming Consistency4/5

Names are uniformly snake_case and grouped under coherent prefixes (memory_fact_*, memory_set_*, memory_graph_*, memory_episode_*, memory_world_*), which makes the surface navigable. Minor deviations exist: some are verb-led (memory_search) while others are bare nouns (memory_dream, memory_alias, memory_graph), and document_* breaks the memory_ prefix.

Tool Count3/5

38 tools is heavy and well above the comfortable band, though the domain (memory, facts, sets, graph, world, lessons, documents, episodes, messaging, dreaming) is genuinely broad. The built-in memory_toolset tiering (minimal/core/full) mitigates this by letting agents hide unused tools, keeping it borderline rather than clearly excessive.

Completeness5/5

Coverage is close to exhaustive: store/search/get/supersede/forget/reinstate/reinforce for memories, full fact-slot and set lifecycle, world facts, lessons, graph relate/unrelate/alias, consolidation and dream/review workflows, episodes, and document ingestion. Lifecycle operations and administrative escape hatches are all present with no obvious dead ends.

Maintenance

ActivityActive
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides long-term memory capabilities for Claude through persistent storage and full-text search of context across conversations. Enables storing, searching, and managing memories organized by categories like facts, preferences, projects, and goals.
    11 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides persistent cross-session memory for Claude Code, enabling it to remember user preferences, decisions, and project context across new sessions.
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    Persistent memory management for Claude Code, allowing agents to store and retrieve user preferences, environment notes, and skills across sessions.
    2 npm
    5
    MIT