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
npm install @memnest/core @memnest/store-sqlite
git clone https://github.com/anidoesdev/MemNest.git && cd MemNest
docker compose up -d # Postgres + pgvector, API and dashboard on :8787not-latest, never in the prompt.
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.
createMemnest with the in-memory store, hashEmbedder for vectors and a scripted model. Nothing leaves your browser.
Same code as npm install @memnest/core
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.
- add()chat turns, markdown or text, with a session id
- redact + chunkcredentials stripped before anything is stored
- extractone model call per quiet session, not per message
- screenno pronouns, secrets, filler or stale expiries
- resolvecompare with up to 10 similar memories
- writeone transaction, with provenance to the source
- query+ container scope + token budget
- lexical ∥ vectorfull text and embeddings, in parallel
- RRF fusereciprocal rank fusion, k = 60
- filterlatest, not forgotten, not expired
- rerankoptional LLM pass
- packmemories first, then source chunks
Nothing similar is stored. Written as is, without a model call.
"Priya wants incident updates by email."Already known. The memory is reinforced and gains a source.
"The team is still six engineers."Replaces a fact. The new version is served; the old one is kept as history.
Postgres → MySQLAdds detail. Linked with an edge; both stay current.
"…runs in eu-west-1."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
- "…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
Memnest
- 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
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.
{
"mcpServers": {
"memnest": {
"command": "npx",
"args": ["-y", "@memnest/cli", "mcp", "--container", "user:me"]
}
}
}In claude_desktop_config.json or .cursor/mcp.json. Add "--url" and a MEMNEST_KEY to share one memory across clients through a Memnest server.
{
"servers": {
"memnest": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@memnest/cli", "mcp", "--container", "user:me"]
}
}
}In .vscode/mcp.json. The container is configuration: the model can never point the tools at someone else's memory.
import { createMemnest, scopeOf } from '@memnest/core'; import { createSqliteStore } from '@memnest/store-sqlite'; const store = createSqliteStore({ filename: 'memnest.db', autoMigrate: true }); const memnest = createMemnest({ store, queue: store.jobQueue() }); await memnest.add({ containerTag: 'user:123', content: turns }); const { memories, trace } = await memnest.search( 'what database does this user use?', scopeOf('user:123'), { tokenBudget: 400 }, );
Swap in createPostgresStore for hybrid recall with pgvector, or createMemnestClient to talk to a server. Same API.
curl -X POST localhost:8787/v1/search \ -H "Authorization: Bearer $MEMNEST_KEY" -H 'content-type: application/json' \ -d '{"containerTag":"user:123","query":"what database?","options":{"tokenBudget":200}}'
Keys are stored as argon2id hashes and can be scoped to one container. Documents, search with trace, profiles, lineage, forget, graph and SSE job events.
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 suitesSecrets 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.tsEvery 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 sourceDegradation is never silent
When vector search can't run, the trace says why: no embedder, no embeddings yet, or a failed query embedding.
trace.degradedGive 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.