mcp-memory-rs
Local-first MCP memory server providing persistent, versioned, queryable memory for AI agents using JSON categories and SQLite FTS5, with optional fleet sync.
README
mcp-memory-rs
Local-first MCP memory server in pure Rust. Persistent, versioned, queryable memory for AI agents — owned by the user, not by a platform.
State lives on your disk as plain JSON categories backed by SQLite (FTS5). Every write is versioned and backed up. A fleet of devices syncs through an optional HTTP endpoint with merkle manifests and optimistic concurrency. No cloud account, no embedding service, no network dependency in the default mode.
Why
Agent memory tied to a vendor dies with the vendor session. This server keeps the agent's long-lived state — identity, projects, infrastructure notes, per-device workflows — in files you can read, grep, diff, and back up yourself. Any MCP client (Claude Code, Codex, or anything speaking MCP stdio) gets the same memory; swapping the model does not lose the state.
Design choices that follow from that:
- Local-first: the stdio server works fully offline against local storage. Sync is a separate, explicit step — never a hidden dependency.
- Plain storage: one JSON file per category plus a SQLite index. The database can be rebuilt from the files; the files are the truth.
- Versioning by default: every write keeps history; deletes create a
backup first.
memory_historyandmemory_compactmanage the tail. - Concurrency-safe writes:
expected_hashturns clobbering races between concurrent agents into explicit conflicts. - Zero ML in the core: full-text search is SQLite FTS5 with BM25;
memory_search_semanticadds an optional TF-IDF hybrid. No model downloads.
The pair: for corpus recall (chunked documents, BM25 retrieval, original-text injection) see the companion server mcp-vl-msa-rs. This server holds the curated agent state; that one holds the queryable corpus.
flowchart LR
A["AI agent<br/>(any MCP client)"]
A -->|"curated state<br/>read / write / sync"| M["mcp-memory-rs<br/><i>the notebook</i>"]
A -->|"corpus recall<br/>index / search / fetch"| V["mcp-vl-msa-rs<br/><i>the library</i>"]
M --- D1[("JSON categories<br/>SQLite FTS5")]
V --- D2[("tantivy BM25<br/>collections")]
Install
Prebuilt binary (recommended) — download the archive for your platform from the latest release, extract, and point your MCP client at the binary:
tar xzf mcp-memory-rs-x86_64-unknown-linux-gnu.tar.gz
install -m755 mcp-memory-rs-*/mcp-memory-rs ~/.local/bin/
Prebuilt targets (Linux + Android): x86_64-unknown-linux-gnu,
x86_64-unknown-linux-musl, aarch64-unknown-linux-gnu,
aarch64-unknown-linux-musl (edge / ARM / Termux), aarch64-linux-android.
macOS: no prebuilt binary is shipped (it would need Apple code-signing).
Install from source instead — cargo install compiles it on your Mac in one
command, no signing needed:
cargo install --git https://github.com/DioNanos/mcp-memory-rs --locked
--locked uses the committed Cargo.lock (reproducible build).
Quick start
cargo build --release
# stdio MCP server, offline mode (default)
./target/release/mcp-memory-rs
Claude Code (~/.claude.json) or any MCP client:
{
"mcpServers": {
"memory": {
"command": "/path/to/mcp-memory-rs",
"env": {
"MCP_MEMORY_CONFIG": "/home/me/.config/mcp-memory-rs/config.toml",
"MCP_MEMORY_REQUIRE_CONFIG": "1",
"MCP_MEMORY_MODE": "offline",
"MCP_DEVICE": "my-laptop"
}
}
}
}
Codex (~/.codex/config.toml):
[mcp_servers.memory]
command = "/path/to/mcp-memory-rs"
env = { MCP_MEMORY_CONFIG = "/home/me/.config/mcp-memory-rs/config.toml", MCP_MEMORY_REQUIRE_CONFIG = "1", MCP_MEMORY_MODE = "offline", MCP_DEVICE = "my-laptop" }
# auto-approve read-only tools (memory_read/list/search/…); writes still gated
default_tools_approval_mode = "approve"
Configuration is TOML — copy config/example.toml to
~/.config/mcp-memory-rs/config.toml and adjust it. The server auto-discovers
that XDG-style path. Environment variables override file values (MCP_DEVICE,
MCP_MEMORY_MODE, MCP_MEMORY_DIR, …); managed deployments should set
MCP_MEMORY_REQUIRE_CONFIG=1 so a missing config fails closed.
Memory model
Memory is organized in categories: named JSON documents (base,
projects, my-laptop, workflow_my-laptop, …). Reads return the document
plus metadata (content hash, size, last writer, timestamp). Writes replace the
category or, with merge=true, patch it per top-level key.
Access control
Multi-device fleets get a small, explicit ACL ([acl] in the config):
| Rule | Effect |
|---|---|
admin_devices |
Listed devices write everything. |
device_categories |
Device name writes category name and workflow_name — its own namespace only. |
| agent scope | Device foo-agent writes foo_* categories. |
| everyone | All devices read everything. |
Unknown categories are denied for non-admin writers — fail closed.
Fleet sync
Each node runs local-first; one node (or any number) exposes the HTTP API as a
sync remote. sync_manifest builds a merkle-rooted fingerprint of all
categories; sync_diff compares manifests; memory_sync pushes dirty
categories or pulls remote changes. Conflicts between nodes are resolved by
the configured conflict_strategy (last-write-wins by default);
expected_hash preconditions protect direct writes (MCP and HTTP), not the
sync envelope.
The HTTP API is an admin/sync plane: the bearer token grants full access to every category. Per-category ACL applies on the MCP stdio surface, where the device identity is known locally; HTTP callers are identified only by the shared token, so no per-device ACL is enforced there.
The HTTP server requires MCP_MEMORY_TOKEN and refuses to start without
it. Bind it to loopback (the default) and tunnel between nodes; do not expose
it to the public internet.
MCP_MEMORY_TOKEN=<secret> ./target/release/mcp-memory-rs --http
Tool surface
| Tool | Description |
|---|---|
memory_read |
Read a category (optional field filtering). |
memory_write |
Replace or merge-patch a category; versioned; expected_hash precondition. |
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. |
memory_delete |
Delete a category (backup created first). |
memory_list |
All categories with hash/size/last-update metadata. |
memory_search |
FTS5 full-text search, BM25 ranking, category/date/actor filters, snippets. |
memory_search_semantic |
Hybrid TF-IDF + FTS5 search. |
memory_history |
Version history of a category. |
memory_delta |
Changes since a known hash (cheap polling). |
memory_context |
Multi-category warmup read, token-budget oriented. |
memory_compact |
Prune old versions and backups. |
memory_status |
Local-first status: dirty queue, manifest hash, last sync. |
memory_doctor |
Diagnostics: paths, database, categories, sync config. |
sync_manifest |
Merkle-rooted manifest of all categories. |
sync_diff |
Compare local vs remote manifest: push/pull/conflict sets. |
sync_push / sync_pull |
Export/import sync envelopes. |
memory_sync |
One sync step against the configured remote (push_dirty / pull_remote). |
The same surface is available over HTTP (/api/v1/*) for non-MCP consumers;
/health is unauthenticated, everything else requires the bearer token.
AI client compatibility
The server is built to be self-explanatory to a weak client model:
- At
initializeit returns an instructions string describing the memory model and the entry-point tools. Some lightweight clients ignore this field; if your client never surfaces it, read the tool descriptions instead — they carry the same guidance (e.g.memory_readtakescategory, notkey). - Read-only tools are annotated
readOnlyHint, so a client such as Codex can auto-approve them. On Codex, anunsupported call/user cancelledresult usually means the tool-approval gate fired, not a server fault — setdefault_tools_approval_mode = "approve"(see the Codex snippet above). - The server speaks the standard MCP handshake. A client must complete
initializeand sendnotifications/initializedlike any MCP client.
Storage layout
With no config and strict mode disabled, standalone base_dir remains
~/.memory for compatibility. The auto-discovered example config uses
~/.local/state/mcp-memory-rs (XDG-style). Config resolution is: explicit
MCP_MEMORY_CONFIG, $XDG_CONFIG_HOME, $HOME/.config, legacy cwd-local
memory-config.toml, then standalone defaults. Either way the layout is:
<base_dir>/
├── categories/ # one .json file per category — the source of truth
├── backups/ # automatic pre-delete/pre-overwrite backups
└── memory.db # SQLite: FTS5 index, versions, sync state (rebuildable)
Building and testing
cargo build --release # single static-friendly binary
cargo test # unit + integration tests
cargo clippy --all-targets -- -D warnings
No build-time network access, no C dependencies beyond bundled SQLite.
License
Apache-2.0. See LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。