Typed, graph-linked, self-consolidating memory for LLM agents — in a single SQLite file.
MintMory gives an agent a long-term memory that is typed (every memory has a
category), linked (memories connect through a concept graph), searchable
(hybrid keyword + substring + vector), and self-improving (a background
"dreaming" pass consolidates, links, summarises, and resolves contradictions).
It is local-first and dependency-light: no external vector database, no required
model downloads, no cloud. One .db file holds everything.
The data model is inspired by the Palantir Foundry Ontology: knowledge is most useful when it is typed, linked, and queryable — not stored as opaque blobs.
- Benchmarked head-to-head. MintMory's generic hybrid search matches or beats the purpose-built mempalace across all four of its agent-memory benchmarks (LongMemEval, LoCoMo, ConvoMem, MemBench) — same embedder, zero benchmark-specific tuning, one SQLite file. Numbers + one-command reproduction: BENCHMARKS.md.
- One core, three transports. The same domain logic is exposed as a CLI (Typer), an MCP server (for Claude Code, Cursor, Open WebUI, …), and an HTTP API (FastAPI).
- Hybrid search. FTS5 keyword (BM25) + FTS5 trigram substring + optional vector similarity, fused with Reciprocal Rank Fusion and relevance-normalised.
- Pure-Python by default. The default embedder is an in-process feature-hashing
embedder — no PyTorch, no network. Swap in
sentence-transformers, Ollama, or OpenAI embeddings via an extra when you want them. - Typed ontology. 8 memory categories and 12 typed concept-link relationships.
- Dreaming. Idempotent background consolidation: anomaly detection, concept linking (hub-aware pruning), LLM summaries, contradiction resolution, archival, rehabilitation. The LLM tier is optional and off by default.
- Agent-supplied summaries. An alternative to the LLM-backed dreaming path:
call
summary_jobs(MCP / CLI / HTTP) to get the concepts that need summarising, write each summary yourself (you are the LLM), and send it back withsummary_put. NoMINTMORY_LLM_*backend required — works withMINTMORY_LLM_PROVIDER=none. - Agent-supplied image understanding (G5). Close the image-description gap
without a vision backend: call
image_jobsto get the indexed raster images that still need a description, look at each (via itspathor inline base64 for online-only files), write one combined description blob, and store it withimage_caption_put. SVG text is extracted automatically (pure-Python, no model). Proprietary formats (.psd,.xd, …) remain metadata-only. Optional[image]/[ocr]extras;ocris a future stub. - Automated LLM vision captioning. Set
MINTMORY_VISION_PROVIDER=llmto caption raster images server-side via an OpenAI-compatible vision model (Ollamallava, LM Studio, hosted endpoint).index-tree --visionthen captions inline during the walk;mintmory vision-run/vision_run(MCP) /POST /images/caption-run(HTTP) caption already-indexed pending images without re-walking. No new required dependency — reuses stdliburllibfromcore/llm.py.agentstays the default (zero behaviour change).ocrstays a stub. - Personal notes. First-class user-authored "remember this" notes that are authoritative, anchorable to other memories, time-aware, and protected from auto-archival.
- Document & folder ingestion. Bulk-ingest files (PDF/DOCX/XLSX/PPTX via the
optional
docsextra) and recurrently index a directory tree — including cloud/online-only folders, indexed by metadata without downloading. - Agent history index. Index your Claude Code / Codex / Kiro chat history
into a separate, secret-redacted changelog and ask "what did I fix/add ~2 months
ago?" via the
mintmory historyCLI or the read-onlymintmory-history-mcpserver — fully isolated from your working store. Guide: agent-history.md. - Single file. SQLite +
sqlite-vec+ FTS5. Back it up by copying one file.
MintMory uses uv. Clone and sync:
git clone https://github.com/WeLikeCode/mint-mory.git
cd mint-mory
uv sync # core + CLI + MCP + API
uv sync --extra docs # optional: PDF/DOCX/XLSX/… ingestion (markitdown)
uv sync --extra local # optional: local sentence-transformers embeddingsRequires Python 3.12+.
export MINTMORY_DB=~/.mintmory/memories.db
mintmory add "The parking integration uses OAuth PKCE" --category fact
mintmory note "Remember the prod gateway rotates certs monthly" --about "gateway"
mintmory search "oauth gateway"
mintmory ingest ./docs --glob "*.md,*.pdf,*.docx" # bulk ingest (needs the docs extra for binaries)
mintmory dream --full # consolidate
mintmory doctor # one-shot health checkTools exposed: memory_add, memory_search, memory_get, memory_archive,
memory_stats, memory_dream, session_feedback, summary_list,
summary_get, summary_jobs, summary_put, memory_note, notes_list,
image_jobs, image_caption_put, vision_run.
mintmory serve --port 8080 # FastAPI + OpenAPI docs at /docsSeveral agents can share one MintMory instance — either as one MCP process per
agent against a shared .db (same machine), or as a single shared server
(HTTP REST or MCP-over-SSE) for networked / many-agent setups. Concurrent access
via MCP is supported in both forms. See docs/multi-agent.md
for the topologies, discovery, and concurrency tuning.
CLI (Typer) MCP (FastMCP) HTTP (FastAPI)
\ | /
\ | /
========= mintmory.core =========
types · storage · search · dreaming
embedder · scoring · notes · config
|
one SQLite .db file
(sqlite-vec + FTS5 porter + FTS5 trigram)
- Storage: a single SQLite database. Vectors via
sqlite-vec; keyword search via FTS5 (porter); substring search via a second FTS5 (trigram) index. - Search: keyword (BM25) + trigram substring + optional vector, fused with RRF, relevance min-max normalised, then nudged by usefulness/recency.
- Dreaming: six idempotent steps run on demand (
mintmory dream) — safe to run repeatedly; the LLM-backed steps are skipped when no LLM tier is configured.
See AGENTS.md for the deep design contract and
docs/ for ADRs, the OpenAPI spec, and experiment records.
Everything is environment-driven (pydantic-settings); every default reproduces sensible local-first behaviour. Common knobs:
| Variable | Default | Meaning |
|---|---|---|
MINTMORY_DB |
~/.mintmory/memories.db |
database path |
MINTMORY_EMBED_PROVIDER |
hashing |
hashing (pure-Python) / local / ollama / openai |
MINTMORY_LLM_PROVIDER |
none |
enable summaries + contradiction resolution (ollama/openai) |
MINTMORY_LLM_BASE_URL / _MODEL / _API_KEY |
— | OpenAI-compatible LLM tier |
MINTMORY_NOTE_BONUS |
0.05 |
ranking boost for user notes |
MINTMORY_SQLITE_BUSY_TIMEOUT_MS |
5000 |
how long a writer waits for a lock (multi-agent); 0 = fail fast |
The LLM tier is OpenAI-compatible, so it works with Ollama, LM Studio, vLLM, or a hosted endpoint. It is off by default (fully offline).
Agent-supplied summaries do not require an LLM tier. Use
mintmory summary-jobs / summary_jobs (MCP) / GET /summaries/jobs to get
the pending L3 summary work-list, write the text, and return it with
mintmory summary-put / summary_put / PUT /summaries/{concept}. The
selection policy (MINTMORY_SUMMARY_* settings, stoplist) is respected
regardless of which path writes the summary.
MintMory supports two vision paths — agent-supplied (default) and automated
server-side (llm). Both use the same persistence layer (image_caption_put,
the image_jobs work-list, the no-drift guarantee) and the same [image] extra
for optional Pillow downscaling.
The agent that already holds the image in context is the vision capability. The loop works identically over all three transports:
-
Index the tree. Run
mintmory index-tree --vision /path/to/folder. SVG files have their embedded<text>/<title>/<desc>content extracted automatically (pure stdlib, no model). Raster images (.jpg,.png,.gif,.webp, …) are queued as agent jobs and recorded in the manifest withindex_mode=vision. Proprietary formats (.xd,.vsdx,.psd, …) are skipped and flagged (vision-skipped) — they remain metadata-only. -
Get the work-list. Call
image_jobs(MCP),mintmory image-jobs(CLI), orGET /images/jobs(HTTP). Only raster images that do NOT yet have an active description are returned by default (include_all=False). Each job carries thefile_id,path,rel,mime,size, andonline_onlyflag. For online-only (cloud-placeholder) images, or wheninclude_bytes=True, the response also includes an inline base64image_b64payload within the configured size cap (MINTMORY_VISION_MAX_IMAGE_MB, default 8 MB); oversized images setoversized=Trueand omit the payload so the agent can fall back topath. -
Describe each image. You (the calling agent) are the vision-capable model. Write one combined description blob: what the image depicts plus any legible text.
-
Store the description. Call
image_caption_put(MCP),mintmory image-caption-put(CLI), orPUT /images/{file_id}(HTTP). MintMory persists the description as a searchablecontextmemory ANNOTATES-linked to the image file-record and archives any prior description (no-drift: the image drops from the defaultimage_jobswork-list immediately).
No vision backend is required for the agent path — it is the default.
Set MINTMORY_VISION_PROVIDER=llm to caption raster images automatically via an
OpenAI-compatible vision model (e.g. Ollama llava, LM Studio, a hosted endpoint).
Inline during indexing — mintmory index-tree --vision /path/to/folder
captions each raster as it is encountered (instead of queuing an agent job).
Per-image failures (network/timeout/empty response) are logged and skipped; one
bad image never aborts the walk.
Caption already-indexed images (without re-walking the tree):
mintmory vision-run # CLI — describes pending images
mintmory vision-run --limit 20 # cap to 20 images
mintmory vision-run --budget 100 # 100 MB download budget for online-only
mintmory vision-run --all # re-caption ALL images (not just pending)Or via MCP: call vision_run (with optional limit, budget_mb, include_all).
Or via HTTP: POST /images/caption-run (body: {"limit":0,"budget_mb":0,"include_all":false}).
With provider=agent (the default), vision-run / vision_run /
POST /images/caption-run are no-ops (they return {"provider":"agent","described":0})
— zero behaviour change unless you opt in.
No new required dependency. The vision HTTP call reuses the stdlib urllib
machinery already in core/llm.py (OpenAI-compatible /chat/completions shape
with a multimodal image_url content part). No openai SDK.
Optional extras:
uv sync --extra image # Pillow: auto-downscale large embedded payloads (lazy import)
uv sync --extra ocr # pytesseract: reserved for the future 'ocr' vision providerRelevant env knobs (MINTMORY_VISION_*):
| Variable | Default | Meaning |
|---|---|---|
MINTMORY_VISION_PROVIDER |
agent |
agent (default) / llm (automated server-side) / ocr (stub, future) |
MINTMORY_VISION_BASE_URL |
http://localhost:11434/v1 |
Base URL for the vision model endpoint (llm path only) |
MINTMORY_VISION_MODEL |
llava |
Vision model name (llm path only) |
MINTMORY_VISION_API_KEY |
— | Bearer API key for the vision endpoint (omitted if not set) |
MINTMORY_VISION_VISION_MAX_TOKENS |
512 |
Max tokens in the caption response (llm path only) |
MINTMORY_VISION_VISION_TIMEOUT_S |
120.0 |
Per-image HTTP timeout in seconds (1–600; llm path only) |
MINTMORY_VISION_VISION_TEMPERATURE |
0.0 |
Sampling temperature for the vision model (0.0–2.0; llm path only) |
MINTMORY_VISION_VISION_PROMPT |
"" |
Override the default caption prompt (empty = use built-in default) |
MINTMORY_VISION_MAX_IMAGE_MB |
8.0 |
Max on-disk size to embed as base64 (0 = no cap) |
MINTMORY_VISION_DOWNSCALE_MAX_PX |
1568 |
Longest-edge downscale target for embedded payloads (needs [image]; 0 = off) |
MINTMORY_VISION_MAX_DOWNLOAD_MB |
200.0 |
Budget for downloading online-only image bytes (shared with --max-download-mb) |
Index your coding agents' chat history (Claude Code, Codex, Kiro) into a separate, secret-redacted, time-aware changelog so you can recall what was fixed or added, when — without polluting your working memory store.
uv sync --extra local # better recall (optional)
mintmory history backfill # index all sessions (idempotent)
mintmory history timeline --since 2m --repo X # "what changed ~2 months ago in X"
mintmory history search "kong jwt" # topic recall
mintmory history sync # keep current (incremental)
mintmory history scrub # audit for residual secretsFor agents, expose the read-only MCP server (tools: history_timeline,
history_search, history_stats) in every project:
claude mcp add agent-history --scope user \
-- uv run --project /ABSOLUTE/PATH/TO/mint-mory mintmory-history-mcpFull guide (humans + agents, how-to, debugging): docs/agent-history.md. MCP setup specifics: docs/agent-history-mcp.md.
uv run pytest # tests (coverage gate ≥ 80%)
uv run ruff check . # lint
uv run ruff format . # format
uv run mypy packages # strict type-checkChanges are designed spec-first under openspec/: each change has a
proposal.md, a frozen design.md, tasks.md, and capability spec deltas. See
CONTRIBUTING.md.
MIT.
{ "mcpServers": { "mintmory": { "command": "uv", "args": ["run", "--project", "/path/to/mint-mory", "mintmory-mcp"], "env": { "MINTMORY_DB": "/path/to/your/memories.db" } } } }