Persistent memory for AI coding tools — resumable session state, the decision context that never reaches git, and injection that is measured rather than assumed. Hybrid vector + FTS5 search over a single SQLite file, zero external services.
한국어 · Quick Start · MCP Setup · MCP Tools · Session & Pins · Architecture · Docker · Contributing
mem-mesh does not claim that searching your past sessions makes a model write better code — it treats that as a hypothesis it instruments and measures (Measured, not assumed). It earns its place on three things git, pull requests, and well-kept docs do not capture:
- Session-to-session work state —
pin_add/pin_completetrack the unit of work;session_resumerestores where the last session stopped. "Where was I" answered directly. - Knowledge that never reaches git — the why of a decision, the approach that failed, the constraint learned during an incident. First-class categories with typed relations, so a superseded decision links to the one that replaced it.
- Observability and retrospective —
weekly_review, the dashboard, and team relay show what your agents recorded, retrieved, and let go stale.
| Differentiator | What it means |
|---|---|
| Pin lifecycle | Lightweight kanban inside every session: pin_add → pin_complete → pin_promote. Restore in-progress work with session_resume. |
| Injection instrumentation | Every auto-surfaced memory is tracked in injected_memories; a deterministic Stop-time heuristic judges whether it was used; weekly_review reports the hit rate. Utility is measured, not assumed. |
| Git-anchored staleness | Code memories carry commit + file anchors; the client verifies freshness and reports it, so stale context is dropped from injection instead of misleading the agent. |
| Human-gated doc promotion | doc_proposal drafts a promotion to version-controlled docs; a person approves; the client applies it. Memory is the staging area, git is the durable layer. |
| Hybrid search | sqlite-vec vector embeddings + FTS5 full-text fused with Reciprocal Rank Fusion (RRF). Korean n-gram optimized out of the box. |
| NLI conflict detection | 2-stage pipeline: vector similarity pre-filter → mDeBERTa NLI model catches contradictory memories before they're stored. |
| 4-Tier Smart Expand | session_resume(expand="smart") uses an importance × status matrix to load only what matters — ~60% token savings. |
| Zero external services | Single SQLite file. pip install mem-mesh and you're running. No Postgres, no Redis, no cloud. |
| Dual MCP transport | stdio (Cursor, Claude Desktop, Kiro) + Streamable HTTP/SSE (MCP spec 2025-03-26). |
| 25+ client auto-detection | Identifies the calling IDE/AI platform from MCP handshake or User-Agent. |
| Batch operations | Pack multiple memory ops into one round-trip: 30–50% token savings. |
The "past-session search boosts coding" claim is instrumented rather than asserted. Every injected memory is recorded in injected_memories (one row per turn); a Stop-time heuristic with no LLM judges whether it was later referenced; weekly_review surfaces the injection hit rate. Offline, scripts/replay_injection_eval.py replays real captured prompts through the legacy and current injection formats and scores both with deterministic metrics plus an optional blind LLM judge. The premise is honest: if the current format shows no advantage, shrinking injection is a valid outcome. For pure code work in a repository whose commits, PRs, and docs are already well kept, the marginal value of retrieving past sessions is unproven — mem-mesh ships the tools to measure it, and the three capabilities above stand regardless of how that measurement resolves.
- Memory CRUD —
add,search,context,update,delete - Hybrid search — sentence-transformers vectors + FTS5 RRF fusion, Korean n-gram support
- Session & pins — short-lived work tracking with importance-based promotion to permanent memory
- Injection instrumentation —
injected_memoriestracking + Stop-time usage heuristic +weekly_reviewinjection stats; offline replay harness to validate injection value - Git-anchored lifespan — commit/file anchors with client-side staleness verification; stale memories excluded from injection
- Human-gated promotion —
doc_proposalpromotes memory toward version-controlled docs (LLM drafts, human approves, client applies) - Auto-redaction — deterministic secret/PII masking on auto-captured content before it reaches long-term memory
- Memory relations —
link,unlink,get_linksacross 7 relation types - Conflict detection — mDeBERTa NLI prevents storing contradictory facts
- Batch operations — 30–50% fewer tokens per multi-op workflow
- Web dashboard — FastAPI REST API + real-time UI at
localhost:8000
One tool to install — uv — and mem-mesh handles the rest. No virtualenv, no pyenv tweaks, no sqlite-vec compile errors. Your MCP client spawns a cached, isolated mem-mesh on-demand.
# 1. Install uv (one-time, ~15 seconds)
curl -LsSf https://astral.sh/uv/install.sh | sh
# 2. Run the onboarding wizard — writes MCP config for detected tools,
# offers to install hooks, warms the uv cache.
uvx mem-meshThat's it. Restart Cursor / Claude Desktop / Kiro and mem-mesh MCP tools are live.
uvx mem-mesh(bare) runs onboarding — no--from "mem-mesh[server]"needed, since the lightweight base package is all the wizard requires (it still writes config that runs the server via the[server]extra).uvx mem-mesh installis the explicit equivalent.
For agents / CI: uvx mem-mesh --json runs onboarding non-interactively and prints a single JSON result (per-step status + next_actions). Non-TTY invocations (pipes, agents) auto-run non-interactively even without --json. Onboarding writes the config single-source-of-truth — ~/.mem-mesh/api_url and ~/.mem-mesh/hook_token — which every tool's hooks read at runtime (reachable from GUI- and terminal-launched tools alike). The MEM_MESH_API_URL / MEM_MESH_HOOK_TOKEN env vars seed those files (and act as a per-session override; mem-mesh doctor flags any that shadow the files). The token must also stay exported for HTTP hooks / authenticated MCP, which read the shell env and have no file fallback. Example: MEM_MESH_API_URL=https://memory.example.com MEM_MESH_HOOK_TOKEN=… uvx mem-mesh --json.
Install or repair hooks directly through the same uvx entrypoint:
uvx mem-mesh hooks install --target codex
uvx mem-mesh hooks statusWant the web dashboard too? uvx --from "mem-mesh[server]" mem-mesh serve — open http://localhost:8000.
If you prefer managing Python environments yourself:
pip install "mem-mesh[server]"
mem-mesh # onboarding wizard (or: mem-mesh install)
mem-mesh serve # web server + SSE MCP at localhost:8000mem-mesh loads the sqlite-vec extension at runtime, so Python's sqlite3 module must support loadable extensions.
- uvx users — uv's managed Python builds already have extension loading enabled. Nothing to do.
- Linux —
pysqlite3-binarywheel installs automatically as a fallback. - macOS — system Python and Homebrew Python both work. Only pyenv's default build is broken.
- Windows — system Python works; install
pysqlite3-binarymanually if needed.
macOS + pyenv users who hit Migration failed: no such module: vec0:
# Option A: rebuild Python against Homebrew sqlite3
brew install sqlite3
SQLITE_PREFIX="$(brew --prefix sqlite3)"
PYTHON_CONFIGURE_OPTS="--enable-loadable-sqlite-extensions" \
LDFLAGS="-L${SQLITE_PREFIX}/lib" \
CPPFLAGS="-I${SQLITE_PREFIX}/include" \
CFLAGS="-I${SQLITE_PREFIX}/include" \
pyenv install 3.13 --force
pyenv rehash
# Option B (simplest): just use uvx — it bypasses system Python entirelyLinux distro Python, Docker images, and conda Python ship with extension loading enabled — no extra steps needed.
mem-mesh install writes these entries for you automatically. The snippets below are what gets written, for reference.
Zero Python-env management. The MCP client spawns a cached mem-mesh process per call; the first run downloads it, subsequent runs are instant.
{
"mcpServers": {
"mem-mesh": {
"command": "uvx",
"args": ["--from", "mem-mesh[server]", "mem-mesh-mcp-stdio"],
"env": { "MEM_MESH_CLIENT": "cursor" }
}
}
}Use your own Python install. Good if you need -e . dev installs.
{
"mcpServers": {
"mem-mesh": {
"command": "python",
"args": ["-m", "app.mcp_stdio"],
"cwd": "/absolute/path/to/mem-mesh",
"env": { "MCP_LOG_LEVEL": "INFO" }
}
}
}For web clients or when multiple tools share one process. Requires mem-mesh serve running. Use "type": "http" — type: "sse" is legacy and hangs after a server restart.
{
"mcpServers": {
"mem-mesh": {
"url": "http://localhost:8000/mcp/sse",
"type": "http"
}
}
}Config file locations by tool:
| Tool | Config file |
|---|---|
| Cursor | .cursor/mcp.json |
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Kiro | ~/.kiro/settings/mcp.json |
| uvx | Stdio | HTTP | |
|---|---|---|---|
| Prereq | uv only |
Python env with mem-mesh[server] |
Running mem-mesh serve |
| First call | ~15s (cache warm) | Instant | Instant |
| Server to manage | None | None | Yes |
| Dashboard | Optional (uvx … serve) |
Optional | Included |
| Hooks support | Requires separate server | Yes (local mode) | Yes (api mode) |
| Tool | Description | Key parameters |
|---|---|---|
add |
Store a memory | content, project_id, category, tags |
search |
Hybrid vector + FTS5 search | query, project_id, category, limit, recency_weight, response_format |
context |
Retrieve memories surrounding a given memory | memory_id, depth, project_id |
update |
Edit a memory | memory_id, content, category, tags |
delete |
Remove a memory | memory_id |
stats |
Usage statistics | project_id, start_date, end_date |
link |
Create a typed relation between memories | source_id, target_id, relation_type |
unlink |
Remove a relation | source_id, target_id |
get_links |
Query relations | memory_id, relation_type, direction |
pin_add |
Add a short-lived work-tracking pin | content, project_id, importance, tags |
pin_complete |
Mark a pin done; optionally promote to permanent memory | pin_id, promote, category |
pin_promote |
Promote an already-completed pin to permanent memory | pin_id, category |
session_resume |
Restore context from the previous session | project_id, expand, limit |
session_end |
Close a session with a summary | project_id, summary, auto_complete_pins |
batch_operations |
Execute multiple ops in one call | operations (array of add/search/pin_add/pin_complete) |
search response formats: minimal | compact | standard | full
mem-mesh runs two retrieval engines in parallel and merges results with Reciprocal Rank Fusion:
- Vector —
dragonkue/snowflake-arctic-embed-l-v2.0-ko(1024-dim, Korean retrieval SOTA, MTEB-ko #1) by default; KURE, E5 and MiniLM models supported - FTS5 — SQLite full-text search with n-gram tokenization for CJK languages
- RRF fusion — balances semantic similarity and keyword precision
- Quality filters — noise removal, intent analysis, vector pre-filter overfetch to improve recall
session_resume(project_id, expand="smart") → work → session_end(project_id, summary)
session_resumerestores incomplete pins and context from the previous session. Stale pins are auto-closed.expand="smart"applies an importance × status matrix that cuts token usage by ~60%.session_endrecords a summary and closes the session. If the session terminates abnormally, the nextsession_resumeautomatically recovers open pins.
Pins are the unit of work inside a session. Track code changes, implementations, and configuration work as pins — not in permanent memory.
pin_add(content, project_id) → do the work → pin_complete(pin_id, promote=True)
# promote=True completes and promotes in one call
Status flow: open (planned, not started) → in_progress (active; default on pin_add) → completed
Multi-step work can pre-register later steps as open pins, then activate them one at a time.
Auto-stale cleanup (triggered on session_resume):
in_progresspins older than 7 days → auto-completedopenpins older than 30 days → auto-completed
When to pin: only when files change. Questions, explanations, and read-only lookups do not need pins. Multi-step tasks get one pin per step.
Importance levels:
| Level | Use for |
|---|---|
5 |
Architecture decisions, core design changes |
3–4 |
Feature implementations, significant fixes |
1–2 |
Minor edits, typo fixes |
| omit | Auto-inferred from content |
Promote: pin_complete(pin_id, promote=True) completes and promotes to permanent memory in one call. To promote after the fact: pin_promote(pin_id).
Client detection: In HTTP mode, the calling client is identified from the MCP initialize handshake or User-Agent header (25+ IDE/AI platforms supported). In stdio mode, set MEM_MESH_CLIENT in the environment.
1. Session start → session_resume(project_id, expand="smart")
2. Past context → search() before coding if referencing previous decisions
3. Track work → pin_add → pin_complete (promote=True to merge into memory)
4. Permanent store → decision / bug / incident / idea / code_snippet only
5. Session end → session_end(project_id, summary, auto_complete_pins=True)
6. Never store → API keys / tokens / passwords / PII
Principle: Hooks are read-only signals. All pin creation, completion, and promotion decisions are made by the LLM with full context.
Seven relation types: related | parent | child | supersedes | references | depends_on | similar
get_links direction: outgoing | incoming | both
| Variable | Description | Default |
|---|---|---|
MEM_MESH_DATABASE_PATH |
SQLite database path | XDG per-user path (see app/core/config.py _default_db_path) |
MEM_MESH_EMBEDDING_MODEL |
Embedding model name | dragonkue/snowflake-arctic-embed-l-v2.0-ko |
MEM_MESH_EMBEDDING_DIM |
Vector dimensions | 1024 |
MEM_MESH_SERVER_PORT |
Web server port | 8000 |
MEM_MESH_SEARCH_THRESHOLD |
Minimum similarity score | 0.5 |
MEM_MESH_USE_UNIFIED_SEARCH |
Enable hybrid search | true |
MEM_MESH_ENABLE_KOREAN_OPTIMIZATION |
Korean n-gram FTS | true |
MEM_MESH_LOG_LEVEL |
Server log level | INFO |
MEM_MESH_LOG_FILE |
Log output file | (none) |
See .env.example for the full list.
Bash hooks (Stop, SessionStart, SubagentStop, …) resolve the API URL in this order:
MEM_MESH_API_URLenvironment variableAPI_URLenvironment variable~/.mem-mesh/api_url— single-line file with the server URL- The URL baked into the hook at install time
http://localhost:8000
Use the config file when you want one installed hook bundle to talk to a remote server (e.g. https://mem.example.com) without editing settings.json and without relying on env-var inheritance — Claude Code does not export settings.json.env retroactively to already-running sessions, so an env var added mid-session won't reach hooks until you restart.
mkdir -p ~/.mem-mesh
echo 'https://mem.example.com' > ~/.mem-mesh/api_url
mem-mesh doctor # confirms the resolved URL and its sourcePer machine — the file is not synced. Delete it (or set MEM_MESH_API_URL) to fall back to the baked default.
- Dashboard: http://localhost:8000
- API docs (Swagger): http://localhost:8000/docs
- Health check: http://localhost:8000/health
flowchart LR
subgraph Clients
Cursor[Cursor]
Claude[Claude Desktop]
Kiro[Kiro]
Web[Web Client]
end
subgraph Transport
Stdio[Stdio MCP]
SSE[SSE / Streamable HTTP]
end
subgraph Core
MCP[mcp_common]
Storage[Storage Service]
end
subgraph Data
SQLite[(SQLite + sqlite-vec + FTS5)]
end
Cursor --> Stdio
Claude --> Stdio
Kiro --> Stdio
Web --> SSE
Stdio --> MCP
SSE --> MCP
MCP --> Storage
Storage --> SQLite
mem-mesh/
├── app/
│ ├── core/ # DB, embeddings, services, schemas
│ ├── mcp_common/ # Shared MCP tools, dispatcher, batch
│ ├── mcp_stdio/ # FastMCP stdio server
│ ├── mcp_stdio_pure/ # Pure MCP stdio server
│ └── web/ # FastAPI (dashboard, SSE MCP, OAuth, WebSocket)
├── static/ # Frontend (Vanilla JS, Web Components)
├── tests/ # pytest
├── scripts/ # Migration and benchmark scripts
├── docs/rules/ # AI agent rule modules
├── data/ # memories.db
└── logs/
# Build and start
make quickstart
# or step by step:
make docker-build && make docker-up
# Open http://localhost:8000When the server starts with no dashboard auth configured, anyone who can reach the port can read/write/delete all memories. To let you close this from the browser (no shell-only config), mem-mesh mints a one-time setup token on first boot and prints it to the server console:
============================================================
FIRST-RUN SETUP — dashboard auth is NOT configured
============================================================
Open : /setup
Token: <one-time-token>
(one-time — consumed the moment you finish setup)
============================================================
Open shows the bare path /setup by default — open it on whatever host:port you bound the server to. Set MEM_MESH_PUBLIC_URL to have the banner print a full URL (e.g. https://your-host/setup).
The token is also written next to the DB (/app/data/setup_token), so it survives a mid-onboarding restart. Retrieve it any time:
docker exec mem-mesh-prod cat /app/data/setup_token
# or scan the logs
docker compose logs mem-mesh | grep -A1 "Token:"Open /setup, enter the token plus an admin username (default admin) and password (≥ 8 chars). On submit mem-mesh saves the credential, enables Basic Auth, consumes the token (single-use), and logs you straight into the dashboard. On a fresh server the first page load auto-redirects to /setup.
Once auth is configured the token is deleted on every startup, so a leftover token can never reconfigure an already-secured server.
Reset the token (lost it, and auth is not configured yet) — delete the file and restart; ensure_setup_token() is idempotent, so a plain restart keeps the same value:
docker exec mem-mesh-prod rm -f /app/data/setup_token
docker restart mem-mesh-prod
docker logs mem-mesh-prod 2>&1 | grep -A1 "Token:"# Install dev dependencies
pip install -e ".[dev]"
# Run tests
python -m pytest tests/ -v
# Format and lint
black app/ tests/
ruff check app/ tests/
# Check embedding migration status
python scripts/migrate_embeddings.py --check-only- CLAUDE.md — AI tool checklist (MUST/SHOULD/MAY rules, security policy)
- AGENTS.md — Project context, Golden Rules, Context Map, session management details
| File | Purpose |
|---|---|
| DEFAULT_PROMPT.md | Standalone behavior rules for projects without installed hooks |
| modules/ | Optional Rule Manager modules: core, search, memory-log, pins, relations, batch, security |
Generated hook rules share the installed hook prompt version:
mem-mesh hooks rules --project-id <project-id> --format plain
mem-mesh hooks rules --project-id <project-id> --format claude- app/core/AGENTS.md — Core service internals
- app/mcp_common/AGENTS.md — MCP common layer
- Open an issue or pull request
- Follow
blackandruffformatting - Add tests for any new behavior
See CONTRIBUTING.md for details and CHANGELOG.md for release history.