MemCore
MemCore's MCP server lets AI tools read and write a shared local SQLite memory store with search, audit, and safety controls.
Search all remembered facts across every scope using FTS5 (strict AND then ranked OR fallback), with optional semantic/vector search, scope filters, limits, and debug modes.
Write or update durable facts (user / feedback / project / reference) via upsert on scope + name, with optional
expected_updated_atconcurrency guard.Retrieve one exact entry, browse entries by scope/type/archived state, or list most recently updated entries.
Read the append-only audit log for writes, conflicts, redactions, and scope events.
View prior versions of an overwritten or deleted entry via history.
Soft-delete and restore entries with a reason, keeping them auditable and recoverable.
List scopes with entry counts, inspect stats and DB path, check embedding status, and run end-to-end healthcheck.
Enforce per-connection access control server-side: read-only, scope sandbox, or both.
Redact secret-shaped values on write and skip
credentials_*files during Markdown import.Preserve provenance (actor, origin, session) for every write and support safe multi-client concurrency.
MemCore
Your AI finally remembers.
A single local database of the things worth keeping β decisions, corrections, "we already tried that" β shared by every AI tool you use, searchable in plain language, across every project.
No daemon. No cloud. No telemetry. One SQLite file you own.
π«π· Version franΓ§aise
You've had this conversation
You're deep in a project with an AI agent. It's flying. Then the context window fills, or you close the terminal, or it's simply tomorrow β and you're back to square one with someone who has no idea who you are.
You re-explain the constraints. You re-explain, again, that "we already tried the buffered approach and it deadlocks." You spent an hour last Tuesday arguing through a design trade-off; today the agent proposes the exact option you threw out.
And then it gets worse β it starts making things up:
You: Remember in the payments project, when we fixed the double-charge bug? What did we actually change?
Agent: Yes β we added an idempotency key on the request, moved the retry logic behind a mutex, and set the timeout to 8 seconds.
None of that happened. You're not even in the payments repo right now. The agent had nothing real to work from, so it produced something that sounds right β and you only catch it because you remember. Most days, you don't.
Now multiply that by every tool on your machine. One agent doesn't know what you told another. Each wakes up blank, every single time. You have become the memory. That's the bug.
Related MCP server: Memory MCP
MemCore fixes it
MemCore is one small local database of durable facts β the decisions, the corrections, the "here's how this actually works", the "we ruled this out and here's why". Every AI tool you run reads and writes the same store.
Ask before you re-explain:
memory_search "why did we drop the queue-based design"
projectnote, from three weeks ago: "queue design deadlocked under load on 2026-08-02, reverted to direct calls." β written in a different repo, found anyway.
And when there's nothing to find, it says so β a plain no match, not a confident answer improvised to fill the silence.
Corrections that finally stick. Tell it once that the API returns cents, not dollars; write it down; the next session β and the next tool β starts already knowing.
A paper trail. Every write is logged with who and when. Overwrites keep the old version. Nothing is ever silently lost.
What it feels like
Coming back:
You: back β where were we?
(agent:
memory_recent,memory_search)Agent: Last real change, 40 minutes ago: v1.0.84 shipped, replacement parts ordered, waiting on delivery. You were about to start the user guide. Nothing else moved.
Not a guess. Not a stale summary. The actual state, reconstructed from what was recorded.
The cross-project question β answered straight:
You: we standardised on the EU date format everywhere, right?
Agent: (
memory_search "date format") Yes βfeedback, global scope, still current. And there's a follow-up note: the CSV export is the one exception, it stays ISO-8601.
Onboarding a fresh agent to a codebase:
memory_list --scope my-project --type feedbackEvery lesson the last ten sessions learned about this repo, in one list β before it writes a single line.
Not re-litigating a settled decision: the agent starts to suggest an approach, checks MemCore first, finds the note where that approach already failed, and suggests something else. You never even see the wrong turn.
Closing out:
You: memcorise all of this.
(agent writes the session's durable facts β what shipped, what's still open, what we decided β into MemCore)
memcorise (verb): to write it down once, so no AI ever has to ask again.
Why the name
The core of memory. Not a transcript, not a log, not "everything the model ever saw" β the small, curated, durable part. The handful of facts that, if you lost them, you'd have to painfully earn back.
The one design decision that matters
Nothing is captured automatically. No background hooks, no daemon watching your session. An AI writes to MemCore the way it writes a note to itself β deliberately, when something is actually worth keeping. That single choice is why it's small, fast, and doesn't break: there's almost nothing to go wrong.
What it won't do
MemCore gives back the text that was written, with its scope, its date, its full edit history. Ask for something nobody ever recorded and you get no match: an honest blank, not a plausible answer improvised on the spot. The failure mode is "nothing found", which you notice, rather than "sounds right", which you don't.
It doesn't go further than that. It won't judge whether a stored fact is still true or current β that's on whoever wrote it, which is why the writing discipline further down is strict. And an assistant can still misread a result it did retrieve. What MemCore removes is the one failure you can't catch on your own: an AI filling a gap in its knowledge with invention.
A setup that works well around it
MemCore is the search layer. It pairs well with a plain-files knowledge base and a bit of discipline β the "LLM wiki" pattern (Karpathy's note):
Files as the source of truth β a Markdown vault (Obsidian works well here). Notes an AI can read, a human can edit, git can diff.
A classification an AI can trust β IPCRA or PARA: a few mutually-exclusive folders and a sort test, so it's always unambiguous what's active, what's reference, what's done. This matters double when an assistant reads the vault β the folder tells it what's live.
A schema file the assistant reads first (how you want it to behave, who it's working with). MemCore's
globalscope can hold this too.MemCore as the index β fast recall across every scope at once, the piece a folder of files alone can't give you. You keep files and index in sync; MemCore never silently rewrites your files.
An append-only log for the time-ordered view per-file notes lose, and a periodic consistency pass ("lint") for contradictions, stale paths and orphaned notes.
Pick the folder names and per-project templates that fit your work β the pattern is what matters, not the specific layout.
Features
Works with Claude Code, Codex CLI, Kimi Code, OpenCode, AgentRoom β or any tool that speaks MCP or can run a script.
Local & private | A single |
Multi-client | MCP server, CLI, line-delimited JSON bridge, or raw SQLite β all read/write the same store. |
Full-text search | SQLite FTS5 across every scope. Multi-word queries try strict AND, then fall back to ranked OR β one missing word never zeroes the result. |
Provenance & audit | Every create / update / conflict / refusal / archive / restore is appended to |
Safe concurrency | Optimistic locking via |
Reversible deletes | Archive (soft-delete) β restore. Overwritten versions kept in history. |
Per-connection access control |
|
Secret hygiene | Secret-shaped values (API keys, tokens, |
Incremental sync |
|
Semantic search (optional) | Install |
Minimal dependencies | The CLI, the JSON bridge and the importers use the Python 3.11+ standard library only. The MCP server needs one package: the official MCP SDK ( |
Install
git clone https://github.com/ManuelWarland/MemCore.git
cd MemCore
python scripts/memcore.py init # creates the database
python scripts/memcore.py healthcheck # end-to-end self-test (~1s)Database location β defaults to ~/MemCore/memcore.db. Override with the
MEMCORE_DB_PATH environment variable (point it wherever you like β a synced
folder, an encrypted volume, a project directory).
memcore.db is git-ignored β the code is shareable, your memories are not.
Semantic search (optional) β pip install -r requirements-semantic.txt
then python scripts/memcore.py embed-backfill. Adds sqlite-vec (a small C
extension) and fastembed (ONNX, no PyTorch). The default model is
paraphrase-multilingual-mpnet-base-v2 (~1 GB, downloaded once); override with
MEMCORE_EMBED_MODEL. Turn it off without uninstalling: MEMCORE_SEMANTIC=0.
Updating
MemCore is a git checkout, not a package β you update it the same way you got it:
cd MemCore
git pullThat's the whole procedure. Your database is untouched β memcore.db is
git-ignored, and if a release changes the schema the next command you run
migrates it in place (forward-only, idempotent, existing entries preserved).
Read CHANGELOG.md for what changed.
python scripts/memcore.py versionβ what you're on right now (offline).python scripts/memcore.py check-updatesβ asks GitHub for the latest release tag and tells you if you're behind. Opt-in: this is the only command in MemCore that makes a network call, it never runs on its own, and it sends nothing but an unauthenticated GET.Prefer a notification? On GitHub, Watch β Custom β Releases.
Use it β as a human (CLI)
Works everywhere, no MCP support required. Everything prints JSON on stdout.
python scripts/memcore.py search "telegram rate limit"
python scripts/memcore.py recent
python scripts/memcore.py list --scope my-project # browse a scope
python scripts/memcore.py list --type feedback # all feedback entries
python scripts/memcore.py list --archived # what's archived
python scripts/memcore.py scopes
python scripts/memcore.py stats
python scripts/memcore.py version # this checkout's version + schema (offline)
python scripts/memcore.py add \
--scope "my-project" \
--type "feedback" \
--name "prefer-tabs-over-spaces" \
--description "One-line summary used for recall ranking" \
--content "The full note."
python scripts/memcore.py sync # incremental .md -> DB re-import
python scripts/memcore.py backup [--dest PATH] # consistent copy (SQLite backup API)
python scripts/memcore.py events [--scope X] [--prune-older-than-days 180]
# Semantic search (needs: pip install -r requirements-semantic.txt)
python scripts/memcore.py embed-backfill # embed entries missing a vector
python scripts/memcore.py embed-status
python scripts/memcore.py search "how do I back things up" --hybrid # FTS + vector (loads the model, ~5-15s cold)
python scripts/memcore.py search "how do I back things up" --semantic # vector onlyWhat comes back β every command prints JSON on stdout. search returns the
full entry plus a highlighted snippet and a relevance rank; recent and
list return the lighter header shape; nothing found is a plain []:
$ python scripts/memcore.py search "queue design deadlock"
[
{
"id": 1,
"scope": "payments-api",
"type": "project",
"name": "queue-ingest-dropped",
"description": "Queue-based ingest abandoned β buffered writes deadlock under load",
"content": "Load test past 200 req/s deadlocks the buffered writer. Switched to direct writes plus optimistic retry. See PR 412. Do not reintroduce a write queue without a load test that proves it holds.",
"source_path": null,
"updated_at": "2026-08-31T19:02:21.258146+00:00",
"snippet": "Load test past 200 req/s [deadlocks] the buffered writer. Switched to...",
"rank": -1.3496562315877076
}
]
$ python scripts/memcore.py recent --limit 2
[
{
"id": 3,
"scope": "payments-api",
"type": "reference",
"name": "oncall-runbook",
"description": "On-call runbook is in the wiki, not the repo",
"updated_at": "2026-08-31T19:02:21.863622+00:00"
},
{
"id": 2,
"scope": "global",
"type": "feedback",
"name": "prefers-direct-writes",
"description": "Prefers direct writes over buffering on latency-sensitive targets",
"updated_at": "2026-08-31T19:02:21.579769+00:00"
}
]
$ python scripts/memcore.py search "something never recorded"
[]type is one of user / feedback / project / reference (see
Conventions). Upsert is automatic on scope + name.
Use it β as an AI assistant (MCP)
The MCP server needs the official MCP Python SDK, version 2. Install it once with
pip install "mcp>=2,<3" (the CLI and the bridge need nothing).
Add MemCore as a local MCP server. The exact config shape differs per host β here are ones verified working:
Host | Config file | Root key |
|
Claude Code |
|
|
|
Codex CLI |
|
|
|
Kimi Code CLI |
|
| same as Claude Code |
OpenCode |
|
|
|
Claude Code example:
{
"mcpServers": {
"memcore": {
"type": "stdio",
"command": "python",
"args": ["/absolute/path/to/MemCore/scripts/memcore_mcp.py",
"--actor", "claude", "--origin", "terminal"]
}
}
}Most CLIs only read their MCP config at startup β restart the tool (new session) after editing it before concluding something is broken.
Access profiles (per connection)
Set on the connection's own args, not globally:
Profile | Args | Effect |
Full trust | (none) | Read/write, all scopes |
Read-only |
| Sees everything, cannot overwrite/delete |
Sandbox |
| Read/write limited to one scope, even if another is requested |
Most restrictive |
| Read-only view of a single scope |
Scope locking is enforced server-side and covered by an adversarial test
(scripts/test_access_profiles.py). Default recommendation: start any unproven
assistant read-only (or sandboxed), widen only after trust.
MCP tools
Tool | Params | Purpose |
|
| Full-text search across all scopes (ANDβOR fallback). |
|
| Create or update. Pass |
|
| One entry. |
|
| Browse entries without a query β everything in a scope, all of one type, or what's archived. |
|
| Most recently modified entries. |
|
| Read the append-only audit log (writes, conflicts, redactions). |
|
| Previous versions of an overwritten/deleted entry. |
|
| Reversible soft-delete / undelete. |
| β | Scopes + entry counts. |
| β | Totals + DB path. |
| β | End-to-end self-test on a throwaway entry. Use before trusting a connection β search can misbehave silently while the connection looks up. |
Safe writes: before updating an existing entry, memory_get it and pass its
updated_at back as expected_updated_at. If another client changed it meanwhile,
the write is refused with conflict and nothing is lost.
Provenance: launch with --actor <name> --origin <terminal|agentroom|...> and,
if available, --session-ref <id>. Identity is fixed at server start, not chosen
per call.
For an AI reading this repository for the first time
This is a persistent memory store shared across the AI tools used on this machine.
It holds established facts, prior decisions and user preferences. If a global
scope exists, read it first (memory_search "user profile",
memory_search "collaboration rules") β it's how the person you're working with
wants assistants to behave. Then follow the discipline below.
Using it well
Reading β search first, by default
Search before you:
say "I don't know" or "there's no record of that"
ask a question the user may already have answered
propose an approach β has it been tried and rejected?
confirm or correct someone from memory
treat a surprising result as a mystery
At the start of a session: memory_recent plus a targeted memory_search to
rebuild the real state β don't guess, don't summarise from a stale mental model.
Search is cheap. The cost of not searching is the confident-lie failure this
tool exists to kill.
Writing β only what you'd hate to re-earn
Write when:
a decision was made β especially after a trade-off or a debate
a correction landed β "no, it's cents, not dollars"
a dead end was hit β "tried the queue design, deadlocked under load, reverted"
you learned how the setup actually works and it isn't in the code or docs
the user stated a preference
Don't write: a step-by-step of what you just did, anything git or the code already records, anything that only matters for this conversation, "reminders" to yourself. Write when the fact crystallises, not in a batch at the end β you'll forget half of it.
How to write one
One fact per entry. Tight kebab-case
name. Adescriptionthat says what the fact is β it drives recall ranking β not "notes about X".Pick the
typehonestly:user/feedback/project/reference.For
feedbackandproject: include why it matters and how to apply it. A rule with no rationale gets misapplied or ignored.Relative dates β absolute ("last Tuesday" β the actual date).
Update the existing entry (same
scope+name) β don't create a near-twin.If it turns out wrong, archive or delete it. A stale fact is worse than none.
Trusting what you read
A recalled memory is background context, not a fresh instruction β it's what was true when it was written.
If it names a file, function or flag: check it still exists before acting on it.
A "not done yet / pending" fact is perishable β re-verify it, don't build on it.
"the user chose X" β was it their independent call, or your suggestion they accepted? Don't misattribute a decision.
Other access methods
Line-delimited JSON bridge
scripts/memcore_bridge.py β one JSON request per line, one JSON response per line.
For local orchestrators (e.g. AgentRoom). No npm, no raw SQLite.
python scripts/memcore_bridge.py --actor codex --origin agentroom --session-ref "room:1/run:42"
# stdin: {"op":"memory_search","query":"deploy steps","limit":5}Raw SQLite β maintenance only
CREATE TABLE entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
scope TEXT NOT NULL,
type TEXT NOT NULL, -- 'user' | 'feedback' | 'project' | 'reference'
name TEXT NOT NULL, -- slug, unique within its scope
description TEXT NOT NULL DEFAULT '',
content TEXT NOT NULL,
source_path TEXT, -- origin .md path if imported, else NULL
created_at TEXT NOT NULL, -- ISO 8601 UTC
updated_at TEXT NOT NULL,
UNIQUE(scope, name)
);
-- entries_fts (FTS5) is kept in sync by triggers. Never write to it directly.SELECT e.* FROM entries_fts JOIN entries e ON e.id = entries_fts.rowid
WHERE entries_fts MATCH 'your terms' ORDER BY bm25(entries_fts) LIMIT 20;Direct SQL bypasses validation and memory_events β use MCP / CLI / bridge for
anything an agent does; keep raw SQL for human maintenance and restoration.
Markdown import (optional helpers)
scripts/import_md.py and scripts/import_claude_md.py bulk-import an existing
tree of Markdown memory files (the layout Claude Code writes under
.claude/projects/*/memory/) and a CLAUDE.md profile. Both redact common secret
patterns and skip credentials_* files. Re-runnable β they upsert on scope + name.
Conventions
scopeβ the project or topic (a project name, orglobalfor facts true everywhere). Consistency is nice but not critical: search spans all scopes.nameβ short kebab-case slug, unique within its scope. Reuse it to update.typeβuser(who the person is / their preferences),feedback(a lesson or correction they gave),project(a fact/state about ongoing work),reference(a pointer to something external).Store only what should survive the current conversation.
Validation on every write: type must be valid, content non-empty and β€ 200 000
chars, scope/name/description β€ 500 chars. Invalid writes return
{"ok": false, "error": "..."} β the store is never corrupted.
Secrets
Two layers:
credentials_*.mdfiles in a Markdown import tree are excluded by filename β never imported.Everything else is redacted, not rejected. On any write, secret-shaped substrings are replaced with
[REDACTED]: private keys (even a truncated paste),ghp_β¦/github_pat_β¦,AKIAβ¦,sk-β¦/sk-ant-β¦,AIzaβ¦,xox[baprs]-β¦, Telegram bot tokens, JWTs,scheme://user:pass@hostconnection strings,Authorization: Bearer β¦headers, andpassword:/api_key =lines (a lone value, no path/URL). The surrounding note is kept, the redaction is returned to the caller ("redacted": [codes]) and logged tomemory_events. A note that merely discusses a secret format is fine; a real leaked value is stripped before it hits disk.
If an assistant needs an actual credential, it should ask the user β not look here.
Backup & restore
python scripts/memcore.py backup [--dest PATH] makes a consistent copy (SQLite
backup API β safe even under concurrent writes). Point --dest (or
MEMCORE_BACKUP_PATH) into a folder that is itself backed up.
Restore, worst case first:
DB corrupt, a backup copy exists β stop every tool using MemCore, replace
memcore.dbwith the backup, runmemcore.py healthcheck.No DB, but the source
.mdfiles exist βimport_claude_md.pythenimport_md.pyrebuild the index. Lost in this case:memory_history,memory_events, archived entries β the live content of every entry that has a.mdcomes back.DB fine, MCP silent β
memcore.py healthcheck(ifok, it's the MCP layer): fully restart the host, check itsmcpServers.memcoreentry. The CLI works without MCP in the meantime.
Design notes
No daemon. No automatic background capture. No mandatory AI summarization. Just reliable storage + search, plus the guardrails (provenance, concurrency, reversible deletes, access control) that make it safe for several assistants to share one store. Markdown files can live alongside it as the human-readable source of truth; MemCore is a faster, more widely reachable index of them.
Contributing & support
Personal project, maintained in spare time β issues and pull requests are read, but replies may be slow.
Bug β open an issue
Question or idea β Discussions
Security β SECURITY.md (report privately)
Sending a PR β CONTRIBUTING.md
Credits
Designed and written by Claude (Anthropic), directed by Manuel Warland (@ManuelWarland), who maintains it.
License
MIT β Copyright (c) 2026 Manuel Warland.
Available Tools
13 toolsmemory_archiveArchive an entryAIdempotent
Archive (soft-delete) one entry: hide it from normal reads and search while keeping it, restorable with memory_restore and audited in memory_events.
Use it for a fact that became wrong or obsolete; to correct a fact, prefer memory_write on the same scope + name. Nothing is ever physically deleted. Returns {"ok": true} if the entry is now archived (also when it already was), {"ok": false} if no entry has that scope and name, or {"ok": false, "error": ...} when the reason is empty. Not available on read-only connections.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Entry name: the short kebab-case slug that identifies the entry within its scope. | |
| scope | Yes | Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used. | |
| reason | Yes | Why you are doing this, in a few words. Required and must not be empty; stored in the audit log (memory_events). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, it discloses that nothing is physically deleted, that the entry remains restorable, that the action is audited in memory_events, and that it is unavailable on read-only connections. It also details idempotent behavior explicitly ('also when it already was') and describes the return shape including error conditions, all consistent with the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core action and scope, then adds only necessary constraints, alternatives, return behavior, and availability. Every sentence earns its place, and there is no redundancy or 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 a three-required-parameter mutation tool with no output schema, the description is complete: it explains what happens, what is preserved, how to restore it, when to prefer a sibling, what the return values mean, and connection limitations. 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 description coverage is 100%, so the schema already documents all three parameters thoroughly. The description adds only marginal parameter meaning, mostly confirming that reason must not be empty and is stored in the audit log, which the schema also states. Baseline 3 is appropriate when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Archive (soft-delete) one entry'. It immediately distinguishes this from siblings by explaining the soft-delete semantics ('hide it from normal reads and search while keeping it, restorable with memory_restore'). An agent can tell this is not memory_write, memory_restore, or memory_get.
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 when-to-use guidance: 'Use it for a fact that became wrong or obsolete'. It also names the preferred alternative and condition: 'to correct a fact, prefer memory_write on the same scope + name'. It additionally states an availability constraint: 'Not available on read-only connections.'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_embed_statusSemantic search statusARead-onlyIdempotent
Report whether semantic search is active, which embedding model is used, and how many entries have a current vector embedding.
Use it to understand why memory_search finds (or misses) entries that
share no keywords with the query: entries without a current embedding are
lexical-only until the next embed-backfill run (a CLI command, not an
MCP tool). For general counts use memory_stats; to test that the store
works, memory_healthcheck. Read-only, takes no arguments.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint/destructiveHint=false, so safety is covered. The description goes beyond them by disclosing the operational state model: entries without a current embedding are lexical-only until the next embed-backfill run, which is a CLI command rather than an MCP tool. That is genuinely useful context an agent cannot get from the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core readout, then the diagnostic rationale, then routing to siblings. All three sentences carry information, though the rationale sentence is somewhat long and could be tightened without losing meaning.
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, yet the description enumerates exactly what the tool reports (active flag, model name, embedded-entry count), which effectively covers the return values. Combined with the routing guidance and the lexical-only caveat, an agent has everything needed to call and interpret it.
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?
Zero parameters, so the schema baseline is 4. The description reinforces this with 'takes no arguments', correctly signalling that invocation requires no input.
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 three concrete resources of the readout: whether semantic search is active, the embedding model, and the count of entries with current embeddings. This scope is distinct from the sibling tools it names, so an agent can tell it apart from memory_stats or memory_healthcheck 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?
Explicitly frames the diagnostic scenario ('understand why memory_search finds or misses entries that share no keywords') and names two alternatives with their own conditions: memory_stats for general counts and memory_healthcheck to test that the store works. When-to-use and when-to-use-something-else are both covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_eventsAudit logARead-onlyIdempotent
Read the append-only audit log, newest first: who wrote, updated, archived or restored what, when, from which client, and any conflicts or secret redactions.
Use it to answer "who changed this, and why?". To see the previous CONTENT of an entry, use memory_history instead; for the latest entries themselves, memory_recent. Read-only and available on any connection; scope-locked connections only see their own scope's events.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Only events about this entry name. Omit for every entry. | |
| limit | No | Maximum number of events to return (default 50). Values are clamped to 1-200. | |
| scope | No | Restrict to one project/topic scope (list them with memory_scopes). Omit to cover every scope. On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, and closed-world, so the safety profile is covered. The description still adds real context: the log is append-only, results are newest-first, and scope-locked connections only see their own scope's events β none of which the annotations 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?
Three tight sentences, front-loaded with the core verb and content, then routing guidance, then execution constraints. No filler and each sentence carries distinct information.
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?
An output schema exists so return shape needn't be explained, and the annotations carry the safety profile. The description still covers ordering, scope-visibility edge cases, and sibling routing, leaving nothing an agent needs to call it 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 name, limit (with clamping to 1-200), and scope are already fully documented in the schema. The description adds no parameter-level detail, so this sits at the baseline for high-coverage schemas.
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 (read) and resource (append-only audit log), then enumerates exactly what the log records: who wrote/updated/archived/restored what, when, from which client, plus conflicts and secret redactions. It explicitly distinguishes itself from the sibling tools it could be confused with (memory_history, memory_recent).
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 a concrete triggering question ("who changed this, and why?") and names two alternatives with the condition that selects each: memory_history for previous CONTENT, memory_recent for the latest entries themselves. This is explicit when/when-not/alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_getGet one entryARead-onlyIdempotent
Fetch one memory entry, complete, by its exact scope and name.
Use it when you already know both keys (from memory_search, memory_list or memory_recent results) and need the full, current content before relying on it. Returns null when no entry has that scope and name, or when it is archived and include_archived is false. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Entry name: the short kebab-case slug that identifies the entry within its scope. | |
| scope | Yes | Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used. | |
| include_archived | No | Also return archived (soft-deleted) entries. Default false: archived entries are hidden. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered. The description adds genuinely new behavioral context: it returns null for unknown scope/name and hides archived entries unless include_archived is true. It stops short of describing other edge behavior (e.g. scope-locked connections), but this is solid added value beyond annotations.
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, followed immediately by the selection condition. Every clause earns its place with 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 an output schema present and annotations covering the safety profile, the description only needs to supply the ambiguous edge cases β and it does: the null return for missing entries and the archive-hiding default. Nothing an agent needs to invoke this 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 all three parameters are already documented at the field level (including the kebab-case name convention and the --scope override). The description only echoes 'exact scope and name' and the archived gating, adding 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?
States a specific verb and resource ('Fetch one memory entry, complete, by its exact scope and name'), which cleanly distinguishes a keyed single-item get from list/search/recent siblings. The 'complete' qualifier signals it returns full content rather than a summary.
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 states the precondition ('when you already know both keys'), names where those keys come from (memory_search, memory_list, memory_recent), and gives the motivating purpose ('need the full, current content before relying on it'). No inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_healthcheckSelf-testAIdempotent
Self-test MemCore end to end (about 1 s): write, read, search (strict AND and OR-fallback modes), history and delete.
Use it instead of assuming a connection is healthy just because it is
listed: a connection can look fine while search silently misbehaves.
The test only writes to a throwaway scope (_healthcheck, or
_healthcheck_<scope> on a scope-locked connection) and always cleans
up; real entries are never touched. Returns {ok, checks[], db_path}.
Not available on read-only connections; use memory_stats there.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: it discloses runtime (~1 s), that writes are confined to a throwaway scope (`_healthcheck` or `_healthcheck_<scope>`), that cleanup always occurs, that real entries are never touched, and the return shape {ok, checks[], db_path}. It also flags the read-only-connection restriction, which the annotations alone 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?
Front-loaded with the operation and its cost, then usage guidance, then the side-effect guarantee, then the return shape and the availability caveat. Every clause carries information an agent needs; nothing is 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?
Despite having no output schema, the description supplies the return structure ({ok, checks[], db_path}), the side-effect envelope, and the availability constraint. For a zero-parameter diagnostic, nothing material 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?
The tool takes zero parameters, so the baseline is 4; there is no parameter syntax the description needs to compensate for. The description instead spends its budget on return shape and side effects, which 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 ('self-test') and resource (MemCore end to end) and enumerates exactly what it exercises: write, read, search (strict AND and OR-fallback), history, delete. This is clearly distinguishable from sibling tools like memory_write or memory_search, which perform one of these operations rather than verifying all of them.
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 says when to use it ('instead of assuming a connection is healthy just because it is listed') and gives the failure mode it guards against ('a connection can look fine while search silently misbehaves'). It also names the when-not condition and alternative: 'Not available on read-only connections; use memory_stats there.'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_historyEntry historyARead-onlyIdempotent
Show the prior versions of one entry that were overwritten or deleted, newest first.
Every write that replaces an entry keeps the previous version automatically, so nothing is silently lost even if another agent overwrote it. Use it to recover or compare earlier content. For who made each change use memory_events; for the current version, memory_get. Read-only, available even on a read-only connection. Returns an empty list when the entry has no prior versions.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Entry name: the short kebab-case slug that identifies the entry within its scope. | |
| limit | No | Maximum number of versions to return (default 20). Values are clamped to 1-200. | |
| scope | Yes | Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnlyHint/idempotentHint annotations by disclosing the automatic version-retention model, that an empty list is returned when there is no history, and that the tool works even on a read-only connection. These are non-obvious traits 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-loads the core action and keeps the routing hints in a compact clause. The sentence about write behavior is slightly explanatory overhead, but it earns its place as rationale for trusting the history.
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 an output schema present, the description needn't explain return shape, yet it still notes the empty-list case. Combined with sibling routing and read-only availability, nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and all three parameters are already documented in the schema, including clamping for limit and scope-locked connection behavior. The description adds no parameter-level detail, so the 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?
States a specific verb and resource ('Show the prior versions of one entry that were overwritten or deleted, newest first'), including the ordering. An agent can distinguish it from memory_get and memory_events 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?
Explicitly names both alternatives and the condition that selects them: memory_events for authorship, memory_get for the current version. It also names the triggering task ('recover or compare earlier content').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_listBrowse entriesARead-onlyIdempotent
Browse entries by scope, type and archived state, WITHOUT a search query.
Use it to see everything in a project scope (onboarding to a codebase),
every entry of one type (e.g. all feedback), or what has been archived
(candidates for memory_restore). To find entries by content use
memory_search; for the latest changes, memory_recent. Read-only. An
invalid type returns [{"error": ...}].
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | Only entries of this type: user, feedback, project or reference. Omit for every type. | |
| limit | No | Maximum number of entries to return (default 100). Values are clamped to 1-200. | |
| scope | No | Restrict to one project/topic scope (list them with memory_scopes). Omit to cover every scope. On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used. | |
| archived | No | If true, return ONLY archived entries instead of active ones. Default false. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered; the description redundantly restates 'Read-only' but adds a genuinely useful behavioral detail, the invalid-type error shape ([{"error": ...}]). It stops short of describing pagination or result volume behavior for a listing 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?
Front-loaded with the core scoping constraint 'WITHOUT a search query' and tightly ordered: what it does, when to use it, alternatives, then an edge-case note. Slightly bloated by the 'Read-only' line, which only repeats the readOnlyHint annotation.
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?
An output schema is present, so return-value documentation is unnecessary, and the description covers purpose, alternatives, edge-case error behavior and read semantics. Nothing an agent needs to select or call this listing tool 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 the baseline is 3, but the description adds meaning beyond the schema by framing the archived flag as 'candidates for memory_restore' and the scope filter as 'everything in a project scope'. The remaining parameters (type, limit) are left to the schema, which documents them adequately.
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 (browse) plus the resource (entries) and the exact filters it operates on (scope, type, archived state), and explicitly contrasts itself with search-based retrieval. An agent can distinguish this from memory_search 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?
Gives three concrete when-to-use scenarios (project onboarding, single-type listing, archived entries for memory_restore) and explicitly routes content-based lookup to memory_search and latest-change lookup to memory_recent. Exclusions and alternatives are both named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_recentRecently updated entriesARead-onlyIdempotent
List the most recently created or updated memory entries, newest first.
Use it to catch up on what changed lately (start of a session, after another agent worked), optionally within one scope. To find entries by content use memory_search; to browse a scope or type without time ordering use memory_list; for who changed what, use memory_events. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of entries to return (default 20). Values are clamped to 1-200. | |
| scope | No | Restrict to one project/topic scope (list them with memory_scopes). Omit to cover every scope. On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used. | |
| include_archived | No | Also return archived (soft-deleted) entries. Default false: archived entries are hidden. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and non-destructive, so the safety profile is covered. The description adds real behavioral context beyond that: the newest-first ordering, the optional single-scope restriction, and the 'Read-only' confirmation. It does not add anything about pagination or result size behavior, 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?
Front-loads the core action and ordering in the first sentence, then the usage guidance, then the alternative routing. Efficient overall, though the trailing 'Read-only' sentence is redundant given the readOnlyHint annotation.
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?
An output schema exists so return values need not be explained, all three parameters are documented, and the description covers purpose, timing, scoping, and alternative selection. Nothing an agent needs in order 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 description coverage is 100%, so limit, scope, and include_archived are already fully documented in the schema, including the clamping range and the scope-locked override. The description's 'optionally within one scope' adds nothing beyond that, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the most recently created or updated memory entries') plus the ordering guarantee ('newest first'). An agent can distinguish it from memory_search, memory_list, and memory_events 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?
Gives an explicit trigger scenario ('catch up on what changed lately, start of a session, after another agent worked') and routes to three named alternatives with the exact condition that selects each (content lookup, unordered browse, change attribution). Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_restoreRestore an entryAIdempotent
Restore one archived entry so it appears again in normal reads and search. The inverse of memory_archive.
Find archived entries with memory_list(archived=true). The restore is logged in memory_events with your reason. Returns {"ok": true} if the entry is now active (also when it was not archived), {"ok": false} if no entry has that scope and name, or {"ok": false, "error": ...} when the reason is empty. Not available on read-only connections.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Entry name: the short kebab-case slug that identifies the entry within its scope. | |
| scope | Yes | Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used. | |
| reason | Yes | Why you are doing this, in a few words. Required and must not be empty; stored in the audit log (memory_events). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the safety profile (readOnlyHint=false, idempotentHint=true, destructiveHint=false), yet the description goes further by disclosing audit logging into memory_events with the supplied reason, the read-only-connection restriction, and all three response shapes with their trigger conditions. It clarifies idempotency in practice ("also when it was not archived"), which the annotation only signals abstractly.
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 effect is front-loaded in the first sentence, the relationship to memory_archive comes second, and follow-up sentences are short and single-purpose. Dense with information but free of filler or repeated annotation content.
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 takes responsibility for return values and does so fully (success, not-archived, missing entry, empty reason). Combined with the discovery workflow and connection restriction, nothing an agent needs to call this correctly is absent.
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; however, the description adds consequences the schema does not, namely that an empty reason yields {"ok": false, "error": ...} and that scope/name misses yield {"ok": false}. It does not restate the kebab-case or scope-locked-connection details, so the gain is modest but real.
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 ("Restore one archived entry") plus the observable effect ("appears again in normal reads and search"), and explicitly frames itself as "The inverse of memory_archive." An agent can distinguish it from memory_archive and memory_list 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?
Names the prerequisite discovery path ("Find archived entries with memory_list(archived=true)") and the environment precondition ("Not available on read-only connections"), which is exactly the context an agent needs before attempting the call. It also covers the no-op case so the agent does not misread a successful restore of a non-archived entry as failure.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_scopesList scopesARead-onlyIdempotent
List every known project/topic scope with its number of entries.
Use it first to discover valid scope values for the other tools, or to
see which projects have memory at all. For the total count and the
database location use memory_stats; to browse the entries of one scope,
memory_list. Read-only, takes no arguments. A scope-locked connection
only sees its own scope.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds a genuine behavioral constraint beyond structured data: a scope-locked connection only sees its own scope, which affects what results an agent should expect.
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 routing guidance, then the visibility caveat β each sentence earns its place. Minor redundancy: 'Read-only' restates the readOnlyHint annotation.
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?
An output schema exists, so return values need no explanation. Combined with explicit routing to siblings and the scope-lock caveat, an agent has everything needed to call this 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?
The tool takes zero parameters, so the schema-description baseline is 4. The description correctly reinforces this ('takes no arguments') without needing to explain any argument semantics.
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 every known project/topic scope') plus the returned payload ('with its number of entries'). It is immediately distinguishable from its siblings like memory_stats and memory_list, which it names.
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 says to use it first to discover valid `scope` values for other tools, and names the alternatives with their selecting conditions: memory_stats for totals/location, memory_list for browsing one scope's entries. Both when-to-use and when-not-to-use are covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_searchSearch memoryARead-onlyIdempotent
Search all remembered facts by content, across every project scope.
The default entry point. Use it before assuming something is not known: it searches EVERY scope at once, so there is no need to guess which project a fact was recorded under. Use memory_get instead when you already know the exact scope and name, memory_list to browse without a query, and memory_recent for what changed lately.
Two layers, blended by default:
Lexical (FTS5): multi-word queries first require ALL terms in one entry, then fall back to ANY term (ranked by how many match), so one non-verbatim word doesn't zero the result.
Semantic (if embeddings are present): vector nearest neighbours on a multilingual sentence model, which finds entries about the same idea even with no shared keywords.
Read-only. Archived entries are never returned. Returns a list of entries, best match first; an empty list when nothing matches or the query is invalid.
| Name | Required | Description | Default |
|---|---|---|---|
| debug | No | If true, return {results, mode, ...} instead of a plain list; `mode` tells which path matched (and / or_fallback / hybrid / vector / lexical). | |
| limit | No | Maximum number of results to return (default 20). Values are clamped to 1-200. | |
| query | Yes | Search terms or a natural-language question, in French or English. | |
| scope | No | Restrict to one project/topic scope (list them with memory_scopes). Omit to cover every scope. On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used. | |
| semantic | No | null = hybrid lexical + semantic (default), true = vector search only, false = lexical (FTS5) only. Semantic search needs the optional embeddings; without them the search is lexical. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent/non-destructive, but the description adds substantial behavior: the two-layer FTS5 + semantic blend, the ALL-terms-then-ANY-term fallback, that embeddings are optional, that archived entries are never returned, and that an empty list is returned on no match or invalid query.
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 routing guidance, then the two-layer mechanics. Slightly long, but every sentence (fallback logic, archived exclusion, return shape) adds distinct value rather than restating structured 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?
With no output schema, the description compensates by specifying the return shape (list of entries, best match first, empty list on no match/invalid query). Combined with the annotations and full schema coverage, an agent has everything needed to call this 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 is 3, but the description adds real meaning: it explains the two search layers that drive the `semantic` default and notes that searching every scope is the norm, reinforcing the `scope` omission behavior 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?
States a specific verb (search) and resource (remembered facts) with explicit scope ('across every project scope'). It explicitly differentiates itself from siblings by naming memory_get, memory_list, and memory_recent with their distinct conditions.
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?
Declares itself 'the default entry point' and instructs the agent to use it before assuming something is unknown. It then routes to three named alternatives with the exact condition that selects each (known scope/name, browse without query, recent changes).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_statsStore statisticsARead-onlyIdempotent
Report the total number of entries, the number of archived entries, the database file location and the list of scopes.
Use it for a quick overview or to check which database file this connection uses. For per-scope counts use memory_scopes; for semantic search coverage, memory_embed_status; to verify the store actually works, memory_healthcheck. Read-only, takes no arguments.
| 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, idempotentHint=true, and destructiveHint=false, and the trailing "Read-only, takes no arguments" largely restates them plus the empty schema. The only added value is disclosing that the result includes the backing database file path, which is useful but thin against an already-complete safety 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?
Front-loaded with the returned fields, then usage and alternatives, then the read-only/no-arg note. Compact and free of filler, though the trailing restatement of annotations is mildly redundant.
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 burden of describing return values and does so by enumerating all four reported items. Combined with the sibling routing, an agent has everything needed to select and call it.
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 tool takes no parameters, so the baseline is 4, and the description correctly confirms "takes no arguments," consistent with the empty schema. There is nothing further to disambiguate.
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 enumerates exactly what is reported: total entries, archived entries, database file location, and scope list. It is clearly distinguishable from siblings like memory_scopes and memory_healthcheck, which are named 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?
Gives explicit use conditions ("quick overview", "check which database file this connection uses") and routes to three alternatives with the condition that selects each: memory_scopes for per-scope counts, memory_embed_status for search coverage, memory_healthcheck to verify the store works.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_writeWrite an entryA
Record a new fact, or update an existing one (same scope + name updates it in place).
Search first (memory_search) to avoid creating a duplicate under a
different name. An update keeps the previous version in
memory_history and is logged in memory_events, so it can be undone.
Updating an archived entry is refused: restore it first with
memory_restore. Secret-shaped values in content (API keys, tokens,
password: ... lines) are replaced by [REDACTED] before storage; if
any were, the result includes "redacted": [<codes>].
Returns {"ok": true, "id": ...} on success, or {"ok": false, "error": ...} for an invalid argument, a conflict (stale expected_updated_at or archived entry) or a busy database (retry). Not available on read-only connections.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Short kebab-case slug, unique within the scope. Reusing an existing scope + name updates that entry. | |
| type | Yes | Kind of fact: user (who the user is), feedback (guidance on how to work, with the reason), project (ongoing work, decisions, constraints) or reference (pointers to external resources). | |
| scope | Yes | Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used. | |
| content | Yes | The full memory content, Markdown allowed. Never put secrets here: secret-shaped values are redacted. | |
| description | No | One-line summary of what this entry covers, shown in search results. Truncated beyond 500 characters. | |
| expected_updated_at | No | Optional optimistic lock: the updated_at value you read with memory_get. If the entry changed since, nothing is written and a conflict error is returned. Omit to write unconditionally. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare non-readOnly, non-idempotent, non-destructive, but the description goes well beyond them: versioning via memory_history, event logging, undoability, archived-entry refusal, secret redaction with the `redacted` codes in the result, error classes (conflict, busy DB retry), and unavailability on read-only connections. This is exactly the behavioral layering the annotations 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?
Dense but front-loaded: purpose first, then the duplicate-avoidance rule, then update side-effects, then redaction, then return/error contract. Line-broken and skimmable; slightly long, but nearly every sentence carries distinct information.
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, the description supplies the success envelope ({"ok": true, "id": ...}), the failure envelope and its causes, and the read-only-connection limitation. Nothing an agent needs in order 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 coverage is 100%, so baseline is 3, but the description adds real meaning: the scope+name update-in-place contract and the redaction behavior affecting `content`. It does not re-explain expected_updated_at, which the schema already covers well, so it stops short of 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?
States a specific verb+resource ('Record a new fact, or update an existing one') and clarifies the update mechanic (same scope + name updates in place). An agent can distinguish this from memory_get or memory_search 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?
Explicitly instructs to search first via memory_search to avoid duplicate names, and states the exclusion path for archived entries ('refused: restore it first with memory_restore'). Names alternatives and the conditions selecting them.
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.
9 tool updates
v0.2.0- Changed
memory_archive3 fields changed- added
Input schema / properties / name / descriptionAdded value: +"Entry name: the short kebab-case slug that identifies the entry within its scope." - added
Input schema / properties / reason / descriptionAdded value: +"Why you are doing this, in a few words. Required and must not be empty; stored in the audit log (memory_events)." - added
Input schema / properties / scope / descriptionAdded value: +"Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used."
- Changed
memory_events3 fields changed- added
Input schema / properties / limit / descriptionAdded value: +"Maximum number of events to return (default 50). Values are clamped to 1-200." - added
Input schema / properties / name / descriptionAdded value: +"Only events about this entry name. Omit for every entry." - added
Input schema / properties / scope / descriptionAdded value: +"Restrict to one project/topic scope (list them with memory_scopes). Omit to cover every scope. On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used."
- Changed
memory_get3 fields changed- added
Input schema / properties / include_archived / descriptionAdded value: +"Also return archived (soft-deleted) entries. Default false: archived entries are hidden." - added
Input schema / properties / name / descriptionAdded value: +"Entry name: the short kebab-case slug that identifies the entry within its scope." - added
Input schema / properties / scope / descriptionAdded value: +"Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used."
- Changed
memory_history3 fields changed- added
Input schema / properties / limit / descriptionAdded value: +"Maximum number of versions to return (default 20). Values are clamped to 1-200." - added
Input schema / properties / name / descriptionAdded value: +"Entry name: the short kebab-case slug that identifies the entry within its scope." - added
Input schema / properties / scope / descriptionAdded value: +"Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used."
- Changed
memory_list4 fields changed- added
Input schema / properties / archived / descriptionAdded value: +"If true, return ONLY archived entries instead of active ones. Default false." - added
Input schema / properties / limit / descriptionAdded value: +"Maximum number of entries to return (default 100). Values are clamped to 1-200." - added
Input schema / properties / scope / descriptionAdded value: +"Restrict to one project/topic scope (list them with memory_scopes). Omit to cover every scope. On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used." - added
Input schema / properties / type / descriptionAdded value: +"Only entries of this type: user, feedback, project or reference. Omit for every type."
- Changed
memory_recent3 fields changed- added
Input schema / properties / include_archived / descriptionAdded value: +"Also return archived (soft-deleted) entries. Default false: archived entries are hidden." - added
Input schema / properties / limit / descriptionAdded value: +"Maximum number of entries to return (default 20). Values are clamped to 1-200." - added
Input schema / properties / scope / descriptionAdded value: +"Restrict to one project/topic scope (list them with memory_scopes). Omit to cover every scope. On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used."
- Changed
memory_restore3 fields changed- added
Input schema / properties / name / descriptionAdded value: +"Entry name: the short kebab-case slug that identifies the entry within its scope." - added
Input schema / properties / reason / descriptionAdded value: +"Why you are doing this, in a few words. Required and must not be empty; stored in the audit log (memory_events)." - added
Input schema / properties / scope / descriptionAdded value: +"Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used."
- Changed
memory_search5 fields changed- added
Input schema / properties / debug / descriptionAdded value: +"If true, return {results, mode, ...} instead of a plain list; `mode` tells which path matched (and / or_fallback / hybrid / vector / lexical)." - added
Input schema / properties / limit / descriptionAdded value: +"Maximum number of results to return (default 20). Values are clamped to 1-200." - added
Input schema / properties / query / descriptionAdded value: +"Search terms or a natural-language question, in French or English." - added
Input schema / properties / scope / descriptionAdded value: +"Restrict to one project/topic scope (list them with memory_scopes). Omit to cover every scope. On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used." - added
Input schema / properties / semantic / descriptionAdded value: +"null = hybrid lexical + semantic (default), true = vector search only, false = lexical (FTS5) only. Semantic search needs the optional embeddings; without them the search is lexical."
- Changed
memory_write6 fields changed- added
Input schema / properties / content / descriptionAdded value: +"The full memory content, Markdown allowed. Never put secrets here: secret-shaped values are redacted." - added
Input schema / properties / description / descriptionAdded value: +"One-line summary of what this entry covers, shown in search results. Truncated beyond 500 characters." - added
Input schema / properties / expected_updated_at / descriptionAdded value: +"Optional optimistic lock: the updated_at value you read with memory_get. If the entry changed since, nothing is written and a conflict error is returned. Omit to write unconditionally." - added
Input schema / properties / name / descriptionAdded value: +"Short kebab-case slug, unique within the scope. Reusing an existing scope + name updates that entry." - added
Input schema / properties / scope / descriptionAdded value: +"Project/topic scope the entry belongs to, e.g. a project folder name (list them with memory_scopes). On a scope-locked connection (--scope), this argument is ignored and the connection's own scope is used." - added
Input schema / properties / type / descriptionAdded value: +"Kind of fact: user (who the user is), feedback (guidance on how to work, with the reason), project (ongoing work, decisions, constraints) or reference (pointers to external resources)."
13 tool updates
v0.1.0- First observed
memory_archive - First observed
memory_embed_status - First observed
memory_events - First observed
memory_get - First observed
memory_healthcheck - First observed
memory_history - First observed
memory_list - First observed
memory_recent - First observed
memory_restore - First observed
memory_scopes - First observed
memory_search - First observed
memory_stats - First observed
memory_write
TDQS
Scored across 13 tools
Each tool has a sharply distinct purpose (write/search/get/list/recent/scopes/stats/events/history/archive/restore/embed_status/healthcheck), and the read-heavy tools explicitly cross-reference each other with 'use X instead when...' guidance, eliminating overlap. An agent can determine the right tool without guessing.
Every tool uses a uniform snake_case memory_ prefix followed by a clear verb or noun (memory_write, memory_search, memory_archive, memory_restore, memory_healthcheck). No mixing of conventions or inconsistent verb styles.
13 tools is well-scoped for a persistent memory store covering full CRUD, search, auditing, and maintenance. Each tool earns its place with no redundant or filler operations.
The full lifecycle is covered: create/update (write), read (get, search, list, recent), soft-delete/restore (archive, restore), plus history, audit events, scope discovery, stats, embedding status, and healthcheck. No obvious gaps for a memory domain.
Maintenance
Related MCP Connectors
Shared memory for coding agents. Stop re-explaining your codebase every session.
- AmberOAuthcom.ambermem
Long-term memory for AI assistants. Hybrid retrieval, query expansion, auto-topics.
Shared memory for AI coding agents. Save once, reuse from Cursor, Claude Code, Codex.
Long-term memory for AI coding agents: durable project facts, recalled by every MCP client.
Related MCP Servers
- AlicenseAqualityAmaintenancePersistent AI memory with SQLite hybrid search (FTS5 + semantic), built-in Qwen3 embedding, and rclone sync across machines.156 npm1,540 PyPI11Apache 2.0
- AlicenseAqualityBmaintenanceProvides persistent cross-session memory and full-text search for AI coding assistants, storing project context, decisions, and preferences while enabling searchable access to conversation history via local SQLite.81MIT
- AlicenseNot gradedqualityBmaintenanceProvides long-term memory for LLMs via local SQLite storage with hybrid search (BM25, vectors, recency decay), enabling AI coding agents to persist and recall memories across sessions without cloud or API keys.53MIT
- AlicenseNot gradedqualityDmaintenancePersistent memory for AI coding agents with local-first, zero-cost, privacy-first SQLite/FTS5 storage and biological-inspired decay.11 npm3MIT