Skip to main content
Glama

mnemos

CI

A persistent memory framework for LLM agents. It gives an agent episodic memory (what was said, and when), semantic memory (distilled facts about the user), procedural memory (which retrieval strategy actually works for this user), and reflection (consolidating and forgetting facts over time) — backed by a choice of three storage backends, exposed over a FastAPI surface, and observable through a React dashboard. It ships with a benchmark that measures whether the agent actually still remembers something 30 simulated days after it was mentioned once — not just whether the demo looks good.

Most "agent memory" projects on GitHub are a vector store wrapped around a chat loop with no way to tell if it's actually working. This one tries to answer that question directly: seed a fact once, ask about it later, measure recall and answer accuracy with and without memory, and report the delta.

See it work

Recorded live, against a real running dashboard, a live Postgres backend, and a real hosted LLM (Groq, Llama 3.3 70B) — not staged, not a mockup. Demo data was seeded across 7 simulated conversation sessions spanning 10 simulated days: a chat turn correctly recalls a fact from one earlier session (the user's dog) and a different fact from another earlier session (the deployment stack), then the walkthrough moves through retrieval scoring, memory stats, and the reflection audit trail.

Static screenshots of each view, for anyone who wants to pause and read the numbers:

It actually remembers across sessions, and shows exactly which memories it used and what it just learned.

Retrieval isn't a black box. Every retrieved memory ships with its real similarity, recency, and combined score — this is the actual ranking the chat loop uses, not a simplified illustration.

That transparency isn't just a dashboard view here — it's now a standalone library, memlens, that traces retrieval for any backend (mem0, raw pgvector, or mnemos itself): every candidate considered, its full score breakdown, and whether it was included or excluded and why.

The system prunes itself. Reflection has merged 4 pairs of near-duplicate facts into single consolidated statements (via real LLM tool-use calls) and decayed stale facts' confidence; a low-value fact that crossed the forget threshold is now archived, not silently deleted.

It knows what's in memory, and how that's changing. Counts by type and status, plus a growth-over-time chart, computed from the same demo data: 18 episodic memories, 18 active facts, 4 merged, 1 forgotten.

It adapts its own retrieval strategy. After live turns, the system already has empirical uses/success-rate data on which similarity/recency weighting works best for this user — the mechanism, not a placeholder.

Related MCP server: cortex-engine

Quickstart

git clone https://github.com/vishalbanwari26/mnemos && cd mnemos
cp .env.example .env   # fill in ANTHROPIC_API_KEY or GROQ_API_KEY + LLM_PROVIDER
docker compose up -d   # Postgres + pgvector
uv sync --extra dev
uv run alembic upgrade head
uv run python -m mnemos.cli.demo   # terminal chat; restart it to test cross-session recall

Not on PyPI yet, so git clone is the install for now. No Docker, want the full HTTP API + dashboard, or want to try the Qdrant/Neo4j backends instead? See Running it below for all of that.

Why this exists

Chat agents without persistent memory forget everything between sessions. Ask ChatGPT-style tools about something you told them yesterday and, unless it happened to land in a fixed context window, it's gone. The interesting systems question isn't "can I bolt a vector DB onto an LLM" — it's how do you decide what to keep, how do you retrieve it cheaply and correctly, how do you forget responsibly, how do you adapt your own retrieval strategy from feedback, and how do you prove any of it is working over time, not just in a single session.

This project started as a deliberately scoped v1 — two memory types, one storage backend, one retrieval strategy, a benchmark — to prove the core loop actually works before building anything else on top of it. It then grew into the full system: procedural memory, reflection/forgetting, three interchangeable storage backends, and a dashboard. See What's intentionally out of scope for what's still not here, and why.

Architecture

flowchart LR
    U[User message] --> CM[ConversationManager]
    CM -->|1. choose strategy| PM[ProceduralMemory]
    CM -->|2. recall| ME[MemoryEngine]
    ME --> RET[MemoryRetriever]
    RET -->|similarity + recency| SB[(StorageBackend:<br/>Postgres / Qdrant / Neo4j)]
    CM -->|3. build context| PB[prompt_builder]
    PB --> LLM[LLMClient]
    LLM -->|4. reply| CM
    CM -->|5. store turn| ME
    ME -->|6. periodically| EX[extraction]
    EX -->|distilled facts| SB
    CM -->|7. resolve prior turn| PM
    RE[ReflectionEngine] -.on demand.-> SB
  • MemoryEngine (src/mnemos/memory/engine.py) is the single seam everything else talks to. It wraps a StorageBackend and an EmbeddingClient. Nothing outside this file touches a backend or the retriever directly — that's what let procedural memory, reflection, and two extra storage backends get added later without rewiring every caller.

  • ConversationManager (src/mnemos/agent/conversation.py) is the per-turn loop: pick a retrieval strategy → recall relevant memories → build a system prompt → call the LLM → store the new episode → periodically extract semantic facts → resolve the previous turn's strategy outcome. The CLI and the API both call the same handle_message().

  • LLMClient (src/mnemos/llm/) is a small provider-agnostic interface (complete() with optional tool use). Two real implementations — AnthropicLLMClient and GroqLLMClient (the latter translates the Anthropic-shaped tool schemas used throughout the app into OpenAI's format internally, since Groq's API is OpenAI-compatible) — plus MockLLMClient, deterministic and offline, used by every test and by --llm mock dev runs.

  • EmbeddingClient (src/mnemos/embeddings/) is the same pattern for embeddings. Anthropic has no embeddings endpoint, so v1 defaults to a local sentence-transformers model (all-MiniLM-L6-v2) — free, deterministic, no second API key, and it keeps the benchmark reproducible without embedding-call cost or latency variance.

Data model

Episodic and semantic memory live in whichever StorageBackend is active (see below). Procedural memory and the reflection log are small operational metadata that always live in Postgres, regardless of STORAGE_BACKEND — they're not the "memory content" the backend comparison is about:

  • episodes — one row/node/point per conversational turn. occurred_at is a logical event time, separate from created_at (real insert time). That's what lets the benchmark simulate 30 days passing without waiting 30 days: seed data is inserted "now" but timestamped as if it happened weeks ago.

  • facts — distilled facts ("prefers FastAPI over Flask"), extracted from episodes via an Anthropic tool-use call, deduplicated against existing facts by cosine similarity before being written. Each fact tracks status (active / merged / forgotten), confidence, and last_reinforced_at — the fields reflection and forgetting operate on.

  • procedural_strategies (Postgres only) — per-user, per-strategy uses/successes counters.

  • reflection_log (Postgres only) — audit trail of every merge/decay/ forget action reflection has taken.

Retrieval

A query (the latest user message, or a benchmark probe question) is embedded and matched against both episodes and facts independently, then merged:

score = similarity * w_sim + recency_factor * w_recency   (episodic only)
score = similarity                                          (semantic facts)
recency_factor = exp(-age_days / 30)

w_sim/w_recency default to 0.7/0.3, but aren't fixed — see Procedural memory below for how they get chosen per turn. Semantic facts are treated as durable knowledge and ranked mostly on similarity; episodic turns get an exponential recency boost so recent context still surfaces even when it's a slightly weaker semantic match. Intentionally simple — a documented default, not a research contribution.

Storage backends

Episodic and semantic memory sit behind a StorageBackend interface (src/mnemos/storage/base.py) — nothing in MemoryEngine, agent/, or api/ knows which one is active. Three real implementations exist, selected via STORAGE_BACKEND=postgres|qdrant|neo4j:

  • Postgres + pgvector (default) — HNSW cosine similarity search.

  • Qdrant, embedded local mode (QdrantClient(path=...)) — no server, no Docker, a single directory-locked process. Qdrant combines payload filtering (user_id) with ANN search in one query.

  • Neo4j — a real graph, not a vector table wearing a costume: episodes and facts are nodes owned by a User via SAID/KNOWS edges, and a fact's provenance is a first-class DERIVED_FROM edge to its source episodes, instead of Postgres's source_episode_ids JSON array. Its native vector index has no pre-filter, so this backend over-fetches candidates and filters by user_id in Cypher afterward — a real, measurable cost, not hidden.

The same behavioral test suite (tests/test_storage_contract.py) runs against all three, so "swap the backend" is proven, not just asserted. A separate, LLM-free comparison script reuses the benchmark dataset to measure write/search latency and confirm recall parity:

uv run python -m benchmark.compare_backends

Backend

Write p50 / p95 (ms)

Search p50 / p95 (ms)

Recall@K

postgres

14.9 / 28.8

14.5 / 25.1

100%

qdrant

11.6 / 18.8

11.1 / 14.3

100%

neo4j

28.5 / 46.2

20.5 / 22.1

100%

All three retrieve the same relevant memories (100% recall on this dataset). Qdrant is fastest — an embedded, in-process client has no network round trip. Postgres is close behind. Neo4j is consistently slowest here: each write also polls its vector index to confirm the new node is searchable before returning (Neo4j's vector index updates asynchronously in the background, unlike Postgres/Qdrant which are immediately consistent — this poll is the only way to give this backend the same read-your-writes guarantee the other two have for free), and its lack of native pre-filtering adds Cypher-side filtering overhead on search. Reproduce this yourself with the command above — numbers will vary with hardware, but the ranking has been stable across repeated local runs.

Procedural memory

The original brainstorm's idea of "procedural memory" (a multi-step task workflow that improves over time) assumes an agent that executes multi-tool procedures. mnemos is a memory-augmented chat loop, not a task-executing agent, so bolting on a fake version of that would be dishonest. Instead, procedural memory here tracks a real recurring decision the system actually makes every turn: which retrieval weighting works best for this specific user.

Three named strategies, different (similarity_weight, recency_weight) pairs:

Strategy

similarity

recency

semantic_heavy

0.9

0.1

balanced (v1 default)

0.7

0.3

recency_heavy

0.4

0.6

Each turn, ProceduralMemory.choose_strategy() picks the empirically best-performing strategy for that user (epsilon-greedy, ε≈0.15 chance of exploring another). The outcome is scored on the user's next message in the same session: if it contains a correction cue ("no,", "that's wrong", "actually,", ...), the previous turn's strategy is marked a failure; otherwise a success. Crude, heuristic, and fully inspectable in src/mnemos/memory/procedural.py — not a hidden metric. The dashboard's Procedural Strategies view shows live uses/success-rate per strategy.

Reflection and forgetting

One consolidation pass per user (ReflectionEngine.run(), src/mnemos/memory/reflection.py), triggered on demand — CLI command or a dashboard button, not a background scheduler, because there's no task queue in this project and pretending otherwise would misrepresent what's actually running:

  1. Merge — active facts are greedily clustered by pairwise cosine similarity above a looser threshold than extraction's own dedup check; each multi-fact cluster becomes one Anthropic tool-use call that produces a single consolidated statement, written with the union of the cluster's source episodes, while the originals are marked status="merged" (soft — never hard-deleted, so there's always an audit trail).

  2. Decay — any fact whose last_reinforced_at is older than REFLECTION_DECAY_DAYS has its confidence multiplied by REFLECTION_DECAY_FACTOR.

  3. Forget — a fact whose confidence drops below REFLECTION_FORGET_CONFIDENCE_THRESHOLD becomes status="forgotten" (still soft — visible in the Memory Browser's "forgotten" filter).

Every action writes a reflection_log row — the dashboard's Reflection Log view is that table, verbatim. Reinforcement is the inverse of decay: MemoryEngine.recall() bumps last_reinforced_at (and nudges confidence up slightly, capped at 1.0) for every fact that actually makes it into a retrieval result — facts you keep needing survive, facts nobody asks about fade, implemented rather than narrated.

"Memory compression" and "dreaming" from the original brainstorm aren't separate features — compression is literally the merge step above, and "dreaming" would just be this same pass on a timer, which is exactly what the on-demand framing is honest about not having.

uv run python -m mnemos.cli.reflect --user demo-user

Time travel

MemoryEngine.recall(..., now=<a past datetime>) scores recency as though the query were asked at that point in simulated time — this already existed to make the benchmark's 30-day-gap questions work. The dashboard's Retrieval Trace view exposes the same parameter as an "as of" date picker: pick a past date, see exactly what would have been retrieved and why, with per-item similarity/recency/score. Not a separate subsystem — the same mechanism, a UI on top of it.

Dashboard

dashboard/ — React 19 + Vite + TypeScript, talking to the FastAPI backend over HTTP (@tanstack/react-query, react-router). Six views:

  • Chat — the product itself, not just an inspector.

  • Memory Browser — episodic list + semantic facts filterable by active/merged/forgotten.

  • Retrieval Trace — type a query (+ optional "as of" date for time travel), see the ranked result with per-item similarity/recency/score.

  • Timeline & Stats — episodic/semantic/merged/forgotten counts and a growth-over-time chart.

  • Reflection Log — audit trail + a "run reflection now" button.

  • Procedural Strategies — live uses/success-rate per strategy.

uv run uvicorn mnemos.api.main:app --port 8000   # backend, one terminal
cd dashboard && npm install && npm run dev        # frontend, another terminal

Then open the Vite dev URL (http://localhost:5173 by default). The backend must run on port 8000 to match dashboard/.env's VITE_API_BASE_URL and the CORS origin configured in src/mnemos/api/main.py.

The benchmark

benchmark/run_benchmark.py is the credibility check: it seeds 18 synthetic conversations across simulated days 0–18, each mentioning exactly one fact about a fictional user, then asks 18 probe questions later — some same-day, some a week later, some 30 days later — and compares two conditions:

  • With memory: the normal retrieve → build context → answer path.

  • No memory: the same question, same LLM, empty context (a stateless baseline).

It scores two things: retrieval recall@K (did the relevant fact or episode actually get retrieved — isolates the memory engine from the LLM's phrasing) and answer accuracy (keyword match against the generated reply — the end-to-end number), both broken down by how much simulated time had passed. It also records latency (p50/p95) and token usage/estimated cost.

uv run python -m benchmark.run_benchmark --llm anthropic   # the real, reported number
uv run python -m benchmark.run_benchmark --llm groq         # same, on a Groq-hosted model
uv run python -m benchmark.run_benchmark --llm mock         # fast offline smoke test of the harness only

--llm mock proves the plumbing works (seeding, simulated time, scoring, report generation) but the mock client ignores its context entirely, so its accuracy numbers are meaningless by construction — that run is for verifying the harness, never for reporting a result. The --llm anthropic/--llm groq runs are the ones that produce a real with-memory-vs-no-memory delta; they require ANTHROPIC_API_KEY/GROQ_API_KEY respectively and aren't included in this repo's history because they cost real API calls to generate — run one yourself and the result lands in benchmark/results/.

Using mnemos as a coding assistant's memory (MCP)

src/mnemos/mcp_server.py exposes mnemos as an MCP server, so a tool-calling assistant (Claude Code, or anything else that speaks MCP) can use it as persistent memory across sessions instead of — or alongside — whatever memory mechanism it already has. Five tools:

Tool

Does

mnemos_remember(fact)

Store a durable fact worth recalling later

mnemos_recall(query, top_k=5)

Retrieve memories relevant to a query

mnemos_list_memories(limit=50)

List active memories with their IDs

mnemos_forget(fact_id, reason)

Archive a specific memory (soft-delete, audited)

mnemos_reflect()

Run a consolidation pass (merge/decay/forget) on demand

All memories are stored under one fixed user (MNEMOS_MCP_USER_ID, default claude-code) — this is a single-user personal memory store, not a multi-tenant one. mnemos_remember/mnemos_recall skip the episodic layer and LLM-based extraction entirely: the calling assistant is already the one deciding what's worth keeping, so re-running extraction over its own summary would be redundant. It respects STORAGE_BACKEND exactly like the CLI, API, and dashboard do, so whatever gets remembered here shows up there too.

uv run python -m mnemos.mcp_server                          # run it directly, stdio transport
claude mcp add mnemos --scope user -- \
  /path/to/mnemos/.venv/bin/python3 -m mnemos.mcp_server     # register with Claude Code

Does it actually help, or is it just plumbing that runs? Two separate checks, because "the tool call succeeds" and "the memory survives a real session boundary" and "the recalled context actually improves the answer" are three different claims:

  1. Cross-process persistence (benchmark/verify_cross_session_persistence.py) — the binary, unfakeable check. Two fully independent OS processes, sharing nothing but the external storage backend: process 1 calls mnemos_remember and exits completely; process 2 starts cold afterward and calls mnemos_recall. This is exactly what a real Claude Code session boundary looks like (each session spawns the MCP server as its own fresh subprocess), not a simulation within one running process.

    uv run python -m benchmark.verify_cross_session_persistence
    # Process 1 — a 'session' that learns something, then exits completely...
    #   Remembered [...]: The user is verifying that mnemos persists memory...
    # Process 2 — a brand-new, independent 'session', shares no state with process 1...
    #   - The user is verifying that mnemos persists memory... (similarity 0.29)
    # PASS — memory written by process 1 was recalled by process 2.
  2. Does recalled context improve answer quality (benchmark/eval_claude_code_memory.py) — the same with-memory-vs-no-memory methodology as the main benchmark above, run through the real mnemos_remember/mnemos_recall tool functions instead of ConversationManager. Ten realistic things a user might tell a coding assistant across past sessions (testing framework, dependency manager, deploy target, editor, code-review preferences...), then ten questions asked cold, scored before vs after:

    Condition

    Answered correctly

    Before mnemos (no memory, today's baseline)

    40%

    After mnemos (recall via MCP)

    100%

    Delta

    +60%

    Real run, --llm groq (openai/gpt-oss-120b), retrieval hit rate 100%. The 40% baseline isn't zero — a couple of questions have a defensible generic best-practice answer (e.g. "should new code have type hints?") that happens to match without any memory at all; the rest are simply unknowable without it. Reproduce with:

    uv run python -m benchmark.eval_claude_code_memory --llm groq   # or --llm anthropic

To actually verify this from inside a real Claude Code session rather than a script: register the server (command above), start a new session (MCP servers load at session start, so an already-running session won't pick it up), ask it to remember something with mnemos_remember, then in a different, later session ask it to recall it — the only test that exercises the real product end to end rather than a proxy for it.

Running it

cp .env.example .env   # fill in ANTHROPIC_API_KEY or GROQ_API_KEY + LLM_PROVIDER

# Postgres + pgvector, via Docker...
docker compose up -d
# ...or a local install (see below if you don't have Docker)

uv sync --extra dev
uv run alembic upgrade head
uv run pytest                                    # unit tests: instant, fully offline
uv run python -m mnemos.cli.demo                 # terminal chat; restart it to test cross-session recall
uv run python -m mnemos.cli.reflect --user demo-user   # manual reflection pass
uv run uvicorn mnemos.api.main:app --port 8000   # full HTTP API (needed for the dashboard)
uv run python -m benchmark.run_benchmark --llm anthropic

Without Docker, install Postgres 16+ and pgvector locally (e.g. on macOS, brew install postgresql@17 pgvector, since Homebrew's pgvector bottle targets newer Postgres major versions than 16), create a mnemos role/db, CREATE EXTENSION vector; as a superuser, and point DATABASE_URL in .env at it. Integration tests default to spinning up an ephemeral Postgres+pgvector via testcontainers (works out of the box in CI); without Docker locally, set MNEMOS_TEST_DATABASE_URL to a scratch database instead and they'll use that.

Postgres is the default backend (STORAGE_BACKEND=postgres, or unset). To try Qdrant, no setup is needed — it runs embedded (set STORAGE_BACKEND=qdrant in .env or the environment). For Neo4j: brew install neo4j, neo4j-admin dbms set-initial-password <password> before first start, brew services start neo4j, then set STORAGE_BACKEND=neo4j and NEO4J_PASSWORD to match.

API surface

POST   /users/{user_id}/messages                chat turn (choose strategy -> recall -> LLM -> store -> maybe extract -> resolve prior turn)
GET    /users/{user_id}/memories/episodic        list stored turns
GET    /users/{user_id}/memories/semantic        list facts (?status=active|merged|forgotten)
DELETE /users/{user_id}/memories/semantic/{id}   explicit forgetting of one fact
POST   /users/{user_id}/reset                    wipe a user's memory
POST   /users/{user_id}/seed                     write pre-scripted turns (used by the benchmark)
POST   /users/{user_id}/retrieval-trace          run recall() without calling the LLM; ?as_of= for time travel
GET    /users/{user_id}/stats                    counts by type/status + growth-over-time buckets
POST   /users/{user_id}/reflect                  run a reflection pass now
GET    /users/{user_id}/reflection-log           audit trail of merge/decay/forget actions
GET    /users/{user_id}/procedural                per-strategy uses/success-rate

Tests

uv run pytest                 # unit tests (mocked LLM/embeddings where the test isn't about real quality)
uv run pytest tests/unit/test_retrieval.py  # runs against real local embeddings, not mocks —
                                              # the point of that file is proving semantic ranking works

63 tests total: the storage backend-contract suite (11 behavioral tests × 3 backends), retrieval ranking (real embeddings), extraction + dedup, procedural strategy selection/outcome-scoring, reflection merge/decay/ forget/reinforce, the conversation loop, the full HTTP API via httpx.AsyncClient, and benchmark scoring logic. Neo4j's contract tests skip gracefully if no local instance is reachable rather than failing the suite.

What's intentionally out of scope

A few things stayed out on purpose, to keep every feature above honestly and fully implemented rather than half-built:

  • Two real LLM providers (Anthropic, Groq) plus a mock — the LLMClient interface supports adding OpenAI/Gemini/local models later without touching agent or memory code, but a third real client isn't built.

  • No background scheduler — reflection runs on demand (CLI/dashboard), not on a timer. Adding one is an infra decision (cron, a task queue) orthogonal to the memory logic itself.

  • Heuristic-only signals — procedural memory's "success" signal (correction-cue keyword matching) and reflection's clustering/decay thresholds are documented defaults, not tuned or validated against a labeled dataset. They're real and inspectable, not research-grade.

License

MIT

Available Tools

5 tools
mnemos_forgetA

Forget (archive) a specific memory by its ID, as shown by mnemos_list_memories. Soft-delete: the fact is archived, not erased, and stays visible in mnemos's audit log — never call this speculatively, only when the user explicitly asks to forget something.

ParametersJSON Schema
NameRequiredDescriptionDefault
reasonNouser_requested
fact_idYes

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?

With no annotations provided, the description carries the full burden. It clearly discloses that this is a soft-delete (archived, not erased), that the fact remains visible in the audit log, and that the operation is not reversible/dismissive. This is strong behavioral context for a mutation-like tool, though it does not specify what happens to related data or the exact response format.

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 sentences with no wasted words. The most important usage constraint ('never call speculatively') is placed at the end but the whole description is compact. It front-loads the core action and the source of the ID.

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

Completeness4/5

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

Given the tool has 2 parameters, no annotations, and no output schema details beyond its existence, the description covers the key decision points: what the tool does, where the ID comes from, the soft-delete behavior, and when to call it. It could add a note about the audit-log implication more explicitly, but it essentially does. This is nearly complete for the tool's complexity.

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

Parameters4/5

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

Schema description coverage is 0%, so the description must compensate. The description explains that fact_id is the memory ID as shown by mnemos_list_memories, giving practical meaning. The reason parameter is not individually explained, but the default value 'user_requested' and the overall guidance about explicit user requests imply its purpose; a small gap remains.

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 uses a specific verb ('Forget (archive)') and resource ('a specific memory by its ID'), and even names the sibling tool mnemos_list_memories as the source of IDs, distinguishing it from other mnemos tools. It is immediately clear what this tool does.

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

Usage Guidelines5/5

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

The description gives explicit when-to-use guidance: only when the user explicitly asks to forget something, and explicitly warns against speculative calls. It also explains the soft-delete behavior and audit-log visibility, which helps an agent decide when this is appropriate.

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

mnemos_list_memoriesA

List all currently active memories with their IDs, for browsing or as input to mnemos_forget.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior3/5

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

With no annotations, the description must carry behavioral disclosure by itself. It does reveal that results are restricted to 'currently active' memories and that IDs are returned, but it fails to disclose the effect of the limit parameter on the 'all' claim, nor does it state ordering or pagination. 'List' implicitly signals a read-only operation.

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?

One sentence with no filler; the active verb and object are front-loaded, and the downstream use for mnemos_forget is appended without redundancy.

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

Completeness2/5

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

Output schema exists so return values need not be spelled out, but the single input parameter is semantically undocumented and the word 'all' conflicts with an optional limit default of 50. An agent cannot tell whether it receives every active memory or only up to the limit, making the tool incompletely specified.

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

Parameters1/5

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

Schema description coverage is 0%; the sole parameter, limit, is never mentioned in the description. The agent must rely entirely on the parameter name and default value, so the description adds no meaning beyond the schema.

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?

Description opens with a specific verb and resource: 'List all currently active memories'. It further clarifies output (IDs) and intended downstream use (input to mnemos_forget), separating it from the sibling tools by its enumerating role.

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

Usage Guidelines4/5

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

'For browsing or as input to mnemos_forget' gives explicit contexts in which this listing tool is appropriate. It does not explicitly say when to prefer mnemos_recall or mnemos_remember, so no exclusion is stated, but the use cases are clear enough.

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

mnemos_recallA

Retrieve memories relevant to a query — call this near the start of a task that might benefit from prior context about the user or their preferences/projects.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes
top_kNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior3/5

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

With no annotations provided, the description carries the full burden for behavioral disclosure. It communicates the core read-only behavior through the verb Retrieve and the notion of relevance-based selection, but it does not mention whether memories are modified, how results are ranked, or what happens when no relevant memories exist. This is minimal but not misleading.

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 a single, front-loaded sentence: the function is stated first, followed by usage context. Both parts carry meaningful information with no redundant wording.

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

Completeness3/5

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

The tool is simple — two parameters, one required, a default value, and an output schema — and the description covers the main purpose and timing. However, it lacks parameter-level detail (notably top_k) and does not explicitly assert non-mutating behavior, leaving minor but real gaps for an agent.

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

Parameters2/5

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

Schema description coverage is 0%, so the description must compensate for the lack of parameter documentation. It only hints at the query parameter by saying 'relevant to a query', and it gives no guidance on composing the query or on the meaning of top_k, such as it being a limit on the number of returned memories. This is insufficient for an agent to confidently set parameters.

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 uses a specific verb (Retrieve) and a resource (memories relevant to a query), which unambiguously defines the tool's function. It also distinguishes this recall operation from the sibling tools (remember, list, forget, reflect) without requiring their schemas.

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

Usage Guidelines4/5

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

The description gives a clear when-to-use hint: 'call this near the start of a task that might benefit from prior context about the user or their preferences/projects.' However, it does not explicitly state when not to use it or directly name alternative tools for other memory operations, relying instead on sibling names to imply the distinction.

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

mnemos_reflectA

Run a consolidation pass: merge near-duplicate memories, decay stale ones, archive anything that decays past the forget threshold. Requires a real LLM provider configured (ANTHROPIC_API_KEY or GROQ_API_KEY) for the merge step. Call this occasionally to keep memory tidy, not on every turn.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

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?

With no annotations provided, the description carries the full burden, and it does well by disclosing side effects (merging, decaying, archiving), the API key requirement, and the intended cadence. It could be more explicit about whether archived memories are still retrievable or if the pass is irreversible, but this is above baseline for an unannotated tool.

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 sentences, each earning its place: the action, the prerequisite, and the usage frequency. No redundancy or filler, and key information is front-loaded.

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

Completeness4/5

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

For a zero-parameter maintenance tool with an output schema available, the description covers behavior, prerequisites, and invocation frequency. It lacks a brief note on reversibility or what happens to archived memories, but that is not blocking given the simplicity of the tool.

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

Parameters4/5

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

The tool has zero parameters and the schema coverage is 100%, so the baseline is 4. The description adds relevant context about external dependencies (ANTHROPIC_API_KEY or GROQ_API_KEY) even though no parameters need documenting.

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 operation: 'Run a consolidation pass' with concrete sub-actions (merge near-duplicates, decay stale ones, archive past threshold). This clearly distinguishes it from the sibling tools, which handle individual memory operations like remember, recall, list, and forget.

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

Usage Guidelines4/5

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

The description gives explicit usage guidance: call it 'occasionally to keep memory tidy' and 'not on every turn.' It also flags a key prerequisite (LLM provider API key). It does not explicitly name alternatives or contrast them, but the guidance is clear enough for an agent to decide when to invoke it.

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

mnemos_rememberA

Store a durable fact worth recalling in a future session — a stated preference, a decision and its reasoning, a recurring convention. Not routine task details, not anything already in the codebase/CLAUDE.md.

ParametersJSON Schema
NameRequiredDescriptionDefault
factYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior3/5

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

No annotations are present, so the description carries the full burden. It does add useful behavioral context by stressing durability ('recalling in a future session') and by excluding content already in the codebase/CLAUDE.md. However, it does not disclose potential behaviors like dedupication, overwriting, or failure modes. For a simple store operation this is adequate but not thorough.

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 tight and front-loaded: it opens with the essential action and object, then gives examples and exclusions. There is no fluff or redundant content; every phrase earns its place. The structure makes it easy to scan.

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

Completeness4/5

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

For a one-parameter tool with an output schema, the description covers the critical aspects: what to store, why to store it, and what not to store. It could mention whether repeated stores are idempotent or whether there are size limits, but these are not essential for basic correct invocation. Overall it is complete enough for the agent to use the tool 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 schema provides no description for 'fact' and coverage is 0%, so the description must compensate. It does so well by defining what 'fact' should contain (stated preferences, decisions with reasoning, recurring conventions) and what to exclude. This gives the agent meaningful guidance beyond the parameter name, though no concrete example or length/format is given.

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 uses a specific verb ('Store') with a clear resource ('a durable fact worth recalling') and gives concrete examples ('stated preference, a decision and its reasoning, a recurring convention'). It also states what it is not for ('routine task details', 'anything already in the codebase/CLAUDE.md'), making it easily distinguishable from the sibling retrieval tools like mnemos_recall and mnemos_list_memories.

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

Usage Guidelines4/5

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

The description provides clear inclusion criteria (durable, future-worthy, preferences/decisions/conventions) and explicit exclusion criteria ('Not routine task details, not anything already in the codebase/CLAUDE.md'). It does not name alternative tools explicitly, but the when-to-use guidance is strong enough to route an agent correctly.

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. 5 tool updatesv0.1.0
    • First observedmnemos_forget
    • First observedmnemos_list_memories
    • First observedmnemos_recall
    • First observedmnemos_reflect
    • First observedmnemos_remember

TDQS

A4.2/5.0

Scored across 5 tools

Disambiguation5/5

Each tool maps to a distinct lifecycle stage: remember stores, recall retrieves relevant memories, list_memories browses all memories, forget archives by ID, and reflect consolidates. There is no meaningful overlap in intended use.

Naming Consistency4/5

All tools share a consistent mnemos_ prefix and lowercase snake_case imperative style. The only minor deviation is that list_memories includes a direct object while remember, recall, forget, and reflect are verb-only.

Tool Count5/5

Five tools is well-scoped for a memory server, and each tool earns its place in the set. It sits comfortably within the ideal 3–15 range with no redundant utilities.

Completeness5/5

The set covers the full memory lifecycle: create (remember), read (recall/list), delete (forget), and maintenance (reflect). No obvious dead ends or necessary operations are missing for the intended use case.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Long-term memory for AI agents. Compiles conversations into a structured knowledge base with Claim/Evidence model, source provenance, append-only timeline, and contradiction detection. Multi-path retrieval (Exact + BM25 + Graph + weighted RRF + reranker) — 96.6% R@5 on LongMemEval-S, zero vector dependencies.
    8
    3
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Long-term memory for AI agents over MCP — episodic + semantic memory, a temporal knowledge graph, and a dialectic user model, exposed as 32 tools (recall, remember, context, graph, dreaming, peers). Zero dependencies, runs fully offline; leads the LoCoMo benchmark at ~35x fewer LLM calls.
    2
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Persistent long-term memory for AI agents via MCP, saving 80-90% memory-related token costs by enabling on-demand recall instead of always-injecting context.
    8 npm
    7
    MIT