DEV Community

bayuh85
bayuh85

Posted on

atmo-mem: persistent memory for Claude Code, Cursor, and Cline in one Go binary

Repo: 👉 github.com/bayuh85/atmo-mem (MIT, pure Go, no CGO)

The problem

Every new coding-agent session starts with amnesia. You spent yesterday's session with Claude Code working out why the auth refresh was flaky, decided on an approach, and agreed on a few project conventions. Today you open a fresh session and the agent has forgotten all of it. Either you explain it again, or you keep a hand-maintained notes file and hope the agent reads it.

What I wanted was boring: a local place where the agent can write down decisions and fixes as it works, and look them up at the start of the next session. No hosted service, no Docker, no Python environment. Just one binary.

What atmo-mem does

atmo-mem is persistent cross-session memory for coding agents, shipped as one static binary.

  • It speaks the Model Context Protocol over stdio, so Claude Code, Cursor, Cline, and other MCP clients can use it.
  • The agent saves decisions, bugfixes, and facts while it works.
  • A later session searches and recalls them from a local SQLite database with an FTS5 full-text index.
  • At the end of a session, atmo-mem can compress the notes into a summary with any OpenAI-compatible model (default: zai/glm-5.3).

Privacy-wise, the important part is: save, search, and recall stay on disk. Only session_summarize calls the network.

SQLite is compiled in through modernc.org/sqlite (pure Go, FTS5 included), so you don't need CGO or a system SQLite library.

Quickstart

You need a Go toolchain. Clone and build:

git clone https://github.com/bayuh85/atmo-mem.git
cd atmo-mem
CGO_ENABLED=0 go build -o atmo-mem ./cmd/atmo-mem
Enter fullscreen mode Exit fullscreen mode

Move the atmo-mem binary somewhere on your PATH, then check it:

atmo-mem version
Enter fullscreen mode Exit fullscreen mode

The README also describes a go install command and GoReleaser archives for Linux, macOS, and Windows (amd64 and arm64) for tagged releases. When I wrote this the repo had no published releases yet, so building from a checkout is the reliable route.

If you want end-of-session summaries, configure an API key:

cp .env.example .env
# edit ATMO_MEM_API_KEY
Enter fullscreen mode Exit fullscreen mode

You can try it without an agent first. The CLI uses the same database:

atmo-mem save --project my-app --kind decision \
  --title "Use FTS5" \
  --content "Memory search is SQLite FTS5." \
  --tags sqlite,search

atmo-mem search --project my-app fts
atmo-mem recall --project my-app memory search
atmo-mem summarize --project my-app \
  --notes "We stored memories in SQLite and exposed them over MCP."
Enter fullscreen mode Exit fullscreen mode

Flags go before positional arguments. Add --json to get JSON output from save, search, recall, list, get, and summarize. atmo-mem help lists every command.

Setting it up with your agent

Running atmo-mem with no arguments (or atmo-mem mcp) reads JSON-RPC from stdin and writes JSON-RPC to stdout, with logs on stderr. In an interactive terminal it prints help instead; when stdin is a pipe, which is how editors launch it, it starts the MCP server. Don't wrap it in a script that prints a banner, because that breaks the protocol.

Two tips from the README:

  • Set ATMO_MEM_PROJECT to the repo name so memories from different projects don't mix.
  • Pass ATMO_MEM_API_KEY only if you want end-of-session summaries.

Claude Code

Project .mcp.json:

{
  "mcpServers": {
    "atmo-mem": {
      "command": "atmo-mem",
      "args": [],
      "env": {
        "ATMO_MEM_API_KEY": "your-key",
        "ATMO_MEM_PROJECT": "my-app"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Or with the CLI (flags vary slightly across Claude Code versions; the JSON above is the stable form):

claude mcp add --transport stdio --scope project \
  --env ATMO_MEM_API_KEY=your-key \
  --env ATMO_MEM_PROJECT=my-app \
  atmo-mem -- atmo-mem
Enter fullscreen mode Exit fullscreen mode

Cursor

Put the same block in .cursor/mcp.json in your project (or ~/.cursor/mcp.json for a global config), then reload MCP servers from Cursor settings. atmo-mem has to be on the PATH Cursor sees, or you can set command to the absolute path.

Cline

Open MCP Servers → Configure (cline_mcp_settings.json) and add:

{
  "mcpServers": {
    "atmo-mem": {
      "command": "atmo-mem",
      "args": [],
      "env": {
        "ATMO_MEM_API_KEY": "your-key",
        "ATMO_MEM_PROJECT": "my-app"
      },
      "disabled": false,
      "autoApprove": ["memory_recall", "memory_search", "memory_list", "memory_get"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The auto-approve list here is a good default: read-only tools run without prompting, while memory_delete (destructive) and session_summarize (a billed network call) still ask for approval.

Teach the agent the workflow

The tools only help if the agent calls them. The README suggests this loop, which works well in CLAUDE.md, Cursor rules, or Cline custom instructions:

  1. At the start of a task, call memory_recall with the task and the project.
  2. When a decision, fix, or preference lands, call memory_save.
  3. At the end of the session, call session_summarize with notes, or with a session_id you used along the way.

The tools

Tool What it does
memory_save Store title, content, optional kind, tags, project, session_id.
memory_search AND keyword search. Words of 3+ characters are prefix-matched (auth finds authentication).
memory_recall Rank memories for a task. An empty query returns recent project memory. The newest summary is pinned first.
memory_list Recent memories, newest first.
memory_get One memory by id.
memory_delete Permanently delete one memory by id.
session_summarize Compress a transcript and that session's memories with the LLM, then store a summary.

Memory kinds: observation (default), decision, bugfix, preference, fact, summary, task.

How it works

Search vs. recall

The two lookups deliberately behave differently:

  • Search is strict. A memory is returned only when every query word matches. Results are ordered by FTS5 bm25, with the title weighted above the body and tags.
  • Recall is wide. Any word can match, and a small recency bonus breaks near ties. The latest summary for the project goes to the front even if it shares no words with the query. A much stronger text match still outranks a memory that's only newer.

Common English filler words (the, and, for, …) are dropped when the query has other words. Matching is case-insensitive, words shorter than two characters are ignored, and two-character words such as go match exactly.

Storage

Everything lives in one SQLite file, by default ~/.atmo-mem/atmo-mem.db. The directory is created with mode 0700, and the file is set to 0600 where the OS allows it. Triggers keep an FTS5 index (unicode61 tokenizer) in sync on insert and delete.

There's no update call: to change a memory, delete the old id and save a new one. Sessions are just bookkeeping rows. What the next session actually reads is a normal memory with kind summary.

Summarization

session_summarize sends the transcript or notes, plus memories saved under that session_id, to POST {ATMO_MEM_BASE_URL}/chat/completions with a standard body (model, messages, temperature, max_tokens) and stores the result as a summary memory. Because recall pins the newest summary, the next session starts with it.

Configuration

Settings are read from flags first, then environment variables, then a .env file in the working directory; the first non-empty value wins. ATMO_MEM_* takes precedence over the OPENAI_* fallbacks.

Variable Default Purpose
ATMO_MEM_API_KEY empty Bearer token for summarization (falls back to OPENAI_API_KEY).
ATMO_MEM_BASE_URL https://atmorouter.dev/v1 OpenAI-compatible base URL, including /v1 (falls back to OPENAI_BASE_URL).
ATMO_MEM_MODEL zai/glm-5.3 Model id (falls back to OPENAI_MODEL).
ATMO_MEM_DB ~/.atmo-mem/atmo-mem.db SQLite file.
ATMO_MEM_PROJECT empty Project used when a call omits project.
ATMO_MEM_TIMEOUT 60s Summarization timeout.

Credit

atmo-mem is inspired by claude-mem and engram, two projects that have explored persistent agent memory further than this one. atmo-mem is not affiliated with either. Its angle is narrow: one dependency-free binary, local SQLite + FTS5, and an optional summary step that works with any OpenAI-compatible endpoint. If you want something richer, look at those projects too.

About AtmoRouter: a cheap AI API gateway and OpenRouter alternative

Disclosure: by default, session_summarize sends requests to AtmoRouter using zai/glm-5.3; summaries don't need the model you code with. Any OpenAI-compatible provider works. AtmoRouter is a cheap AI API gateway that speaks both the OpenAI Chat Completions API and the Anthropic Messages API, so one key reaches 100+ models from Cursor, Cline, Claude Code, or any compatible client. It is pay-per-token with no subscription, accepts QRIS and USDC, and prices sit well below official rates: AtmoRouter advertises an average of 83.7% below official prices, which makes it an affordable LLM API and a practical OpenRouter alternative for agent workloads.

Try it

If you're tired of re-explaining your project to a fresh agent session every morning:

👉 github.com/bayuh85/atmo-mem

Issues and PRs are welcome. Real-world reports on how recall ranking holds up across a long-running project would be especially useful.

Top comments (0)