Holographic Memory MCP
Holographic Memory
Local memory for agents and apps: full-text and vector search, a memory API and a vector-symbolic fact store in one embeddable Rust engine that makes no network calls.
Website · Docs · Get started · Results · Why "holographic"? · Capabilities · Limits · Contribute
What it is
Holographic Memory (HMS for short) is a Rust library that stores text, vectors and facts in a directory on your machine and finds them again. It ships with Node.js bindings, C, WASM and UniFFI bindings, an HTTP server with a Python client, and an MCP server. There are no service calls, no telemetry and no model downloads unless you turn on a feature that asks for one.
Capability | What you get |
Document search | Chunking, stemmed BM25 over blocked postings, Boolean and phrase queries, hybrid fusion with embeddings you supply, metadata filters, byte offsets |
Vector index | Graph index with a memory-mapped vertex file, an updatable delta for inserts and deletes, optional query-aware routing trained on sample queries |
Memory API |
|
Storage | Atomic replace and delete, crash-safe journal and checkpoints, verified compaction, one writer per store |
Security | Optional AES-256-GCM encryption at rest, signed audit log, COSE/SCITT provenance |
Vector-symbolic layer | Sparse hypervectors with bind, bundle and permute, triples with multi-hop queries, Hopfield cleanup |
Related MCP server: mnemo-mcp
Who it's for
You are building | What you get |
An agent or assistant that needs memory | A memory API with forgetting that removes facts from the store, and an MCP server for editors and agent hosts |
A local-first or offline app | Embedded search and storage in one directory, with no server process and optional encryption |
A service that needs vector or text search |
|
Research on vector-symbolic memory | The bind/bundle algebra, meaning memory and capacity experiments with their result files |
Headline results
Every row links to the committed file it comes from. These are single machines and stated protocols, not a substitute for testing your own data.
Result | Conditions | Source file |
Bemula forgetting score 0.322; best published row 0.310 (LangMem) | Full split: 50 dialogues, 135 forget requests. Agent and judge both | |
LongMemEval dev 89.75 of 100 correct (mean of 4 runs, range 87 to 93) | Official generation and judge prompts, | |
VIBE llama-128-ip strict recall@100 up to 0.99687 at 1,457 QPS | Official test queries, one thread, query-aware graph (ef=512). Neither frozen RoarGraph configuration reached 0.99. Behind at 0.95 and 0.99 QPS (see below). | |
VIBE yi-128-ip at recall 0.95: 3,214 QPS against 1,953 (MLANN RF80) | Same protocol; compared only with the frozen RoarGraph and MLANN configurations measured there. Recall 0.99 was not reached. | |
SIFT-1M QPS at recall@10 0.95: 17,481 against hnswlib 7,630 (2.29x, interval 1.95 to 2.39) | Core Ultra 9 185H, queries issued one at a time from one client thread, process not pinned to a core, index built with 8 threads. Timed through the Rust API, hnswlib through its Python binding. | |
SciFact nDCG@10 hybrid 0.729, above its own BM25 (0.688) and dense (0.645) | 5,183 documents, 300 queries, MiniLM embeddings supplied by the harness. NFCorpus: 0.355 against 0.323 and 0.317. |
Where it's behind
Result | Conditions | Source file |
ann-benchmarks at recall 0.95: sift 2,819 QPS against faiss-ivfpqfs 3,788 (0.74x); glove 1,019 against ScaNN 1,535 (0.66x) | Official ann-benchmarks runner, each run pinned to one CPU, AVX2 host | |
VIBE llama-128-ip QPS: 24.6% behind RoarGraph at recall 0.95, 54.8% behind MLANN RF80 at 0.99 | One thread, official test queries | |
SIFT-1M build time 0.58x and serving RSS 0.46x of hnswlib | Same run as the QPS row above | |
Text search ingest time, serving RSS and cold start behind SQLite FTS5 and tantivy on most corpora | BEIR and memory corpora, two hosts | |
glove-100-angular with the sparse vector path: recall@10 0.252 at 33.7 QPS against FAISS HNSW 0.832 at 4,111 QPS | One thread, Apple M4; the sparse path is experimental |
Method, intervals and every loss: docs/benchmarks and the benchmarks pages.
Generated from benchmarks/results/competitive/ by hms-bench table; method and caveats in docs/benchmarks/COMPARISON.md. W = HMS ahead (95% interval excludes parity), T = tie, L = a comparator is ahead.
Dataset | Metric | HMS | Best other | Advantage | |
sift-128-euclidean | QPS @ recall@10 >= 0.95 (QPS) | 17,481 | hnswlib 7,630 | 2.29x | W |
sift-128-euclidean | p99 latency @ 0.95 (us) | 105 | hnswlib 220 | 2.10x | W |
sift-128-euclidean | Build time (s) | 312.60 | hnswlib 182.49 | 0.58x | L |
sift-128-euclidean | RSS, serving (MiB) | 2,204 | hnswlib 1,019 | 0.46x | L |
sift-128-euclidean | Index size (MiB) | 446.3 | hnswlib 873.8 | 1.96x | - |
beir:fiqa | nDCG@10 | 0.2654 | SQLite FTS5 0.2678 | 0.99x | T |
beir:fiqa | QPS (single thread) (QPS) | 3,576 | tantivy 1,617 | 2.21x | W |
beir:fiqa | p99 latency (us) | 501 | tantivy 1,269 | 2.53x | W |
beir:fiqa | Ingest/build time (s) | 2.16 | SQLite FTS5 1.18 | 0.55x | L |
beir:fiqa | Peak RSS, serving (MiB) | 2,480 | SQLite FTS5 2,480 | 1.00x | T |
beir:fiqa | On-disk size (MiB) | 20.6 | Apache Lucene 9.12.3 (JVM) 12.2 | 0.59x | L |
memory | nDCG@10 | 0.4561 | Apache Lucene 9.12.3 (JVM) 0.4627 | 0.99x | T |
memory | QPS (single thread) (QPS) | 2,225 | tantivy 2,399 | 0.93x | T |
memory | p99 latency (us) | 786 | tantivy 733 | 0.93x | T |
memory | Ingest/build time (s) | 2.52 | SQLite FTS5 2.06 | 0.82x | L |
memory | Peak RSS, serving (MiB) | 66 | SQLite FTS5 26 | 0.39x | L |
memory | On-disk size (MiB) | 9.8 | Apache Lucene 9.12.3 (JVM) 10.3 | 1.05x | W |
memory | nDCG@10 | 0.4561 | Apache Lucene 9.12.3 (JVM) 0.4627 | 0.99x | T |
memory | QPS (single thread) (QPS) | 11,857 | tantivy 4,591 | 2.58x | W |
memory | p99 latency (us) | 145 | tantivy 412 | 2.85x | W |
memory | Ingest/build time (s) | 1.21 | SQLite FTS5 0.88 | 0.73x | L |
memory | Peak RSS, serving (MiB) | 2,480 | SQLite FTS5 2,480 | 1.00x | T |
memory | On-disk size (MiB) | 9.8 | Apache Lucene 9.12.3 (JVM) 10.3 | 1.05x | W |
beir:nfcorpus | nDCG@10 | 0.2916 | SQLite FTS5 0.2914 | 1.00x | T |
beir:nfcorpus | QPS (single thread) (QPS) | 38,963 | tantivy 14,705 | 2.65x | W |
beir:nfcorpus | p99 latency (us) | 57 | tantivy 150 | 2.62x | W |
beir:nfcorpus | Ingest/build time (s) | 0.23 | SQLite FTS5 0.14 | 0.59x | L |
beir:nfcorpus | Peak RSS, serving (MiB) | 748 | SQLite FTS5 748 | 1.00x | T |
beir:nfcorpus | On-disk size (MiB) | 2.1 | Apache Lucene 9.12.3 (JVM) 1.5 | 0.72x | L |
beir:nfcorpus | nDCG@10 | 0.2916 | SQLite FTS5 0.2914 | 1.00x | T |
beir:nfcorpus | QPS (single thread) (QPS) | 9,532 | tantivy 7,105 | 1.34x | T |
beir:nfcorpus | p99 latency (us) | 298 | tantivy 300 | 1.01x | T |
beir:nfcorpus | Ingest/build time (s) | 0.59 | SQLite FTS5 0.29 | 0.48x | L |
beir:nfcorpus | Peak RSS, serving (MiB) | 57 | SQLite FTS5 30 | 0.53x | L |
beir:nfcorpus | On-disk size (MiB) | 2.1 | Apache Lucene 9.12.3 (JVM) 1.5 | 0.72x | L |
beir:scifact | nDCG@10 | 0.7027 | Apache Lucene 9.12.3 (JVM) 0.7022 | 1.00x | T |
beir:scifact | QPS (single thread) (QPS) | 17,782 | tantivy 6,450 | 2.76x | W |
beir:scifact | p99 latency (us) | 95 | tantivy 292 | 3.09x | W |
beir:scifact | Ingest/build time (s) | 0.31 | SQLite FTS5 0.20 | 0.63x | L |
beir:scifact | Peak RSS, serving (MiB) | 196 | tantivy 174 | 0.89x | T |
beir:scifact | On-disk size (MiB) | 2.9 | Apache Lucene 9.12.3 (JVM) 2.0 | 0.68x | L |
beir:scifact | nDCG@10 | 0.7027 | Apache Lucene 9.12.3 (JVM) 0.7022 | 1.00x | T |
beir:scifact | QPS (single thread) (QPS) | 2,711 | tantivy 2,879 | 0.94x | L |
beir:scifact | p99 latency (us) | 526 | tantivy 520 | 0.99x | T |
beir:scifact | Ingest/build time (s) | 1.42 | SQLite FTS5 0.43 | 0.30x | L |
beir:scifact | Peak RSS, serving (MiB) | 160 | SQLite FTS5 54 | 0.34x | L |
beir:scifact | On-disk size (MiB) | 2.9 | Apache Lucene 9.12.3 (JVM) 2.0 | 0.68x | L |
Get started
Clone once: git clone https://github.com/writerslogic/holographic-memory && cd holographic-memory.
Run the offline agent-memory demo with cargo run --release --example demo-offline-agent.
From your own Rust project, add the current Apache-2.0 source:
cargo add holographic-memory --git https://github.com/writerslogic/holographic-memoryThe published crate 0.6.1 remains AGPL-3.0-or-later.
use holographic_memory::{DocumentInput, HmsCore, SearchOptions};
let dir = tempfile::tempdir()?;
let hms = HmsCore::new(4096, Some(dir.path().display().to_string()), None)?;
hms.memorize_document(DocumentInput {
id: "backups".into(),
text: "Restore deleted documents from verified backups.".into(),
source_uri: Some("operations.md".into()),
version: Some("1".into()),
metadata: Some(serde_json::json!({ "project": "alpha" })),
chunk_words: None, overlap_words: None, store_text: None, embeddings: None, title_weight: None,
})?;
let hits = hms.search_documents("restore backups", &SearchOptions::default())?;
println!("{} {:?}", hits[0].document_id, hits[0].source_uri);The npm registry carries only the deprecated 0.3.0; build the native module from source (Rust required):
npm ci --omit=optional && npm run buildconst { HolographicMemorySystem } = require('./index.js')
const { DocumentMemory } = require('./semantic.js')
async function main() {
const dir = require('node:fs').mkdtempSync(require('node:path').join(require('node:os').tmpdir(), 'hms-readme-'))
const memory = new DocumentMemory(new HolographicMemorySystem(4096, dir))
await memory.memorize({
id: 'backups', text: 'Restore deleted documents from verified backups.',
sourceUri: 'operations.md', version: '1', metadata: { project: 'alpha' },
})
const [hit] = await memory.search('restore backups', { filter: { project: 'alpha' } })
console.log(hit.documentId, hit.sourceUri, hit.text)
await memory.flush()
}
main().catch(error => { console.error(error); process.exit(1) })The Python package holographic-memory-sdk is an HTTP client for hms-server, which stores vectors you
supply. (holographic-memory-py provides native vector search and phase vectors, but no document search.)
cargo build --release --features server --bin hms-server
HMS_API_KEY=change-me target/release/hms-server --input-dim 384 &
uv pip install holographic-memory-sdkfrom holographic_sdk import HolographicClient
client = HolographicClient(url="http://127.0.0.1:8080", api_key="change-me", tenant_id="demo")
client.add_documents([{"id": "a", "vector": [0.1] * 384, "text": "hello", "metadata": {}}])
print(client.query([0.1] * 384, top_k=1)["matches"][0]["id"])Endpoints, flags, tenants and limits are in the
server docs. A runnable Python quickstart
with a toy embedder is in examples/quickstart/python.
hms-mcp serves a store to any Model Context Protocol client over stdio or HTTP:
cargo build --release --features mcp,security --bin hms-mcp # add local-models for semantic recall
./target/release/hms-mcp # stdio
./target/release/hms-mcp --transport http # http://127.0.0.1:8765/mcpClient setup, data directory and the one-writer rule: MCP integration.
The five-minute quickstart
walks through ingest, search, a SQLite FTS5 comparison and encryption in Rust, Python and Node,
and CI runs that code on every pull request. For a whole agent memory with no embeddings and no
network, run cargo run --example demo-offline-agent.
Why is it called "holographic"?
In a hologram, every fragment of the plate carries a blurry copy of the whole picture. Cut it in half and you get the whole picture at lower resolution, not half a picture. Holographic Reduced Representations and Vector Symbolic Architectures (VSA) give memory that property:
Bind two vectors (here, XOR of sparse bit sets) to tie a role to a filler:
capital ⊗ paris. Binding again with the role recovers a noisy copy of the filler.Bundle many bound pairs into one vector of fixed size. The result stays similar to every part, so one vector can hold a whole record, with no single bit holding any one fact.
Recall by resonance. Present a noisy cue and the nearest stored pattern wins (similarity search, Hopfield cleanup). Damaging the vector degrades every item a little instead of deleting a few, so recall falls off gradually under noise.
You can watch it happen in a few lines (a union bundle of 100 items, damaged step by step):
cargo run --release --manifest-path examples/quickstart/rust/Cargo.toml --bin holographic.
What is literally that here: the sparse hypervector type and its bind, bundle and permute
operations; the relational "meaning memory" (triples stored as bound composites, multi-hop
queries, Hopfield cleanup); the union-bundle membership test and the resonator network (both
behind the experimental feature).
What is conventional engineering: document search (stemmed BM25 plus exact cosine; it does not read the hypervectors), the graph and inverted-file vector indexes, the memory API, the journaled storage and the servers. No holographic behavior is claimed for these. The longer version, with the tested numbers, is in Concepts.
What ships today
Capability | Status | Documented limit |
Hybrid document search (BM25 + exact cosine, filters, byte offsets) | Ships | Exact scan of eligible chunks; benchmark your corpus before assuming capacity (guide) |
Boolean lexical search ( | Ships | Flat queries only, no nesting; an empty or only-excluded query matches nothing (design) |
Atomic replace/delete, crash-safe journal, verified compaction, process lock | Ships | One writer per store; compaction is not secure erasure |
Vector index ( | Ships | Build time and serving RSS behind hnswlib on SIFT-1M (gaps) |
Updatable vector index (durable delta for inserts and deletes) | Ships | Single writer; format in VECTOR-UPDATES.md |
Query-aware graph routing (trained on sample queries) | Ships (opt-in) | Needs representative training queries; tested on VIBE llama and yi (report) |
Memory API: remember, recall, forget, explain | Ships | Forgetting removes a fact from the store; it does not cover what a model inferred (docs) |
Conversation memory ( | Ships | Bounded context with exact source provenance; native recall not yet benchmarked on LongMemEval end to end |
Meaning memory: triples, multi-hop, Hopfield cleanup | Ships (runtime opt-in) | Tested on synthetic and 2,120-fact sets ( |
Union-bundle membership ( | Research | At D = 16,384: positive gap to N = 500 (4% false positives), tested to 2,500 ( |
Sparse vector index ( | Experimental | Not competitive with HNSW (results) |
Encrypted stores ( | Ships | AES-256-GCM, Argon2id; covers the log, index caches and the text segment. Metadata, file names and sizes are not encrypted |
Signed audit log, credential-gated access, COSE/SCITT/C2PA ( | Ships | Host must authenticate DIDs; PoSME, RATS, |
On-device models ( | Ships | Local weights only; never downloads |
| Ships | Single node; TLS, per-tenant scoped keys (guide) |
MCP server ( | Ships | Encryption needs |
Node.js bindings | Ships (build from source) | npm package not yet republished |
C, WASM and UniFFI bindings | Ships (build from source) | See |
Python | Client + phase substrate | No in-process document search |
Query-private search ( | Experimental | For searching an index someone else holds; hides the query vector from the scorer only (notes) |
Every capability above except document search and the vector index is behind a Cargo feature or runtime option that is off by default; Cargo features lists them all.
Architecture
flowchart TB
subgraph bindings["Bindings and servers"]
rust["Rust crate"]
node["Node.js (napi)"]
ffi["C, WASM, UniFFI"]
python["Python: holographic-memory-sdk"]
server["hms-server (HTTP)"]
mcp["hms-mcp (stdio, HTTP)"]
end
subgraph engine["Engine API"]
core["HmsCore: documents, vectors, facts, mutation gate"]
prov["Security and provenance: signed audit log, COSE/SCITT"]
end
subgraph indexes["Indexes and memory"]
text["Text index: blocked postings, BM25, Boolean and phrase"]
vector["Vector index (qgraph): vertex file, updatable delta, query-aware projection"]
memory["Memory API: remember, recall, forget, conversation memory"]
vsa["Vector-symbolic layer: bind, bundle, Hopfield cleanup"]
end
subgraph storage["Storage"]
journal["Journal and checkpoints"]
segments["Append-only segments (CRC32, LZ4)"]
mapped["Memory-mapped files"]
crypto["AES-256-GCM encryption (optional)"]
end
python --> server
rust --> core
node --> core
ffi --> core
server --> core
mcp --> core
core --> text
core --> vector
core --> memory
core --> vsa
core --> prov
memory --> text
text --> journal
vector --> journal
memory --> journal
journal --> segments
segments --> mapped
segments --> cryptoModule map: docs/ARCHITECTURE.md.
Limits
Not anonymous. Hypervector sketches and term counts are not encryption; anyone with a store can encode candidate text and compare.
storeText: falsekeeps term counts, IDs and embeddings, so it is not a privacy control.Document search is conventional. It does not use the hypervectors; the holographic parts are the algebra and the meaning memory.
One writer per store. A second writing process fails at open with a lock error.
Capacity numbers are points, not guarantees, and are stated only over the tested range (capacity).
Benchmark results are single machines. Conditions are in each results file; the single-core ann-benchmarks run and the VIBE high-recall points are losses (see Where it's behind).
Masking is obfuscation. Client-side vector masking preserves every pairwise similarity.
A store is a directory and
HmsCore::new(.., None, ..)uses the current directory.
Full statement: docs/PRIVACY.md. Report vulnerabilities privately via SECURITY.md.
Contributing
CONTRIBUTING.md: setup, the checks CI runs, DCO sign-off (git commit -s, no
CLA). Start with a good first issue
or Discussions. See the
Code of Conduct. Benchmark changes regenerate the results file they touch;
the README cites files, not remembered figures.
License
Apache-2.0. See LICENSE. Use, modify and embed it in open-source or proprietary software, including network services, with no copyleft obligation. Releases up to and including crate 0.6.1 were published under AGPL-3.0-or-later and stay available under those terms.
Available Tools
8 toolsexportExport memory contentsARead-onlyIdempotent
Read a portable snapshot of memory contents as JSON, JSONL or Markdown; use list for short previews instead. Read-only. The payload is returned as text and capped by the server's export byte limit; pass next_cursor as cursor to continue.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | Include only memories carrying this tag. | |
| limit | No | Maximum memories in this export segment, also bounded by the server's byte limit. | |
| cursor | No | `next_cursor` from the previous export segment. | |
| format | No | Serialization of the returned text payload. | json |
| prefix | No | Include only memory ids starting with this prefix. |
Output Schema
| Name | Required | Description |
|---|---|---|
| bytes | Yes | Size of the returned text payload in bytes. |
| count | Yes | Number of exported memories in this segment. |
| format | Yes | Serialization used for the text payload. |
| next_cursor | Yes | Pass a string value as `cursor` for the next segment; null ends the export. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true and destructiveHint=false, so the safety profile is covered. The description adds genuinely new behavior: the payload comes back as text, it is capped by a server-side export byte limit, and segments are chained via cursor — exactly the truncation semantics an agent must know. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the verb, resource, and formats, followed by the sibling alternative and the pagination note, with no filler. It is dense — three distinct points packed into two sentences — but each sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value details need not be explained, and annotations handle the safety profile. Between the description and schema, formats, byte cap, and cursor continuation are all covered; only snapshot consistency across segments is left implicit.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter (tag, limit, cursor, format, prefix) is already documented in the schema, and the description mostly restates the format enum and the cursor/next_cursor relationship. Baseline 3 applies when the schema does the heavy lifting; the description adds little syntax or edge-case meaning beyond it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Read a portable snapshot of memory contents") plus the three serializations produced. It explicitly differentiates from the `list` sibling by naming it as the short-preview alternative, so an agent can route without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives one explicit exclusion with a condition: use `list` for short previews instead. It also explains the continuation path ("pass `next_cursor` as `cursor` to continue"). It does not address when to prefer export over `get`, `recall`, or `search`, so it stops short of full when/when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
forgetDelete memoriesADestructiveIdempotent
Delete memories from the active store by id, ids or id prefix; use remember to update content instead. Inspect prefix matches with list first: prefix deletion requires confirm: true. Returns the deletion count and ids that were absent.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | One exact memory id to delete; cannot combine with `prefix`. | |
| ids | No | Exact memory ids to delete in one call; cannot combine with `prefix`. | |
| prefix | No | Delete every id starting with this prefix; use `list` with the same prefix first. | |
| confirm | No | Set true to authorize prefix deletion; exact ids do not need it. |
Output Schema
| Name | Required | Description |
|---|---|---|
| deleted | Yes | Number of memories deleted. |
| missing | Yes | Requested ids that were not present. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint and idempotentHint, so the description's added value is the confirm gate for prefix deletion and the note that the tool reports deletion count plus absent ids. That is genuine behavioral context the annotations do not carry; it stops short of stating irreversibility or how partial failures are handled across a 1000-id batch.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense sentence front-loads the action and selection modes, then the alternative, the pre-check, and the return contract follow in order. Every clause carries information; nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a destructive, no-required-param tool and an output schema, the description covers the destructive scope of prefix deletion, the confirmation requirement, the recommended inspection step, and the sibling to use instead. An agent can call this correctly without further reading.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the schema already documents the id/ids/prefix mutual exclusion and the confirm flag, so the description is largely restating it. It does tie `confirm` to the prefix mode operationally, but adds no new syntax or edge-case semantics (e.g. calling with no selector at all, where all params are optional).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ("Delete") plus resource ("memories from the active store") and names the three selection modes (id, ids, prefix). It also routes away from a sibling by naming `remember` for the update case, so the tool is distinguishable without inspecting the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternative for the adjacent use case ("use `remember` to update content instead") and gives a precondition/ordering rule ("Inspect prefix matches with `list` first"). It also states the gate condition for the risky path (prefix deletion requires `confirm: true`), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
getGet memoryARead-onlyIdempotent
Read one known memory id in full, including tags, metadata and version. Use an id from list or search; use recall when only a natural-language question is known. Read-only; an unknown id returns an error.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Exact id of the memory to read, as returned by `remember`, `list` or `search`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| memory | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive, and closed-world behavior. The description adds useful behavioral context by stating that an unknown id returns an error, though it repeats the read-only trait already covered by annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three compact sentences with no wasted text. The core operation is front-loaded, followed by sourcing and alternative guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter read tool with rich annotations and an output schema, the description is complete. It covers the operation, id source, alternative tool, and error behavior for unknown ids.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the `id` parameter is fully documented in the schema as the exact id returned by `remember`, `list`, or `search`. The description reinforces the id provenance but adds little parameter-level detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: reading one known memory id in full. It also names included fields and explicitly differentiates this from `recall`, `list`, and `search`, so an agent can distinguish it from siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit sourcing guidance: use an id from `list` or `search`, and use `recall` when only a natural-language question is known. This directly covers when to use this tool versus a key alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
listList memoriesARead-onlyIdempotent
Browse memories in id order, optionally limited by id prefix or tag; use search for relevance ranking. Read-only. Returns text previews unless full is true; pass next_cursor as cursor to continue.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | Include only memories carrying this tag. | |
| full | No | Return full text instead of a 240-character preview. | |
| limit | No | Maximum memories in this page. | |
| cursor | No | `next_cursor` of the previous page. | |
| prefix | No | Include only memory ids starting with this prefix. |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | Number of memories in this page. |
| memories | Yes | Memories in id order, with full text or previews. |
| next_cursor | Yes | Pass a string value as `cursor` to continue; null ends the listing. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive behavior, so the safety profile is covered. The description adds useful behavioral detail beyond that: results are in id order, previews are returned unless `full` is true, and pagination continues by passing `next_cursor` as `cursor`.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is a single dense sentence with zero filler, front-loading the core operation and ordering before alternatives and return behavior. Every clause carries operational information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple, has full schema coverage, has output schema support, and has clear annotations. The description still covers ordering, filtering, preview-vs-full behavior, pagination, and the sibling alternative, so nothing important is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already documents tag, full, limit, cursor, and prefix in detail. The description reinforces the important filter and pagination semantics but largely restates what the schema provides, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Browse memories') plus the ordering ('id order') and filtering options ('id prefix or tag'), and explicitly contrasts this with `search` for relevance ranking. An agent can distinguish it from siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit routing: use this tool for browsing in id order, and use `search` when relevance ranking is needed. That is the key alternative among the listed sibling tools, and the condition for choosing it is stated directly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recallRecall matching memoriesARead-onlyIdempotent
Retrieve full memories ranked for a natural-language question. Use this for ordinary recall; use search when you need metadata filters, ranking controls or score details. Read-only; default builds rank lexically, and a configured model adds semantic ranking.
| Name | Required | Description | Default |
|---|---|---|---|
| k | No | Maximum number of ranked memories to return. | |
| tags | No | Labels; filters match memories carrying every listed tag. | |
| query | Yes | Natural-language question or phrase to match against stored memories; nonblank, at most 4,096 UTF-8 bytes. | |
| min_score | No | Drop hits below this relative rank score (0 to 1); this is not a probability. |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | Number of returned hits. |
| results | Yes | Ranked hits with full memory text. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only, idempotent, closed-world, non-destructive, so the safety bar is met elsewhere. The description still adds genuine value by disclosing that ranking is lexical by default and becomes semantic when a model is configured, which explains why identical queries may order results differently across deployments.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero waste, with the core purpose front-loaded before the sibling routing and the behavioral note. Nothing is repeated from the title or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Rich annotations, a full-coverage schema and an output schema mean the description need not explain return values. It covers purpose, routing and ranking behavior, but the mismatch between 'use `search` for metadata filters' and this tool's own `tags`/`min_score` params leaves a small ambiguity an agent must resolve.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so `k`, `tags`, `query` and `min_score` are already fully documented with bounds and defaults. The description adds no per-parameter meaning beyond a general mention of ranking, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('retrieve full memories') plus the ranking model ('ranked for a natural-language question'), which precisely distinguishes it from `search`. An agent can tell the two apart without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: 'use this for ordinary recall; use `search` when you need metadata filters, ranking controls or score details.' However, the boundary is muddied because this tool's own schema exposes `tags` (a metadata filter) and `min_score` (a ranking control), so the stated exclusion is not fully consistent with what it accepts.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rememberStore memoryADestructive
Store a durable fact, decision or preference; use recall to find one instead. A supplied id replaces that memory, while an omitted id creates a new one. Use if_version to reject concurrent changes or create_only to reject overwrites.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | Stable id; replaces an existing memory with this id. | |
| tags | No | Labels; filters match memories carrying every listed tag. | |
| text | Yes | UTF-8 memory content; must be nonblank unless `allow_empty` is true. The server also enforces a byte limit and a 4,096-word limit. | |
| title | No | Optional display title; does not change the id. | |
| metadata | No | Arbitrary JSON object; keys starting with "hms." are reserved. | |
| if_version | No | Version from `remember` or `get`; write only if the stored version still matches. Cannot combine with `create_only`. | |
| allow_empty | No | Permit blank text for file-like use; otherwise blank text is rejected. | |
| create_only | No | Reject the write if the chosen id already exists; cannot combine with `if_version`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Id of the stored memory. |
| bytes | Yes | Stored text size in UTF-8 bytes. |
| created | Yes | True when this call created an id. |
| version | Yes | New version for a later conditional update. |
| namespace | Yes | Store namespace used for the write. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations flag destructiveHint=true, and the description supplies the reason an agent needs: supplying an `id` overwrites that memory, omitting it creates a new one. It also discloses the two opt-in guards against clobbering. It stops short of saying what happens to fields not supplied on replace, but that is the only meaningful gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, both load-bearing: the first establishes scope and the sibling routing, the second covers id semantics and the two guard parameters. The most decision-critical fact (replace vs create) is front-loaded after the routing hint.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and the schema documents all eight parameters including tags, title, metadata and limits. The description fills the remaining conceptual gap – overwrite behavior and concurrency guards – so an agent has everything needed to call this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description earns above baseline by explaining the interaction between `if_version` and `create_only` as a pair of mutually exclusive conflict strategies, rather than just restating the per-field definitions. It adds intent, not syntax, which the schema already handles.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (store) plus the resources it stores (durable fact, decision or preference), and explicitly routes the agent away from the sibling `recall`. The create-vs-replace distinction makes the operation unambiguous even before opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternative tool explicitly ('use `recall` to find one instead') and gives the conditions that select the two conflict-control modes: `if_version` for concurrent-change rejection, `create_only` for overwrite rejection. Nothing about when-to-use 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.
searchSearch memory with filtersARead-onlyIdempotent
Find memories with metadata filters or ranking controls; use recall for simpler full-text answers. Read-only. Results contain text previews and relative ranking scores, not probabilities; use get to read a result in full. Semantic scores require a configured model.
| Name | Required | Description | Default |
|---|---|---|---|
| k | No | Maximum number of ranked previews to return. | |
| tags | No | Labels; filters match memories carrying every listed tag. | |
| query | Yes | Natural-language phrase to match against stored memories; nonblank, at most 4,096 UTF-8 bytes. | |
| filter | No | Exact-match filter on top-level metadata keys. | |
| fusion | No | How to combine lexical and semantic ranks: weighted score blend or reciprocal rank fusion. | |
| min_score | No | Drop hits below this relative rank score (0 to 1); this is not a probability. | |
| lexical_weight | No | Weight for lexical BM25 ranking before fusion, from 0 to 100. | |
| semantic_weight | No | Weight for model-based semantic ranking before fusion, from 0 to 100; requires a configured model. |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | Number of returned hits. |
| results | Yes | Ranked hits with text previews and score details. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive, so the bare 'Read-only' sentence earns no credit. The description does add real behavioral context beyond the annotations: results are previews with relative ranking scores that are not probabilities, and semantic scoring requires a configured model. These are useful operational caveats, though the preview/pagination details are partly redundant with the output schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three compact sentences with zero filler, front-loading the core purpose before alternatives and output caveats. Every clause carries a distinct instruction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 8 parameters, rich annotations, and an output schema, an agent has everything needed: purpose, sibling routing, and the key caveat that scores are relative not probabilistic and that semantic ranking requires a model. Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so every parameter including `k`, `tags`, `fusion`, `min_score`, and the weights is already documented. The description adds only the semantic-weight model requirement, which the schema also states; otherwise it does not add syntax or fusion guidance beyond structured fields, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource ('Find memories with metadata filters or ranking controls') and explicitly contrasts itself with `recall` for simpler full-text answers and `get` for full reads. An agent can distinguish this tool from its siblings without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It names both alternatives and the conditions that select them: `recall` when you want simple full-text answers, `get` when you need a result in full. This is explicit routing, not implied usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
statsStore statisticsARead-onlyIdempotent
Inspect store counts, recall mode, encryption status, storage size and active limits without reading memory contents. Read-only; use list to inspect stored memories.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| limits | Yes | Active text, metadata, tag, result and export caps. |
| recall | Yes | Active lexical or hybrid recall mode. |
| memories | Yes | Number of stored memories. |
| namespace | Yes | Store namespace being inspected. |
| dimensions | Yes | Configured vector dimensions. |
| store_path | Yes | Local filesystem path of the store. |
| storage_bytes | Yes | Approximate local store size in bytes. |
| server_version | Yes | Version of the core server binary. |
| embedded_memories | Yes | Memories with a semantic embedding. |
| encryption_at_rest | Yes | Whether the store is encrypted at rest. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and closed-world, so the safety profile is covered. The description adds genuinely new context — that no memory contents are exposed — which matters for a stats endpoint. It stops short of noting cost or latency, but nothing essential is missing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, both earning their place: the first defines scope, the second defines the read-only nature and the alternative. Front-loaded with the resource list before the routing hint.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values needn't be spelled out; combined with annotations covering safety and a zero-parameter schema, the description supplies everything an agent needs to select and invoke it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline 4 applies. The listed fields describe the output rather than inputs, which is harmless.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Inspect') plus an enumerated set of resources (counts, recall mode, encryption status, storage size, active limits). It also distinguishes itself from the sibling `list` by clarifying it does not read memory contents.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: use `list` to inspect stored memories, implying this tool is for store-level metadata only. The when-not-to-use condition ('without reading memory contents') is stated up front.
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.
8 tool updates
v0.1.2- First observed
export - First observed
forget - First observed
get - First observed
list - First observed
recall - First observed
remember - First observed
search - First observed
stats
TDQS
Scored across 8 tools
Each tool targets a distinct operation, but `recall` and `search` both perform retrieval and rely on the descriptions to separate natural-language lookup from filtered/ranked lookup; `list` also overlaps with `search` for browsing. The descriptions do a good job of redirecting between them, so an agent is unlikely to misselect in practice.
All names are short, single-word imperative/lowercase tokens (remember, recall, search, get, forget, list, export) with `stats` as the only noun form. The convention is consistent and readable, just not a strict verb_noun pattern.
Eight tools is a well-scoped surface for a memory store: write, several read modes, delete, and two introspection utilities. No tool looks redundant enough to remove, and nothing feels missing at the count level.
Covers the full lifecycle: create/overwrite (remember with id/if_version/create_only), read (get, recall, search, list), delete (forget), diagnostics (stats) and snapshot export. The notable gap is an `import`/restore counterpart to `export`, plus explicit tag/metadata-only mutation, which agents must work around by rewriting content.
Maintenance
Related MCP Connectors
Persistent memory for AI agents. Search, store, and recall across sessions.
Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.
Persistent memory for AI agents. Semantic search, memory graph, W3C DID identity.
Memory system for AI agents with semantic search. Store and recall memories with ease.
Related MCP Servers
- AlicenseAqualityAmaintenanceEnables Claude AI to seamlessly interact with Scrivener projects for document management, AI-powered content analysis, and advanced writing assistance.57120 npm69AGPL 3.0
- AlicenseAqualityCmaintenancePortable cognitive memory for AI agents with semantic search, decay, and deduplication.89 npm2MIT
- AlicenseAqualityCmaintenancePersistent memory MCP server for AI agents, using SQLite with hybrid keyword and semantic search for long-term memory storage.5Do What The F*ck You Want To Public
- AlicenseNot gradedqualityBmaintenanceLocal-first, multi-user shared memory for AI agents with semantic search, offline support, and team synchronization.MIT