Memnest
Open-source memory engine for AI agents

Memory that knows what changed.

A vector store hands your agent everything that looks like the question, the stale and the current mixed together. Memnest pulls out atomic facts, works out whether each new one replaces, enriches or repeats what it already knows, keeps the history, and serves only what's true now.

claude mcp add memnest -- npx -y @memnest/cli mcp --container user:me
MIT licensed SQLite or Postgres + pgvector Ollama or any OpenAI-compatible model MCP for Claude, Cursor, VS Code
container customer:acmekept, not overwritten
Acme runs its payments service on Postgres. superseded mem_0003 · from the onboarding call
Acme runs its payments service on MySQL, migrated from Postgres in June 2026. updates mem_0003 · from ticket #4411
Acme's payments MySQL cluster runs in eu-west-1. extends the MySQL fact · both stay current
search("what database does Acme use?") Agent gets MySQL, in eu-west-1. The Postgres fact is in the trace as not-latest, never in the prompt.
Live preview

See it work. This is the real engine.

Below, @memnest/core runs in your browser with an in-memory store. Ingest a customer's conversations, watch each fact get screened and resolved, then ask questions and read the trace. The only stand-in is the language model, whose answers are scripted; every rule applied to them is Memnest's own.

container customer:acme · Ledgerly support copilot 3 Mar 2026
LoadingStarting the engine…
1 · Conversationsingest in order
2 · Memory
3 · Recallsearch(query, scope, budget)
Running createMemnest with the in-memory store, hashEmbedder for vectors and a scripted model. Nothing leaves your browser. Same code as npm install @memnest/core
How it works

Two paths: write facts in the background, read what's true now.

add() returns as soon as the text is indexed. Extraction runs as a job. search() fuses keyword and vector retrieval, drops what's no longer true, and packs the rest into your token budget.

Write path · add()
  1. add()chat turns, markdown or text, with a session id
  2. redact + chunkcredentials stripped before anything is stored
  3. extractone model call per quiet session, not per message
  4. screenno pronouns, secrets, filler or stale expiries
  5. resolvecompare with up to 10 similar memories
  6. writeone transaction, with provenance to the source
Read path · search()
  1. query+ container scope + token budget
  2. lexical ∥ vectorfull text and embeddings, in parallel
  3. RRF fusereciprocal rank fusion, k = 60
  4. filterlatest, not forgotten, not expired
  5. rerankoptional LLM pass
  6. packmemories first, then source chunks
new

Nothing similar is stored. Written as is, without a model call.

"Priya wants incident updates by email."
duplicate

Already known. The memory is reinforced and gains a source.

"The team is still six engineers."
updates

Replaces a fact. The new version is served; the old one is kept as history.

Postgres → MySQL
extends

Adds detail. Linked with an edge; both stay current.

"…runs in eu-west-1."
Why not a vector store?

Similar isn't the same as true.

When a fact changes, the old and new versions look alike, so similarity search returns both and the agent picks one. Often the wrong one.

A plain vector store

top-k for "what database does Acme use?"
  • "…our payments service runs on Postgres. The platform team is…"0.91
  • "…finished moving payments from Postgres to MySQL…"0.89
  • "…here's our staging key: sk-demo-0000…"0.62
"Acme is on Postgres." Wrong, with a live credential in the prompt.

Memnest

search("what database does Acme use?", scopeOf('customer:acme'))
  • Acme runs its payments service on MySQL, migrated from Postgres in June 2026.fact
  • Acme's payments MySQL cluster runs in eu-west-1.fact
"Acme is on MySQL, in eu-west-1." The key was redacted before it was ever stored.
Stale facts keep coming backEvery new fact is resolved against similar ones. Search serves only the latest version.
Overwriting loses accountabilityNothing is thrown out. Superseded facts stay, linked to what replaced them and where they came from.
Nobody can see why the agent said itEach search returns a trace. The dashboard shows the graph, lineage and timeline, and a wrong memory can be forgotten.
Secrets and small talk end up in memoryRedaction runs before storage. Pronoun-laden, secret or low-confidence candidates are rejected, with the reason logged.
One customer leaks into anotherA container is a hard boundary, down to scoped API keys. Every route is tested for leaks.
Transcripts blow the context windowMemories are packed into a token budget, and a profile gives a prompt-ready summary.
Integrations

One line for an MCP client. One import for your own agent.

  • MCP serverMemory tools for Claude Code, Claude Desktop, Cursor, VS Code and any MCP agent: recall, remember, ingest, forget, history, profile.
  • Embedded TypeScriptThe engine in your own process, on SQLite or Postgres + pgvector. Core has no runtime dependencies.
  • Server + clientREST and SSE with container-scoped API keys, a same-API TypeScript client, and a dashboard for people to review what agents believe.
  • Any modelOllama, OpenAI, or any OpenAI-compatible endpoint (LM Studio, vLLM, Groq, OpenRouter). Completions and embeddings can come from different providers.
claude mcp add memnest -- npx -y @memnest/cli mcp --container user:me

Memories live in ~/.memnest/memnest.db, created on first use. No database or model needed to start: remember and recall work out of the box, and extraction switches on once a model is configured.

Built to be trusted

Guarantees, each with a test that fails if it breaks.

A container is a hard boundary

Every store method and every HTTP route is probed from another container, with a key and with a session. A new route without a probe fails typecheck.

store-contract + server leakage suites

Secrets never reach storage

Credentials are redacted before hashing, chunking or saving. The test greps the raw SQLite file, WAL and SHM for them.

secrets.test.ts

Every memory has provenance

Stores refuse a memory without a source document and extraction run. Even direct writes get one.

walks every memory back to its source

Degradation is never silent

When vector search can't run, the trace says why: no embedder, no embeddings yet, or a failed query embedding.

trace.degraded

Give your agent a memory it can explain.

Self-hosted, MIT licensed, and small enough to read. Start with one command; move to Postgres and the dashboard when you need them.