Skip to main content

Local-first, graph-linked persistent memory for AI coding agents (MCP server + CLI)

Project description

trailmem

PyPI Python License: MIT

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. 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 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 hosts that read Agent Skills (Claude Code, Codex, Kilo, OpenCode) it installs a lazy-loaded trailmem usage skill so agents learn the tool semantics without reading source.

Windows note: the MCP server is registered as python -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 the trailmem CLI itself is blocked by SAC, run it as python -m trailmem from 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:

  • Statuslinetrailmem statusline reports 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 (not mcpServers); 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> -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> -u -m trailmem.mcp_server
Codex add an [mcp_servers.trailmem] table to ~/.codex/config.toml
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
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:

  1. Transport: stdio (no URL, no port, no HTTP).
  2. Command: <python> -u -m trailmem.mcp_server — the interpreter trailmem is installed into, launched as a module. There is deliberately no trailmem-mcp executable: Windows Smart App Control silently blocks per-install unsigned launcher .exes, which killed host-spawned servers with no error. python -m needs no launcher and works on every OS.
  3. Identity: set TRAILMEM_AGENT_TYPE=<lowercase-agent-slug> for attribution. If the host can expose a stable conversation ID to child processes, also set TRAILMEM_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": ["-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 with trailmem 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.log diagnostic); 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trailmem-0.1.8.tar.gz (145.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trailmem-0.1.8-py3-none-any.whl (99.2 kB view details)

Uploaded Python 3

File details

Details for the file trailmem-0.1.8.tar.gz.

File metadata

  • Download URL: trailmem-0.1.8.tar.gz
  • Upload date:
  • Size: 145.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for trailmem-0.1.8.tar.gz
Algorithm Hash digest
SHA256 cb4272fc5e665e6709475b561f4583d00c089abf11183279c67d73cee1312e87
MD5 80ffa27a6fec9124024e666380b4f6b8
BLAKE2b-256 8a007048eecca0e5db5672138a2966ec703182a411a19c863fcea4b7c4eda080

See more details on using hashes here.

File details

Details for the file trailmem-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: trailmem-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 99.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for trailmem-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 da903b0341090fdcd57f6051775cfbbf064ca9670865b5be9ac18e45ffca495b
MD5 a6a087d5c3ab815d2bb74a2c34b47713
BLAKE2b-256 9cfe61be665c8850fbea5df1231f98754471064d4ab996dbddcdeb44aacb345a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page