{
  "markdown": "# mcp-memory-rs\n\n[![CI](https://img.shields.io/github/actions/workflow/status/DioNanos/mcp-memory-rs/ci.yml?branch=main&style=flat-square&logo=githubactions&logoColor=white&label=CI)](https://github.com/DioNanos/mcp-memory-rs/actions/workflows/ci.yml)\n[![Tests](https://img.shields.io/badge/tests-106%20passing-2ea44f?style=flat-square)](https://github.com/DioNanos/mcp-memory-rs/actions/workflows/ci.yml)\n[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue?style=flat-square)](LICENSE)\n[![Rust](https://img.shields.io/badge/rust-stable-orange?style=flat-square&logo=rust)](https://www.rust-lang.org)\n[![Local First](https://img.shields.io/badge/local--first-no%20cloud%20deps-111827?style=flat-square)](#why)\n\nLocal-first MCP memory server in pure Rust. Persistent, versioned, queryable\nmemory for AI agents — owned by the user, not by a platform.\n\nState lives on your disk as plain JSON categories backed by SQLite (FTS5).\nEvery write is versioned and backed up. A fleet of devices syncs through an\noptional HTTP endpoint with merkle manifests and optimistic concurrency.\nNo cloud account, no embedding service, no network dependency in the default\nmode.\n\n## Why\n\nAgent memory tied to a vendor dies with the vendor session. This server keeps\nthe agent's long-lived state — identity, projects, infrastructure notes,\nper-device workflows — in files you can read, grep, diff, and back up\nyourself. Any MCP client (Claude Code, Codex, or anything speaking MCP stdio)\ngets the same memory; swapping the model does not lose the state.\n\nDesign choices that follow from that:\n\n- **Local-first**: the stdio server works fully offline against local storage.\n  Sync is a separate, explicit step — never a hidden dependency.\n- **Plain storage**: one JSON file per category plus a SQLite index. The\n  database can be rebuilt from the files; the files are the truth.\n- **Versioning by default**: every write keeps history; deletes create a\n  backup first. `memory_history` and `memory_compact` manage the tail.\n- **Concurrency-safe writes**: `expected_hash` turns clobbering races between\n  concurrent agents into explicit conflicts.\n- **Zero ML in the core**: full-text search is SQLite FTS5 with BM25;\n  `memory_search_semantic` adds an optional TF-IDF hybrid. No model downloads.\n\n> **The pair:** for *corpus* recall (chunked documents, BM25 retrieval,\n> original-text injection) see the companion server\n> [mcp-vl-msa-rs](https://github.com/DioNanos/mcp-vl-msa-rs). This server holds\n> the curated agent state; that one holds the queryable corpus.\n\n```mermaid\nflowchart LR\n    A[\"AI agent<br/>(any MCP client)\"]\n    A -->|\"curated state<br/>read / write / sync\"| M[\"mcp-memory-rs<br/><i>the notebook</i>\"]\n    A -->|\"corpus recall<br/>index / search / fetch\"| V[\"mcp-vl-msa-rs<br/><i>the library</i>\"]\n    M --- D1[(\"JSON categories<br/>SQLite FTS5\")]\n    V --- D2[(\"tantivy BM25<br/>collections\")]\n```\n\n## Install\n\n**Prebuilt binary** (recommended) — download the archive for your platform from\nthe [latest release](https://github.com/DioNanos/mcp-memory-rs/releases/latest),\nextract, and point your MCP client at the binary:\n\n```bash\ntar xzf mcp-memory-rs-x86_64-unknown-linux-gnu.tar.gz\ninstall -m755 mcp-memory-rs-*/mcp-memory-rs ~/.local/bin/\n```\n\nPrebuilt targets (Linux + Android): `x86_64-unknown-linux-gnu`,\n`x86_64-unknown-linux-musl`, `aarch64-unknown-linux-gnu`,\n`aarch64-unknown-linux-musl` (edge / ARM / Termux), `aarch64-linux-android`.\n\n**macOS**: no prebuilt binary is shipped (it would need Apple code-signing).\nInstall from source instead — `cargo install` compiles it on your Mac in one\ncommand, no signing needed:\n\n```bash\ncargo install --git https://github.com/DioNanos/mcp-memory-rs --locked\n```\n\n`--locked` uses the committed `Cargo.lock` (reproducible build).\n\n## Quick start\n\n```bash\ncargo build --release\n\n# stdio MCP server, offline mode (default)\n./target/release/mcp-memory-rs\n```\n\nClaude Code (`~/.claude.json`) or any MCP client:\n\n```json\n{\n  \"mcpServers\": {\n    \"memory\": {\n      \"command\": \"/path/to/mcp-memory-rs\",\n      \"env\": {\n        \"MCP_MEMORY_CONFIG\": \"/home/me/.config/mcp-memory-rs/config.toml\",\n        \"MCP_MEMORY_REQUIRE_CONFIG\": \"1\",\n        \"MCP_MEMORY_MODE\": \"offline\",\n        \"MCP_DEVICE\": \"my-laptop\"\n      }\n    }\n  }\n}\n```\n\nCodex (`~/.codex/config.toml`):\n\n```toml\n[mcp_servers.memory]\ncommand = \"/path/to/mcp-memory-rs\"\nenv = { MCP_MEMORY_CONFIG = \"/home/me/.config/mcp-memory-rs/config.toml\", MCP_MEMORY_REQUIRE_CONFIG = \"1\", MCP_MEMORY_MODE = \"offline\", MCP_DEVICE = \"my-laptop\" }\n# auto-approve read-only tools (memory_read/list/search/…); writes still gated\ndefault_tools_approval_mode = \"approve\"\n```\n\nConfiguration is TOML — copy [`config/example.toml`](config/example.toml) to\n`~/.config/mcp-memory-rs/config.toml` and adjust it. The server auto-discovers\nthat XDG-style path. Environment variables override file values (`MCP_DEVICE`,\n`MCP_MEMORY_MODE`, `MCP_MEMORY_DIR`, …); managed deployments should set\n`MCP_MEMORY_REQUIRE_CONFIG=1` so a missing config fails closed.\n\n## Memory model\n\nMemory is organized in **categories**: named JSON documents (`base`,\n`projects`, `my-laptop`, `workflow_my-laptop`, …). Reads return the document\nplus metadata (content hash, size, last writer, timestamp). Writes replace the\ncategory or, with `merge=true`, patch it per top-level key.\n\n### Access control\n\nMulti-device fleets get a small, explicit ACL (`[acl]` in the config):\n\n| Rule | Effect |\n|---|---|\n| `admin_devices` | Listed devices write everything. |\n| `device_categories` | Device `name` writes category `name` and `workflow_name` — its own namespace only. |\n| agent scope | Device `foo-agent` writes `foo_*` categories. |\n| everyone | All devices read everything. |\n\nUnknown categories are denied for non-admin writers — fail closed.\n\n### Fleet sync\n\nEach node runs local-first; one node (or any number) exposes the HTTP API as a\nsync remote. `sync_manifest` builds a merkle-rooted fingerprint of all\ncategories; `sync_diff` compares manifests; `memory_sync` pushes dirty\ncategories or pulls remote changes. Conflicts between nodes are resolved by\nthe configured `conflict_strategy` (last-write-wins by default);\n`expected_hash` preconditions protect direct writes (MCP and HTTP), not the\nsync envelope.\n\nThe HTTP API is an **admin/sync plane**: the bearer token grants full access\nto every category. Per-category ACL applies on the MCP stdio surface, where\nthe device identity is known locally; HTTP callers are identified only by the\nshared token, so no per-device ACL is enforced there.\n\nThe HTTP server **requires** `MCP_MEMORY_TOKEN` and refuses to start without\nit. Bind it to loopback (the default) and tunnel between nodes; do not expose\nit to the public internet.\n\n```bash\nMCP_MEMORY_TOKEN=<secret> ./target/release/mcp-memory-rs --http\n```\n\n## Tool surface\n\n| Tool | Description |\n|---|---|\n| `memory_read` | Read a category (optional field filtering). |\n| `memory_write` | Replace or merge-patch a category; versioned; `expected_hash` precondition. |\n| `memory_append` | Append a timestamped entry to a bounded append-only **log** category; auto-prunes by `max_entries`/`max_age_days`. For event streams / session journals, so they don't bloat memory categories. |\n| `memory_delete` | Delete a category (backup created first). |\n| `memory_list` | All categories with hash/size/last-update metadata. |\n| `memory_search` | FTS5 full-text search, BM25 ranking, category/date/actor filters, snippets. |\n| `memory_search_semantic` | Hybrid TF-IDF + FTS5 search. |\n| `memory_history` | Version history of a category. |\n| `memory_delta` | Changes since a known hash (cheap polling). |\n| `memory_context` | Multi-category warmup read, token-budget oriented. |\n| `memory_compact` | Prune old versions and backups. |\n| `memory_status` | Local-first status: dirty queue, manifest hash, last sync. |\n| `memory_doctor` | Diagnostics: paths, database, categories, sync config. |\n| `sync_manifest` | Merkle-rooted manifest of all categories. |\n| `sync_diff` | Compare local vs remote manifest: push/pull/conflict sets. |\n| `sync_push` / `sync_pull` | Export/import sync envelopes. |\n| `memory_sync` | One sync step against the configured remote (`push_dirty` / `pull_remote`). |\n\nThe same surface is available over HTTP (`/api/v1/*`) for non-MCP consumers;\n`/health` is unauthenticated, everything else requires the bearer token.\n\n### AI client compatibility\n\nThe server is built to be self-explanatory to a weak client model:\n\n- At `initialize` it returns an **instructions** string describing the memory\n  model and the entry-point tools. Some lightweight clients ignore this field;\n  if your client never surfaces it, read the tool descriptions instead — they\n  carry the same guidance (e.g. `memory_read` takes `category`, not `key`).\n- Read-only tools are annotated `readOnlyHint`, so a client such as Codex can\n  auto-approve them. On Codex, an `unsupported call` / `user cancelled`\n  result usually means the tool-approval gate fired, not a server fault — set\n  `default_tools_approval_mode = \"approve\"` (see the Codex snippet above).\n- The server speaks the standard MCP handshake. A client must complete\n  `initialize` and send `notifications/initialized` like any MCP client.\n\n## Storage layout\n\nWith no config and strict mode disabled, standalone `base_dir` remains\n`~/.memory` for compatibility. The auto-discovered example config uses\n`~/.local/state/mcp-memory-rs` (XDG-style). Config resolution is: explicit\n`MCP_MEMORY_CONFIG`, `$XDG_CONFIG_HOME`, `$HOME/.config`, legacy cwd-local\n`memory-config.toml`, then standalone defaults. Either way the layout is:\n\n```\n<base_dir>/\n├── categories/   # one .json file per category — the source of truth\n├── backups/      # automatic pre-delete/pre-overwrite backups\n└── memory.db     # SQLite: FTS5 index, versions, sync state (rebuildable)\n```\n\n## Building and testing\n\n```bash\ncargo build --release   # single static-friendly binary\ncargo test              # unit + integration tests\ncargo clippy --all-targets -- -D warnings\n```\n\nNo build-time network access, no C dependencies beyond bundled SQLite.\n\n## License\n\nApache-2.0. See [LICENSE](LICENSE).\n",
  "bytes": 10187,
  "sha": "d24a2f9b66b9acd33d504f02f707dfb0b0da54a4ff0d06ec1dbdce1ccdc71a46",
  "repo_slug": "dionanos/mcp-memory-rs",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_dionanos_mcp_memory_rs_a95d687a/readme"
}