Skip to content

Repository files navigation

🧠 memd

Universal local memory for every LLM tool you use.

One always-on daemon that turns a local Meilisearch into a persistent, time-aware, semantically-searchable memory layer — shared across Claude Code, Codex, Cursor, and anything else that speaks MCP.

CI Release License: MIT Rust


The problem: every LLM tool keeps its own siloed, forgetful memory. A decision you explained to Claude is invisible to Codex tomorrow. Context gets rebuilt from scratch in every session.

memd fixes it: save a memory in one tool, recall it in every other — and in every future session. Local-first, private, and embeddings run on your machine.

   Claude Code  ┐
   Codex        ├──MCP──▶  memd daemon ──▶  local Meilisearch  (your memories,
   Cursor  …    ┘          (always-on)       embedded on-device)

North star: open a new chat in any tool → it already knows the relevant context about your machine, projects, and preferences, because they all share memd.


🚀 Install in 30 seconds

curl -fsSL https://raw.githubusercontent.com/qdequele/memd/main/scripts/install.sh | bash

This downloads the prebuilt binary to ~/.local/bin/memd and runs memd setup, which does everything: starts the daemon, registers memd with your installed agents, and wires up memory injection. That's it — open a new agent session and it has memory.

Build from source (requires Rust)
git clone https://github.com/qdequele/memd && cd memd
./install.sh        # builds, then runs `memd setup`

What memd setup does (so you don't have to)

Step What happens
📦 Binary Copied to a stable path (~/.local/bin/memd)
🔍 Meilisearch Pinned engine downloaded + started as a managed service (no Docker)
🔌 Agents Interactive picker — choose from detected agents (Claude Code, Codex, Gemini CLI, Cursor, Windsurf, Cline, Zed); selected ones get memd's MCP server registered
📝 Directives Usage block written into agent instruction files (Claude Code, Codex, Gemini CLI)
🪝 Hooks SessionStart (ensure daemon + auto-recall) + Stop (auto-capture) wired into Claude Code
🎯 Skills /memd-doctor (diagnose & repair) + /memd-memory (recall/save playbook) installed for Claude Code

It's idempotent — re-run any time (e.g. after an upgrade) to reconverge.


✨ What you get

  • 🔁 Shared across tools — one MCP server every MCP-capable client can mount.
  • 🧲 Semantic recall — hybrid keyword + vector search; embeddings computed locally inside Meilisearch (nothing leaves your machine).
  • ⏱️ Time-aware & typed — every memory has timestamps, a type, tags, and a scope.
  • 🐝 Passive ingestion — a crawler indexes the knowledge already on disk (README*, CLAUDE.md/AGENTS.md, memory files) and keeps it in sync.
  • 🪶 Token-safe by design — search returns lightweight snippets, not blobs; fetch the full text only when you ask.
  • 🔒 Local-first — bound to 127.0.0.1, local-only key, no cloud by default.
  • 🔄 Self-updating — a daily check keeps memd and its Meilisearch engine current; engine upgrades migrate via dump/import with automatic rollback.

🎬 Quickstart

memd status                                   # health, index stats, last crawl
memd add "We chose Postgres for billing" --type decision --tags billing
memd search "what database for billing"       # hybrid keyword + semantic
memd doctor --fix                             # diagnose & repair
memd down                                     # stop the daemon

First run downloads the pinned Meilisearch binary; the embedding model is fetched once, by Meilisearch, on your first memory.


🔌 Using memd from your agents

memd setup shows an interactive checklist of the agents detected on your machine (Claude Code, Codex, Gemini CLI, Cursor, Windsurf, Cline, Zed) and wires the selected ones to its MCP server — un-checking a connected agent removes memd from it. Run memd status to see each agent's wiring state. Any other MCP-capable client can connect to the same daemon two ways:

  • HTTP (always-on, preferred): point the client at http://127.0.0.1:7702/mcp.
  • stdio bridge: { "command": "memd", "args": ["mcp", "--stdio"] }

MCP tools

Tool Purpose
save_memory(content, type?, tags?, scope?, title?) Persist a memory → { id }
get_memory(query, …, crop_length?, facets?) Hybrid recall → lightweight rows with a query-aware snippet
read_memory(id) Fetch the full content for one memory
update_memory(id, …) · forget_memory(id) Edit / delete
list_memories(type?, scope?, …) Browse (metadata-only by default)
stats(group_by?) Counts + server-side facet distributions

The recall funnel keeps results token-safe: get_memory/list_memories return metadata + a cropped, highlighted snippet → read_memory(id) returns the full blob on demand. Projection, cropping, highlighting, and grouped counts all run server-side in Meilisearch.


🎯 Making memd the default memory

Models can't be forced to call a tool, but memd biases them at three layers, weakest → strongest (all set up by memd setup):

  1. In-protocol (universal): memd's MCP initialize advertises usage instructions that compliant clients inject into the model's system prompt.
  2. Instruction files (cross-tool): memd directives install writes a managed block into ~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, …
  3. Harness hooks (deterministic): a Claude Code SessionStart hook injects relevant memories into every session; a Stop hook auto-captures turns that signal durable intent ("remember…", "we decided…").

🛠️ CLI reference

memd setup [--no-hooks]     One-command install / reconverge
memd up | down              Start / stop the daemon
memd status                 Daemon + Meilisearch health, index stats, last crawl
memd logs [-f]              Tail daemon logs
memd doctor [--fix]         Diagnose (and repair) common issues

memd add "<text>" [--type --tags --scope --title]
memd add --file <path> --note "<desc>"        Annotate an important file
memd search "<query>" [--type --since --semantic-ratio --limit]
memd forget <id>

memd crawl run|status|config                  Passive ingestion
memd context [--scope --query --limit]        Print memories as markdown (for hooks)
memd capture                                  Auto-capture a turn (Stop-hook stdin)
memd directives install|uninstall            Inject usage directives into agent files
memd mcp --stdio                              stdio bridge for stdio-only clients
memd service install|uninstall               launchd integration (macOS)

--since accepts unix seconds or a relative span (30d, 12h, 45m, 90s).


⚙️ Configuration

~/.config/memd/config.toml is created with sane defaults on first run.

[meilisearch]
version = "v1.45.1"      # pinned engine, downloaded + managed by memd
host = "127.0.0.1"
port = 7701
master_key = ""         # auto-generated, local-only

[mcp]
host = "127.0.0.1"
port = 7702              # always-on HTTP MCP endpoint

[embedder]
source = "huggingFace"   # runs locally inside Meilisearch
model = "BAAI/bge-small-en-v1.5"
default_semantic_ratio = 0.5

[crawler]
roots = ["~/Projects"]
exclude_dirs = ["node_modules", "target", ".git", "dist", "build", ".next", ""]
max_file_bytes = 1000000
deny_globs = ["**/.env", "**/*.pem", "**/*.key", ""]   # secrets hygiene
reconcile_secs = 3600

🗂️ Data model

Each memory is one Meilisearch document in the memories index:

id, content, title?, summary?, type, tags[], scope, source, source_path?, source_client?, created_at, updated_at, last_accessed_at?, content_hash.

Types: fact, preference, decision, task, project_overview, agent_instruction, file_annotation, code_note, reference.


🔒 Privacy

Local-first by design. Meilisearch is bound to 127.0.0.1 with a local-only key, and embeddings are computed on-device. Nothing leaves your machine unless you explicitly switch to a remote embedder. The crawler skips excluded dirs and denylisted files (.env, keys) so secrets are never indexed.


🧑‍💻 Development

cargo build          # build
cargo test           # unit tests
cargo clippy         # lints
cargo fmt            # format

The codebase is a single binary with clear modules: meili/ (engine manager + client), memory/ (model, classification, service), mcp/ (JSON-RPC server), crawler/, daemon, launchd, cli.

Releases are built by .github/workflows/release.yml for macOS (arm64/x64) and Linux (x64/arm64) on every v* tag.


License

MIT © Quentin de Quelen

About

Persistent, long-term, shareable memory for your workflows, agents, and personal use.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages