Skip to main content

mnimi-mcp

A Model Context Protocol server over one mnimi store: five tools and one resource, stdio transport, one SQLite file. Give Claude Code (or any MCP host) a long-term memory that lives on your disk, writes facts with a small local model, never calls a model when it reads, and can be read whole as Markdown.

The server is its own package so the library keeps its two dependencies (sqlite-vec, numpy). It depends on mnimi-memory[embed,extract]>=3.1.1,<4 (the PyPI name of import mnimi) (the BGE embedder and the Qwen3-1.7B extractor; 3.1.1 is the floor because the server uses mnimi.extract.wrappers — the sub-round split, the backslash pre-escape and client facts, new in 3.1.0 — and 3.1.1 is where a fact that mentions a \ path comes back spelled the turn's way) and on the mcp SDK (written and tested against mcp 2.3.0, 2026-10-02).

Install

mnimi-memory[extract] pins llama-cpp-python==0.3.35, which PyPI ships only as a source distribution (it needs a C++ compiler). The project's own index carries a prebuilt CPU wheel, so pass it on the install line and nothing is compiled:

pip install mnimi-mcp --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu

Before mnimi-memory 3.1.1 is on PyPI, install both from a checkout of the repository (the release/v3 branch), into a venv of its own:

pip install -e . -e integrations/mcp --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu

Either line puts a mnimi-mcp console script on the venv's Scripts/bin path. The GPU profile (MNIMI_EXTRACTOR=gpu) needs a CUDA build of the same version instead (docs/DECISIONS.md "Extractor runtime"); the CPU profile is the default and measured at 21.0 s per round on an 8-core laptop CPU (examples/README.md).

Register it with Claude Code

claude mcp add --scope user mnimi -e MNIMI_DB=/absolute/path/to/mnimi/agent.db -- mnimi-mcp

Give MNIMI_DB an absolute path: outside bash a ~ reaches the server literally (the server does expand ~ and $VAR itself, and makes a relative path absolute against its working directory, which under Claude Code is the project folder, not your home). --scope user makes the memory follow you across projects; the default scope is one project. mnimi-mcp must resolve on the PATH Claude Code starts the server with; give the venv's full path to the script otherwise. Then claude mcp list shows the server connected and /mcp inside a session lists the five tools. On the first tool call the server opens the DB, loads the embedder (one 130 MB download, pinned by revision) and, under MNIMI_EXTRACTOR=cpu or gpu, the extractor (one 1.8 GB download, pinned by revision). Listing the tools loads nothing.

Tell the model to use it

The server sends an instructions string on connect, and hosts differ in how much weight they give it. What makes the memory fire reliably is a rule in the file the host reads on every turn (CLAUDE.md, AGENTS.md, GEMINI.md, .cursorrules, a system prompt). Paste this and adjust the names:

## Memory (mnimi)

- Before answering anything that may depend on an earlier session, call mnimi `context` with the
  question.
- When I state a durable fact, preference or decision, call mnimi `remember` with that exchange
  (both turns).
- For a dense turn (results, decisions, many numbers), pass the facts yourself:
  `remember(messages, facts=[...])`, one sentence each, starting with "The user" or "The assistant".
- Do not store secrets, keys or passwords in memory.

The first two lines are the whole protocol: read before you answer, write when something durable was said. Without them a model calls the tools only when asked to, and the memory stays empty.

One memory or several

One server process is one Memory over one DB and one user_id. Two hosts (two Claude Code projects, Claude Desktop beside a CLI agent) are two processes, and three layouts are possible with nothing but the environment:

layout settings what you get
one DB per agent MNIMI_DB=agent1.db for one, agent2.db for the other independent memories, independent files; each process loads its own models (≈ 2 GB of RAM each while both run) and keeps its own extraction cache beside its DB
one DB, one user_id per agent the same MNIMI_DB; MNIMI_USER_ID=claude for one, codex for the other one file, still independent: recall, context, export, the logical clock and the decay pass are all per user_id; one shared extraction cache
one DB, one user_id the same MNIMI_DB and the same MNIMI_USER_ID (or the default me) shared memory: what one host remembers the other recalls, and the session day either sees is the same day

How the shared layout holds up. Each process opens its own connection in WAL mode with a 30 s busy timeout, so a write from one host waits for the other's instead of failing; a second host under other pins (another embedder, gpu on a cpu store) is refused with MemoryMetaError; nothing about the store is cached in process memory, every call reads the file; the extraction cache is a deterministic insert-or-replace, so two writers cannot disagree. A test starts two servers over stdio against one DB and reads from the second what the first stored.

Two limits, stated plainly. The in-process lock does not span processes, and the dedup and conflict screens are read-then-write: two remember calls storing the same round at the same instant can both store it (a duplicate, never lost data; rare for one person talking to one assistant at a time). And consolidate from either host decays the other's records too, which is what shared means. The server is stdio-only: a host that wants a remote (HTTP) MCP server cannot reach it today.

Configuration (environment)

variable values default what it is
MNIMI_DB a path required the SQLite store; created with its memory_meta on first use
MNIMI_USER_ID a string me the user_id every record is stored and recalled under
MNIMI_EXTRACTOR cpu · gpu · none cpu cpu = the measured recipe ({**DECODE, "n_gpu_layers": 0, "max_tokens": 2048}; the output budget doubled 2026-10-14 for dense turns); gpu = the pinned decode (CUDA build); none = no model: rounds only, plus the facts you pass to remember
MNIMI_EMBEDDER bge · hashing bge bge = BAAI/bge-small-en-v1.5 (the real one); hashing = numpy only, for tests
MNIMI_SPLIT_CHARS an integer 600 the sub-round split: each round is extracted whole first, and only when that output is truncated, at the fact cap or empty are turns longer than this cut at sentence boundaries into sub-rounds and re-extracted (the reading with more facts is kept), so a long answer is not lost to the model's output budget; 0 = off

A store remembers these pins in memory_meta. Opening it later under other pins (another embedder, the GPU profile on a CPU store) is refused with MemoryMetaError, and the tool call returns that message as its error text (so the model, and you, read it): re-point MNIMI_DB at a fresh file or use the pins the store was built with. A model that cannot load (MNIMI_EXTRACTOR=gpu on a CPU build) and a bad setting come back the same way. A store written before mnimi 3.1.0's resolver_version = "v3" (the 2026-10-14 test-week fixes), or a MNIMI_EXTRACTOR=none store from before client facts, is refused the same way: point MNIMI_DB at a fresh file and re-remember the rounds (the extraction cache replays what it has). A none store of this server carries the client-facts pins; examples/chat.py --extractor none builds a store with no extractor at all, so the two are not interchangeable.

Tools

tool arguments returns
remember messages: [{role: user|assistant, content}] (at least one); facts: [{content, raw?, when?, subject?, predicate?, object?, salience?}] (optional) {round, fact, merged, superseded, ts, facts_supplied, facts_unused?}: the records this call added by kind (a duplicate round adds 0), the facts the dedup merged into a near-identical stored fact instead of adding (two facts quoting the same long raw excerpt collide this way — give each its own clause), the earlier facts it superseded, the session timestamp they were stamped with, how many facts you supplied, and — only when the pre-filter skipped the round — how many of them went unused
recall query, k (default 10) [{score, salience, kind, text, session_date}], best first; the returned memories count as accessed today, which protects them from the next decay pass
context query the context block the library hands a reader: session-date headers, user:/assistant: lines, the facts
export — the whole memory as Markdown (the same text mnimi export --format md writes)
consolidate — {conflict, decay, now_logical}: what this call changed (the conflict pass's counters; the decay pass's passes, decayed, at_floor) and the logical now; a second call changes nothing

Resource: memory://export (text/markdown), the same Markdown as the export tool.

Supplying facts yourself. The local 1.7B extractor skips dense assistant turns (results, decisions, commit ids) and extracts at most eight facts per sub-round. When you know the facts, pass them: remember(messages, facts=[...]) stores your facts for that round and does not run the model. They go through the same date resolution, the same dedup screens and the same conflict pass as the model's facts; raw must be verbatim text of the turn (omit it and the server picks the closest sentence), when must be the turn's own words (an expression the turn does not contain is dropped and the fact is stored undated). Needs exactly one round per call.

The clock lives here, not in the library. Every message remember stores is stamped ts = the server's calendar day at the call (YYYY-MM-DDT00:00:00): a day is a session, the shape the benchmark has. A ts the model puts in a message is ignored. The same day reaches recall and context as the library's documented query prefix ([Current date: <ts>], parsed for the question's own time window and stripped before embedding), so the shipped configuration's time-aware term fires on "last week" or "three days ago". The library reads only what it is handed and never a clock.

stdout belongs to the transport. Everything the server and the library log goes to stderr, including mnimi.memory's INFO lines (superseded …, routed to conflict …, decayed …); Claude Code shows them in its MCP log.

Try it

  • Tell it three things about yourself over a conversation, then ask about one of them indirectly in a new session: context should carry the right round.
  • Contradict one of them another day: the superseded … line, and export shows the old fact struck through.
  • Give it a relative date ("dentist next Thursday") and later ask when: the fact's valid_time is resolved from the session date.
  • consolidate twice: the second pass changes nothing.
  • Read memory://export as a resource.

Development

pip install -e integrations/mcp --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
pytest integrations/mcp/tests            # also run by the root pytest; skipped when mcp is absent

(The mcp package is not in the root [dev] extra; the index is the CPU wheel's, as above.)

The tests drive the server through the SDK's in-memory client (Client(server.mcp)) under MNIMI_EMBEDDER=hashing MNIMI_EXTRACTOR=none over a temporary DB. The server imports mnimi.extract.llama for the decode dict, mnimi.extract.wrappers for the split, the pre-escape and client facts, and mnimi_cli.main for the Markdown export; it changes nothing under src/mnimi/. The published numbers were measured on tag v2.14.0; 3.1.x departs from it in extraction only.

Apache-2.0, like the library.

Metadata

Release files for mnimi-mcp 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mnimi-mcp 0.2.0
File Size Uploaded
mnimi_mcp-0.2.0.tar.gz 21.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mnimi-mcp 0.2.0
File Interpreter ABI Platform
mnimi_mcp-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 39.7 kB

Release files / mnimi_mcp-0.2.0.tar.gz

Download URL mnimi_mcp-0.2.0.tar.gz
Size 21.9 kB
Tags Source
SHA-256 checksum
How to use checksums
abad25b63464805998632743bdd39963c4a302d088d4031895b1112c8d019e93
BLAKE2b-256 checksum
How to use checksums
d7cd2e783936cefb3232d53e844af09d92f5ef65052a42fa382c220837f732f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / mnimi_mcp-0.2.0-py3-none-any.whl

Download URL mnimi_mcp-0.2.0-py3-none-any.whl
Size 17.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6f2e3099d02de82be87f1effa3186cd4bf73dd0fa0611dfd074da9cf206689b6
BLAKE2b-256 checksum
How to use checksums
c5f5a30d5f894bf905a0313ad713d55cec3e88ac9f329156c4785f5ec8687d02
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page