CPersona
Officialcpersona
MCP-Speicherserver
Verleihen Sie Claude ein persistentes Gedächtnis über Sitzungen hinweg. Einzelne SQLite-Datei. 21 Tools. Keine LLM-Abhängigkeit.
Schnellstart · Funktionen · Architektur · Alle Tools · Zenn-Buch (JP)
Eigenständiges Repository — Dies ist die eigenständige Version zur Verwendung mit Claude Desktop, Claude Code und jedem MCP-Client. Wenn Sie ein ClotoCore-Benutzer sind, verwenden Sie stattdessen die Version in cloto-mcp-servers.
Das Problem
Claude vergisst alles zwischen den Sitzungen. Jedes Gespräch beginnt bei Null — kein Kontext über Ihr Projekt, Ihre Vorlieben oder das, was Sie gestern besprochen haben.
cpersona behebt dies. Es ist ein MCP-Server, der Erinnerungen in einer lokalen SQLite-Datei speichert und sie durch hybride Suche abruft. Claude erinnert sich an Sie.
Related MCP server: mcp-memory-graph
Schnellstart
Voraussetzungen: Python 3.10+, Git
1. cpersona installieren
git clone https://github.com/Cloto-dev/cpersona.git
cd cpersona
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
# source .venv/bin/activate
pip install .2. Embedding-Server einrichten (Empfohlen)
Die hybride Suche von cpersona funktioniert am besten mit einem Embedding-Server für Vektorähnlichkeit. Wir empfehlen die Verwendung von cloto-mcp-servers/embedding mit dem Modell jina-v5-nano (33M Parameter, 768d, läuft lokal auf der CPU):
git clone https://github.com/Cloto-dev/cloto-mcp-servers.git
cd cloto-mcp-servers/servers
pip install ./embeddingOhne einen Embedding-Server greift cpersona nur auf FTS5 + Schlüsselwortsuche zurück. Die Vektorsuche (die stärkste Abrufebene) wird deaktiviert.
3. Ihren MCP-Client konfigurieren
Claude Desktop — fügen Sie dies zu claude_desktop_config.json hinzu:
{
"mcpServers": {
"embedding": {
"command": "/path/to/.venv/bin/python",
"args": ["/path/to/servers/embedding/server.py"],
"env": {
"EMBEDDING_PROVIDER": "onnx_jina_v5_nano",
"EMBEDDING_HTTP_PORT": "8401"
}
},
"cpersona": {
"command": "/path/to/.venv/bin/python",
"args": ["/path/to/cpersona/server.py"],
"env": {
"CPERSONA_DB_PATH": "/home/you/.claude/cpersona.db",
"CPERSONA_EMBEDDING_MODE": "http",
"CPERSONA_EMBEDDING_URL": "http://127.0.0.1:8401/embed"
}
}
}
}Windows: verwenden Sie
.venv/Scripts/python.exeundC:/Users/you/.claude/cpersona.db
Claude Code:
claude mcp add-json embedding '{"type":"stdio","command":"/path/to/.venv/bin/python","args":["/path/to/servers/embedding/server.py"],"env":{"EMBEDDING_PROVIDER":"onnx_jina_v5_nano","EMBEDDING_HTTP_PORT":"8401"}}' -s user
claude mcp add-json cpersona '{"type":"stdio","command":"/path/to/.venv/bin/python","args":["/path/to/cpersona/server.py"],"env":{"CPERSONA_DB_PATH":"/home/you/.claude/cpersona.db","CPERSONA_EMBEDDING_MODE":"http","CPERSONA_EMBEDDING_URL":"http://127.0.0.1:8401/embed"}}' -s userDas war's. Claude verfügt nun über ein persistentes Gedächtnis. Bitten Sie es, etwas zu store (speichern) und in einer späteren Sitzung wieder recall (abzurufen).
Funktionen
Hybride Suche — Drei unabhängige Abrufstrategien laufen parallel und führen die Ergebnisse mittels Reciprocal Rank Fusion (RRF) zusammen:
Ebene | Methode | Stärke |
Vektor | Kosinus-Ähnlichkeit (jina-v5-nano, 768d) | Semantische Bedeutung |
FTS5 | SQLite Volltextsuche mit Trigram-Tokenizer | Exakte Begriffe, Namen, IDs |
Schlüsselwort | Fallback-Musterabgleich | Grenzfälle, Teilübereinstimmungen |
Speichertypen:
Deklaratives Gedächtnis — Einzelne Fakten, Entscheidungen, Anweisungen, gespeichert via
storeEpisodisches Gedächtnis — Gesprächszusammenfassungen, archiviert via
archive_episodeProfil-Gedächtnis — Angesammelte Benutzer-/Projektattribute via
update_profile
Konfidenzbewertung — Jede abgerufene Erinnerung erhält einen Konfidenzwert, der Folgendes kombiniert:
Kosinus-Ähnlichkeit (semantische Relevanz)
Dynamischer Zeitverfall (passt sich dem Zeitbereich des Korpus an — ein 1 Jahr altes Korpus und ein 1 Tag altes Korpus verwenden unterschiedliche Zerfallskurven)
Abruf-Boost (häufig nützliche Erinnerungen tauchen leichter auf, mit natürlichem Ausblenden)
Abschlussfaktor (gelöste Themen verfallen schneller)
Keine LLM-Abhängigkeit — cpersona ist ein reiner Datenserver. Er ruft intern niemals ein LLM auf. Die gesamte Zusammenfassung und Extraktion wird vom aufrufenden Agenten durchgeführt. Das bedeutet null API-Kosten durch cpersona selbst, deterministisches Verhalten und keine versteckte Latenz.
Zusätzliche Funktionen:
Agenten-Namensraum-Isolierung — mehrere Agenten teilen sich eine Datenbank ohne gegenseitige Störung
Hintergrund-Task-Warteschlange — DB-persistente, absturzsichere asynchrone Verarbeitung
JSONL-Export/Import — volle Speicherportabilität zwischen Umgebungen
Agent-zu-Agent-Speicherzusammenführung — atomares Kopieren/Verschieben mit Deduplizierung
Automatische Kalibrierung — statistische Schwellenwertanpassung via Nullverteilungs-Z-Score (keine Labels erforderlich)
Gesundheitsprüfung — 16 automatisierte Erkennungen mit Selbstreparatur (Kontamination, Duplikate, FTS-Desync, ungültige Daten, veraltete Aufgaben, leerer Inhalt, ungültige Quellen)
Tiefenprüfung — semantische Datenqualitätsanalyse (Wiederherstellung anonymer Quellen, kurzer Inhalt, veraltete Profile, verwaiste Episoden)
Speicherschutz — Sperren/Entsperren, um versehentliches Löschen oder Bearbeiten zu verhindern
Strafe für kürzlichen Abruf — unterdrückt den Echokammereffekt bei häufig abgerufenen Erinnerungen
stdio + Streamable HTTP-Transport
Einzeldatei-SQLite — keine externe Datenbank erforderlich
Architektur
┌─────────────────────────────────────┐
│ MCP Host │
│ (Claude Desktop / Claude Code) │
└──────────────┬──────────────────────┘
│ MCP (JSON-RPC)
┌──────────────▼──────────────────────┐
│ cpersona │
│ (server.py) │
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ store │ │ recall │ ... │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ ┌────▼─────────────▼────────────┐ │
│ │ SQLite DB │ │
│ │ │ │
│ │ memories (content + embed) │ │
│ │ episodes (summaries) │ │
│ │ profiles (attributes) │ │
│ │ memories_fts (FTS5 index) │ │
│ │ episodes_fts (FTS5 index) │ │
│ │ task_queue (async jobs) │ │
│ └────────────────────────────────┘ │
│ │
└──────────────┬───────────────────────┘
│ HTTP
┌──────────────▼──────────────────────┐
│ Embedding Server │
│ (jina-v5-nano ONNX, 768d) │
└─────────────────────────────────────┘Abruffluss (RRF-Modus):
Query → ┌── Vector search (cosine similarity) ──┐
├── FTS5 search (episodes + memories) ──┼── RRF merge → Confidence scoring → Top-K
└── Keyword fallback ──┘Benchmarks
Getestet auf LMEB (Long-term Memory Evaluation Benchmark, Ergebnisse) — 22 Bewertungsaufgaben zur Messung der Qualität des Speicherabrufs:
Embedding-Modell | Parameter | Dimensionen | Mittlerer NDCG@10 |
MiniLM-L6-v2 | 22M | 384 | 36.88 |
e5-small | 33M | 384 | 46.36 |
jina-v5-nano | 33M | 768 | 54.14 |
jina-v5-nano erzielt eine Verbesserung von +47% gegenüber der MiniLM-Baseline.
Alle Tools
Tool | Beschreibung |
| Nachricht im Agentengedächtnis speichern |
| Relevante Erinnerungen abrufen (Vektor + FTS5 + Schlüsselwort, RRF-Zusammenführung) |
| Aktuelles Agentenprofil abrufen |
| Vorausberechnetes Agentenprofil speichern |
| Gesprächsepisode mit Zusammenfassung und Schlüsselwörtern archivieren |
| Kürzliche Erinnerungen auflisten |
| Archivierte Episoden auflisten |
| Einzelne Erinnerung löschen (Eigentümerschaft erzwungen) |
| Einzelne Episode löschen (Eigentümerschaft erzwungen) |
| Alle Daten für einen Agenten löschen |
| Vektorsuchschwellenwert automatisch via Z-Score kalibrieren |
| In JSONL exportieren (Erinnerungen, Episoden, Profile) |
| Aus JSONL importieren (idempotent via msg_id-Deduplizierung) |
| Daten eines Agenten in einen anderen zusammenführen (atomar, mit Deduplizierung) |
| Status der Hintergrund-Task-Warteschlange |
| Mit externem Gesprächskontext abrufen (automatische Deduplizierung) |
| Speicherinhalt aktualisieren (wird abgelehnt, wenn gesperrt) |
| Speicher sperren, um Löschen/Bearbeiten zu verhindern |
| Speicher entsperren, um Löschen/Bearbeiten zu erlauben |
| 16-Punkte-Datenbank-Gesundheitsprüfung mit Selbstreparatur |
| Tiefe semantische Datenqualitätsanalyse mit Selbstreparatur |
Konfiguration
Alle Einstellungen erfolgen über Umgebungsvariablen mit sinnvollen Standardwerten:
Variable | Standardwert | Beschreibung |
|
| Pfad zur SQLite-Datenbank |
|
| Embedding-Modus ( |
|
| URL des Embedding-Servers |
|
| Vektorsuchmodus |
|
| Suchstrategie ( |
|
| RRF-Glättungsparameter |
|
| Konfidenz-Metadaten in Ergebnisse einbeziehen |
|
| Automatische Kalibrierung beim Start |
|
| Hintergrund-Task-Warteschlange aktivieren |
|
| Strafe für kürzlich abgerufene Erinnerungen |
|
| Fenster (Minuten) für die Strafe bei kürzlichem Abruf |
Statistiken
~3.500 Zeilen Code Python (einzelne Datei,
server.py)117 Tests in 12 Testmodulen
Schema v8 (automatische Migration)
MIT-Lizenz
Funktioniert mit
cpersona ist ein MCP-Server — er funktioniert mit jedem MCP-kompatiblen Host:
ClotoCore (KI-Agentenplattform, aus der cpersona stammt)
Jeder benutzerdefinierte MCP-Client
Teil von ClotoCore
cpersona ist die Speicherebene von ClotoCore, einer Open-Source-KI-Agentenplattform, die in Rust geschrieben ist. Obwohl cpersona vollständig eigenständig ist (MIT-Lizenz), wurde es entwickelt, um KI-Agenten ein persistentes, durchsuchbares Gedächtnis innerhalb des ClotoCore-Ökosystems zu geben.
Mehr erfahren
Zenn-Buch (Japanisch) — Vollständige Design-Anleitung und Einrichtungsleitfaden
Design des Speichersystems — Technische Spezifikation
ClotoCore — Die KI-Agentenplattform
Lizenz
MIT — frei verwendbar von jedem MCP-Host ohne Einschränkung.
Available Tools
34 toolsarchive_episodeA
Archive a conversation episode with pre-computed summary, keywords, and resolved status. All LLM processing is performed by the caller. A summary that runs past the embedding window adds nodes:{status:'queued'} to the response, as on store.
| Name | Required | Description | Default |
|---|---|---|---|
| channel | No | v2.4.22 conversation-channel tag (e.g. a Discord channel id). Default '' (= unscoped). Channel-scoped recall returns episodes whose channel matches; this powers the per-channel episodic loop. | |
| history | No | Original conversation messages (used for start/end timestamp extraction; the episode embedding is computed from summary) | |
| summary | Yes | Episode summary (pre-computed by caller) | |
| agent_id | Yes | Agent identifier | |
| keywords | No | Space-separated keywords (pre-computed by caller) | |
| resolved | No | Whether the topic was completed/concluded | |
| project_id | No | v2.4.17 isolation axis. Omit or pass '' for the global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it. | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this is a non-idempotent write that is not destructive; the description adds a genuine behavioral fact the schema does not: a summary exceeding the embedding window appends nodes:{status:'queued'} to the response. That is useful, non-obvious state disclosure, though permissions and duplicate-handling semantics remain unstated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences; the core purpose and the caller-side processing constraint are front-loaded. The trailing 'as on store' reference is slightly oblique and costs a bit of clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter mutation with no output schema, the description is only partially adequate: it explains the queued-node suffix but leaves the broader return shape and write semantics to the schema and annotations. An agent can call it, but not fully predict the response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the property docs are extremely detailed (channel, project_id '@auto', session_key nuances), so the schema carries the semantics. The description only restates that summary/keywords are pre-computed by the caller, adding little beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Archive a conversation episode') plus the key inputs it expects ('pre-computed summary, keywords, and resolved status'). It is distinguishable from store and delete_episode, though it only implicitly differentiates via the 'as on store' reference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It establishes one prerequisite ('All LLM processing is performed by the caller'), which tells an agent this tool does no inference. But it never says when to choose this over store, list_episodes, or delete_episode, and gives no exclusion conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calibrate_thresholdA
Auto-calibrate the vector search threshold from the null (random-pair) cosine distribution. Samples random memory pairs and places the threshold ABOVE the null mean so unrelated pairs are rejected. method='separation' (default) learns the operating point from two populations — null pairs vs temporally-adjacent same-session positives (nearest-neighbour fallback when too few exist); method='percentile' uses a quantile of the null distribution (robust to anisotropic models such as bge-m3); method='zscore' uses mean + z*std. No labels used, purely statistical. Adapts to both embedding model and corpus characteristics.
| Name | Required | Description | Default |
|---|---|---|---|
| method | No | 'separation' (default; two-population — learns the operating point from null pairs vs temporally-adjacent same-session positives, falling back to nearest-neighbour when too few exist), 'percentile', or 'zscore' | |
| agent_id | Yes | Agent ID whose memories to sample | |
| z_factor | No | Z-score multiplier for method='zscore' (default: 1.0, higher = stricter) | |
| percentile | No | Null-distribution quantile for method='percentile' (default: 0.95, higher = stricter) | |
| sample_size | No | Number of embeddings to sample (default: 200) | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and non-idempotent, and the description says the calibration is 'purely statistical' with no labels, adding algorithmic transparency. However, it does not disclose operational behavior: whether the computed threshold is persisted to the agent, whether it overwrites a prior threshold, the cost of sampling 200 embeddings, or any auth requirement.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded in the first sentence, followed by method semantics. It is a bit dense with parenthetical detail, but nearly every clause (null mean placement, no-labels, model/corpus adaptation) carries information an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter, no-output-schema tool, the description thoroughly explains the calibration algorithm and method choices, which is the core complexity. It leaves minor gaps (persistence of the resulting threshold, response shape), which keeps it from a 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 genuine value beyond the schema by explaining the tradeoffs between the method enum values (separation's two-population learning, percentile's robustness to anisotropic models like bge-m3, zscore's mean+z*std), which helps an agent pick the right value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Auto-calibrate the vector search threshold from the null (random-pair) cosine distribution.' An agent immediately understands the operation. It does not, however, name or contrast with any sibling tool (e.g. set_recall_precision), 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives guidance on which of the three methods to choose (percentile for anisotropic models like bge-m3), which is useful, but it never states when to call this tool versus alternatives such as set_recall_precision, nor any precondition like requiring existing memories. Usage is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_healthA
Check memory database health (38-check registry, each issue tagged critical/warn/info): contamination, duplicates, oversized content, embeddings, FTS and schema integrity, SQLite file integrity, naming, JSON and timestamp drift, stale tasks, profiles and sources. Returns storage stats with project_id/channel distributions. The verdict is status (healthy / degraded / unhealthy, from severity counts; info never degrades). Read it as a verdict on what is IN the database, not on whether the pipeline that fills it is working: a corpus whose every embedding is NULL scores healthy while semantic recall is dead. Only fix=true judges the backend's liveness: a report-only run still contacts it (a probe embed, its identity, token counts) but reads none of that as liveness, so a quiet report-only run is not evidence the backend answered; the null_embedding finding says why its repair cannot run. checks runs a subset: an unknown name is rejected (ok=false), and every response echoes checks_run. fix=true repairs, agent-scoped and locked-safe, except block_index, coarse_index, dedup_msg_id_index, file_permissions, whose repairs reach every agent, so under an ACL they need read-write on '*'; leave them out of checks to stay agent-scoped. critical file-integrity findings are report-only. Lossy, irreversible repairs: oversized memories are cut to CPERSONA_MAX_CONTENT_LENGTH (default 16000) and the profile to CPERSONA_MAX_PROFILE_LENGTH (default 2000), keeping the start (lowering a cap shortens rows that fit the old one); duplicate_content and empty_content delete rows; memory_annotation and discord_mention strip text; invalid_json blanks unparseable JSON to '{}'; stale_pending_tasks deletes queued writes over an hour old; dedup_msg_id_index blanks colliding msg_ids; timestamp checks copy created_at over bad stamps. Some repairs are bounded per run (source canonicalisation classifies at most 10000 rows): a fix response with remaining > 0 has NOT converged; run fix again until remaining stops decreasing.
| Name | Required | Description | Default |
|---|---|---|---|
| fix | No | Auto-fix detected issues | |
| checks | No | Registry check names to run (empty = all). See cpersona.checks.HEALTH_CHECK_NAMES. | |
| agent_id | No | Agent ID to check (empty = all agents) | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Far beyond the single readOnlyHint=false annotation, it discloses exactly which repairs are lossy and irreversible (row deletion, content truncation with env caps, JSON blanking), which repairs escape agent scope and need read-write on '*', which findings are report-only, and per-run bounds with a non-convergence signal. It also warns that a report-only run still contacts the backend but is not liveness evidence.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The verdict semantics and the liveness caveat are front-loaded, which is good, but the middle is a dense run-on catalog of repairs with heavy parenthetical nesting. Much of it earns its place, yet it reads as one long block rather than structured sections.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutating tool with no output schema, the description covers the salient response fields (`status` semantics and the info-never-degrades rule, `checks_run`, `remaining`) plus repair safety and convergence, which is what an agent needs to call it correctly and to interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, yet the description adds semantics the schema lacks: unknown check names are rejected with ok=false, `checks_run` is echoed back, and fix=true behaves differently per check with ACL implications. `agent_id` and `session_key` get no additional treatment, which keeps it off a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states a specific verb and resource ('Check memory database health') and enumerates the check registry domains, so an agent knows exactly what it gets. It does not, however, differentiate itself from the sibling `deep_check`, which an agent could easily confuse with it.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives real decision rules: report-only vs fix=true, using `checks` to stay agent-scoped, and looping while `remaining` decreases. The notable gap is that it never names `deep_check` or states which of the two an agent should prefer.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_updateA
Report whether a newer release of this server exists, and — only if you ask — install it. The check itself runs ONCE per process start, in a background task that nothing waits on, and its verdict is cached for 24h (CPERSONA_UPDATE_CHECK_INTERVAL_SECONDS) in a file beside the database; a bare call here reads that verdict and reaches neither the network nor the disk. state is one of: ok (running the newest release) / newer (a newer final release exists — pre-releases are never proposed) / yanked (every file of the RUNNING version has been withdrawn on PyPI; reason carries the publisher's text) / unlisted (this version is not on the index at all — a development checkout; not a defect) / unknown (no check has completed, e.g. no network) / disabled. install names how this process was installed (uvx / pip / checkout / unknown) and the exact command that would update it. refresh=true performs the fetch now (3s budget) and updates the cache. apply=true runs that command as an argv list (never a shell), returning exit_code and the last 40 lines of output — supported for pip and checkout installs only; under uvx the environment is a cache entry keyed by the launch arguments, so the update belongs in your client's config (uvx cpersona@latest), and an install here would be discarded on the next launch. A checkout parked at a tag (detached HEAD) is likewise refused before anything runs, and answers with the git fetch --tags && git checkout <tag> form to use instead (install.command shows it too). One apply runs at a time; a second is refused until it ends. Updating is NEVER automatic and never a side effect of any other call. A RESTART IS ALWAYS REQUIRED afterwards: this process keeps serving the old code until it is replaced. Unaffected by pause_persistence — an install writes no memory row, so a no-persist session can still repair a withdrawn version. Set CPERSONA_UPDATE_CHECK=false to disable the feature entirely: no fetch, no cache, no notice on recall or check_health, and this tool answers state=disabled.
| Name | Required | Description | Default |
|---|---|---|---|
| apply | No | Run the detected update command (pip / checkout installs only). Off by default; a restart is required afterwards. | |
| refresh | No | Fetch the package index now instead of reading the cached verdict (3s budget). A failed fetch keeps the verdict already known, so `checked_at` does not move; with none known it answers state=unknown. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses extensive behavior beyond the single readOnlyHint=false annotation: background check timing, 24h caching, state values, install detection, argv execution, output truncation, refusal cases, and the restart requirement. It is consistent with the annotation because the tool can mutate when apply=true.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense, but it is front-loaded with the core purpose and every detail is relevant to this unusually conditional tool. It could be structured with clearer sections, but it avoids redundant filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and lack of an output schema, the description is complete: it defines every state, explains refresh and apply behavior, documents practical failure modes, and covers configuration and restart requirements.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is already 100%, the description adds meaningful semantics for both parameters: refresh's 3s fetch budget and failed-fetch behavior, and apply's supported install types, argv execution, exit_code, and output lines. This goes well beyond the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: report whether a newer server release exists and optionally install it. It clearly separates checking from applying and is unmistakably distinct from the memory-oriented sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit conditions for refresh and apply, states that updating is never automatic, and explains unsupported cases such as uvx installs and detached-HEAD checkouts. It also notes the one-apply-at-a-time constraint and the required restart.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
declare_associationsADestructiveIdempotent
Declare associative memory after the fact: entities with aliases, and subject–predicate–object relations, recorded verbatim and walked by reconstruct. The server extracts nothing and infers nothing — coverage is exactly what was declared. anchor_ref names the record (mem:<id> / ep:<id>) the declaration is evidenced by: every entity named is recorded as mentioned by it and every relation carries it. Malformed items are reported in dropped and skipped; nothing else in the call is refused for them. retract removes relations by id and mentions by {entity, ref} — the only way a declaration leaves the store. Response: {ok, result:'declared', entities:[{id, name, created}], mentions, relations:[ids], dropped:[{item, reason}], retracted?:{relations, mentions}}. Under pause_persistence nothing is written (result:'skipped', persisted:false).
| Name | Required | Description | Default |
|---|---|---|---|
| channel | No | Memory channel the declaration belongs to. Default: '' (shared). | |
| retract | No | Declarations to remove: {relations: [relation ids], mentions: [{entity: <entity id>, ref: 'mem:<id>'}]}. Only this agent's rows are touched. | |
| agent_id | Yes | Agent identifier | |
| anchor_ref | No | The record this declaration is evidenced by: 'mem:<id>' or 'ep:<id>' of this agent. Optional. | |
| project_id | No | Project the declaration belongs to. Optional — omit or pass '' for the global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it. | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. | |
| associations | No | The entities and relations to declare. Malformed items are reported in `dropped` and skipped. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral detail beyond the annotations: nothing is extracted or inferred, coverage equals exactly what was declared, malformed items are dropped and skipped without refusing the call, retract removes relations/mentions, and pause_persistence causes a skipped non-persisted result. This is especially valuable given destructiveHint=true.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but front-loaded: the first sentence establishes purpose, and subsequent sentences cover anchoring, dropping, retraction, response shape, and pause behavior. For a complex nested tool, every part appears to earn its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the nested schema, destructive/idempotent annotations, and absence of an output schema, the description is complete enough: it explains declaration behavior, malformed-item handling, retraction, response fields, and pause_persistence behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 still adds meaning beyond the schema by explaining that anchor_ref makes every named entity mentioned by that record and every relation carry that ref, and by clarifying retract behavior over relations and mentions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: declaring associative memory with entities, aliases, and subject–predicate–object relations. It also clarifies that the declaration is recorded verbatim and later walked by reconstruct, which distinguishes this from traversal/read tools like reconstruct or recall.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear usage context: declare associations after the fact, anchor them to a record, and use retract as the only way a declaration leaves the store. It does not explicitly compare against sibling write tools like store, so it stops short of full when-to-use/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
deep_checkA
Deep heuristic analysis of memory data quality. Detects issues requiring recovery or judgment (anonymous sources, short/trivial content, stale profiles, orphaned episodes, stale threshold calibration, embedding-space near-duplicate pairs as merge candidates). fix=true applies repairs for: anonymous_source, short_content. The short_content repair deletes every unlocked row it reports; run without fix first and read its samples. Report-only (fix is accepted and ignored): stale_profile, orphaned_episodes, calibration_staleness, near_duplicate, unnormalized_content, embedding_norm — apply those decisions via merge_memories / delete_memory / calibrate_threshold / update_profile. Use checks parameter to select specific checks.
| Name | Required | Description | Default |
|---|---|---|---|
| fix | No | Apply repairs (default: dry-run preview only) | |
| checks | No | Checks to run (empty = all). Options: anonymous_source, short_content, stale_profile, orphaned_episodes, calibration_staleness, near_duplicate, unnormalized_content, embedding_norm | |
| agent_id | Yes | Agent ID to check (required) | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false, so the description carries the safety burden and does it well: it states exactly which two checks fix=true repairs, warns that the short_content repair deletes every unlocked row it reports, and lists which checks silently ignore fix. That is the destructive-behavior disclosure an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and the destructive warning comes before the report-only list, so nothing is buried. The parenthetical issue list is dense and the sentence about report-only checks runs long, costing a little readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With four parameters, no output schema, and a mutation path, the definition covers safety, scope, and follow-up actions well. It stops short of saying what the dry-run preview returns (it mentions 'samples' but not the shape), which is the one remaining gap for a no-output-schema tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 genuine meaning the schema cannot: it scopes fix=true to only anonymous_source and short_content and names all six report-only checks. The agent learns the effective domain of fix, which the boolean schema does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Deep heuristic analysis of memory data quality') and enumerates exactly which issue classes it detects, so an agent can distinguish it from the shallower sibling check_health 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing: run without fix first and read the samples, use the checks parameter to narrow scope, and for the report-only checks apply the decisions through merge_memories / delete_memory / calibrate_threshold / update_profile. That is when-to-use plus named alternatives in one place.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_agent_dataADestructiveIdempotent
Delete ALL data (memories, profiles, episodes) for a specific agent. Used by kernel during agent deletion.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | Agent ID whose data should be purged | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=true, so the safety profile is covered. The description adds genuine value by enumerating exactly what is destroyed (memories, profiles, episodes) and the scope (single agent), which the annotations do not convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with zero padding; the destructive scope is front-loaded before the caller-context note. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an irreversible bulk-delete tool, the description pairs the destructive scope with annotations that flag destructiveness, and no output schema exists to explain. It could state irreversibility or permission requirements, but nothing critical for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both parameters carry detailed schema descriptions, including a lengthy note on session_key semantics, so the schema does the heavy lifting. The description adds no parameter-level meaning, making the baseline 3 correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Delete) plus resource scope (ALL data for a specific agent) and enumerates what is covered: memories, profiles, episodes. This distinguishes it from the finer-grained delete_memory and delete_episode siblings, though it never names them explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Used by kernel during agent deletion" gives context about who calls it and when, but provides no guidance for an agent choosing between this and delete_memory/delete_episode/archive_episode, and no when-not-to-use condition or prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_episodeADestructiveIdempotent
Delete a single episode by ID. Ownership is enforced when agent_id is provided.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | No | Agent ID for ownership verification (injected by kernel) | |
| episode_id | Yes | Episode ID to delete | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare destructiveHint=true, idempotentHint=true, and readOnlyHint=false, covering the safety profile. The description adds meaningful context by stating that ownership is enforced only when agent_id is provided, which affects authorization behavior. It does not explain cascading effects, but this is a valuable addition beyond structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with zero waste. The core action and ownership condition are front-loaded and concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive single-resource deletion with rich annotations and full schema coverage, the description provides the key ownership condition. Minor gaps exist, such as no guidance on choosing archive_episode over deletion or on data cascade, but these are not critical given the tool's complexity and the available structured data.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 three parameters thoroughly. The description restates the episode_id and agent_id ownership concept but adds no syntax, format, or constraint details beyond the schema. The session_key parameter is not mentioned in the description at all, though it is fully documented in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Delete') and resource ('a single episode by ID'), making the action clear. It implies scoping to one episode, which helps distinguish it from sibling operations like delete_agent_data or archive_episode, but it does not explicitly name any sibling for differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit guidance on when to use this tool versus alternatives such as archive_episode or delete_agent_data. The word 'single' weakly implies a non-bulk operation, but no routing conditions or exclusions are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_memoryBDestructiveIdempotent
Delete a single memory by ID. Ownership is enforced when agent_id is provided.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | No | Agent ID for ownership verification (injected by kernel) | |
| memory_id | Yes | Memory ID to delete | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=true, and readOnlyHint=false, so the safety profile is covered structurally. The description adds useful authorization context ('Ownership is enforced when agent_id is provided'), but does not state that the deletion is permanent/irreversible or what happens to associated data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the core action and followed by the ownership condition. Efficient with no padding, though minimally terse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive mutation with strong annotations and full schema coverage, the description covers the core action and one behavioral constraint. It omits irreversibility, cascade effects, and failure/error behavior when ownership check fails, leaving meaningful gaps for a delete tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all three parameters (agent_id, memory_id, session_key) are already documented in the schema, including the ownership-verification note on agent_id. The description restates the ownership rule but adds no syntax or format detail beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource (delete a single memory by ID), which distinguishes it from siblings like delete_agent_data and delete_episode. However, it does not explicitly name those siblings or scope the difference beyond the singular 'single memory'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use or when-not-to-use guidance, and no alternatives named. The only conditional is about ownership enforcement, which is behavioral rather than a usage rule.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
export_memoriesADestructive
Export memories, episodes, profiles and declared associations (entities, aliases, mentions, relations) to a JSONL file for backup or portability.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | Agent identifier (empty string to export all agents) | |
| output_path | Yes | File path for the JSONL output | |
| include_embeddings | No | Include embedding BLOBs as base64 (default false, usually not needed) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=false and destructiveHint=true, so the agent knows this tool writes and may be destructive. The description adds useful scope by listing what data is exported and the JSONL format, but it does not explain the destructive side effect—for example, whether output_path is overwritten or what permissions are required.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no filler. The verb, exported resources, output format, and purpose are all stated efficiently.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a three-parameter export tool with full schema coverage and no output schema, the description is nearly complete: it identifies the exported data and file format. The main missing context is the destructive overwrite behavior, though the destructiveHint annotation partially covers that safety concern.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all three parameters are already documented in the schema itself. The description only implies the output_path behavior through 'to a JSONL file' and does not add syntax or format details beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (export), enumerates the exact resources exported (memories, episodes, profiles, declared associations), and names the output format (JSONL). It also gives the intended use (backup or portability), making it easy to distinguish from sibling tools like import_memories or list_memories.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'for backup or portability' clearly indicates the primary use case. However, it does not explicitly state when not to use it or point to alternative tools such as import_memories for restoring backups or list_memories for browsing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_contentsARead-only
Fetch full, untrimmed content for recall preview refs. Use after a preview-tier recall to expand only the rows that matter instead of opting the whole recall out with full_content=true. Bounded twice: at most 20 refs per call, and a 40,000-character budget across the batch (2.5.4a2) that does not move when CPERSONA_MAX_CONTENT_LENGTH does. Rows are never cut to fit — when the budget is spent the remaining refs come back in deferred (absent otherwise) alongside budget_chars; re-fetch them in a second call. A single row larger than the budget is still returned in full, because this tool is the only path back to a row's complete text. RANGES: a ref may instead be an object that names part of its record -- {ref, node: i} or {ref, node: [first, last]} (inclusive) for overflow-tree nodes, e.g. the node.index of a reconstruct quote and its neighbours, or {ref, span: [start, end]} for characters. Offsets are in the stored text (a memory's content, an episode's summary without the '[Episode] ' label). The item then carries that slice as content and range = {span, content_len, and node + of when nodes were named, or block + of when blocks were}; a span end past the text is clamped and range.span says what was served. A ref may also carry revision, the digest a reconstruct quote's expand hands out for the text its offsets were measured in: a record rewritten since refuses rather than serving different characters under the same numbers. A range that cannot be served exactly is never widened to the whole row: it comes back in unresolved (absent otherwise) as {ref, reason}, reason one of invalid_range, no_current_nodes (the record has no complete node set -- short records have none, and a new long one gets them shortly after store), node_out_of_range, span_out_of_range, no_current_blocks, block_out_of_range, stale_revision. Only the slice counts against the budget.
| Name | Required | Description | Default |
|---|---|---|---|
| refs | Yes | Refs from recall messages ('mem:<id>' / 'ep:<id>'), or range objects such as {'ref': 'mem:<id>', 'node': [2, 3]} / {'ref': 'ep:<id>', 'span': [0, 800]} / {'ref': 'mem:<id>', 'block': 4, 'revision': '<from an expand>'} (max 20 per call). At most one of node / span / block | |
| agent_id | Yes | Agent identifier (ownership check — another agent's refs come back in `missing`) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations carry only readOnlyHint=true; the description shoulders the rest and does so richly: two hard bounds (20 refs, 40,000-char budget), the deliberate non-truncation policy with `deferred`/`budget_chars`, the oversized-single-row exception, staleness refusal via `revision`, and enumerated `unresolved` reasons. Nothing here contradicts the read-only annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is long, but the tool's surface genuinely is large (ranges, node vs block vs span, revisions, deferral), and the purpose is front-loaded before the mechanics. The version tag and env-var mention are minor noise, keeping this just under a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must explain returns — and it does: `deferred`, `budget_chars`, `unresolved` with enumerated reasons, `range` contents, `missing` for foreign refs, and the oversized-row pass-through. An agent has everything needed to call and interpret results correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds substantially more than the schema: concrete ref formats ('mem:<id>' / 'ep:<id>'), example range objects, offset semantics (offsets are in stored text; episode summary excludes the '[Episode] ' label), the clamp behavior for spans past the end, and the meaning of `revision`. This far exceeds the schema's 'At most one of node / span / block' note.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch full, untrimmed content for recall preview refs') and positions itself relative to sibling behaviors: it expands preview-tier rows rather than opting the whole recall out with full_content=true. An agent can distinguish this from recall and reconstruct without inspecting schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the trigger ('after a preview-tier recall') and the anti-pattern it replaces (setting full_content=true on the whole recall), steering the agent toward 'expand only the rows that matter.' The alternative path and the condition selecting it are stated, not implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_operating_contextARead-only
Read the server-served operating context (v2.5.1): the operator-owned doctrine distributed to every connected client. Without arguments returns the preview tier — context_revision, instructions_summary, project_id registry (+ enforce mode), @auto defaults, and doctrine section names. Pass section to fetch one section's full body. Read-only: the context is edited by the operator on the filesystem (~/.cpersona/operating-context.toml), never via MCP.
| Name | Required | Description | Default |
|---|---|---|---|
| section | No | Doctrine section name to fetch in full (from doctrine_sections). Empty = preview tier. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description not only aligns with the readOnlyHint annotation but adds significant context: the context is edited on the filesystem (~/.cpersona/operating-context.toml), never via MCP. This discloses the source of truth and mutation path, which annotations alone do not provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences, front-loaded with the core purpose, followed by mode details and behavioral note. No wasted words. Structure is logical: what, how, important note.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given one optional parameter, no output schema, and full annotations, the description covers all necessary aspects: return types (preview tier components, full section body), usage modes, and behavioral constraints (read-only, filesystem editing). It is sufficient for an AI agent to correctly invoke this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with a description for the 'section' parameter. The description adds value by explaining the default behavior (preview tier) and that the section is from 'doctrine_sections'. It clarifies the parameter's effect beyond the schema's own description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool reads the 'server-served operating context' with a specific version (v2.5.1). It identifies the resource and its nature as 'operator-owned doctrine'. This is specific and distinct from sibling tools like 'get_profile' or 'get_contents'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states the two modes: without arguments returns the preview tier, and with a 'section' argument returns the full body. It mentions read-only and that editing is done via filesystem, not MCP. While it doesn't contrast with siblings, it provides clear usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_profileCRead-only
Get the current profile for an agent.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | Agent identifier |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Description aligns with readOnlyHint by stating 'Get', but adds no further behavioral details such as error handling for missing agents, return format, or scope of the profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence, concise and front-loaded. Could include more detail without being overly long, but the brevity aids readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Adequate for a simple 1-parameter tool with no output schema. However, lack of return value description may leave the agent uncertain about the output format.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with a clear description for 'agent_id'. The tool description does not add any additional meaning beyond the schema, so baseline score applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description 'Get the current profile for an agent' clearly states the action (get) and resource (profile) with specifier 'for an agent'. It distinguishes from sibling 'update_profile' but is slightly redundant with the tool name.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool vs alternatives, no prerequisites or limitations mentioned. The only implicit guidance is from the readOnlyHint annotation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_queue_statusARead-only
Get the status of the background task queue (pending tasks, retry config).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the description's addition of 'pending tasks, retry config' provides some context. However, it could be more transparent about the return format or any other behavioral traits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence of 13 words. Every word is purposeful and concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple, parameterless tool with readOnlyHint annotation, the description is fairly complete. It could benefit from specifying the output structure, but given no output schema, it's adequate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero parameters, so baseline is 4. The description does not add parameter details, which is acceptable since there are none.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb 'Get' and resource 'background task queue status', and mentions what is included (pending tasks, retry config). This distinguishes it from sibling tools like check_health or list_episodes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for checking queue status but provides no explicit guidance on when to use it versus alternatives, nor when not to use it. No sibling comparisons mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recall_precisionARead-only
Read an agent's effective recall precision (knob 3) — the read-back companion to set_recall_precision. Returns the resolved specificity weight (beta) and its named precision level (strict / balanced / lenient, or 'custom' for a raw beta), and flags whether the value is a per-agent override or the global CPERSONA_RECALL_PRECISION default (overridden + global_precision / global_beta). Read-only: it never recalibrates and never persists, so a UI can load the current setting, let the user edit it, and write it back instead of the control being write-only.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | Yes | Agent whose precision to read |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true. The description goes further by stating it never recalibrates or persists, and details the returned fields (beta, precision level, override flags). This adds behavioral context beyond the annotation, though it doesn't cover all edge cases.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single paragraph that front-loads the main purpose and then provides additional details. It is reasonably concise, though some sentences could be tightened. Every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simple input (1 param, no output schema), the description thoroughly explains the return value and its relationship to the global default and override behavior. It is complete for a read-only tool of this complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers the single required parameter (agent_id) with a description. The tool description does not add meaning beyond that, but since schema coverage is 100%, a baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool reads an agent's effective recall precision, identifies it as the read-back companion to set_recall_precision, and specifies it is read-only. This distinguishes it from its sibling and provides a specific verb-resource combination.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains when to use this tool (as a read-back companion to set_recall_precision, for UI loading before editing) and implies it should be used before writing. However, it does not explicitly mention alternatives or when not to use it, though the sibling set tool is clearly the counterpart.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_session_findingsARead-only
Pull the storage-integrity findings on demand (the SuperAuditor pull contract, docs/SUPERAUDITOR_STANDARD.md) instead of reading them off check_health: the same detector as check_health(fix=false) over the WHOLE database. Read-only, never repairs. Each finding carries kind (a check registry name, or an escalation tier this seam mints for a runner that grades its own severity, e.g. null_embedding_pipeline_down — a tier is NOT a registry name), check (the registry name that produced it: check_health(checks=[finding['check']]) re-runs exactly that probe) and a static per-kind severity (critical = the read contract is broken now / warn = two stored facts contradict / info = an observation). check_health's own verdict rides along as health_severity; a probe that raised is reported as kind check_crashed instead of failing the pull. NOT free: the registry runs unfiltered, including two whole-database reads (the FTS5 integrity-check over both indexes and PRAGMA quick_check), so every pull is O(database); budget it by call frequency. There is deliberately no cheap subset. Findings are NOT filtered by agent_id or project_id: slicing them by the caller's bucket would hide exactly the forgotten rows (scope a repair with check_health(agent_id=...)). Honest caps: findings holds at most per_kind_limit rows per kind, capped_kinds names every kind that had more, and total and the counts describe the RETURNED set only. summary restates it in prose (include_summary=false skips it). Without a session_key on a shared remote transport the response carries identity_shared: true; _meta.server_version identifies the running instance.
| Name | Required | Description | Default |
|---|---|---|---|
| session_key | No | Opaque client-declared session identity (partition hint, not authentication). Empty on a non-stdio transport marks the response identity_shared. | |
| per_kind_limit | No | Maximum findings returned per kind (default 5, minimum 1). Kinds that hit it are listed in capped_kinds. | |
| include_summary | No | Include the human-readable `summary` rendering (default true). It restates `findings` in prose — set false when machine-reading. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only supply readOnlyHint=true, yet the description adds substantial behavior: never repairs, cost is O(database) with two named whole-database reads, findings are unfiltered by agent_id/project_id, hard caps with capped_kinds, and identity_shared on shared transports. This is far beyond what the annotation conveys.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Core purpose and the read-only contract are front-loaded, and nearly every sentence carries load-bearing detail on return fields and cost. It is nonetheless dense and heavy on capitalized emphasis and parentheticals, so it is slightly longer than strictly necessary for a three-parameter tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description carries the full burden and does so: it defines kind, check, severity tiers, health_severity, the check_crashed case, findings, capped_kinds, total, summary, identity_shared and _meta.server_version. An agent has everything needed to interpret the return.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 three params, setting a baseline of 3. The description goes further by tying session_key empty-on-shared-transport to identity_shared, per_kind_limit to capped_kinds, and include_summary to prose output, adding real semantic context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Pull the storage-integrity findings on demand') and immediately disambiguates from the sibling check_health, noting it is 'the same detector as check_health(fix=false) over the WHOLE database'. An agent can distinguish this from check_health 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes usage: use this 'instead of reading them off check_health', but scope a repair with check_health(agent_id=...). It also gives a budgeting rule ('budget it by call frequency') and warns there is deliberately no cheap subset, covering when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
import_memoriesADestructiveIdempotent
Import memories, episodes, profiles and declared associations from a JSONL file; the associations are pointed at the records they name as restored. Idempotent: memories deduplicate on msg_id (and on content within a project/channel), episodes on their summary within a project/channel.
| Name | Required | Description | Default |
|---|---|---|---|
| dry_run | No | Count records without writing to DB (preview mode) | |
| input_path | Yes | Path to the JSONL file to import | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. | |
| target_agent_id | No | Remap all records to this agent ID (empty to use original agent_id from file) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=true; the description adds real value beyond them by specifying the exact dedup keys (msg_id, content within project/channel; episode summary within project/channel) and clarifying that associations are re-pointed at restored records. It stops short of saying which existing records may be overwritten or merged, which matters for a destructive tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the scope, then the idempotency contract. No filler and nothing repeated from the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The dedup and association-restoration behavior is well covered, but with no output schema the description should say what the call returns (imported/skipped counts, dry-run preview shape) and how it interacts with the persistence-pause tools that appear as siblings. Those gaps keep it at minimum viable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so input_path, dry_run, session_key and target_agent_id are already documented. The description adds no parameter-level detail (e.g., how target_agent_id remapping interacts with dedup), so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb (import) plus the exact resources handled (memories, episodes, profiles, declared associations) and the input format (JSONL). An agent can distinguish this from export_memories and from store/declare_associations without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the import framing and by the described dedup semantics, but there is no explicit 'use this when...' guidance or comparison against alternatives like store/declare_associations, and no prerequisite such as pausing persistence before a bulk load is stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_episodesARead-only
List archived episodes for an agent (for dashboard display). bug-385: limit is clamped to 200 rows. When the caller asked for more and rows past the cap exist, the response carries budget_rows (the cap), so a capped listing can be told from one that reached the end of the data — reach the rest through export_memories or a narrower filter, not a larger limit. bug-255: within that cap the response holds an 800,000-character budget across summary and keywords together, with the same degradation and ceiling semantics as list_memories — rows past the budget that exceed the preview cap carry pure prefixes plus summary_truncated/summary_len and keywords_truncated/keywords_len; budget_chars appears iff at least one row was degraded. Their ref expands the summary via get_contents (under the row's own agent_id); a full keywords string is only available through export_memories.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max episodes to return | |
| agent_id | No | Agent identifier (empty for all agents) | |
| project_id | No | v2.4.17 γ filter. Same semantics as list_memories. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint; the description adds concrete caps (200 rows, 800,000 characters), degradation behavior, response fields such as budget_rows and budget_chars, and alternative data-access paths. This is substantial behavioral context beyond what the annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded in the first sentence, and bug-385/bug-255 labels organize the constraints. The text is dense and relies on cross-tool semantics ('same as list_memories'), but every sentence carries relevant operational detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must explain return behavior, and it covers caps, truncation flags, budget_chars, and ref expansion. It omits a full episode field list or ordering, but gives enough for an agent to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 adds important meaning for `limit` by explaining the 200-row clamp and budget_rows behavior; it adds little for agent_id or project_id beyond the schema, but the limit semantics elevate the score.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'List archived episodes for an agent,' and notes the intended use case of dashboard display. It does not explicitly contrast with the sibling list_memories, but the resource is distinct enough for an agent to identify the tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly routes around the limit cap to export_memories or a narrower filter, and routes summary expansion to get_contents and full keywords to export_memories. It does not state when to choose list_episodes over list_memories, leaving one sibling comparison implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_memoriesARead-only
List recent memories for an agent (for dashboard display). bug-385: limit is clamped to 500 rows. When the caller asked for more and rows past the cap exist, the response carries budget_rows (the cap), so a capped listing can be told from one that reached the end of the data — reach the rest through export_memories or a narrower filter, not a larger limit. bug-255: within that cap the response holds a 1,000,000-character content budget. Rows are returned newest-first and none is dropped by the budget; once it is spent, later rows LONGER than the preview cap (CPERSONA_RECALL_PREVIEW_CHARS, default 500) degrade to a pure prefix with content_truncated/content_len and a ref that get_contents expands under the row's own agent_id (in an all-agents listing, pair the ref with the row's agent_id field). budget_chars appears iff at least one row was degraded. The effective ceiling is the budget plus one whole row plus the degraded rows' prefixes, so it scales with the preview cap; preview cap 0 disables trimming and the budget with it.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max memories to return | |
| agent_id | No | Agent identifier (empty for all agents) | |
| project_id | No | v2.4.17 γ filter. Omit → no filter; '' → global pool only; 'X' → 'X' ∪ global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, and the description supplies substantial extra behavior: the 500-row clamp, the 1,000,000-character content budget, newest-first ordering, degradation of long rows to prefixes with content_truncated/content_len and a ref, the conditional presence of budget_chars, and preview-cap-zero disabling. This is exactly the kind of edge-case context 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded, but the remainder is a dense wall of text with bug-385/bug-255 tags and deep edge cases (preview cap 0, ceiling formula) packed into unbroken sentences. Most content is substantive, yet the presentation makes it hard to parse and would benefit from structure.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and does so: it explains budget_rows, budget_chars, content_truncated, content_len, ref, and the degradation rules. An agent has enough to interpret a capped or truncated response correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description goes beyond the schema by noting limit is clamped to 500 and by explaining that in an all-agents listing the ref must be paired with the row's agent_id — practical usage semantics the schema does not convey. project_id is left to its already-rich schema description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'List recent memories for an agent'. It also names the scope condition and references the export_memories sibling for reaching more data. However, it does not explicitly distinguish itself from recall/reconstruct/recall_with_context, which are the closest read siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives one concrete routing instruction — to get past the 500-row cap, use export_memories or a narrower filter, not a larger limit. But it never states when to choose list_memories over recall, recall_with_context or reconstruct, so overall selection guidance is only partial.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lock_memoryAIdempotent
Lock a memory to prevent deletion and editing (delete_memory, update_memory). Operator re-scoping (migrate_channel_axis, merge_memories move) still relocates it, lock kept; delete_agent_data removes it. Ownership enforced when agent_id provided.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | No | Agent ID for ownership verification | |
| memory_id | Yes | Memory ID to lock | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare it is a non-destructive, idempotent mutation, but the description adds important behavioral scope beyond them: exactly which operations are blocked, that re-scoping still moves the memory while preserving the lock, and that delete_agent_data bypasses it entirely. It also discloses that ownership verification is enforced when agent_id is supplied — auth-relevant context the annotations do not carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The main purpose and blocked operations are front-loaded, and every phrase carries useful scope information. The nested parentheticals make it denser than necessary — split into three shorter sentences, it would be easier to scan — but there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations covering the safety profile, a fully documented input schema, and no output schema to explain, the description supplies the remaining behavioral contract: what the lock blocks, what still operates on the memory, and the ownership condition. 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.
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 three parameters, including agent_id's ownership-verification role and session_key's partition semantics. The description only reiterates the agent_id ownership behavior already stated in the schema and says nothing additional about session_key or memory_id, so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
It states a specific verb and resource ('Lock a memory') plus its immediate effect ('prevent deletion and editing'). It then explicitly names the sibling operations it blocks (delete_memory, update_memory) and the ones that still affect locked memories (migrate_channel_axis, merge_memories, delete_agent_data), so the agent can distinguish it from related tools without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description says exactly when to use it (to prevent deletion and editing) and gives explicit exceptions: operator re-scoping relocates the memory but keeps the lock, while delete_agent_data removes it. By naming delete_memory and update_memory as the alternatives this tool supersedes, it leaves the selection condition unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
merge_memoriesADestructiveIdempotent
Merge memories, episodes, profiles and declared associations from one agent into another. Atomic one-shot equivalent of export→import without intermediate files. Strategy 'skip' deduplicates by msg_id (memories) and summary (episodes).
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Merge mode: 'copy' (preserve source) or 'move' (delete source after merge) | copy |
| dry_run | No | Preview merge without writing to DB | |
| strategy | No | Merge strategy: 'skip' (default) — skip duplicates, keep target's version | skip |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. | |
| source_agent_id | Yes | Agent ID to merge FROM | |
| target_agent_id | Yes | Agent ID to merge INTO |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=true, so the safety profile is partly covered. The description adds real context beyond that: the operation is atomic and one-shot, and 'skip' deduplicates by msg_id (memories) and summary (episodes). It does not restate the delete-on-'move' behavior, which lives only in the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, zero filler. Purpose and sibling differentiation are front-loaded, with the strategy detail trailing where it belongs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive mutation tool with full schema coverage and annotations, the description covers atomicity and dedup semantics well. Remaining gaps are minor – e.g., how profile/association conflicts are resolved, and what a dry_run preview returns – but nothing essential to calling it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description goes beyond the schema by naming the actual dedup keys (msg_id for memories, summary for episodes) for the 'skip' strategy, which the schema only describes generically as 'skip duplicates'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource set ('Merge memories, episodes, profiles and declared associations') and scopes it to one agent into another. It explicitly frames itself against the export/import siblings as the atomic one-shot equivalent, so an agent can tell it apart without opening another schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly signals when to choose this over export_memories + import_memories ('without intermediate files'). It also documents the default dedup strategy condition. It stops short of stating when *not* to use it (e.g., cross-tenant or auth caveats), but the primary alternative is named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
migrate_channel_axisA
Re-channel bridge-type memories to their concrete channel (knob2 v2 default flip prep). Memories the kernel filed under the bridge type ('discord') are rewritten to the concrete channel recovered from the stored session_id ('{channel_id}:{user_id}:{chunk}' | '{channel_id}:shared' → channel_id), so per-channel recall can match them. Non-destructive (only the channel column changes) and idempotent (re-running is a no-op once moved). dry_run=true (default) reports the recoverable count, the channels that would be recovered, and an unrecoverable bucket (channel='discord' rows with no snowflake session_id) without mutating. globalize_unrecoverable=true moves the unrecoverable bucket to channel='' (global, matched by every channel-scoped recall) so the flip orphans nothing; default false (report only).
| Name | Required | Description | Default |
|---|---|---|---|
| dry_run | No | Preview counts only, no mutation (default: true) | |
| agent_id | No | Agent ID to migrate (empty = all agents) | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. | |
| globalize_unrecoverable | No | Also move channel='discord' rows with no snowflake session_id to channel='' (global). Default false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only give readOnlyHint=false; the description carries the rest and does it well, disclosing non-destructiveness ('only the channel column changes'), idempotence ('re-running is a no-op'), the default dry_run preview, and precisely what globalize_unrecoverable alters. This is exactly the mutation context an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then progressively the mechanics, safety properties, and the two flag behaviors. Sentences are dense and jargon-heavy ('snowflake session_id', '{channel_id}:shared'), but every clause conveys information about behavior or defaults rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by describing what dry_run returns (recoverable count, recovered channels, unrecoverable bucket) and what the two flags do. For a 4-param mutation tool with safety annotations, 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real meaning: it explains that dry_run=true (default) reports recoverable count, channels, and an unrecoverable bucket without mutating, and that globalize_unrecoverable=true moves the unrecoverable bucket to channel='' (global). That clarifies the effect of both booleans beyond their terse schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('re-channel bridge-type memories to their concrete channel') and explains the underlying mechanism (session_id '{channel_id}:{user_id}:{chunk}' parsing). No sibling tool shares this migration purpose, so the agent can immediately tell it apart from recall/store/profile tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Frames the when ('knob2 v2 default flip prep' so per-channel recall can match) and explains the default dry_run=true preview mode as the safe entry point. It doesn't name an explicit alternative tool or state when-not-to-use, but no plausible sibling overlaps, so the guidance is effectively complete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pause_persistenceAIdempotent
Pause write operations on this MCP server for an opt-in TTL window (default 1800 seconds, at most 86400). While paused, every write tool — store, declare_associations, archive_episode, update_memory, delete_memory, delete_episode, delete_agent_data, lock_memory, unlock_memory, update_profile, import_memories, merge_memories, calibrate_threshold, set_recall_precision — returns a no-op response carrying persisted: false, dry_run: true and a reason (with the TTL remaining) instead of writing. Branch on persisted: false, not on an id: an id reads "no-persist" (store, archive_episode), and the action-specific id keys (deleted_id / updated_id / locked_id / unlocked_id / episode_id) are null. migrate_channel_axis is instead forced to dry_run and reports repairs_skipped, with no persisted key; check_health and deep_check downgrade to fix=false (repairs_skipped: true). Read tools answer normally, but recall does not bump recall_count / last_recalled_at. Blast radius follows session_key (response scope). Pass the same session_key here and on your write calls and the pause covers that key alone (scope: "session"): a session that sends a different key is neither silenced by it nor able to clear it. The key is a partition hint, not a credential, so anyone who sends the same string shares the pause. Omit it and you arm the bucket every keyless caller shares (scope: "process") — on a streamable-HTTP deployment one process serves every client, so a keyless pause silences writes for every other keyless session until resume or TTL elapse, and those sessions get no signal. Under stdio (one process per client) that bucket is the session. This affects only cpersona; pause cscheduler separately.
| Name | Required | Description | Default |
|---|---|---|---|
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. | |
| ttl_seconds | No | TTL until automatic resume. Min 1, max 86400 (clamped). Default 1800. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Far exceeds annotations: it discloses the exact no-op response contract (persisted:false, dry_run:true, reason, "no-persist" id), the special-cased behavior of migrate_channel_axis/check_health/deep_check, and the recall side effect. The detailed blast-radius discussion (session vs process bucket, shared keyless bucket on streamable-HTTP, no signal to silenced sessions) is behavioral context the readOnly/idempotent/destructive hints 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and the affected-tool list, then layers the response contract and blast radius. The bolded blast-radius paragraph is dense and the asterisked emphasis is heavy for prose, but each section carries distinct, non-redundant information rather than restating the name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex gating tool with no output schema, the description covers the return shape, the scope partitioning, the cross-server caveat (cscheduler paused separately), and the read-tool behavior. Nothing essential for calling it correctly is missing, aside from not naming resume_persistence directly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 genuine meaning beyond the schema by tying session_key to scope semantics ("scope: session" vs "scope: process") and the partition-not-credential caveat, and reiterating the ttl default/max. The extra meaning lifts it above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Pause write operations on this MCP server") plus the scoping mechanism (opt-in TTL window), and enumerates the exact write tools affected. An agent can distinguish this from sibling pause/status tools like resume_persistence and persistence_status 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the conditions that select behavior for the key parameter (pass session_key for a per-session pause vs omit for the shared process bucket) and gives an explicit when-to-care warning about blast radius. It lacks an explicit pointer to alternatives such as resume_persistence for ending the pause early or persistence_status for checking, so routing 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.
persistence_statusARead-only
Report whether persistence is currently paused and the TTL remaining (in seconds). It reports the bucket session_key selects (response scope), not the server as a whole: with a session_key it answers for your session only, so paused: false here does not mean no other session is paused. Without one it reflects the bucket every keyless caller shares, which on a streamable-HTTP deployment means paused: true may have been armed by a different keyless session.
| Name | Required | Description | Default |
|---|---|---|---|
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered. Beyond that, the description adds valuable behavioral context: what the `scope` response reflects, and the non-obvious caveat that `paused: false` for your session does not imply no other session is paused, including the keyless-HTTP scenario.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose in the first sentence, then spends the remaining text on the genuinely useful scoping caveats. It is dense but every sentence carries weight; no filler or repetition of the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description correctly enumerates what is returned (paused flag, TTL, scope). For a single-parameter read tool with nuanced scoping, the description supplies everything an agent needs to interpret the result correctly, including the keyless-bucket edge case.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 session_key in depth (opaque partition hint, default, maxLength). The description reinforces which bucket it selects but adds no syntax or format detail beyond the schema, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (report) and resource (persistence paused state) plus the returned value (TTL in seconds). It also distinguishes itself from the broader system by clarifying it reports the session-scoped bucket rather than the server as a whole.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description thoroughly explains the two usage modes (with vs. without session_key) and their differing semantics, which is genuine guidance. However, it never names the natural alternatives (pause_persistence, resume_persistence) or states explicitly when to call this tool versus them, leaving routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recallARead-only
Recall memories by multi-strategy search (vector + FTS5 + keyword). To answer a question from memory, prefer reconstruct: it returns items that quote the rows supporting them, within a character budget. Rows come in ascending order of relevance: the LAST row is the best match (docs/behavior-contracts.md section 1). gate_fallback: true (absent otherwise) means every candidate fell below the quality gate and the below-gate lexical matches were returned: treat them as low-confidence. That rescue runs only with CPERSONA_CONFIDENCE_ENABLED=true and CPERSONA_CONFIDENCE_ORDERING=legacy; otherwise an all-below-gate recall is empty. suggestion (absent otherwise, at most once per session) is something only the user can decide: that this scope has outgrown the scan window with no coarse index for a time cue to search the rest. Relay its message, and run its fix only if the user agrees. Content is a preview of the record's start by default. A row the preview cut also carries excerpt, the part of the record that matched the query (at most 800 characters, CPERSONA_RECALL_EXCERPT_CHARS, 0 = none; passages joined by ' … ' in text order), and excerpt_basis (blocks / lexical / start). Read the excerpt before expanding a row with get_contents(refs). full_content=true returns whole rows, capped at 200,000 characters per response: rows past it fall back to the preview and the response carries full_content_budget_chars. Each scored row carries match_reason {signal, score, ...}: signal is the branch the quality gate keyed on (rsf > cosine > rrf), the other keys are the per-retriever contributions, and prior is the age weight when one ordered the row. An enabled confidence score is returned beside each row but neither orders nor gates it (unless CPERSONA_CONFIDENCE_ORDERING=legacy). Unscored rows (cascade FTS/keyword) have no match_reason.
| Name | Required | Description | Default |
|---|---|---|---|
| deep | No | Deep recall — halves the quality gate (and the calibrated fused gate), so weaker matches are admitted. It also disables time and completion decay, which are inert unless CPERSONA_CONFIDENCE_ENABLED=true, and it does NOT widen the scan window (CPERSONA_MAX_MEMORIES) — deep is about how weak a match may be, not how far back the search reaches. | |
| limit | No | Per-retriever search depth, not a pure response cap: the value is handed to each retrieval channel (vector / episode FTS / keyword) as its top-K, so lowering it shrinks the candidate pool itself — rows beyond the depth are unreachable at any gate value, and score normalization / autocut operate on the smaller pool, which can also reorder what remains. Fewer rows than this may be returned, and more: places held beside it add up to 2 rows the block arm reached, which the quality gate does not judge (CPERSONA_BLOCK_RETRIEVAL_ENABLED, on by default), and any `time_cue`, far or propagation seats. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.) | |
| query | Yes | Search query (empty returns recent memories) | |
| trace | No | 2.6 recall trace: true adds `trace` to the response — which rows each stage (retrieval arms, fusion, quality gate, autocut, final order, count cut, reserved seats) kept, dropped or reordered, and why, with ranks and scores. It carries references only, never stored text, and is not stored on the server. trace_version identifies its shape. False (the default) returns the response unchanged. | |
| channel | No | Filter memories by channel (e.g. 'chat', 'discord'). Default: '' (all channels). | |
| agent_id | Yes | Agent identifier | |
| time_cue | No | 2.6: when the answer was stored, as far as you remember. Give after and/or before (a date YYYY-MM-DD, which includes that whole day, or an ISO-8601 timestamp), or ago, plus confidence. The server also searches that period. Among the rows it returns anyway, one found there moves up by at most 3 / 2 / 1 places (sure / likely / vague), and as many extra seats (3 / 2 / 1) hold the best records that search found which the answer does not already hold: one no other search reached, or one the count cut. The returned rows are those of a recall without the cue, reordered, plus at most those seats: no row is removed or re-scored, and which rows pass the quality gate does not change. If the period holds nothing it is widened once, one confidence step. likely widens the period by half its length on each side, vague by its whole length. The response then carries time_cue: the period searched, whether it was widened, how many rows moved and how many seats were used, and remainder when the period held more records than the vector search reads and no coarse index could search the rest. Pass it only when the request itself says when (a date, a month, "last week", "in the spring"); omit it when the request names no time, and never fill it with today's date or a guess. A cue whose own period starts within the last 24 hours (today, or later) is not used: the rows are those of a recall without a cue, and time_cue in the response says ignored. A cue that cannot be read returns an error and no messages. | |
| source_id | No | v2.4.20 per-user source filter. Empty (default) = no filter. Non-empty = match against json_extract(source, '$.id'): a value ending in a letter or digit matches that one id exactly, e.g. 'discord:12345' to restrict to one Discord user; a value ending in any other character is a prefix, e.g. 'discord:' to scope to all Discord-sourced memories. Episodes carry no per-user source tagging, so they are skipped when this is set — UNLESS channel is also set, which scopes episodes to one conversation and re-admits them. | |
| project_id | No | v2.4.17 γ filter. Omit → no filter (all projects). '' → global pool only. 'X' → 'X' bucket ∪ global pool. Threaded through cascade / RRF / vector / FTS / keyword paths. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it. | |
| session_key | No | Opaque session identity you declare — a partition hint, NOT authentication. It scopes this process's per-session state: the degraded-recall advisory's "already told you" memory, and which no-persist pause applies to this call. It does NOT filter stored data (use agent_id / project_id / channel for that), and it never reaches the database. Omit it to share one bucket with every other caller that omits it, which is the behaviour that predates this parameter. | |
| full_content | No | v2.5.0 preview tier opt-out. By default message content longer than the preview cap (CPERSONA_RECALL_PREVIEW_CHARS, default 500) is returned as a pure prefix with content_truncated/content_len markers; each message's `ref` expands via get_contents. true returns full text. | |
| exclude_contents | No | Normalized content strings to exclude from results (starts-with match). Used to prevent duplication with conversation context already known to the caller. bug-399: the starts-with rule holds only at or above 32 characters. A shorter entry has to EQUAL the stored content (after the normalization this parameter already asks for: stripped and lower-cased) — a short prefix would otherwise suppress every memory beginning with it, inside the retrievers and with nothing in the response reporting the exclusion. Size entries at or above that length when you mean a prefix. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only `readOnlyHint: true` in annotations, the description carries the burden of explaining behavior and does so extensively: row relevance ordering (last row best), `gate_fallback` meaning low-confidence rescue rows, `suggestion` semantics, preview/excerpt behavior, `full_content` budget caps, and `match_reason` contents. It adds substantial context beyond the annotation without contradicting the read-only nature of the tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and alternative are front-loaded, and much of the detail is relevant for a complex 12-parameter tool with no output schema. However, the description is a single dense paragraph packed with environment variables, parenthetical caveats, and response internals, making it harder to scan than it should be. It is adequate but not concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, 12 parameters, and absence of an output schema, the description explains return behavior, ordering, confidence/gate handling, preview limits, and important edge cases. Combined with the fully documented input schema, an agent has enough information to invoke and interpret this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema itself documents every parameter in detail, including `deep`, `limit`, `time_cue`, `source_id`, `project_id`, and `exclude_contents`. The main description adds mostly response-shape semantics rather than additional parameter meaning beyond the schema. Baseline 3 is appropriate when the schema already does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence gives a specific verb and resource: 'Recall memories by multi-strategy search (vector + FTS5 + keyword).' It also distinguishes this tool from the sibling `reconstruct` by stating when to prefer that alternative. An agent can identify the tool and its role 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'To answer a question from memory, prefer `reconstruct`,' naming the alternative and the condition that selects it. It also gives operational guidance to read `excerpt` before expanding a row with `get_contents(refs)`. This is clear routing guidance, not merely implied usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recall_with_contextARead-only
Recall memories and merge with external conversation context. Automatically deduplicates, sorts chronologically, and returns a unified list. Replaces separate recall + manual merge in the caller. Content is preview-tiered by default — see recall's full_content / get_contents (full_content shares recall's 200k-char response budget, bug-211); a recalled row the preview cut carries excerpt / excerpt_basis as recall's do. Every external_context entry's content filters the recall (the caller already holds that text), but only role=user / role=assistant entries are merged into messages. When entries of other roles are present the response carries context_filter_only={roles:[...]} — those entries filtered the recall without appearing in the output, whether or not they dropped a memory this time. context_field_issues={entries:[{index, fields}]} (absent otherwise) names entries whose declared field was not a string: it was read as absent and the entry merged without it. CPERSONA_EXTERNAL_CONTEXT_MODE=reject refuses such a call instead. gate_fallback=true (absent otherwise) is forwarded from the underlying recall: every candidate fell below the quality gate and the below-gate lexical matches were returned — treat them as low-confidence. Only CPERSONA_CONFIDENCE_ORDERING=legacy with confidence on does that; otherwise such a recall contributes no rows.
| Name | Required | Description | Default |
|---|---|---|---|
| deep | No | Deep recall — same semantics as in `recall`: halves the quality gate so weaker matches are admitted. | |
| limit | No | Per-retriever search depth for the underlying recall, not a pure response cap — same semantics as recall's limit: lowering it shrinks the candidate pool itself, not just the rows returned. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.) | |
| query | Yes | Search query | |
| channel | No | Memory channel filter | |
| agent_id | Yes | Agent ID | |
| source_id | No | v2.4.20 per-user source filter — passed through to recall. Same semantics as in `recall`. | |
| project_id | No | v2.4.17 γ filter — passed through to recall. Same semantics as in `recall`. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it. | |
| session_key | No | Opaque session identity you declare — a partition hint, NOT authentication. It scopes this process's per-session state: the degraded-recall advisory's "already told you" memory, and which no-persist pause applies to this call. It does NOT filter stored data (use agent_id / project_id / channel for that), and it never reaches the database. Omit it to share one bucket with every other caller that omits it, which is the behaviour that predates this parameter. | |
| full_content | No | v2.5.0 preview tier opt-out — same semantics as in `recall`. | |
| external_context | No | Conversation history entries [{role, content, name?, user_id?, timestamp?}, ...]. Every declared field is a string; one that is not is read as absent and reported in context_field_issues (CPERSONA_EXTERNAL_CONTEXT_MODE). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only readOnlyHint available, the description carries the disclosure burden and does so thoroughly: deduplication and chronological ordering, preview-tiering by default with the shared 200k-char budget (bug-211), excerpt/excerpt_basis on cut rows, non-user/assistant roles silently filtering without merging, and the CPERSONA_EXTERNAL_CONTEXT_MODE=reject refusal path. The gate_fallback semantics (low-confidence rows returned only under legacy confidence ordering) is exactly the kind of silent-degradation signal an agent could not get elsewhere.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose is front-loaded in the first two sentences, which is correct. After that the text becomes a dense run of nested parentheticals, bug IDs, env var names and cross-references to `recall`'s semantics, which slows scanning even though most details are individually useful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Ten parameters, no output schema, yet the description names and explains the response-side signals an agent must interpret: context_filter_only, context_field_issues, gate_fallback, resolved_project_id and excerpt fields. For a composite recall tool with silent filtering behavior, this is complete enough to act on.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the per-parameter descriptions are already unusually rich, so the baseline would be 3. The prose adds genuine meaning beyond the schema: that external_context content doubles as a query filter even when it is not merged, and which roles make it into messages versus only into context_filter_only.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening states a specific verb and resource (recall memories) plus the composite behavior (merge with external conversation context), and explicitly positions itself against the sibling: 'Replaces separate recall + manual merge in the caller.' An agent can distinguish this from `recall` 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Replaces separate recall + manual merge in the caller' tells the agent when to prefer this over the plain `recall` sibling, and the external_context paragraph explains the filtering-vs-merging split that determines how to shape input. It stops short of an explicit when-not-to-use clause, so it is clear context rather than full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reconstructARead-only
The recommended way to answer a question from memory. Returns up to count ITEMS (10 by default): units of memory, each quoting verbatim its most relevant record. content is the parts of that record that matched the query; pass head_ref, or an item's expand as it is, to get_contents to read more. No model is called and nothing is summarised; stored rows are never modified. COUNT IS A CEILING, not a fill target or a search depth: fewer items is a normal result and carries shortfall_reason, and nothing is padded. reserved_count held items (block, cue, far, mode seats) may trail the window. Breadth is set apart from count: raise top_k when items are missing evidence; max_hops and max_evidence bound the declared-relation walk and the rows kept per item. BUDGET bounds the characters of quoted text (content plus excerpts), where count bounds items: excerpts are omitted before items, and raising the budget never removes an item or an excerpt. Omit it for the server default. mode (lite / pro) caps the tokens returned (see it). READ FURTHER IN STEPS, smallest first: an item's expand, then the neighbours of a quote's node or block index ({ref, node: [i-1, i+1]}, or block), and the bare ref, the whole record, last. ITEM: claims lists the retained rows newest first (ref, as_of, why, hops, roles); excerpts quote the other claims. roles[].role names what the REFERENCED row is to this claim (supports / supersedes / corrects / qualifies / contradicts / temporal_predecessor); ignore a role you do not know. conflicts appears only when two rows cannot be ordered. A response adds fields only when the server did something other than asked (count_policy, budget_policy, bounds, gate_fallback); their absence does not say the items suffice or that the whole store was searched. trace=true returns the full audit. Declared associations widen the lexical search and add walked evidence inside items, never items. Full contract: docs/RELIABLE_RECALL_2_6.md section 7.
| Name | Required | Description | Default |
|---|---|---|---|
| deep | No | Deep recall for the candidate stage -- same semantics as in `recall`. | |
| lite | No | The same as `mode: "lite"`, which is the name to use; kept so that older callers keep working. Not together with `mode`. Default false. | |
| mode | No | A named answer size whose returned JSON is at most `cap` cl100k_base tokens, counted as sent (`used_tokens`). "lite" (cap 3,000) is for a question one or two records can answer: up to 2,800 characters unless `budget` is given, short records whole first, then the best-ranked passages of longer ones. "pro" (cap 5,000) reads 15 items by default and quotes as much as the cap holds. Both leave out `ranges`, `bounds` and the budget fields; an item whose only claim is its head carries that claim's `as_of` instead of `claims`. A larger cap never removes an item or an excerpt. Omit it for the default response, which has no cap. `trace=true` returns every field and is not held to the cap. | |
| count | No | Ceiling on recall items returned -- not a fill target, not a search depth. Declare per call; omit to take the server default (10 unless configured). An operator-forced value overrides both. Requesting 5 with only 2 valid items returns 2; neither setting requires filling the window. Clamped to the server maximum, and the clamp is reported in count_policy rather than applied silently. | |
| query | Yes | Search query (empty returns recent memories) | |
| top_k | No | Candidate depth: how many rows the retrieval hands to bundling. This is the breadth knob; it is independent of `count` and is what to raise when items are missing evidence. | |
| trace | No | Include candidate refs and cluster membership for local diagnosis, and the trace of the recall it made as trace.recall; no full text is added. | |
| budget | No | Payload budget: characters of quoted text (item `content` plus `excerpts`) the response may carry. Bounds depth, where `count` bounds breadth, and breadth wins: excerpts are omitted before any item is. Omit for the server default, which is never less than one quote per item of the window; an operator-forced value overrides both; clamped to the server maximum and raised to one head quote, and budget_policy says which. | |
| channel | No | Memory channel filter | |
| agent_id | Yes | Agent identifier | |
| max_hops | No | Relation hops the walk may follow from an item's candidates through declared entity -> entity relations. 0, or any mode, adds no walked evidence. A relation left unfollowed at the bound is named in bounds.omitted. At most 5, as for traverse: a larger value is lowered to it, and bounds.max_hops then states the value applied. | |
| time_cue | No | 2.6: when the answer was stored, as far as you remember — the same object recall takes (see recall's time_cue), applied to the candidate recall this reconstruction reads. The response carries time_cue when one was applied. | |
| source_id | No | Per-user source filter -- same semantics as in `recall`. | |
| project_id | No | γ filter -- same semantics as in `recall`, including the '@auto' sentinel, which resolves this agent's default from the server's operating context and echoes the resolution as resolved_project_id. With no configured operating context the sentinel is NOT resolved: it is filtered as the literal project_id '@auto'. Read resolved_project_id before relying on the resolution. | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Forwarded to the candidate recall. | |
| max_evidence | No | Maximum retained rows per item, bounding its claims and role targets. A cut is named in bounds.omitted and counted in the item's claims_omitted. At most 100, as traverse's limit: a larger value is lowered to it, and bounds.max_evidence then states the value applied. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, yet the description adds substantial non-obvious behavior: no model is called, nothing is summarised, stored rows are never modified, count is a ceiling rather than a fill target with `shortfall_reason` on shortfall, `reserved_count` items may trail the window, `budget` omits excerpts before items, and response fields like `count_policy`/`bounds` appear only when the server deviated from the request. This is exactly the kind of context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded sensibly — purpose first, then count/budget semantics, then reading workflow — and with no output schema the return-shape detail is load-bearing. But the prose is dense and jargon-heavy (ALL-CAPS emphasis, 'reserved_count held items (block, cue, far, mode seats) may trail the window'), and several return-field sentences (`claims`, `roles[].role`, `conflicts`) could be tightened without loss.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 16-parameter tool with only a readOnlyHint annotation and no output schema, the description supplies what an agent actually needs: what an item contains, how to expand incrementally, how count differs from budget and top_k, when policy fields appear, and what `trace=true` does. Nothing material about calling it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the per-parameter schema text already spells out count-as-ceiling, the budget/excerpt precedence, max_hops clamping and mode caps, so the description largely restates the schema. It does add the framing that 'count bounds items, budget bounds characters' and that `top_k` is the breadth knob separate from count, but that distinction is also present in the schema descriptions. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence pins a specific verb and resource — 'answer a question from memory' returning 'up to `count` ITEMS ... each quoting verbatim its most relevant record' — which is far more concrete than a tautology. However, it never distinguishes itself from the sibling tools it repeatedly defers to ('same semantics as in `recall`', 'as for traverse'); an agent choosing between `recall`, `recall_with_context` and `reconstruct` gets no differentiation from the description body.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is genuine operational guidance: 'READ FURTHER IN STEPS, smallest first: an item's `expand`, then the neighbours ... and the bare ref, the whole record, last' and 'raise `top_k` when items are missing evidence'. But this is guidance for using the result, not guidance on when to pick this tool versus `recall`/`recall_with_context`; tool selection is left entirely to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resume_persistenceAIdempotent
Re-enable persistence immediately, clearing this caller's active no-persist TTL. Returns was_active=true if THIS bucket was paused before the call. It clears only the bucket session_key selects (response scope): with a session_key, your own pause and no other session's; without one, the shared keyless bucket, which on a streamable-HTTP deployment re-enables writes for every other keyless session too.
| Name | Required | Description | Default |
|---|---|---|---|
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover idempotency (idempotentHint=true) and non-destructiveness, and the description goes well beyond them: it discloses the return value (was_active), the response `scope` field, and critically warns that omitting session_key on a streamable-HTTP deployment re-enables writes for every other keyless session. That shared-side-effect warning is exactly the kind of behavioral context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The action is front-loaded in the first sentence, followed by return semantics and then scope caveats. It is dense but every sentence earns its place; the bolded scope clause is long but carries the highest-stakes information, so it is justified rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must carry return-value meaning, and it does (was_active, response `scope`). Combined with the side-effect disclosure, an agent has everything needed to invoke it correctly and understand the consequences of the session_key choice.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 beyond the schema: it explains that session_key selects which pause bucket is cleared, that with a key only your own pause is affected, and that omitting it targets a shared keyless bucket. This meaningfully clarifies the parameter's blast radius.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Re-enable persistence immediately, clearing this caller's active no-persist TTL'), which precisely names the action and its mechanism. It is unambiguously the inverse of the sibling pause_persistence, so an agent can select it 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear usage context: re-enable persistence, and the scope of what gets re-enabled depending on session_key. However, it never explicitly names pause_persistence as the counterpart or states conditions for choosing between them, so routing relies on inference from the obvious inverse relationship.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_recall_precisionA
Set an agent's recall precision (knob 3) and recalibrate its quality gate. precision = strict | balanced | lenient maps to a specificity weight beta of 2.0 / 1.0 / 0.5 in the gate separation objective (sensitivity + beta*specificity): strict sits the gate higher (fewer contaminants, more misses), lenient lower (fewer misses, more contaminants). A raw beta > 0 overrides the named level; an empty precision with beta <= 0 clears the per-agent override and returns the agent to the global CPERSONA_RECALL_PRECISION default. The gate is recalibrated at the new beta immediately and persisted, so the change is live without a restart. Precision is a per-agent setting, not a per-recall argument: the gate threshold is precomputed on the separation curve at a fixed beta, so this tool recalibrates once instead.
| Name | Required | Description | Default |
|---|---|---|---|
| beta | No | Raw specificity weight; overrides the named precision when > 0. | |
| agent_id | Yes | Agent whose precision to set | |
| precision | No | strict / balanced / lenient. Empty (with beta <= 0) clears the override. | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds real behavioral context beyond annotations: the gate is recalibrated at the new beta immediately and persisted, so the change is live without a restart, and clearing behavior returns the agent to the global default. Consistent with readOnlyHint=false. It does not cover auth/permission requirements, so not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and the set+recalibrate behavior are front-loaded, and the beta-mapping detail follows logically. It is dense but each sentence carries specification value; the final explanatory sentence is slightly long but justifies the per-agent design.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-param mutation tool with full schema coverage and annotations but no output schema, the description covers semantics, override precedence, persistence, and reset behavior adequately. Only the response shape and permission needs are unaddressed, which is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (baseline 3), and the description adds meaning beyond the schema by mapping precision levels to concrete beta values (2.0/1.0/0.5), explaining that raw beta > 0 overrides the named level, and defining the empty-precision clearing condition. This enrichment of the override interaction earns a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — 'Set an agent's recall precision ... and recalibrate its quality gate' — which is clearly distinct from the read-side sibling get_recall_precision and from calibrate_threshold. The dual action (set + recalibrate) is spelled out rather than implied.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains the named levels, the beta override precedence, and the clearing semantics, and the closing sentence clarifies this is a per-agent setting rather than a per-recall argument. It gives clear context for when to use it, though it never explicitly names the sibling (get_recall_precision) for reading the current value.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
storeAIdempotent
Store a memory for future recall: put the text in content. agent_id may be omitted on a connection that can write to exactly one agent (the agent used is echoed as resolved_agent_id), and source defaults to that agent. lock:true also locks the memory, as lock_memory does, and the response says locked:true|false. Every response carries result, the one field to branch on: 'stored' (a new row: id, and embedded, whether a vector was kept; truncated:true when content was shortened to the length cap; nodes:{status:'queued'} when the text runs past the embedding window and its overflow-tree nodes were queued), 'skipped' (nothing written and nothing wrong: a duplicate id, or content already stored under the same source id, echoes the existing row's id, except reason='duplicate (unique index)', a concurrent-write race, which has none), or 'rejected' (ok:false, nothing written; reason is human-readable, not a stable token). Under pause_persistence the write is skipped and the response carries persisted:false (id:'no-persist'), which tells a paused write from a duplicate. The older message={content, id, source, timestamp, metadata} form still works; use it or the top level, not both.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | Optional caller id: a later store with the same id in the same project is skipped as a duplicate. | |
| lock | No | Also lock the memory against edits and deletion. Default false. | |
| source | No | Who produced the content, {type, id, name}. Default: {type:'Agent', id:<agent_id>}. Pass {} only when the producer is unknown. | |
| channel | No | Memory channel for context separation (e.g. 'chat'). Default: '' (shared). | |
| content | No | The text to store; a [Memory from …] annotation is removed anywhere in it, and text left empty is refused with ok:false, result:'rejected'. | |
| message | No | The older form of content, id, source, timestamp and metadata. | |
| agent_id | No | Agent identifier. May be omitted when this connection can write to exactly one agent. | |
| metadata | No | Free-form JSON, at most 8000 characters serialised (source has the same cap). Larger is rejected, not truncated. | |
| timestamp | No | ISO-8601 with an offset, e.g. '2026-07-22T12:00:00+00:00'. Default: now. | |
| project_id | No | Isolation bucket; omit or '' for the global pool (a recall in 'X' reads 'X' and the global pool). '@auto' resolves this agent's default from the operating context, echoed as resolved_project_id; with no operating context configured it is stored as the literal '@auto'. | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. | |
| associations | No | Entities and relations to declare against this memory, as declare_associations takes them: {entities: [{name, aliases}], relations: [{subject, predicate, object}]}, subject / object being entity names or 'mem:<id>' / 'ep:<id>' refs. Malformed items are reported in the response's associations.dropped and skipped; the memory is stored regardless. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses the full result branching contract: 'stored', 'skipped', and 'rejected', including duplicate handling, concurrent-race reason behavior, truncation, queued overflow nodes, and persisted:false under pause_persistence. It also explains that rejected responses are ok:false and not written. This is rich behavioral context that an agent cannot get from readOnlyHint/idempotentHint/destructiveHint alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core action and then proceeds through defaults, result values, pause behavior, and the legacy form. It is dense and paragraph-shaped rather than cleanly sectioned, but nearly every sentence carries operational meaning for a 12-parameter tool. It is not padded with fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter write tool with no output schema, the description covers the essential response semantics, duplicate/skip distinctions, pause_persistence interaction, lock echo, truncation, and legacy message form. The schema already documents the remaining parameter details at 100% coverage. An agent has enough information to call the tool and interpret the result correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline would be 3. The description still adds cross-parameter meaning: agent_id omission is resolved and echoed as resolved_agent_id, source defaults to that agent, lock:true produces locked:true|false, and content is explicitly the text to store. It does not restate every schema detail, but it materially improves how the main parameters interact.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: "Store a memory for future recall" and tells the caller to put the text in content. It also distinguishes the lock behavior from lock_memory, though it does not broadly route against all sibling write/update tools. The main action is unambiguous, but sibling differentiation is partial rather than complete.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explains when agent_id may be omitted, that source defaults to that agent, and that lock:true also locks the memory as lock_memory does. It also clarifies that the older message form still works but must not be combined with the top-level form. What is missing is explicit guidance on when to use store versus update_memory or import_memories.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
traverseARead-only
The neighbourhood of a declared entity, as a graph: the entity named, its aliases, the entity -> entity relations declared on it and on what they reach, up to max_hops in either direction, and the refs of the records that mention each entity. Only what was declared (declare_associations, or associations on store); nothing is inferred. No record text: expand a ref with get_contents. entity is a name or an alias, compared after normalization; when it names more than one entity this call can read (a project's and the global pool's), all are starts. ORDER: entities by hops, then by the most recently declared relation that reached them, then by id; mentions by record id; relations most recently declared first. limit bounds both the entities returned and the refs listed per entity. Response: {entity, max_hops, limit, entities:[{id, name, hops, aliases?, mentions?, mentions_omitted?}], relations:[{id, subject, predicate, object, declared_by, declared_at, anchor_ref?}] (subject/object are entity ids from entities; a relation is listed when both ends are), entities_omitted?, bounds?:{omitted:[max_hops | limit]}, reason?:'no_such_entity'}. Relation ids are what declare_associations' retract takes. Records are listed only when this call could read them: the project, channel and source_id filters apply as in recall.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum entities returned, and maximum mentioning refs listed per entity. | |
| entity | Yes | A declared entity name or alias. | |
| channel | No | Memory channel filter -- same semantics as in `recall`. | |
| agent_id | Yes | Agent identifier | |
| max_hops | No | Relations to follow from the entity, in either direction. 0 returns the entity alone. | |
| source_id | No | Per-user source filter on the mentioning records -- same semantics as in `recall`. | |
| project_id | No | γ filter -- same semantics as in `recall`. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Consistent with readOnlyHint=true. Beyond the annotation it discloses that nothing is inferred, that no record text is returned, the exact ordering rules (hops, then most recently declared relation, then id; mentions by record id; relations newest first), how `limit` is applied at two levels, the visibility constraint on records, the omission/bounds signaling, and the 'no_such_entity' reason. It also notes relation ids are the tokens `declare_associations.retract` consumes, which is genuinely non-obvious behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with purpose and scope before detail, and every clause carries information: ordering, bounds, error reason, visibility. The trade-off is a dense single block with the response shape enumerated inline; in the absence of an output schema this earns its place, but paragraph or bullet structure would improve scanability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter graph tool with no output schema, the description fully specifies the return shape (entity, max_hops, limit, entities[], relations[], omission fields, bounds, reason), ordering, truncation behavior, and visibility filtering. Nothing an agent needs in order to call and interpret it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema coverage the baseline is 3, and the description adds real meaning beyond the schema: `entity` is normalized and matched against aliases, and when it resolves to an entity in both a project and the global pool, all matches are traversal starts. It also clarifies `limit` bounds both entity count and per-entity ref count. `max_hops` bi-directionality and the filter semantics largely restate the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise statement of the resource and scope: the declared entity's neighbourhood as a graph, including aliases, entity→entity relations up to `max_hops` in either direction, and refs of mentioning records. It explicitly separates itself from siblings by stating there is no record text and pointing to get_contents for expansion. An agent can distinguish this from `recall` or `declare_associations` 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It defines the data's provenance ('Only what was declared (declare_associations, or `associations` on store); nothing is inferred') and routes the agent to `get_contents` when record text is needed. It also explains that the project/channel/source_id filters behave as in `recall`. It stops short of explicitly saying when to prefer this over `recall`/`recall_with_context` for non-graph lookups, so it is clear but not a full when/when-not map.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unlock_memoryAIdempotent
Unlock a memory to allow deletion and editing. Ownership enforced when agent_id provided.
| Name | Required | Description | Default |
|---|---|---|---|
| agent_id | No | Agent ID for ownership verification | |
| memory_id | Yes | Memory ID to unlock | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (idempotent, non-destructive, not read-only), and the description adds genuinely new behavior: the unlock gate that enables subsequent edits/deletes, plus the conditional 'Ownership enforced when agent_id provided' authorization rule. It omits failure behavior for non-owners and whether the unlock state persists, so not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with zero filler; the purpose and its consequence are front-loaded before the authorization caveat.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-param state-changing gate tool with no output schema, the definition covers purpose, downstream effect, and the ownership rule. Minor gaps remain around non-owner error behavior and the scope/duration of the unlock, but the essential call-time information is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already describes agent_id as 'Agent ID for ownership verification', memory_id as the target, and session_key at length. The description's 'Ownership enforced when agent_id provided' adds only a marginal conditional nuance (what happens when it is omitted), so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Unlock a memory') and the concrete effect ('to allow deletion and editing'), which is enough to tell it apart from the inverse sibling lock_memory without opening a schema. It stops short of naming that sibling explicitly, but the intent is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: by saying unlocking enables deletion and editing, an agent can infer it precedes delete_memory/update_memory. There is no explicit when-to-use statement, no prerequisites, and no named alternative, so it lands at minimum-viable guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_memoryAIdempotent
Update memory content by ID. Rejects if memory is locked. Ownership enforced when agent_id provided. The new content passes through the same sanitizer as store: it is capped at the content length limit (the response carries truncated:true when the cap bit) and [Memory from ...] annotations are stripped, so content consisting only of those is refused rather than written as an empty row. A new text that another memory in the same agent, project and channel already holds is refused with ok:false naming that memory, since one copy is kept per bucket. A new text that runs past the embedding window gets nodes:{status:'queued'}, as on store.
| Name | Required | Description | Default |
|---|---|---|---|
| content | Yes | New content for the memory | |
| agent_id | No | Agent ID for ownership verification | |
| memory_id | Yes | Memory ID to update | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false, idempotentHint=true and destructiveHint=false. The description goes well beyond them, disclosing sanitization rules, truncation signaling (truncated:true), duplicate-bucket refusal with ok:false naming the conflicting memory, and embedding-window queueing behavior – exactly the behavioral context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action and constraint in the first two sentences, then details failure modes. The middle sentence is dense and slightly run-on, but every clause carries actionable information about rejection conditions and response fields.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with no output schema, it explains the shape of responses (ok:false, truncated:true, nodes:{status:'queued'}) and all major failure modes, so an agent knows what outcomes to expect. Nothing essential to correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real meaning: it explains the ownership role of agent_id, the sanitizer applied to content, and that content-only annotations are refused. session_key semantics are left entirely to the already-rich schema description, which is acceptable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Update memory content by ID') with an immediate constraint (rejects if locked). It implicitly distinguishes from store (store creates, update modifies) and from lock/delete siblings, but never names a sibling that performs an alternative path.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear conditions and exclusions: refuses when locked, when ownership fails, when content is only annotations, and when the text duplicates another memory in the same agent/project/channel bucket. However, it never tells the agent which sibling to use instead (e.g., lock_memory, store) for those rejected cases.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_profileA
Save a pre-computed agent profile to the database. The text passes through the same sanitizer as store, against the profile's own ceiling: it is capped at 2000 characters (CPERSONA_MAX_PROFILE_LENGTH) and the response carries truncated:true when the cap bit — branch on it, the discarded remainder is not stored anywhere else.
| Name | Required | Description | Default |
|---|---|---|---|
| profile | Yes | Profile text to save (pre-computed by caller). Capped at 2000 characters (CPERSONA_MAX_PROFILE_LENGTH); the response says truncated:true when the cap cut it. | |
| agent_id | Yes | Agent identifier | |
| session_key | No | Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, and the description adds real behavioral value beyond them: it names the sanitizer, discloses the 2000-char ceiling, reveals the truncated:true response flag, and warns that the discarded remainder is not stored elsewhere. It omits whether an existing profile is overwritten on save, which is the one meaningful gap for a save operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the core action, then a dense but relevant clause on cap behavior. Every clause carries information; the parenthetical constant name is the only mild filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates by explaining the response's truncated:true flag and its consequence. For a non-destructive write tool with full annotation and schema coverage, this is nearly complete; only overwrite/replace semantics are left unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 three parameters, including the same 2000-char cap, making 3 the baseline. The description's only added nuance is that discarded text is not persisted anywhere, which is behavioral rather than parameter-meaning clarification.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Save a pre-computed agent profile to the database") and the 'pre-computed' qualifier tells the agent the caller supplies the text. It references the sibling 'store' as the sanitizer source but never explicitly contrasts itself with get_profile or store, so sibling differentiation is only implicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is a weak hint that the profile is 'pre-computed by caller', implying this is a write-only sink rather than a generator. But there is no statement of when to use this versus get_profile, store, or update_memory, and no preconditions are named. The agent must infer routing from the name alone.
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.
10 tool updates
v2.6.12- Changed
archive_episode3 fields changed- added
Input schema / properties / channel / maxLengthAdded value: +256 - changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 isolation axis. Omit or pass '' for the global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."New value: +"v2.4.17 isolation axis. Omit or pass '' for the global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it." - added
Input schema / properties / project_id / maxLengthAdded value: +256
- Changed
check_update1 field changed- changed
Input schema / properties / refresh / descriptionPrevious value: -"Fetch the package index now instead of reading the cached verdict (3s budget; a failure answers state=unknown)."New value: +"Fetch the package index now instead of reading the cached verdict (3s budget). A failed fetch keeps the verdict already known, so `checked_at` does not move; with none known it answers state=unknown."
- Changed
declare_associations1 field changed- changed
Input schema / properties / project_id / descriptionPrevious value: -"Project the declaration belongs to. Optional — omit or pass '' for the global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."New value: +"Project the declaration belongs to. Optional — omit or pass '' for the global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it."
- Changed
list_episodes1 field changed- changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 γ filter. Same semantics as list_memories. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."New value: +"v2.4.17 γ filter. Same semantics as list_memories. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it."
- Changed
list_memories1 field changed- changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 γ filter. Omit → no filter; '' → global pool only; 'X' → 'X' ∪ global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."New value: +"v2.4.17 γ filter. Omit → no filter; '' → global pool only; 'X' → 'X' ∪ global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it."
- Changed
recall3 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Per-retriever search depth, not a pure response cap: the value is handed to each retrieval channel (vector / episode FTS / keyword) as its top-K, so lowering it shrinks the candidate pool itself — rows beyond the depth are unreachable at any gate value, and score normalization / autocut operate on the smaller pool, which can also reorder what remains. Fewer rows than this may be returned. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.)"New value: +"Per-retriever search depth, not a pure response cap: the value is handed to each retrieval channel (vector / episode FTS / keyword) as its top-K, so lowering it shrinks the candidate pool itself — rows beyond the depth are unreachable at any gate value, and score normalization / autocut operate on the smaller pool, which can also reorder what remains. Fewer rows than this may be returned, and more: places held beside it add up to 2 rows the block arm reached, which the quality gate does not judge (CPERSONA_BLOCK_RETRIEVAL_ENABLED, on by default), and any `time_cue`, far or propagation seats. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.)" - changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 γ filter. Omit → no filter (all projects). '' → global pool only. 'X' → 'X' bucket ∪ global pool. Threaded through cascade / RRF / vector / FTS / keyword paths. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."New value: +"v2.4.17 γ filter. Omit → no filter (all projects). '' → global pool only. 'X' → 'X' bucket ∪ global pool. Threaded through cascade / RRF / vector / FTS / keyword paths. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it." - changed
Input schema / properties / source_id / descriptionPrevious value: -"v2.4.20 per-user source filter. Empty (default) = no filter. Non-empty = prefix match against json_extract(source, '$.id'), e.g. 'discord:12345' to restrict to one Discord user, or 'discord:' to scope to all Discord-sourced memories. Episodes carry no per-user source tagging, so they are skipped when this is set — UNLESS channel is also set, which scopes episodes to one conversation and re-admits them."New value: +"v2.4.20 per-user source filter. Empty (default) = no filter. Non-empty = match against json_extract(source, '$.id'): a value ending in a letter or digit matches that one id exactly, e.g. 'discord:12345' to restrict to one Discord user; a value ending in any other character is a prefix, e.g. 'discord:' to scope to all Discord-sourced memories. Episodes carry no per-user source tagging, so they are skipped when this is set — UNLESS channel is also set, which scopes episodes to one conversation and re-admits them."
- Changed
recall_with_context1 field changed- changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 γ filter — passed through to recall. Same semantics as in `recall`. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."New value: +"v2.4.17 γ filter — passed through to recall. Same semantics as in `recall`. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it."
- Changed
reconstruct4 fields changed- changed
Input schema / properties / max_evidence / descriptionPrevious value: -"Maximum retained rows per item, bounding its claims and role targets. A cut is named in bounds.omitted and counted in the item's claims_omitted."New value: +"Maximum retained rows per item, bounding its claims and role targets. A cut is named in bounds.omitted and counted in the item's claims_omitted. At most 100, as traverse's limit: a larger value is lowered to it, and bounds.max_evidence then states the value applied." - added
Input schema / properties / max_evidence / maximumAdded value: +100 - changed
Input schema / properties / max_hops / descriptionPrevious value: -"Relation hops the walk may follow from an item's candidates through declared entity -> entity relations. 0 adds no walked evidence. A relation left unfollowed at the bound is named in bounds.omitted. At most 5, as for traverse: a larger value is lowered to it, and bounds.max_hops then states the value applied."New value: +"Relation hops the walk may follow from an item's candidates through declared entity -> entity relations. 0, or any mode, adds no walked evidence. A relation left unfollowed at the bound is named in bounds.omitted. At most 5, as for traverse: a larger value is lowered to it, and bounds.max_hops then states the value applied." - changed
Input schema / properties / mode / descriptionPrevious value: -"A named answer size whose returned JSON is at most `cap` cl100k_base tokens, counted as sent (`used_tokens` says how many). \"lite\" (cap 3,000) is for a question one or two records can answer: up to 2,800 characters unless `budget` is given, short records whole first, then the best-ranked passages of longer ones. \"pro\" (cap 5,000) reads 15 items by default and quotes as much as the cap holds. Both leave out `ranges`, `bounds` and the budget fields; an item whose only claim is its head carries that claim's `as_of` instead of `claims`. A larger cap never removes an item or an excerpt. Omit it for the default response, which has no cap. `trace=true` returns every field and is not held to the cap."New value: +"A named answer size whose returned JSON is at most `cap` cl100k_base tokens, counted as sent (`used_tokens`). \"lite\" (cap 3,000) is for a question one or two records can answer: up to 2,800 characters unless `budget` is given, short records whole first, then the best-ranked passages of longer ones. \"pro\" (cap 5,000) reads 15 items by default and quotes as much as the cap holds. Both leave out `ranges`, `bounds` and the budget fields; an item whose only claim is its head carries that claim's `as_of` instead of `claims`. A larger cap never removes an item or an excerpt. Omit it for the default response, which has no cap. `trace=true` returns every field and is not held to the cap."
- Changed
store7 fields changed- added
Input schema / properties / channel / maxLengthAdded value: +256 - changed
Input schema / properties / content / descriptionPrevious value: -"The text to store. Content that is empty — or that sanitizes to empty — is refused with ok:false, result:'rejected'."New value: +"The text to store; a [Memory from …] annotation is removed anywhere in it, and text left empty is refused with ok:false, result:'rejected'." - added
Input schema / properties / id / maxLengthAdded value: +256 - added
Input schema / properties / message / properties / id / maxLengthAdded value: +256 - added
Input schema / properties / message / properties / timestamp / maxLengthAdded value: +64 - added
Input schema / properties / project_id / maxLengthAdded value: +256 - added
Input schema / properties / timestamp / maxLengthAdded value: +64
- Changed
traverse1 field changed- changed
Input schema / properties / project_id / descriptionPrevious value: -"γ filter -- same semantics as in `recall`. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."New value: +"γ filter -- same semantics as in `recall`. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context, echoed as resolved_project_id. An unmapped agent gets '' with operating_context_warning; enforce='off' omits the warning, and enforce='reject' refuses a write. bug-186: resolution requires a configured operating context. With none (the default, or an unparsable sidecar) '@auto' is NOT resolved: it is stored and filtered as a literal project_id, resolved_project_id echoes '@auto', and no warning is raised. Check resolved_project_id before relying on it."
2 tool updates
v2.6.10- Changed
reconstruct2 fields changed- changed
Input schema / properties / lite / descriptionPrevious value: -"A smaller, cheaper response, for a question one or two records can answer. Quotes at most 2,800 characters unless `budget` is given, short records whole first and then the best-ranked passages of longer ones. Leaves out `ranges`, `bounds` and the budget fields; an item whose only claim is its head carries that claim's `as_of` instead of `claims`. The response says `lite: true`. It can miss evidence the default finds: use the default when the question needs several records or a lite answer was incomplete. `trace=true` returns every field. Default false."New value: +"The same as `mode: \"lite\"`, which is the name to use; kept so that older callers keep working. Not together with `mode`. Default false." - added
Input schema / properties / modeAdded value: +{ + "description": "A named answer size whose returned JSON is at most `cap` cl100k_base tokens, counted as sent (`used_tokens` says how many). \"lite\" (cap 3,000) is for a question one or two records can answer: up to 2,800 characters unless `budget` is given, short records whole first, then the best-ranked passages of longer ones. \"pro\" (cap 5,000) reads 15 items by default and quotes as much as the cap holds. Both leave out `ranges`, `bounds` and the budget fields; an item whose only claim is its head carries that claim's `as_of` instead of `claims`. A larger cap never removes an item or an excerpt. Omit it for the default response, which has no cap. `trace=true` returns every field and is not held to the cap.", + "enum": [ + "lite", + "pro" + ], + "type": "string" +}
- Changed
store19 fields changed- changed
Input schema / properties / agent_id / descriptionPrevious value: -"Agent identifier"New value: +"Agent identifier. May be omitted when this connection can write to exactly one agent." - changed
Input schema / properties / associations / descriptionPrevious value: -"Associative memory to declare alongside this call: entities the text mentions, with their aliases, and subject–predicate–object relations. Stored verbatim; the server extracts nothing and infers nothing. On store, the stored memory is recorded as mentioning every entity named here and anchors every relation. Malformed items are reported in the response's associations.dropped and skipped; the memory is stored regardless. Optional."New value: +"Entities and relations to declare against this memory, as declare_associations takes them: {entities: [{name, aliases}], relations: [{subject, predicate, object}]}, subject / object being entity names or 'mem:<id>' / 'ep:<id>' refs. Malformed items are reported in the response's associations.dropped and skipped; the memory is stored regardless." - removed
Input schema / properties / associations / propertiesRemoved value: -{ - "entities": { - "description": "Entities to register (if new) and mark as mentioned. Names and aliases are compared after normalization (NFKC, case-folded, whitespace collapsed), so two that normalize alike name one entity.", - "items": { - "properties": { - "aliases": { - "description": "Other names for the same entity. An alias resolves to at most one entity per scope; a second claim on it is dropped.", - "items": { - "type": "string" - }, - "type": "array" - }, - "name": { - "description": "The canonical name, kept as written.", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "type": "array" - }, - "relations": { - "description": "Declared relations. subject / object are entity names (registered if new) or record refs 'mem:<id>' / 'ep:<id>' of this agent; predicate is free text, normalized. A predicate from the role vocabulary (supports, supersedes, corrects, qualifies, contradicts, temporal_predecessor) on a record → record relation is read by reconstruct as that role.", - "items": { - "properties": { - "object": { - "type": "string" - }, - "predicate": { - "type": "string" - }, - "subject": { - "type": "string" - } - }, - "required": [ - "subject", - "predicate", - "object" - ], - "type": "object" - }, - "type": "array" - } -} - changed
Input schema / properties / channel / descriptionPrevious value: -"Memory channel for context separation (e.g. 'chat', 'discord'). Default: '' (shared)."New value: +"Memory channel for context separation (e.g. 'chat'). Default: '' (shared)." - added
Input schema / properties / contentAdded value: +{ + "description": "The text to store. Content that is empty — or that sanitizes to empty — is refused with ok:false, result:'rejected'.", + "type": "string" +} - added
Input schema / properties / idAdded value: +{ + "description": "Optional caller id: a later store with the same id in the same project is skipped as a duplicate.", + "type": "string" +} - added
Input schema / properties / lockAdded value: +{ + "default": false, + "description": "Also lock the memory against edits and deletion. Default false.", + "type": "boolean" +} - changed
Input schema / properties / message / descriptionPrevious value: -"ClotoMessage to store. Legacy source shapes are normalized server-side where unambiguous (e.g. lowercase type words, Rust serde externally-tagged dicts, bare 'user'/'assistant' strings); unknown shapes are stored verbatim and surfaced by check_health(invalid_source_type)."New value: +"The older form of content, id, source, timestamp and metadata." - removed
Input schema / properties / message / properties / content / descriptionRemoved value: -"The text to store. Content that is empty — or that sanitizes to empty — is refused with ok:false, result:'rejected'." - removed
Input schema / properties / message / properties / id / descriptionRemoved value: -"Caller-supplied message id used for msg_id-based dedup (γ-project-scoped). Optional." - removed
Input schema / properties / message / properties / metadata / descriptionRemoved value: -"Free-form JSON object for producer-specific context. Empty when unused. Serialised size is capped at 8000 characters (same cap for source); an oversized field is refused with result='rejected' rather than truncated, because a truncated JSON document is not a JSON document." - changed
Input schema / properties / message / properties / source / descriptionPrevious value: -"Attribution of who produced the content. Canonical shape is {type, id, name}. Type is the discriminator; id / name identify the concrete producer. Store null / empty {} only when the producer is genuinely unknown. A null source is normalized to {} at the write seam, so both persist (and recall) as the anonymous {}."New value: +"Also a bare 'user' / 'assistant' word, or null, stored as the anonymous {}." - removed
Input schema / properties / message / properties / source / propertiesRemoved value: -{ - "id": { - "description": "Stable producer id (e.g. discord user id, agent id). Empty when anonymous.", - "type": "string" - }, - "name": { - "description": "Human-readable label for display. Empty when unknown.", - "type": "string" - }, - "type": { - "description": "Producer role — send one of 'User', 'Agent', 'System'. Legacy producers that cannot are folded server-side at the write seam ('ai' / 'assistant' are normalized to 'Agent'; 'session' is normalized to 'System' (type words are matched case-insensitively)), and shapes outside that table are stored verbatim for check_health(invalid_source_type) to surface.", - "type": "string" - } -} - removed
Input schema / properties / message / properties / timestamp / descriptionRemoved value: -"UTC ISO-8601 timestamp with offset (e.g. '2026-07-22T12:00:00+00:00'). Defaults to server-time UTC when omitted. Aware non-UTC offsets are accepted; naive strings are surfaced by check_health(timestamp_format_drift)." - added
Input schema / properties / metadataAdded value: +{ + "description": "Free-form JSON, at most 8000 characters serialised (source has the same cap). Larger is rejected, not truncated.", + "type": "object" +} - changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 isolation axis. Optional — omit or pass '' to store in the global pool. Reads via γ semantics: a recall with project_id='X' returns 'X' rows + global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."New value: +"Isolation bucket; omit or '' for the global pool (a recall in 'X' reads 'X' and the global pool). '@auto' resolves this agent's default from the operating context, echoed as resolved_project_id; with no operating context configured it is stored as the literal '@auto'." - added
Input schema / properties / sourceAdded value: +{ + "description": "Who produced the content, {type, id, name}. Default: {type:'Agent', id:<agent_id>}. Pass {} only when the producer is unknown.", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "description": "One of 'User', 'Agent', 'System'; legacy spellings are folded ('ai' / 'assistant' are normalized to 'Agent'; 'session' is normalized to 'System' (type words are matched case-insensitively)).", + "type": "string" + } + }, + "type": "object" +} - added
Input schema / properties / timestampAdded value: +{ + "description": "ISO-8601 with an offset, e.g. '2026-07-22T12:00:00+00:00'. Default: now.", + "type": "string" +} - removed
Input schema / requiredRemoved value: -[ - "agent_id", - "message" -]
1 tool update
v2.6.5- Changed
reconstruct1 field changed- added
Input schema / properties / liteAdded value: +{ + "description": "A smaller, cheaper response, for a question one or two records can answer. Quotes at most 2,800 characters unless `budget` is given, short records whole first and then the best-ranked passages of longer ones. Leaves out `ranges`, `bounds` and the budget fields; an item whose only claim is its head carries that claim's `as_of` instead of `claims`. The response says `lite: true`. It can miss evidence the default finds: use the default when the question needs several records or a lite answer was incomplete. `trace=true` returns every field. Default false.", + "type": "boolean" +}
1 tool update
v2.6.3- Changed
recall1 field changed- changed
Input schema / properties / time_cue / descriptionPrevious value: -"2.6: when the answer was stored, as far as you remember. Give after and/or before (a date YYYY-MM-DD, which includes that whole day, or an ISO-8601 timestamp), or ago, plus confidence. The server also searches that period. Among the rows it returns anyway, one found there moves up by at most 3 / 2 / 1 places (sure / likely / vague), and as many extra seats (3 / 2 / 1) hold the best records that search found which the answer does not already hold: one no other search reached, or one the count cut. The returned rows are those of a recall without the cue, reordered, plus at most those seats: no row is removed or re-scored, and which rows pass the quality gate does not change. If the period holds nothing it is widened once, one confidence step. likely widens the period by half its length on each side, vague by its whole length. The response then carries time_cue: the period searched, whether it was widened, how many rows moved and how many seats were used. Pass it only when the request itself says when (a date, a month, \"last week\", \"in the spring\"); omit it when the request names no time, and never fill it with today's date or a guess. A cue whose own period starts within the last 24 hours (today, or later) is not used: the rows are those of a recall without a cue, and time_cue in the response says ignored. A cue that cannot be read returns an error and no messages."New value: +"2.6: when the answer was stored, as far as you remember. Give after and/or before (a date YYYY-MM-DD, which includes that whole day, or an ISO-8601 timestamp), or ago, plus confidence. The server also searches that period. Among the rows it returns anyway, one found there moves up by at most 3 / 2 / 1 places (sure / likely / vague), and as many extra seats (3 / 2 / 1) hold the best records that search found which the answer does not already hold: one no other search reached, or one the count cut. The returned rows are those of a recall without the cue, reordered, plus at most those seats: no row is removed or re-scored, and which rows pass the quality gate does not change. If the period holds nothing it is widened once, one confidence step. likely widens the period by half its length on each side, vague by its whole length. The response then carries time_cue: the period searched, whether it was widened, how many rows moved and how many seats were used, and remainder when the period held more records than the vector search reads and no coarse index could search the rest. Pass it only when the request itself says when (a date, a month, \"last week\", \"in the spring\"); omit it when the request names no time, and never fill it with today's date or a guess. A cue whose own period starts within the last 24 hours (today, or later) is not used: the rows are those of a recall without a cue, and time_cue in the response says ignored. A cue that cannot be read returns an error and no messages."
25 tool updates
v2.6.1- Changed
archive_episode1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
calibrate_threshold1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
check_health1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
declare_associations3 fields changed- changed
Input schema / properties / associations / descriptionPrevious value: -"Associative memory to declare alongside this call: entities the text mentions, with their aliases, and subject–predicate–object relations. Stored verbatim; the server extracts nothing and infers nothing. On store, the stored memory is recorded as mentioning every entity named here and anchors every relation. Malformed items are reported in the response's associations.dropped and skipped; the memory is stored regardless. Optional."New value: +"The entities and relations to declare. Malformed items are reported in `dropped` and skipped." - changed
Input schema / properties / associations / properties / entities / descriptionPrevious value: -"Entities to register (if new) and mark as mentioned. Names are compared after normalization (NFKC, case-folded, whitespace collapsed)."New value: +"Entities to register (if new) and mark as mentioned. Names and aliases are compared after normalization (NFKC, case-folded, whitespace collapsed), so two that normalize alike name one entity." - added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
deep_check1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
delete_agent_data1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
delete_episode1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
delete_memory1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
get_contents2 fields changed- changed
Input schema / properties / refs / descriptionPrevious value: -"Refs from recall messages ('mem:<id>' / 'ep:<id>'), or range objects such as {'ref': 'mem:<id>', 'node': [2, 3]} / {'ref': 'ep:<id>', 'span': [0, 800]} (max 20 per call)"New value: +"Refs from recall messages ('mem:<id>' / 'ep:<id>'), or range objects such as {'ref': 'mem:<id>', 'node': [2, 3]} / {'ref': 'ep:<id>', 'span': [0, 800]} / {'ref': 'mem:<id>', 'block': 4, 'revision': '<from an expand>'} (max 20 per call). At most one of node / span / block" - changed
Input schema / properties / refs / items / anyOfPrevious value: -[ - { - "type": "string" - }, - { - "properties": { - "node": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "items": { - "minimum": 0, - "type": "integer" - }, - "maxItems": 2, - "minItems": 2, - "type": "array" - } - ] - }, - "ref": { - "type": "string" - }, - "span": { - "items": { - "minimum": 0, - "type": "integer" - }, - "maxItems": 2, - "minItems": 2, - "type": "array" - } - }, - "required": [ - "ref" - ], - "type": "object" - } -]New value: +[ + { + "type": "string" + }, + { + "properties": { + "block": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "items": { + "minimum": 0, + "type": "integer" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + } + ] + }, + "node": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "items": { + "minimum": 0, + "type": "integer" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + } + ] + }, + "ref": { + "type": "string" + }, + "revision": { + "type": "string" + }, + "span": { + "items": { + "minimum": 0, + "type": "integer" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + } + }, + "required": [ + "ref" + ], + "type": "object" + } +]
- Changed
get_session_findings1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
import_memories1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
lock_memory1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
merge_memories1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
migrate_channel_axis1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
pause_persistence1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
persistence_status1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
recall3 fields changed- added
Input schema / properties / session_key / maxLengthAdded value: +256 - added
Input schema / properties / time_cueAdded value: +{ + "additionalProperties": false, + "description": "2.6: when the answer was stored, as far as you remember. Give after and/or before (a date YYYY-MM-DD, which includes that whole day, or an ISO-8601 timestamp), or ago, plus confidence. The server also searches that period. Among the rows it returns anyway, one found there moves up by at most 3 / 2 / 1 places (sure / likely / vague), and as many extra seats (3 / 2 / 1) hold the best records that search found which the answer does not already hold: one no other search reached, or one the count cut. The returned rows are those of a recall without the cue, reordered, plus at most those seats: no row is removed or re-scored, and which rows pass the quality gate does not change. If the period holds nothing it is widened once, one confidence step. likely widens the period by half its length on each side, vague by its whole length. The response then carries time_cue: the period searched, whether it was widened, how many rows moved and how many seats were used. Pass it only when the request itself says when (a date, a month, \"last week\", \"in the spring\"); omit it when the request names no time, and never fill it with today's date or a guess. A cue whose own period starts within the last 24 hours (today, or later) is not used: the rows are those of a recall without a cue, and time_cue in the response says ignored. A cue that cannot be read returns an error and no messages.", + "properties": { + "after": { + "type": "string" + }, + "ago": { + "description": "Relative to now: {\"unit\": \"days\" | \"weeks\" | \"months\", \"value\": N} names the unit-long period centred N units ago (a month is 30 days); \"long_ago\" names the oldest third of what this scope holds.", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "unit": { + "enum": [ + "days", + "weeks", + "months" + ], + "type": "string" + }, + "value": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "unit", + "value" + ], + "type": "object" + }, + { + "enum": [ + "long_ago" + ], + "type": "string" + } + ] + }, + "before": { + "type": "string" + }, + "confidence": { + "enum": [ + "sure", + "likely", + "vague" + ], + "type": "string" + } + }, + "required": [ + "confidence" + ], + "type": "object" +} - added
Input schema / properties / traceAdded value: +{ + "default": false, + "description": "2.6 recall trace: true adds `trace` to the response — which rows each stage (retrieval arms, fusion, quality gate, autocut, final order, count cut, reserved seats) kept, dropped or reordered, and why, with ranks and scores. It carries references only, never stored text, and is not stored on the server. trace_version identifies its shape. False (the default) returns the response unchanged.", + "type": "boolean" +}
- Changed
recall_with_context1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
reconstruct7 fields changed- changed
Input schema / properties / budget / descriptionPrevious value: -"Payload budget: characters of quoted text (item `content` plus `excerpts`) the response may carry. Bounds depth, where `count` bounds breadth, and breadth wins: excerpts are omitted before any item is. Omit for the server default, which is never less than one quote per item of the window; an operator-forced value overrides both; clamped to the server maximum and raised to one preview-tier excerpt, and budget_policy says which."New value: +"Payload budget: characters of quoted text (item `content` plus `excerpts`) the response may carry. Bounds depth, where `count` bounds breadth, and breadth wins: excerpts are omitted before any item is. Omit for the server default, which is never less than one quote per item of the window; an operator-forced value overrides both; clamped to the server maximum and raised to one head quote, and budget_policy says which." - changed
Input schema / properties / count / descriptionPrevious value: -"Ceiling on recall items returned -- not a fill target, not a search depth. Declare per call; omit to take the server default (1 unless configured). An operator-forced value overrides both. Requesting 5 with only 2 valid items returns 2; neither setting requires filling the window. Clamped to the server maximum, and the clamp is reported in count_policy rather than applied silently."New value: +"Ceiling on recall items returned -- not a fill target, not a search depth. Declare per call; omit to take the server default (10 unless configured). An operator-forced value overrides both. Requesting 5 with only 2 valid items returns 2; neither setting requires filling the window. Clamped to the server maximum, and the clamp is reported in count_policy rather than applied silently." - changed
Input schema / properties / max_hops / descriptionPrevious value: -"Relation hops the walk may follow from an item's candidates through declared entity -> entity relations. 0 adds no walked evidence. A relation left unfollowed at the bound is named in bounds.omitted."New value: +"Relation hops the walk may follow from an item's candidates through declared entity -> entity relations. 0 adds no walked evidence. A relation left unfollowed at the bound is named in bounds.omitted. At most 5, as for traverse: a larger value is lowered to it, and bounds.max_hops then states the value applied." - added
Input schema / properties / max_hops / maximumAdded value: +5 - added
Input schema / properties / session_key / maxLengthAdded value: +256 - added
Input schema / properties / time_cueAdded value: +{ + "description": "2.6: when the answer was stored, as far as you remember — the same object recall takes (see recall's time_cue), applied to the candidate recall this reconstruction reads. The response carries time_cue when one was applied.", + "type": "object" +} - changed
Input schema / properties / trace / descriptionPrevious value: -"Include candidate refs and cluster membership for local diagnosis; no full text is added."New value: +"Include candidate refs and cluster membership for local diagnosis, and the trace of the recall it made as trace.recall; no full text is added."
- Changed
resume_persistence1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
set_recall_precision1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
store2 fields changed- changed
Input schema / properties / associations / properties / entities / descriptionPrevious value: -"Entities to register (if new) and mark as mentioned. Names are compared after normalization (NFKC, case-folded, whitespace collapsed)."New value: +"Entities to register (if new) and mark as mentioned. Names and aliases are compared after normalization (NFKC, case-folded, whitespace collapsed), so two that normalize alike name one entity." - added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
unlock_memory1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
update_memory1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
- Changed
update_profile1 field changed- added
Input schema / properties / session_key / maxLengthAdded value: +256
8 tool updates
v2.5.12- Added
declare_associations - Changed
deep_check1 field changed- changed
Input schema / properties / checks / descriptionPrevious value: -"Checks to run (empty = all). Options: anonymous_source, short_content, stale_profile, orphaned_episodes, calibration_staleness, near_duplicate"New value: +"Checks to run (empty = all). Options: anonymous_source, short_content, stale_profile, orphaned_episodes, calibration_staleness, near_duplicate, unnormalized_content, embedding_norm"
- Changed
get_contents3 fields changed- changed
Input schema / properties / refs / descriptionPrevious value: -"Refs from recall messages, e.g. ['mem:123', 'ep:45'] (max 20 per call)"New value: +"Refs from recall messages ('mem:<id>' / 'ep:<id>'), or range objects such as {'ref': 'mem:<id>', 'node': [2, 3]} / {'ref': 'ep:<id>', 'span': [0, 800]} (max 20 per call)" - added
Input schema / properties / refs / items / anyOfAdded value: +[ + { + "type": "string" + }, + { + "properties": { + "node": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "items": { + "minimum": 0, + "type": "integer" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + } + ] + }, + "ref": { + "type": "string" + }, + "span": { + "items": { + "minimum": 0, + "type": "integer" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + } + }, + "required": [ + "ref" + ], + "type": "object" + } +] - removed
Input schema / properties / refs / items / typeRemoved value: -"string"
- Changed
recall1 field changed- changed
Input schema / properties / exclude_contents / descriptionPrevious value: -"Normalized content strings to exclude from results (starts-with match). Used to prevent duplication with conversation context already known to the caller."New value: +"Normalized content strings to exclude from results (starts-with match). Used to prevent duplication with conversation context already known to the caller. bug-399: the starts-with rule holds only at or above 32 characters. A shorter entry has to EQUAL the stored content (after the normalization this parameter already asks for: stripped and lower-cased) — a short prefix would otherwise suppress every memory beginning with it, inside the retrievers and with nothing in the response reporting the exclusion. Size entries at or above that length when you mean a prefix."
- Changed
recall_with_context8 fields changed- changed
Input schema / properties / external_context / descriptionPrevious value: -"Conversation history entries [{role, name?, user_id?, content, timestamp?}, ...]"New value: +"Conversation history entries [{role, content, name?, user_id?, timestamp?}, ...]. Every declared field is a string; one that is not is read as absent and reported in context_field_issues (CPERSONA_EXTERNAL_CONTEXT_MODE)." - added
Input schema / properties / external_context / items / properties / content / descriptionAdded value: +"The entry's text. A string — a value that is not one is read as absent and reported in context_field_issues." - removed
Input schema / properties / external_context / items / properties / content / typeRemoved value: -"string" - added
Input schema / properties / external_context / items / properties / nameAdded value: +{ + "description": "Display label for a role=user entry; becomes source.name, and source.id when user_id is absent. Default 'User'. A string — a value that is not one is read as absent and reported in context_field_issues." +} - added
Input schema / properties / external_context / items / properties / role / descriptionAdded value: +"'user' or 'assistant'; other roles filter the recall without being merged. A string — a value that is not one is read as absent and reported in context_field_issues." - removed
Input schema / properties / external_context / items / properties / role / typeRemoved value: -"string" - added
Input schema / properties / external_context / items / properties / timestampAdded value: +{ + "description": "ISO-8601 stamp deciding where this entry lands in the merged chronology. An entry without one — or with one that names no instant — sorts ahead of every dated message. A string — a value that is not one is read as absent and reported in context_field_issues." +} - added
Input schema / properties / external_context / items / properties / user_idAdded value: +{ + "description": "Stable id for a role=user entry; becomes source.id as 'discord:<user_id>'. A string — a value that is not one is read as absent and reported in context_field_issues." +}
- Added
reconstruct - Changed
store2 fields changed- added
Input schema / properties / associationsAdded value: +{ + "description": "Associative memory to declare alongside this call: entities the text mentions, with their aliases, and subject–predicate–object relations. Stored verbatim; the server extracts nothing and infers nothing. On store, the stored memory is recorded as mentioning every entity named here and anchors every relation. Malformed items are reported in the response's associations.dropped and skipped; the memory is stored regardless. Optional.", + "properties": { + "entities": { + "description": "Entities to register (if new) and mark as mentioned. Names are compared after normalization (NFKC, case-folded, whitespace collapsed).", + "items": { + "properties": { + "aliases": { + "description": "Other names for the same entity. An alias resolves to at most one entity per scope; a second claim on it is dropped.", + "items": { + "type": "string" + }, + "type": "array" + }, + "name": { + "description": "The canonical name, kept as written.", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "type": "array" + }, + "relations": { + "description": "Declared relations. subject / object are entity names (registered if new) or record refs 'mem:<id>' / 'ep:<id>' of this agent; predicate is free text, normalized. A predicate from the role vocabulary (supports, supersedes, corrects, qualifies, contradicts, temporal_predecessor) on a record → record relation is read by reconstruct as that role.", + "items": { + "properties": { + "object": { + "type": "string" + }, + "predicate": { + "type": "string" + }, + "subject": { + "type": "string" + } + }, + "required": [ + "subject", + "predicate", + "object" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" +} - changed
Input schema / properties / message / properties / source / typePrevious value: -"object"New value: +[ + "object", + "string", + "null" +]
- Added
traverse
23 tool updates
v2.5.10- Changed
archive_episode1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
calibrate_threshold1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
check_health1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Added
check_update - Changed
deep_check1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
delete_agent_data1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
delete_episode1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
delete_memory1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Added
get_session_findings - Changed
import_memories1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
lock_memory1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
merge_memories1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
migrate_channel_axis1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
pause_persistence1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
persistence_status1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
recall2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Per-retriever search depth, not a pure response cap (CSC #716): the value is handed to each retrieval channel (vector / episode FTS / keyword) as its top-K, so lowering it shrinks the candidate pool itself — rows beyond the depth are unreachable at any gate value, and score normalization / autocut operate on the smaller pool, which can also reorder what remains. Fewer rows than this may be returned. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.)"New value: +"Per-retriever search depth, not a pure response cap: the value is handed to each retrieval channel (vector / episode FTS / keyword) as its top-K, so lowering it shrinks the candidate pool itself — rows beyond the depth are unreachable at any gate value, and score normalization / autocut operate on the smaller pool, which can also reorder what remains. Fewer rows than this may be returned. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.)" - added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare — a partition hint, NOT authentication. It scopes this process's per-session state: the degraded-recall advisory's \"already told you\" memory, and which no-persist pause applies to this call. It does NOT filter stored data (use agent_id / project_id / channel for that), and it never reaches the database. Omit it to share one bucket with every other caller that omits it, which is the behaviour that predates this parameter.", + "type": "string" +}
- Changed
recall_with_context2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Per-retriever search depth for the underlying recall, not a pure response cap — same semantics as recall's limit (CSC #716): lowering it shrinks the candidate pool itself, not just the rows returned. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.)"New value: +"Per-retriever search depth for the underlying recall, not a pure response cap — same semantics as recall's limit: lowering it shrinks the candidate pool itself, not just the rows returned. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.)" - added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare — a partition hint, NOT authentication. It scopes this process's per-session state: the degraded-recall advisory's \"already told you\" memory, and which no-persist pause applies to this call. It does NOT filter stored data (use agent_id / project_id / channel for that), and it never reaches the database. Omit it to share one bucket with every other caller that omits it, which is the behaviour that predates this parameter.", + "type": "string" +}
- Changed
resume_persistence1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
set_recall_precision1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
store1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
unlock_memory1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
update_memory1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
- Changed
update_profile1 field changed- added
Input schema / properties / session_keyAdded value: +{ + "default": "", + "description": "Opaque session identity you declare: a partition hint, not authentication and not a data filter. Selects which no-persist pause applies to this call. Omit to share one bucket with every caller that omits it. Full text on recall.", + "type": "string" +}
2 tool updates
v2.5.6- Changed
recall1 field changed- changed
Input schema / properties / deep / descriptionPrevious value: -"Deep recall — disable time and completion decay for exhaustive search"New value: +"Deep recall — halves the quality gate (and the calibrated fused gate), so weaker matches are admitted. It also disables time and completion decay, which are inert unless CPERSONA_CONFIDENCE_ENABLED=true, and it does NOT widen the scan window (CPERSONA_MAX_MEMORIES) — deep is about how weak a match may be, not how far back the search reaches."
- Changed
recall_with_context1 field changed- changed
Input schema / properties / deep / descriptionPrevious value: -"Disable time decay"New value: +"Deep recall — same semantics as in `recall`: halves the quality gate so weaker matches are admitted."
5 tool updates
v2.5.4- Changed
calibrate_threshold1 field changed- added
Input schema / properties / method / enumAdded value: +[ + "separation", + "percentile", + "zscore" +]
- Changed
recall1 field changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Max memories to return (agent-facing cap; the library layer accepts up to the scan window for direct callers)"New value: +"Per-retriever search depth, not a pure response cap (CSC #716): the value is handed to each retrieval channel (vector / episode FTS / keyword) as its top-K, so lowering it shrinks the candidate pool itself — rows beyond the depth are unreachable at any gate value, and score normalization / autocut operate on the smaller pool, which can also reorder what remains. Fewer rows than this may be returned. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.)"
- Changed
recall_with_context1 field changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Max recalled memories (agent-facing cap; the library layer accepts up to the scan window for direct callers)"New value: +"Per-retriever search depth for the underlying recall, not a pure response cap — same semantics as recall's limit (CSC #716): lowering it shrinks the candidate pool itself, not just the rows returned. (Agent-facing cap; the library layer accepts up to the scan window for direct callers.)"
- Changed
store2 fields changed- changed
Input schema / properties / message / properties / source / properties / type / descriptionPrevious value: -"Producer role — this enum IS the contract; send one of these. Legacy producers that cannot are folded server-side at the write seam ('ai' / 'assistant' are normalized to 'Agent'; 'session' is normalized to 'System' (type words are matched case-insensitively)), and shapes outside that table are stored verbatim for check_health(invalid_source_type) to surface."New value: +"Producer role — send one of 'User', 'Agent', 'System'. Legacy producers that cannot are folded server-side at the write seam ('ai' / 'assistant' are normalized to 'Agent'; 'session' is normalized to 'System' (type words are matched case-insensitively)), and shapes outside that table are stored verbatim for check_health(invalid_source_type) to surface." - removed
Input schema / properties / message / properties / source / properties / type / enumRemoved value: -[ - "User", - "Agent", - "System" -]
- Changed
update_profile1 field changed- changed
Input schema / properties / profile / descriptionPrevious value: -"Profile text to save (pre-computed by caller)"New value: +"Profile text to save (pre-computed by caller). Capped at 2000 characters (CPERSONA_MAX_PROFILE_LENGTH); the response says truncated:true when the cap cut it."
17 tool updates
v2.5.2- Changed
archive_episode1 field changed- changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 isolation axis. Omit or pass '' for the global pool."New value: +"v2.4.17 isolation axis. Omit or pass '' for the global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."
- Added
calibrate_threshold - Added
check_health - Added
delete_episode - Added
delete_memory - Added
export_memories - Added
get_operating_context - Added
get_recall_precision - Changed
list_episodes1 field changed- changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 γ filter. Same semantics as list_memories."New value: +"v2.4.17 γ filter. Same semantics as list_memories. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."
- Changed
list_memories1 field changed- changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 γ filter. Omit → no filter; '' → global pool only; 'X' → 'X' ∪ global pool."New value: +"v2.4.17 γ filter. Omit → no filter; '' → global pool only; 'X' → 'X' ∪ global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."
- Added
pause_persistence - Added
recall - Added
recall_with_context - Added
resume_persistence - Changed
store4 fields changed- changed
Input schema / properties / message / properties / content / descriptionPrevious value: -"The text to store. Empty content is skipped."New value: +"The text to store. Content that is empty — or that sanitizes to empty — is refused with ok:false, result:'rejected'." - changed
Input schema / properties / message / properties / metadata / descriptionPrevious value: -"Free-form JSON object for producer-specific context. Empty when unused."New value: +"Free-form JSON object for producer-specific context. Empty when unused. Serialised size is capped at 8000 characters (same cap for source); an oversized field is refused with result='rejected' rather than truncated, because a truncated JSON document is not a JSON document." - changed
Input schema / properties / message / properties / source / properties / type / descriptionPrevious value: -"Producer role. 'Assistant' / 'ai' are normalized to 'Agent'; 'session' is normalized to 'System'."New value: +"Producer role — this enum IS the contract; send one of these. Legacy producers that cannot are folded server-side at the write seam ('ai' / 'assistant' are normalized to 'Agent'; 'session' is normalized to 'System' (type words are matched case-insensitively)), and shapes outside that table are stored verbatim for check_health(invalid_source_type) to surface." - changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 isolation axis. Optional — omit or pass '' to store in the global pool. Reads via γ semantics: a recall with project_id='X' returns 'X' rows + global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (echoed as resolved_project_id)."New value: +"v2.4.17 isolation axis. Optional — omit or pass '' to store in the global pool. Reads via γ semantics: a recall with project_id='X' returns 'X' rows + global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (the resolution is echoed as resolved_project_id; an unmapped agent yields operating_context_warning). bug-186: resolution requires a configured operating context. With none — the default, and equally the outcome of a sidecar that fails to parse — the sentinel is NOT resolved: it is stored and filtered as the literal project_id '@auto', resolved_project_id echoes '@auto', and no warning is raised. Read resolved_project_id before relying on the resolution."
- Added
unlock_memory - Added
update_profile
15 tool updates
v2.5.1- Changed
archive_episode1 field changed- changed
Input schema / properties / history / descriptionPrevious value: -"Original conversation messages (used for timestamp extraction and embedding)"New value: +"Original conversation messages (used for start/end timestamp extraction; the episode embedding is computed from summary)"
- Removed
calibrate_threshold - Removed
check_health - Removed
delete_episode - Removed
delete_memory - Removed
export_memories - Added
get_contents - Removed
get_recall_precision - Removed
pause_persistence - Removed
recall - Removed
recall_with_context - Removed
resume_persistence - Changed
store3 fields changed- changed
Input schema / properties / message / descriptionPrevious value: -"ClotoMessage to store (id, content, source, timestamp, metadata)"New value: +"ClotoMessage to store. Legacy source shapes are normalized server-side where unambiguous (e.g. lowercase type words, Rust serde externally-tagged dicts, bare 'user'/'assistant' strings); unknown shapes are stored verbatim and surfaced by check_health(invalid_source_type)." - added
Input schema / properties / message / propertiesAdded value: +{ + "content": { + "description": "The text to store. Empty content is skipped.", + "type": "string" + }, + "id": { + "description": "Caller-supplied message id used for msg_id-based dedup (γ-project-scoped). Optional.", + "type": "string" + }, + "metadata": { + "description": "Free-form JSON object for producer-specific context. Empty when unused.", + "type": "object" + }, + "source": { + "description": "Attribution of who produced the content. Canonical shape is {type, id, name}. Type is the discriminator; id / name identify the concrete producer. Store null / empty {} only when the producer is genuinely unknown. A null source is normalized to {} at the write seam, so both persist (and recall) as the anonymous {}.", + "properties": { + "id": { + "description": "Stable producer id (e.g. discord user id, agent id). Empty when anonymous.", + "type": "string" + }, + "name": { + "description": "Human-readable label for display. Empty when unknown.", + "type": "string" + }, + "type": { + "description": "Producer role. 'Assistant' / 'ai' are normalized to 'Agent'; 'session' is normalized to 'System'.", + "enum": [ + "User", + "Agent", + "System" + ], + "type": "string" + } + }, + "type": "object" + }, + "timestamp": { + "description": "UTC ISO-8601 timestamp with offset (e.g. '2026-07-22T12:00:00+00:00'). Defaults to server-time UTC when omitted. Aware non-UTC offsets are accepted; naive strings are surfaced by check_health(timestamp_format_drift).", + "type": "string" + } +} - changed
Input schema / properties / project_id / descriptionPrevious value: -"v2.4.17 isolation axis. Optional — omit or pass '' to store in the global pool. Reads via γ semantics: a recall with project_id='X' returns 'X' rows + global pool."New value: +"v2.4.17 isolation axis. Optional — omit or pass '' to store in the global pool. Reads via γ semantics: a recall with project_id='X' returns 'X' rows + global pool. v2.5.1: pass '@auto' to resolve this agent's default from the server's operating context (echoed as resolved_project_id)."
- Removed
unlock_memory - Removed
update_profile
2 tool updates
v2.4.37- Changed
check_health1 field changed- added
Input schema / properties / checksAdded value: +{ + "description": "Registry check names to run (empty = all). See cpersona.checks.HEALTH_CHECK_NAMES.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
deep_check1 field changed- changed
Input schema / properties / checks / descriptionPrevious value: -"Checks to run (empty = all). Options: anonymous_source, short_content, stale_profile, orphaned_episodes"New value: +"Checks to run (empty = all). Options: anonymous_source, short_content, stale_profile, orphaned_episodes, calibration_staleness, near_duplicate"
13 tool updates
v2.4.34- Changed
archive_episode2 fields changed- added
Input schema / properties / channelAdded value: +{ + "description": "v2.4.22 conversation-channel tag (e.g. a Discord channel id). Default '' (= unscoped). Channel-scoped recall returns episodes whose channel matches; this powers the per-channel episodic loop.", + "type": "string" +} - added
Input schema / properties / project_idAdded value: +{ + "description": "v2.4.17 isolation axis. Omit or pass '' for the global pool.", + "type": "string" +}
- Changed
calibrate_threshold3 fields changed- added
Input schema / properties / methodAdded value: +{ + "description": "'percentile' (default), 'zscore', or 'separation' (two-population, learns the operating point from null vs nearest-neighbour positives)", + "type": "string" +} - added
Input schema / properties / percentileAdded value: +{ + "description": "Null-distribution quantile for method='percentile' (default: 0.95, higher = stricter)", + "type": "number" +} - changed
Input schema / properties / z_factor / descriptionPrevious value: -"Z-score multiplier (default: 1.0, higher = stricter)"New value: +"Z-score multiplier for method='zscore' (default: 1.0, higher = stricter)"
- Added
get_recall_precision - Changed
list_episodes1 field changed- added
Input schema / properties / project_idAdded value: +{ + "description": "v2.4.17 γ filter. Same semantics as list_memories.", + "type": "string" +}
- Changed
list_memories1 field changed- added
Input schema / properties / project_idAdded value: +{ + "description": "v2.4.17 γ filter. Omit → no filter; '' → global pool only; 'X' → 'X' ∪ global pool.", + "type": "string" +}
- Added
migrate_channel_axis - Added
pause_persistence - Added
persistence_status - Changed
recall2 fields changed- added
Input schema / properties / project_idAdded value: +{ + "description": "v2.4.17 γ filter. Omit → no filter (all projects). '' → global pool only. 'X' → 'X' bucket ∪ global pool. Threaded through cascade / RRF / vector / FTS / keyword paths.", + "type": "string" +} - added
Input schema / properties / source_idAdded value: +{ + "default": "", + "description": "v2.4.20 per-user source filter. Empty (default) = no filter. Non-empty = prefix match against json_extract(source, '$.id'), e.g. 'discord:12345' to restrict to one Discord user, or 'discord:' to scope to all Discord-sourced memories. Episodes are skipped when set (no per-user source tagging).", + "type": "string" +}
- Changed
recall_with_context2 fields changed- added
Input schema / properties / project_idAdded value: +{ + "description": "v2.4.17 γ filter — passed through to recall. Same semantics as in `recall`.", + "type": "string" +} - added
Input schema / properties / source_idAdded value: +{ + "default": "", + "description": "v2.4.20 per-user source filter — passed through to recall. Same semantics as in `recall`.", + "type": "string" +}
- Added
resume_persistence - Added
set_recall_precision - Changed
store1 field changed- added
Input schema / properties / project_idAdded value: +{ + "description": "v2.4.17 isolation axis. Optional — omit or pass '' to store in the global pool. Reads via γ semantics: a recall with project_id='X' returns 'X' rows + global pool.", + "type": "string" +}
6 tool updates
v2.4.10- Added
deep_check - Added
lock_memory - Changed
recall1 field changed- added
Input schema / properties / exclude_contentsAdded value: +{ + "description": "Normalized content strings to exclude from results (starts-with match). Used to prevent duplication with conversation context already known to the caller.", + "items": { + "type": "string" + }, + "type": "array" +}
- Added
recall_with_context - Added
unlock_memory - Added
update_memory
TDQS
Scored across 34 tools
Most tools target distinct resources or actions, but several families have subtle boundaries: recall, recall_with_context, and reconstruct all retrieve memories, while check_health, deep_check, and get_session_findings all report diagnostics. The detailed descriptions explicitly steer between them, but an agent must read carefully to choose correctly.
All tool names use snake_case and most follow a verb_noun pattern such as get_profile, update_memory, delete_episode, and check_health. A few are bare verbs (store, recall, reconstruct, traverse) or noun-like (persistence_status), so the convention is readable but not perfectly uniform.
At 34 tools, the surface is well above the typical well-scoped range, mixing core memory operations with many niche admin, diagnostic, and maintenance tools such as check_update, get_queue_status, migrate_channel_axis, and pause/resume persistence. The breadth may reflect a mature backend, but it is heavy for an MCP server and increases selection cost.
The core memory lifecycle is well covered: store, update, delete, lock/unlock, list, recall/reconstruct, profiles, episodes, declared associations, import/export/merge, and health diagnostics. Minor gaps remain, such as no dedicated get/update for a single episode and no queue-management operations, but agents can work around them.
Maintenance
Related MCP Connectors
Persistent memory, hybrid search and a goal graph for AI agents, over stdio or remote HTTP.
Persistent, outcome-grounded episodic memory for Claude. 14ms CPU retrieval, no GPU, no vector DB.
Persistent memory for AI agents. Semantic search, memory graph, W3C DID identity.
Persistent memory for AI agents across Claude, ChatGPT and any MCP client.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenancePersistent AI memory server with 3-layer hybrid search (vector + FTS5 + keyword), confidence scoring via Reciprocal Rank Fusion, episodic/profile memory, and 16 tools. Zero LLM dependency. Works standalone with Claude Desktop and Claude Code. MIT licensed.3Business Source 1.1
- AlicenseAqualityCmaintenanceLocal-first memory for Claude Code and any MCP client: hybrid vector + keyword search and a bi-temporal knowledge graph in one SQLite file. Local embeddings, no API key, $0/token.5172 npm2PolyForm Noncommercial 1.0.0
- FlicenseNot gradedqualityDmaintenanceA lightweight MCP memory server built on SQLite + FTS5, providing cross-session long-term memory for Claude Code.-

Loreofficial
AlicenseBqualityBmaintenanceCross-agent memory server: hybrid (vector + full-text + knowledge-graph) recall, per-user private/shared visibility, write-side secret/PII redaction, and as-of-date bi-temporal fact queries. Auto-injection hooks for Claude Code / Cursor / Codex; self-hostable on PostgreSQL or SQLite.469MIT