A local-first MCP server that gives any MCP-compatible LLM client persistent, semantically searchable memory. No cloud services, no running daemons — just a Rust binary, a local Ollama instance for embeddings, and a LanceDB directory on disk.
Built primarily for OpenCode but works with any MCP client that supports stdio transport (Claude Desktop, Cursor, etc.).
OpenCode (stdio)
│ JSON-RPC 2.0
▼
mcp-memory (Rust binary)
├── Ollama → nomic-embed-text embeddings (768-dim)
└── LanceDB → ~/.local/share/mcp-memory/db/ (vector store, no server)
When you call store_memory, the content is embedded via Ollama and persisted to a local LanceDB table alongside its metadata. When you call search_memories, your query is embedded the same way and matched against stored vectors using cosine similarity.
All data stays on your machine.
| Tool | Description |
|---|---|
store_memory |
Embed and persist a memory. Returns its UUID. |
search_memories |
Semantic similarity search. Returns ranked results with scores. |
browse_recent |
List recent memories, newest first. Filterable by type and time window. |
recall_session |
Retrieve all memories from a named session, in chronological order. |
delete_memory |
Remove a memory by UUID. |
get_stats |
Counts by type, oldest/newest timestamps, unique session count. |
Each stored memory has a memory_type field:
| Type | Use for |
|---|---|
conversation |
A turn or exchange in an LLM conversation |
fact |
A distilled assertion — "user prefers dark mode", "project X uses Rust" |
code |
A code snippet worth keeping |
decision |
An architectural or design decision and its rationale |
note |
Anything else (default) |
- A running Ollama instance with
nomic-embed-textpulled - Rust stable (for building from source)
ollama pull nomic-embed-textgit clone https://git.4ray.co/jean/mcp-memory.git
cd mcp-memory
cargo build --release
cp target/release/mcp-memory ~/.cargo/bin/Requires openssl, pkg-config, and protobuf (protoc) at build time. Inside the dev shell these are provided automatically.
nix build git+https://git.4ray.co/jean/mcp-memory.git
# binary at ./result/bin/mcp-memory
# or run directly without installing
nix run git+https://git.4ray.co/jean/mcp-memory.gitAll configuration is via environment variables. Every variable has a default so zero-config local usage works out of the box.
| Variable | Default | Description |
|---|---|---|
OLLAMA_URL |
http://localhost:11434 |
Ollama base URL (no trailing slash) |
EMBED_MODEL |
nomic-embed-text |
Ollama model used for embeddings |
EMBED_DIMS |
768 |
Embedding vector dimensions — must match the model |
MEMORY_DB_PATH |
~/.local/share/mcp-memory/db |
LanceDB directory (XDG-compliant) |
MCP_MEMORY_SOURCE |
opencode |
Label attached to every stored memory identifying the client |
RUST_LOG |
warn |
Log level. All logs go to stderr — stdout is reserved for MCP. |
Add to ~/.config/opencode/opencode.jsonc (global, available in every project):
Any client that supports local stdio MCP servers works. For Claude Desktop, add to claude_desktop_config.json:
{
"mcpServers": {
"memory": {
"command": "/home/you/.cargo/bin/mcp-memory",
"env": {
"OLLAMA_URL": "http://localhost:11434",
"MEMORY_DB_PATH": "/home/you/.local/share/mcp-memory/db"
}
}
}
}The dev shell is managed by devenv (devenv.nix), not the
flake. The flake.nix exists only for packaging (nix build / nix run).
nix develop does not work — use devenv shell instead.
# Enter the dev shell (Rust stable, mold linker, rust-analyzer, pre-commit hooks)
devenv shell
# Or with direnv (add `use devenv` to .envrc)
direnv allow
# Build (debug)
cargo build
# Run with test config
OLLAMA_URL=http://localhost:11434 RUST_LOG=debug cargo run
# Check + lint
cargo clippy
cargo fmt --checksrc/
├── main.rs — entry point: parse config, init embedder + store, start stdio server
├── config.rs — Config struct, env var reading with defaults
├── server.rs — MemoryServer: all 6 MCP tools + ServerHandler impl
└── memory/
├── embedder.rs — OllamaEmbedder: calls Ollama /api/embed
├── store.rs — MemoryStore: LanceDB table (upsert, search, browse, delete, stats)
└── types.rs — MemoryType, MemoryEntry, ScoredMemory, MemoryStats
| Layer | Crate |
|---|---|
| MCP protocol | rmcp — official Rust MCP SDK |
| Embeddings | ollama-rs |
| Vector storage | lancedb — embedded, no server |
| Async | tokio |
The default model is nomic-embed-text (137 MB, 768-dim, runs fast on CPU or GPU). To use a different model, set EMBED_MODEL and EMBED_DIMS — the server validates dimensions on first embed call and errors early if they don't match.
Other good options available via Ollama:
| Model | Dims | Notes |
|---|---|---|
nomic-embed-text |
768 | Default. Fast, solid English quality. |
qwen3-embedding:0.6b |
1024 | Better quality, multilingual, 32K context. |
mxbai-embed-large |
1024 | Strong English quality, 512-token context limit. |
Note: If you switch models, existing vectors in the DB were embedded with the old model and similarity scores will be meaningless. Either wipe
MEMORY_DB_PATHor use a separate path per model.
LanceDB stores data as Apache Arrow columnar files in ~/.local/share/mcp-memory/db/memories.lance/. The directory is safe to back up, copy between machines, or inspect with any Arrow-compatible tool.
To wipe all memories:
rm -rf ~/.local/share/mcp-memory/db/The directory and table are recreated automatically on next startup.
{ "mcp": { "memory": { "type": "local", "command": ["/home/you/.cargo/bin/mcp-memory"], "enabled": true, "environment": { "OLLAMA_URL": "http://localhost:11434", "EMBED_MODEL": "nomic-embed-text", "EMBED_DIMS": "768", "MEMORY_DB_PATH": "/home/you/.local/share/mcp-memory/db", "MCP_MEMORY_SOURCE": "opencode", "RUST_LOG": "warn", }, }, }, }