{
  "markdown": "# mnemon-mcp\n\n[![CI](https://github.com/nikitacometa/mnemon-memory-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/nikitacometa/mnemon-memory-mcp/actions/workflows/ci.yml)\n[![npm version](https://img.shields.io/npm/v/mnemon-mcp)](https://www.npmjs.com/package/mnemon-mcp)\n[![Node.js](https://img.shields.io/badge/node-%E2%89%A520-brightgreen)](https://nodejs.org/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\n**Persistent layered memory for AI agents.**\nLocal-first. Zero-cloud. Single SQLite file.\n\n[Landing Page](https://aisatisfy.me/mnemon/) · [npm](https://www.npmjs.com/package/mnemon-mcp) · [GitHub](https://github.com/nikitacometa/mnemon-memory-mcp)\n\nYour AI agent forgets everything after each session. Mnemon fixes that.\n\nIt gives any [MCP](https://modelcontextprotocol.io)-compatible client — [OpenClaw](https://openclaw.ai), Claude Code, Cursor, Windsurf, or your own — a structured long-term memory backed by a single SQLite database on your machine. No API keys, no cloud, no telemetry. Just `npm install` and your agent remembers.\n\n<p align=\"center\">\n  <img src=\"demo/mnemon-demo.gif\" alt=\"mnemon-mcp demo — memory_add, memory_search, memory_inspect, memory_update\" width=\"800\">\n</p>\n\n---\n\n## Why Layered Memory?\n\nFlat key-value stores treat \"what happened yesterday\" the same as \"never commit without tests.\" That's wrong — different kinds of knowledge have different lifetimes and access patterns.\n\nMnemon organizes memories into **four layers**:\n\n| Layer | What it stores | How it's accessed | Lifetime |\n|-------|---------------|-------------------|----------|\n| **Episodic** | Events, sessions, journal entries | By date or period | Decays (30-day half-life) |\n| **Semantic** | Facts, preferences, relationships | By topic or entity | Stable |\n| **Procedural** | Rules, workflows, conventions | Loaded at startup | Rarely changes |\n| **Resource** | Reference material, book notes | On demand | Decays slowly (90 days) |\n\nA journal entry from last Tuesday and a coding rule that never changes live in different layers — because they should.\n\n## Retrieval Quality\n\nRetrieval is measured against a 50-case golden set on a real 797-memory\nbilingual (RU/EN) corpus, through the actual MCP server — not a\nreimplementation. Current numbers ([methodology & history](docs/EVALUATION.md)):\n\n| Metric | FTS-only | Vector-only | Hybrid (RRF) |\n|--------|---------:|------------:|-------------:|\n| Composite score | 88.9 | 89.2 | **91.7** |\n| Recall@5 | 0.907 | 0.898 | **0.919** |\n| MRR | 0.817 | 0.832 | **0.878** |\n| nDCG@5 | 0.816 | 0.828 | **0.869** |\n| Negative precision | 1.000 | 1.000 | 1.000 |\n\nHybrid beats **both** legs individually, which is the whole argument for\nfusing them: lexical search has the better raw recall, vector search the\nbetter ranking, and RRF keeps both instead of averaging them away.\n\nThe eval doc tracks the failures too — score drift under corpus growth, the\nBM25 field-weight bug the eval caught, the two cases where fusion still loses\nto pure lexical search, and what the golden set does *not* cover. Numbers you\ncan't audit are marketing; [read how these are produced](docs/EVALUATION.md).\n\n## Architecture\n\n```mermaid\nflowchart LR\n    C[\"MCP client<br/>Claude Code · Cursor · …\"] -- \"stdio / HTTP\" --> T[\"10 tools · 4 resources · 3 prompts\"]\n    T --> R[\"retrieval pipeline<br/>FTS5 · vector · RRF fusion\"]\n    T --> M[\"memories + supersede chains\"]\n    I[\"KB import pipeline<br/>markdown → memories\"] --> M\n    M -- triggers --> F[\"FTS5 index (stemmed EN+RU)\"]\n    R --> F\n    R --> V[\"sqlite-vec (optional, BYOK)\"]\n```\n\nOne SQLite file holds memories, the FTS5 index, and the optional vector\nindex. Writes go through transactions that keep the supersede-chain invariant;\nreads run the staged retrieval pipeline described under [Search](#search).\n\nThe full picture — module boundaries, write/read paths, invariants, and known\nlimitations — is in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). Design\ndecisions are recorded as ADRs: [SQLite+FTS5 core](docs/adr/0001-sqlite-fts5-over-vector-db.md),\n[hybrid RRF retrieval](docs/adr/0002-hybrid-retrieval-rrf.md),\n[synchronous driver](docs/adr/0003-synchronous-better-sqlite3.md),\n[layered memory model](docs/adr/0004-layered-memory-model.md).\n\n## Quick Start\n\n### Install\n\n```bash\nnpm install -g mnemon-mcp\n```\n\nOr from source:\n\n```bash\ngit clone https://github.com/nikitacometa/mnemon-memory-mcp.git\ncd mnemon-memory-mcp && npm install && npm run build\n```\n\n### Configure Your MCP Client\n\n<details open>\n<summary><strong>OpenClaw</strong></summary>\n\n```bash\nopenclaw mcp register mnemon-mcp --command=\"mnemon-mcp\"\n```\n\nOr add to `~/.openclaw/mcp_config.json`:\n\n```json\n{\n  \"mnemon-mcp\": {\n    \"command\": \"mnemon-mcp\"\n  }\n}\n```\n\n</details>\n\n<details>\n<summary><strong>Claude Code</strong></summary>\n\nAdd to `~/.claude/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"mnemon-mcp\": {\n      \"command\": \"mnemon-mcp\"\n    }\n  }\n}\n```\n\n</details>\n\n<details>\n<summary><strong>Cursor / Windsurf / Other MCP clients</strong></summary>\n\nAdd to your client's MCP config:\n\n```json\n{\n  \"mcpServers\": {\n    \"mnemon-mcp\": {\n      \"command\": \"mnemon-mcp\"\n    }\n  }\n}\n```\n\n</details>\n\n<details>\n<summary><strong>Running from source?</strong></summary>\n\nUse the full path to the compiled entry point:\n\n```json\n{\n  \"mnemon-mcp\": {\n    \"command\": \"node\",\n    \"args\": [\"/absolute/path/to/mnemon-mcp/dist/index.js\"]\n  }\n}\n```\n\n</details>\n\n### Verify\n\n```bash\necho '{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":1}' | mnemon-mcp\n```\n\nYou should see 10 tools in the response. The database (`~/.mnemon-mcp/memory.db`) is created automatically on first run.\n\nThat's it. Your agent now has persistent memory.\n\n## What It Can Do\n\n### 10 MCP Tools\n\n| Tool | What it does |\n|------|-------------|\n| **`memory_add`** | Store a memory with layer, entity, confidence, importance, and optional TTL |\n| **`memory_search`** | Full-text or exact search with filters by layer, entity, date, scope, confidence |\n| **`memory_update`** | Update in-place or create a versioned replacement (superseding chain) |\n| **`memory_delete`** | Delete a memory; re-activates its predecessor if any |\n| **`memory_inspect`** | Get layer statistics or trace a single memory's version history |\n| **`memory_export`** | Export to JSON, Markdown, or Claude-md format with filters |\n| **`memory_health`** | Run diagnostics: expired entries, orphaned chains, stale memories; optionally GC |\n| **`memory_session_start`** | Start an agent session — returns session ID for grouping memories |\n| **`memory_session_end`** | End a session with optional summary; returns duration and memory count |\n| **`memory_session_list`** | List sessions with filters by client, project, or active status |\n\n### MCP Resources & Prompts\n\n**Resources** — live data your agent can read:\n\n| URI | Returns |\n|-----|---------|\n| `memory://stats` | Aggregate stats per layer |\n| `memory://recent` | Memories created/updated in last 24h |\n| `memory://layer/{layer}` | All active memories in a layer |\n| `memory://entity/{name}` | All active memories about an entity |\n\n**Prompts** — pre-built workflows:\n\n| Prompt | Purpose |\n|--------|---------|\n| `recall` | \"Tell me everything you know about X\" |\n| `context-load` | Load relevant context before starting a task |\n| `journal` | Create a structured journal entry |\n\n## Search\n\nFour modes, all supporting layer / entity / scope / date / confidence filters:\n\n**FTS mode** (default without embeddings) — tokenized full-text search with BM25 ranking. Multi-word queries use AND; if too few results, OR supplements with a score penalty. Progressive AND relaxation tries top-3 most specific terms before falling back to full OR.\n\n**Hybrid mode** (default when embeddings configured) — combines FTS5 + vector search via [Reciprocal Rank Fusion](https://www.singlestore.com/blog/hybrid-search-using-reciprocal-rank-fusion-in-sql/). Detects quoted entities in queries (e.g., `'Essentialism'`) and runs weighted sub-queries for cross-reference retrieval.\n\n**Vector mode** — pure cosine similarity search over embeddings.\n\n**Exact mode** — `LIKE` substring match for precise phrase lookups.\n\nScores: `bm25 × (0.3 + 0.7 × importance) × decay(layer) × recency`\n\nRecency boost: `1 / (1 + daysSince / 365)` — gently rewards recently created memories without penalizing old ones.\n\n### Stemming\n\nSnowball stemmer applied at both **index time** and **query time** for English and Russian. This means `\"running\"` matches `\"runs\"`, and `\"книги\"` matches `\"книга\"`. Stop words are filtered from queries to improve precision.\n\n## Fact Versioning\n\nKnowledge evolves. Mnemon doesn't delete old facts — it chains them:\n\n```\nv1: \"Team uses React 17\"  →  superseded_by: v2\nv2: \"Team uses React 19\"  →  supersedes: v1 (active)\n```\n\nSearch returns only the latest version. `memory_inspect` with `include_history: true` reveals the full chain. `memory_delete` re-activates the predecessor — nothing is lost.\n\n## Vector Search (Optional, BYOK)\n\nEnable semantic similarity search by providing your own embedding API:\n\n```bash\n# OpenAI\nMNEMON_EMBEDDING_PROVIDER=openai MNEMON_EMBEDDING_API_KEY=sk-... mnemon-mcp\n\n# Ollama (local, free)\nMNEMON_EMBEDDING_PROVIDER=ollama mnemon-mcp\n```\n\nThis unlocks two additional search modes:\n- **`mode: \"vector\"`** — pure cosine similarity search\n- **`mode: \"hybrid\"`** — FTS5 + vector combined via [Reciprocal Rank Fusion](https://www.singlestore.com/blog/hybrid-search-using-reciprocal-rank-fusion-in-sql/)\n\nRequires `sqlite-vec` (installed as optional dependency). New memories are embedded on add; existing ones can be backfilled.\n\n<details>\n<summary>Embedding configuration</summary>\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MNEMON_EMBEDDING_PROVIDER` | — | `openai` or `ollama` (unset = disabled) |\n| `MNEMON_EMBEDDING_API_KEY` | — | API key (required for OpenAI) |\n| `MNEMON_EMBEDDING_MODEL` | `text-embedding-3-small` / `nomic-embed-text` | Model name |\n| `MNEMON_EMBEDDING_DIMENSIONS` | `1024` / `768` | Vector dimensions |\n| `MNEMON_OLLAMA_URL` | `http://localhost:11434` | Ollama endpoint |\n\n</details>\n\n## Importing a Knowledge Base\n\nGot a folder of Markdown files? Import them in bulk:\n\n```bash\ncp config.example.json ~/.mnemon-mcp/config.json   # edit this first\nnpm run import:kb -- --kb-path /path/to/your/kb     # incremental (skips unchanged files)\n```\n\nThe config maps glob patterns to memory layers:\n\n```json\n{\n  \"owner_name\": \"your-name\",\n  \"extra_stop_words\": [],\n  \"mappings\": [\n    {\n      \"glob\": \"journal/*.md\",\n      \"layer\": \"episodic\",\n      \"entity_type\": \"user\",\n      \"entity_name\": \"$owner\",\n      \"importance\": 0.6,\n      \"split\": \"h2\"\n    },\n    {\n      \"glob\": \"people/*.md\",\n      \"layer\": \"semantic\",\n      \"entity_type\": \"person\",\n      \"entity_name\": \"from-heading\",\n      \"importance\": 0.8,\n      \"split\": \"h3\"\n    }\n  ]\n}\n```\n\n### Config Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `owner_name` | string | Your name — used for `$owner` substitution in `entity_name` |\n| `extra_stop_words` | string[] | Words to filter from FTS queries (e.g., your name forms) |\n| `glob` | string | File pattern to match |\n| `layer` | string | Target memory layer |\n| `entity_type` | string | `user` / `person` / `project` / `concept` / `file` / `rule` / `tool` |\n| `entity_name` | string | Literal name, `\"$owner\"`, or `\"from-heading\"` (extract from H2/H3) |\n| `split` | string | `\"whole\"` (one memory per file), `\"h2\"`, or `\"h3\"` (split on headings) |\n| `importance` | number | 0.0–1.0, affects search ranking |\n| `confidence` | number | 0.0–1.0, filterable in search |\n| `scope` | string | Optional namespace |\n\n## HTTP Transport\n\nFor remote or multi-client setups:\n\n```bash\nMNEMON_AUTH_TOKEN=your-secret MNEMON_HOST=0.0.0.0 MNEMON_PORT=3000 npm run start:http\n```\n\n| Endpoint | Description |\n|----------|-------------|\n| `POST /mcp` | MCP JSON-RPC (Bearer auth if token set) |\n| `GET /health` | `{\"status\":\"ok\",\"version\":\"...\"}` |\n\nBinds to `127.0.0.1` by default. Binding to any other host requires `MNEMON_AUTH_TOKEN` — the server refuses to expose the memory store to the network unauthenticated (override with `MNEMON_ALLOW_INSECURE_HTTP=1` on a trusted network). Rate limiting (100 req/min/IP by default), opt-in CORS, 1MB body limit, timing-safe auth, graceful shutdown on SIGTERM.\n\n## Configuration Reference\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MNEMON_DB_PATH` | `~/.mnemon-mcp/memory.db` | Database path |\n| `MNEMON_KB_PATH` | `.` | Knowledge base root for import |\n| `MNEMON_CONFIG_PATH` | `~/.mnemon-mcp/config.json` | Import config path |\n| `MNEMON_AUTH_TOKEN` | — | Bearer token for HTTP transport |\n| `MNEMON_HOST` | `127.0.0.1` | HTTP transport bind address |\n| `MNEMON_PORT` | `3000` | HTTP transport port |\n| `MNEMON_CORS_ORIGIN` | — | CORS `Access-Control-Allow-Origin` (no CORS headers unless set) |\n| `MNEMON_RATE_LIMIT` | `100` | Max requests per minute per IP (0 = off) |\n\n## Tool Reference\n\n<details>\n<summary><code>memory_add</code> — full parameter list</summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `content` | string | Yes | Memory text (max 100K chars) |\n| `layer` | string | Yes | `episodic` / `semantic` / `procedural` / `resource` |\n| `title` | string | No | Short title (max 500 chars) |\n| `entity_type` | string | No | `user` / `project` / `person` / `concept` / `file` / `rule` / `tool` |\n| `entity_name` | string | No | Entity name for filtering |\n| `confidence` | number | No | 0.0–1.0 (default 0.8) |\n| `importance` | number | No | 0.0–1.0 (default 0.5) |\n| `scope` | string | No | Namespace (default `global`) |\n| `source_file` | string | No | Source file path — triggers auto-supersede of matching entries |\n| `ttl_days` | number | No | Auto-expire after N days |\n| `valid_from` / `valid_until` | string | No | Temporal fact window (ISO 8601) |\n\n</details>\n\n<details>\n<summary><code>memory_search</code> — full parameter list</summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `query` | string | Yes | Search text |\n| `mode` | string | No | `fts` (default), `exact`, `vector`, `hybrid` |\n| `layers` | string[] | No | Filter by layers |\n| `entity_name` | string | No | Filter by entity (supports aliases) |\n| `scope` | string | No | Filter by scope |\n| `date_from` / `date_to` | string | No | Date range (ISO 8601) |\n| `as_of` | string | No | Temporal fact filter — facts valid at this date |\n| `min_confidence` | number | No | Minimum confidence |\n| `min_importance` | number | No | Minimum importance |\n| `limit` | number | No | Max results (default 10, max 100) |\n| `offset` | number | No | Pagination offset |\n\n</details>\n\n<details>\n<summary><code>memory_update</code> — full parameter list</summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `id` | string | Yes | Memory ID |\n| `content` | string | No | New content |\n| `title` | string | No | New title |\n| `confidence` | number | No | New confidence |\n| `importance` | number | No | New importance |\n| `supersede` | boolean | No | `true` = versioned replacement; `false` (default) = in-place |\n| `new_content` | string | No | Content for superseding entry |\n\n</details>\n\n<details>\n<summary><code>memory_delete</code></summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `id` | string | Yes | Memory ID. Re-activates predecessor if part of a superseding chain |\n\n</details>\n\n<details>\n<summary><code>memory_inspect</code></summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `id` | string | No | Memory ID (omit for aggregate stats) |\n| `layer` | string | No | Filter stats by layer |\n| `entity_name` | string | No | Filter stats by entity |\n| `include_history` | boolean | No | Show superseding chain |\n\n</details>\n\n<details>\n<summary><code>memory_export</code></summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `format` | string | Yes | `json` / `markdown` / `claude-md` |\n| `layers` | string[] | No | Filter by layers |\n| `scope` | string | No | Filter by scope |\n| `date_from` / `date_to` | string | No | Date range |\n| `limit` | number | No | Max entries (default all, max 10K) |\n\n</details>\n\n<details>\n<summary><code>memory_health</code></summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `cleanup` | boolean | No | `true` = garbage-collect expired entries (default: report only) |\n\nReturns: status (`healthy` / `warning` / `degraded`), per-layer stats, expired entries, orphaned chains, stale/low-confidence counts, cleaned count when `cleanup=true`.\n\n</details>\n\n<details>\n<summary><code>memory_session_start</code></summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `client` | string | Yes | Client identifier (e.g. `claude-code`, `cursor`, `api`) |\n| `project` | string | No | Project scope for this session |\n| `meta` | object | No | Additional session metadata |\n\nReturns: `id` (session UUID), `started_at` (ISO 8601).\n\n</details>\n\n<details>\n<summary><code>memory_session_end</code></summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `id` | string | Yes | Session ID to end |\n| `summary` | string | No | Summary of what was accomplished (max 10K chars) |\n\nReturns: `id`, `ended_at`, `duration_minutes`, `memories_count`.\n\n</details>\n\n<details>\n<summary><code>memory_session_list</code></summary>\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `limit` | number | No | Max sessions (default 20, max 100) |\n| `client` | string | No | Filter by client |\n| `project` | string | No | Filter by project |\n| `active_only` | boolean | No | Only return sessions that haven't ended (default false) |\n\nReturns: array of sessions with `id`, `client`, `project`, `started_at`, `ended_at`, `summary`, `memories_count`.\n\n</details>\n\n## How It Compares\n\n| | **mnemon-mcp** | mem0 | basic-memory | Engram | Anthropic KG |\n|---|---|---|---|---|---|\n| **Architecture** | SQLite FTS5 + vector | Cloud API + Qdrant | Markdown + vector | SQLite FTS5 | JSON file |\n| **Memory structure** | 4 typed layers | Flat | Flat | Flat + sessions | Graph |\n| **Search** | FTS5 + hybrid RRF | Semantic | Hybrid | FTS5 | Exact |\n| **Fact versioning** | Superseding chains | Partial | No | No | No |\n| **Stemming** | EN + RU (Snowball) | EN only | EN only | None | None |\n| **Embeddings** | BYOK (OpenAI / Ollama) | Built-in | FastEmbed | None | None |\n| **Dependencies** | 0 required | Qdrant, Neo4j | Python 3.12 | Go binary | None |\n| **Cloud required** | No | Yes | No | No | No |\n| **Cost** | Free | $19–249/mo | Free | Free | Free |\n| **Setup** | `npm install -g` | Docker + API keys | pip + deps | Go install | Built-in |\n| **License** | MIT | Apache 2.0 | AGPL | MIT | MIT |\n\nExtended competitive analysis with sources: [docs/COMPETITORS.md](docs/COMPETITORS.md).\n\n## Development\n\n```bash\nnpm run dev        # run via tsx (no build step)\nnpm run build      # TypeScript → dist/\nnpm run lint       # eslint (flat config)\nnpm test           # vitest — unit + integration + MCP dispatch + HTTP transport + hybrid RRF\nnpm run bench      # performance benchmarks\nnpm run db:backup  # backup database\n```\n\nCI runs build + lint + tests on Node 20 and 22, then smoke-tests the compiled\nserver over real JSON-RPC (`tools/list` must match the exact tool set).\n\n**Stack:** TypeScript 5.9 (strict mode), better-sqlite3, @modelcontextprotocol/sdk, Snowball stemmer, Zod, vitest.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for code guidelines.\n\n## Design Principles\n\n- **Air-gapped by default** — zero telemetry, ever. Out of the box nothing leaves the machine; the only component that talks to the network is the optional embedder, and only to the provider you configure (including a local Ollama).\n- **Single file** — one SQLite database, zero ops, instant backup via file copy.\n- **Deterministic search** — FTS5, not embeddings, is the default. Interpretable, reproducible, no GPU needed.\n- **Structured over flat** — layers encode access patterns; superseding chains encode time.\n- **Minimal** — 4 production dependencies. Works everywhere Node runs.\n- **Measured, not asserted** — retrieval changes are judged against a golden set, [regressions included](docs/EVALUATION.md).\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 20637,
  "sha": "2ee94e6303365da89c136b53831376d15947397dbdcee31013815d60f61f45a4",
  "repo_slug": "nikitacometa/mnemon-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_nikitacometa_mnemon_mcp_1403c6c1/readme"
}