Local-first, graph-linked persistent memory for AI coding agents (MCP server + CLI)
Project description
trailmem
Persistent, local-first graph memory for AI coding agents.
Trailmem gives agents durable cross-session memory without provider lock-in: a local SQLite knowledge graph, typed relationships, explicit knowledge evolution, and token-disciplined briefings. It is designed for multiple local agents—Claude, Kiro, Codex, OpenCode, Kilo, and Gemini—to share useful project knowledge without silently creating junk memories.
Quick start
Same commands on Windows, macOS, and Linux.
Install (recommended: uv — no Python needed)
trailmem is a command-line tool, so install it as one — this puts trailmem on your PATH in every terminal. The cleanest way is uv, a standalone binary that needs no pre-installed Python (it fetches one for you):
# 1. Install uv (standalone — does NOT require Python):
curl -LsSf https://astral.sh/uv/install.sh | sh # Linux / macOS
# Windows (PowerShell): powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# 2. Install trailmem (uv downloads a Python for it if you don't have one):
uv tool install trailmem
Already have Python and prefer pipx? pipx install trailmem then pipx ensurepath works the same way (pipx needs an existing Python).
Plain pip (only inside a virtualenv or CI)
pip install trailmem
pip install drops the trailmem command into the current Python's bin/Scripts folder, which is often not on your PATH — a global pip install --user or a system Python will leave you with zsh: command not found: trailmem (and on Debian/Ubuntu, a PEP 668 "externally-managed" error). Use uv/pipx above unless you're deliberately working inside an activated virtualenv. If you already ran pip install and hit command not found, either activate the venv you installed into or run it as python -m trailmem — or just switch to uv tool install trailmem.
Set up and register
trailmem setup # creates ~/.trailmem/, inits DB, downloads the default embedding model (~130 MB, one time)
trailmem doctor # health check
# Register the MCP server with your agent host(s):
trailmem integrate # detects installed agent hosts, asks before writing any config
trailmem integrate auto-detects nine hosts: Claude Code, Codex, Kiro, Kilo, OpenCode, Antigravity, Zed, Cursor, Windsurf. It shows what it found, asks once (y/N), backs up every config it touches (.bak-trailmem), and skips hosts that are already registered. Configs are auto-written only for hosts whose format is verified against the live binary — Claude Code (via its own claude mcp add), Codex, Kiro, Kilo, OpenCode, Antigravity. For the other detected hosts it prints the exact entry to paste instead of editing their config (hand-written entries have corrupted host configs before; a host is promoted to auto-write once its format is verified). It also never rewrites a config it can't parse losslessly (JSONC with comments gets the manual entry printed too). On Claude Code, Kilo, and OpenCode it installs a /tm-save slash command; on Codex a /prompts:trailmem-save prompt and a SessionStart hook (~/.codex/hooks.json — trust it via /hooks after restarting Codex); on Kiro a SessionStart hook (~/.kiro/hooks/ — user-level, one install covers every workspace); on Antigravity a deduped PreInvocation welcome hook (~/.gemini/config/hooks.json — injects the briefing once per conversation, restart agy after install). On hosts that read Agent Skills it installs a lazy-loaded trailmem usage skill so agents learn the tool semantics without reading source — user-level on Claude Code, Codex, Kilo, OpenCode; per-workspace on Antigravity (<workspace>/.agents/skills/, the only non-builtin skills dir agy reads — re-run integrate per workspace).
Windows note: the MCP server is registered as
python -P -u -m trailmem.mcp_server— never as a generated.exe. Windows Smart App Control silently blocks unsigned per-install launcher.exes (the kind pip/uv generate), which kills a host-spawned server with no error anywhere. If thetrailmemCLI itself is blocked by SAC, run it aspython -m trailmemfrom the environment it's installed into.
Saving a session before you exit
An agent that forgets to record memory (or a hard /exit) can drop a session's context — a host end-of-session hook can't help, because it runs after the agent is gone and never sees the conversation. Only the live agent, mid-session, can capture. trailmem gives it a portable trigger plus reminders.
Trigger a save — use whichever your client supports (they all end in the same instruction: extract this session's decisions/lessons/tasks and call trailmem_store):
| How | Works in | Invoke |
|---|---|---|
MCP prompt save_session (zero-config, portable) |
Any client that surfaces MCP prompts | Claude Code /mcp__trailmem__save_session · VS Code /mcp.trailmem.save_session · Cursor & Windsurf: slash/prompt list · Zed: text threads only |
/tm-save command (installed by integrate) |
Claude Code | /tm-save |
| Plain text (always works) | Every client — the trailmem_store tool is universal |
Type "save this session to trailmem" |
Clients with no prompt support (e.g. Codex, aider) use the plain-text path — nothing is lost, the tool is always available. If your agent supports custom slash commands, you can point one at the same instruction yourself; formats differ per host, so check that agent's command-file docs (and avoid the config landmines below).
Reminders so you remember to trigger it:
- Statusline —
trailmem statuslinereports successful creates and edits for the authoritative session ID from hook stdin or env. Without a real session ID it prints nothing. - Welcome tip — the briefing ends with a save reminder (shown by hosts that surface the session-start output, e.g. Codex, Kilo).
- Next-session flag — if the previous session stored nothing, the next welcome opens with a loud reminder.
Wiring an unlisted agent yourself? MCP config formats are not uniform, and a wrong guess can break the agent's launch. Known landmines: VS Code / Copilot uses the key
servers(notmcpServers); Continue and Goose use YAML (a JSON writer corrupts them); aider has no MCP support at all. Always follow the agent's own current docs. The one thing that works everywhere without any of this is the plain-text path above.
Prefer manual MCP registration? Each host has its own mechanism:
The server launch command everywhere is <python> -P -u -m trailmem.mcp_server, where <python> is the interpreter trailmem is installed into (print it: trailmem doctor shows the home; or python -c "import sys; print(sys.executable)" inside that environment). Add TRAILMEM_AGENT_TYPE=<host> to the entry's env so memories are attributed correctly.
| Host | Manual registration |
|---|---|
| Claude Code | claude mcp add trailmem -e TRAILMEM_AGENT_TYPE=claude -- <python> -P -u -m trailmem.mcp_server |
| Codex | add an [mcp_servers.trailmem] table to $CODEX_HOME/config.toml (default ~/.codex) |
| Kiro | add trailmem under mcpServers in ~/.kiro/settings/mcp.json |
| Kilo | add trailmem under mcp in ~/.config/kilo/kilo.jsonc as {"type":"local","command":["<python>","-u","-m","trailmem.mcp_server"]} (kilo 7.x format) |
| OpenCode | add trailmem under mcp in ~/.config/opencode/opencode.json |
| Antigravity | add trailmem under mcpServers in ~/.gemini/config/mcp_config.json (no agy mcp CLI — file edit only; restart agy) |
| Zed | add trailmem under context_servers in ~/.config/zed/settings.json |
| Cursor | add trailmem under mcpServers in ~/.cursor/mcp.json |
| Windsurf | add trailmem under mcpServers in ~/.codeium/windsurf/mcp_config.json |
Any other MCP agent
Trailmem works with any agent that speaks MCP — Cursor, Windsurf, Cline, Zed, Gemini CLI, or anything newer. trailmem integrate only automates the hosts above; for everything else, register it yourself. You need exactly three facts:
- Transport: stdio (no URL, no port, no HTTP).
- Command:
<python> -P -u -m trailmem.mcp_server— the interpreter trailmem is installed into, launched as a module. There is deliberately notrailmem-mcpexecutable: Windows Smart App Control silently blocks per-install unsigned launcher.exes, which killed host-spawned servers with no error.python -mneeds no launcher and works on every OS. - Identity: set
TRAILMEM_AGENT_TYPE=<lowercase-agent-slug>for attribution. If the host can expose a stable conversation ID to child processes, also setTRAILMEM_SESSION_ID=<real-id>.
Most agents use a JSON block shaped like this (key name varies — mcpServers, mcp, servers):
{
"mcpServers": {
"trailmem": {
"command": "/path/to/python",
"args": ["-P", "-u", "-m", "trailmem.mcp_server"],
"env": {
"TRAILMEM_AGENT_TYPE": "myagent",
"TRAILMEM_SESSION_ID": "the-hosts-real-session-id"
}
}
}
}
Print the right interpreter path from inside the environment trailmem is installed into:
python -c "import sys; print(sys.executable)"
TRAILMEM_SESSION_ID is optional. Without it, all six tools still work with
agent/project attribution, but welcome is stateless: no boundary, anti-bloat,
or zero-save claims. A host can instead pass the optional session_id MCP
argument on each call. Never invent a PID as a session ID.
Native host fields do not belong in TrailMem core. Each integration module in
trailmem/hosts/ owns detection, native session/project fields, hooks, and
config lifecycle, then emits one versioned session_context:
{
"schema_version": 1,
"agent_type": "myagent",
"session_id": "the-hosts-real-session-id",
"project": "/absolute/project",
"event": "tool-context",
"source": "myagent-adapter"
}
Host modules are auto-discovered, so adding a verified integration requires
one new trailmem/hosts/<host>.py file. Unknown MCP hosts still work through
the generic TRAILMEM_AGENT_TYPE / TRAILMEM_SESSION_ID contract; without a
real session ID they intentionally remain stateless.
Then restart the agent and check the wiring: the agent should see six trailmem_* tools, and calling trailmem_welcome should return a briefing. trailmem doctor verifies the database side.
Updating
trailmem update # checks PyPI, upgrades in place using however you installed it
trailmem update detects whether this copy was installed with uv / pipx / pip and runs the right upgrade command (uv-tool installs need uv tool install trailmem@latest --force — a bare uv tool upgrade is a no-op on a pinned tool, which trailmem update handles for you). Editable/dev installs are refused (upgrade via git). After upgrading, run trailmem integrate once to refresh host configs (it upgrades old entries in place — e.g. the pre-0.1.7 trailmem-mcp launch to the current python -m shape), then restart your agents so their MCP servers reload — a schema migration runs on first start of the new code, and a still-running old server must not keep writing.
Prefer to do it by hand:
uv tool install trailmem@latest --force # if installed with uv
pipx upgrade trailmem # if installed with pipx
pip install --upgrade trailmem # if installed with pip (inside the venv)
There is no in-app "update available" notice — trailmem sends no telemetry, by design. trailmem update only checks PyPI when you run it.
Uninstalling
trailmem uninstall # remove trailmem from agent configs — memories are KEPT
trailmem uninstall --purge # ALSO delete ~/.trailmem (every memory, irreversible)
trailmem uninstall surgically reverses everything integrate (this or any older release) wrote — the trailmem MCP entry in each host's config, the usage skills, /tm-save, the Codex prompt and SessionStart hook — and leaves the rest of every config untouched. Your memories at ~/.trailmem are kept by default: reinstalling trailmem later brings them all back automatically. Only --purge (with a typed confirmation) deletes them. At the end it prints the command to remove the package itself (uv tool uninstall trailmem / pipx uninstall trailmem / pip uninstall trailmem, matching how you installed).
The agent then gets six tools: trailmem_welcome (once-per-session briefing), trailmem_store, trailmem_query, trailmem_show, trailmem_edit, trailmem_link. Everything is also available to humans via the trailmem CLI (store, query, show, list, stats, link, archive, ...).
Try it from the CLI (note: content is positional; --agent user for your own notes):
trailmem store --title "First note" --type lesson --agent user "Something worth remembering."
trailmem query "what did I note earlier"
trailmem list
trailmem help # or: trailmem <command> --help
Why
- Local-first. One SQLite file (
~/.trailmem/trailmem.db), WAL mode, no cloud, no daemon. Embeddings run locally via ONNX (default: bge-small-en-v1.5, user-swappable withtrailmem model use). - A graph, not a list. Typed edges (
related,supersedes,evolves,contradicts,derived_from), orphan warnings at store time, supersede chains instead of destructive overwrites. - Token discipline. Context is injected exactly once per session (welcome, ~600–800 tokens). No per-turn injection, ever. Repeat welcomes return a short form.
- No junk memories. 4-band duplicate detection (exact hash reject → >0.92 block → 0.85–0.92 warn → accept), mandatory titles, hard-reject on unattributed stores, no auto-store lifecycle hooks.
- No telemetry. The server writes only what the user needs (e.g. a local
hooks.logdiagnostic); it never emits analytics — a deliberate anti-goal, not an oversight.
Status
v0.1.0 is live on PyPI. Core implemented and tested: schema, store/dedup, query/show, welcome, MCP server, CLI, hooks, model management, loopback dashboard, host integration. The design contract lives in docs/ — schema, welcome lifecycle, duplicate policy, evolution rules, CLI/MCP surfaces, hooks, seeding playbook, and the dashboard contract.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file trailmem-0.1.12.tar.gz.
File metadata
- Download URL: trailmem-0.1.12.tar.gz
- Upload date:
- Size: 162.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f4ed96733b6ea71138554106550a8b7452542ce257e702e9080dde41a914b03
|
|
| MD5 |
01358b0877cf65b037eb76e7958395eb
|
|
| BLAKE2b-256 |
ab5e0e8d7d1461a9e77fc965d231ae47716f8877f6bf86fba70af9e2e53bf054
|
File details
Details for the file trailmem-0.1.12-py3-none-any.whl.
File metadata
- Download URL: trailmem-0.1.12-py3-none-any.whl
- Upload date:
- Size: 108.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9994b7f8a22e11016ba501775e2115cfa258046dade9aecb361a84e7d9fe8914
|
|
| MD5 |
2f2e798305fbfcbc2e9fca27ff6f6b8f
|
|
| BLAKE2b-256 |
df1a9031ebcda3aabdcc2e24a59ca9023005cf24d475f850600e27e3b9bfc246
|