Skip to main content
Glama

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"

project note, 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 feedback

Every 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 global scope 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 memcore.db (standard SQLite). Readable by any tool. Nothing leaves the machine.

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 memory_events with actor + origin + optional session id.

Safe concurrency

Optimistic locking via expected_updated_at. Concurrent writers get a conflict β€” nothing is silently overwritten.

Reversible deletes

Archive (soft-delete) β†’ restore. Overwritten versions kept in history.

Per-connection access control

--readonly and/or --scope <name> sandboxing, enforced server-side (a locked connection cannot escape its scope even if it asks).

Secret hygiene

Secret-shaped values (API keys, tokens, password: … lines) are redacted on write β€” the note is kept, the value stripped, the redaction flagged and audited. credentials_* files are skipped from import by filename.

Incremental sync

memcore.py sync re-imports only the Markdown files whose mtime changed since last time β€” a run that changed nothing touches the DB zero times.

Semantic search (optional)

Install sqlite-vec + fastembed and MemCore blends FTS with vector nearest-neighbours on a multilingual sentence model β€” finds entries about the same idea with no shared keywords. Embeddings are computed off the write path (embed-backfill). Not installed β†’ lexical only, zero deps.

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 (pip install "mcp>=2,<3").


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 pull

That'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 only

What 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

command shape

Claude Code

~/.claude.json

mcpServers

command (string) + args (array) + type: "stdio"

Codex CLI

~/.codex/config.toml

[mcp_servers.memcore] (TOML)

command + args

Kimi Code CLI

~/.kimi-code/mcp.json

mcpServers

same as Claude Code

OpenCode

~/.config/opencode/opencode.jsonc

mcp (not mcpServers)

command = one array combining interpreter + script, plus type: "local" and enabled: true

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

--readonly

Sees everything, cannot overwrite/delete

Sandbox

--scope <name>

Read/write limited to one scope, even if another is requested

Most restrictive

--readonly --scope <name>

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

memory_search

query, scope?, limit?, debug?

Full-text search across all scopes (AND→OR fallback). debug=true returns the matched mode + raw queries.

memory_write

scope, type, name, content, description?, expected_updated_at?

Create or update. Pass expected_updated_at to guard against concurrent overwrites.

memory_get

scope, name

One entry.

memory_list

scope?, type?, archived?, limit?

Browse entries without a query β€” everything in a scope, all of one type, or what's archived.

memory_recent

scope?, limit?

Most recently modified entries.

memory_events

scope?, name?, limit?

Read the append-only audit log (writes, conflicts, redactions).

memory_history

scope, name, limit?

Previous versions of an overwritten/deleted entry.

memory_archive / memory_restore

scope, name, reason

Reversible soft-delete / undelete.

memory_scopes

β€”

Scopes + entry counts.

memory_stats

β€”

Totals + DB path.

memory_healthcheck

β€”

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. A description that says what the fact is β€” it drives recall ranking β€” not "notes about X".

  • Pick the type honestly: user / feedback / project / reference.

  • For feedback and project: 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, or global for 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:

  1. credentials_*.md files in a Markdown import tree are excluded by filename β€” never imported.

  2. 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@host connection strings, Authorization: Bearer … headers, and password: / 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 to memory_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.db with the backup, run memcore.py healthcheck.

  • No DB, but the source .md files exist β†’ import_claude_md.py then import_md.py rebuild the index. Lost in this case: memory_history, memory_events, archived entries β€” the live content of every entry that has a .md comes back.

  • DB fine, MCP silent β†’ memcore.py healthcheck (if ok, it's the MCP layer): fully restart the host, check its mcpServers.memcore entry. 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.

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 tools
memory_archiveArchive an entryA
Idempotent

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesEntry name: the short kebab-case slug that identifies the entry within its scope.
scopeYesProject/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.
reasonYesWhy you are doing this, in a few words. Required and must not be empty; stored in the audit log (memory_events).

TDQS

A4.7/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all 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.

Purpose5/5

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

The description states a specific verb and resource: '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.

Usage Guidelines5/5

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 statusA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 logA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoOnly events about this entry name. Omit for every entry.
limitNoMaximum number of events to return (default 50). Values are clamped to 1-200.
scopeNoRestrict 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

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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

An output schema exists so return 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.

Parameters3/5

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.

Purpose5/5

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

States a specific verb (read) and resource (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.

Usage Guidelines5/5

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 entryA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesEntry name: the short kebab-case slug that identifies the entry within its scope.
scopeYesProject/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_archivedNoAlso return archived (soft-deleted) entries. Default false: archived entries are hidden.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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-testA
Idempotent

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 historyA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesEntry name: the short kebab-case slug that identifies the entry within its scope.
limitNoMaximum number of versions to return (default 20). Values are clamped to 1-200.
scopeYesProject/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

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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

With an output schema present, the description needn't explain 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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 entriesA
Read-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": ...}].

ParametersJSON Schema
NameRequiredDescriptionDefault
typeNoOnly entries of this type: user, feedback, project or reference. Omit for every type.
limitNoMaximum number of entries to return (default 100). Values are clamped to 1-200.
scopeNoRestrict 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.
archivedNoIf true, return ONLY archived entries instead of active ones. Default false.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds 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.

Purpose5/5

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.

Usage Guidelines5/5

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 entriesA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of entries to return (default 20). Values are clamped to 1-200.
scopeNoRestrict 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_archivedNoAlso return archived (soft-deleted) entries. Default false: archived entries are hidden.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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

An output schema exists so return values need 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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 entryA
Idempotent

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesEntry name: the short kebab-case slug that identifies the entry within its scope.
scopeYesProject/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.
reasonYesWhy you are doing this, in a few words. Required and must not be empty; stored in the audit log (memory_events).

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3; 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.

Purpose5/5

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.

Usage Guidelines5/5

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 scopesA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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

An output schema exists, so return values need no explanation. 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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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_statsStore statisticsA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesShort kebab-case slug, unique within the scope. Reusing an existing scope + name updates that entry.
typeYesKind 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).
scopeYesProject/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.
contentYesThe full memory content, Markdown allowed. Never put secrets here: secret-shaped values are redacted.
descriptionNoOne-line summary of what this entry covers, shown in search results. Truncated beyond 500 characters.
expected_updated_atNoOptional 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

A4.8/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

  1. 9 tool updatesv0.2.0
    • Changedmemory_archive3 fields changed
      • addedInput schema / properties / name / description
        Added value: +"Entry name: the short kebab-case slug that identifies the entry within its scope."
      • addedInput schema / properties / reason / description
        Added value: +"Why you are doing this, in a few words. Required and must not be empty; stored in the audit log (memory_events)."
      • addedInput schema / properties / scope / description
        Added 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."
    • Changedmemory_events3 fields changed
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of events to return (default 50). Values are clamped to 1-200."
      • addedInput schema / properties / name / description
        Added value: +"Only events about this entry name. Omit for every entry."
      • addedInput schema / properties / scope / description
        Added 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."
    • Changedmemory_get3 fields changed
      • addedInput schema / properties / include_archived / description
        Added value: +"Also return archived (soft-deleted) entries. Default false: archived entries are hidden."
      • addedInput schema / properties / name / description
        Added value: +"Entry name: the short kebab-case slug that identifies the entry within its scope."
      • addedInput schema / properties / scope / description
        Added 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."
    • Changedmemory_history3 fields changed
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of versions to return (default 20). Values are clamped to 1-200."
      • addedInput schema / properties / name / description
        Added value: +"Entry name: the short kebab-case slug that identifies the entry within its scope."
      • addedInput schema / properties / scope / description
        Added 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."
    • Changedmemory_list4 fields changed
      • addedInput schema / properties / archived / description
        Added value: +"If true, return ONLY archived entries instead of active ones. Default false."
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of entries to return (default 100). Values are clamped to 1-200."
      • addedInput schema / properties / scope / description
        Added 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."
      • addedInput schema / properties / type / description
        Added value: +"Only entries of this type: user, feedback, project or reference. Omit for every type."
    • Changedmemory_recent3 fields changed
      • addedInput schema / properties / include_archived / description
        Added value: +"Also return archived (soft-deleted) entries. Default false: archived entries are hidden."
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of entries to return (default 20). Values are clamped to 1-200."
      • addedInput schema / properties / scope / description
        Added 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."
    • Changedmemory_restore3 fields changed
      • addedInput schema / properties / name / description
        Added value: +"Entry name: the short kebab-case slug that identifies the entry within its scope."
      • addedInput schema / properties / reason / description
        Added value: +"Why you are doing this, in a few words. Required and must not be empty; stored in the audit log (memory_events)."
      • addedInput schema / properties / scope / description
        Added 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."
    • Changedmemory_search5 fields changed
      • addedInput schema / properties / debug / description
        Added value: +"If true, return {results, mode, ...} instead of a plain list; `mode` tells which path matched (and / or_fallback / hybrid / vector / lexical)."
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of results to return (default 20). Values are clamped to 1-200."
      • addedInput schema / properties / query / description
        Added value: +"Search terms or a natural-language question, in French or English."
      • addedInput schema / properties / scope / description
        Added 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."
      • addedInput schema / properties / semantic / description
        Added 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."
    • Changedmemory_write6 fields changed
      • addedInput schema / properties / content / description
        Added value: +"The full memory content, Markdown allowed. Never put secrets here: secret-shaped values are redacted."
      • addedInput schema / properties / description / description
        Added value: +"One-line summary of what this entry covers, shown in search results. Truncated beyond 500 characters."
      • addedInput schema / properties / expected_updated_at / description
        Added 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."
      • addedInput schema / properties / name / description
        Added value: +"Short kebab-case slug, unique within the scope. Reusing an existing scope + name updates that entry."
      • addedInput schema / properties / scope / description
        Added 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."
      • addedInput schema / properties / type / description
        Added 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)."
  2. 13 tool updatesv0.1.0
    • First observedmemory_archive
    • First observedmemory_embed_status
    • First observedmemory_events
    • First observedmemory_get
    • First observedmemory_healthcheck
    • First observedmemory_history
    • First observedmemory_list
    • First observedmemory_recent
    • First observedmemory_restore
    • First observedmemory_scopes
    • First observedmemory_search
    • First observedmemory_stats
    • First observedmemory_write

TDQS

A4.7/5.0

Scored across 13 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness5/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Provides 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.
    8
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides 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.
    53
    MIT