chatmem

by sid077

Not rated
GitHub

About

Local LLM chat history served over MCP. Embedded Postgres + pgvector, cross-tool memory, everything stays on your machine.

Details

Author
sid077
Categories
AI

Setup

Install chatmem in your MCP client (Claude Desktop, Cursor, Windsurf, and others).

Repository: https://github.com/sid077/chatmem

Follow the installation instructions in the repository README, then restart your MCP client.

chatmem telemetry {enable|disable|status}

Manage the anonymous telemetry setting.statusprints the effective state, the precedence source (env|config|default), and the install id.

chatmemcan send anonymous usage pings to help track adoption.Message content, query strings, and filenames are never sent, ever.Whatissent when enabled:

- install UUID(generated locally on first run, in<data>/install_id)
- chatmemversion
- countersfor the flush window (default 5 minutes): captures, searches, gets, errors
- model + client distributions— e.g.{"claude-opus-4-7": 12, "gpt-5": 4},{"windsurf": 8, "cursor": 8}
- latency percentilesper operation — p50/p95/p99 in ms

The client operates in three modes depending on config:
- CHATMEM_TELEMETRY=0(alsofalse,off) — hard off
- <data>/telemetry.json({"enabled": true|false}) — persistent choice
- chatmem telemetry {enable|disable}— writes the above
- Default:enabled

chatmem initprompts on first run (interactive TTY only) and writes the config. Non-TTY inits print a notice and leave the default; opt out non-interactively withchatmem telemetry disableor the env var.

chatmem telemetry status # current state + source + ingest URL chatmem telemetry enable # persist enabled = true chatmem telemetry disable # persist enabled = false chatmem telemetry dump # list <data>/pending/*.json (unshipped pings)

The client posts to any endpoint that speaksPOST /v1/pingwith the payload documented ininternal/telemetry/client.go:Payload. A ready-to-deploy Cloudflare Worker + D1 lives in](#roadmap)server/telemetry-worker/— onewrangler deployand you have an endpoint. Seethat READMEfor the 5-command setup.

LLM client (Claude Code, Cursor, aider, custom) │ ▼ MCP over stdio chatmem mcp │ ▼ embedded Postgres 18 + pgvector 0.8.5 │ ▼ chunks (with vector(384) column, HNSW cosine index) messages (btree on conv_id + created_at) conversations (append-only event log ready for future sync)

- Language: Go 1.26, single static binary (CGO off).
- Storage:fergusstrange/embedded-postgresv1.34 driving Postgres 18.3 in a per-user data dir. Cold start ~600 ms on M-series (~7 s on the very first run because ofinitdb).
- Vector column:vector(384)on thechunkstable with an HNSW cosine index (m=16, ef_construction=64), primed for semantic search. Values are zero-vectors until the ONNX embedder lands.
- Search (MVP): Postgres full-text —to_tsvector('english', content)+plainto_tsquery, GIN index (chunks_tsv_idx). Ranked withts_rank_cd.
- MCP: official
modelcontextprotocol/go-sdkv1.6.1. Stdio transport.
- CLI:spf13/cobrav1.10.

The pgvector.dylibfordarwin_arm64is committed underinternal/pg/assets/darwin_arm64/and shipped inside the Go binary via//go:embed. On firstStart(),internal/pgcopies the dylib into<runtimeDir>/lib/postgresql/and the control/SQL files into<runtimeDir>/share/postgresql/extension/— thenCREATE EXTENSION vectorworks out of the box.

chatmem/ ├── cmd/chatmem/ # cobra CLI entrypoint + subcommands │ ├── main.go │ ├── init.go # chatmem init │ ├── daemon.go # chatmem daemon (+ dataHome / cacheHome helpers) │ ├── mcp.go # chatmem mcp (stdio MCP server) │ ├── telemetry.go # chatmem telemetry {enable,disable,status} │ └── mcp_e2e_test.go # spawns built binary, drives stdio MCP as a real client ├── internal/ │ ├── pg/ # embedded-postgres wrapper + pgvector install │ │ ├── embedded.go │ │ └── assets/ │ │ ├── darwin_arm64/vector.dylib + extension/{vector.control,vector--0.8.5.sql} │ │ ├── linux_amd64/vector.so + extension/{vector.control,vector--0.8.3.sql} │ │ └── linux_arm64/vector.so + extension/{vector.control,vector--0.8.3.sql} │ ├── telemetry/ │ │ ├── telemetry.go # State/Config, install_id, opt-out precedence │ │ ├── aggregator.go # Thread-safe counters + latency reservoir + percentiles │ │ └── client.go # Flush loop, HTTP POST with retry, local pending dir │ ├── store/ # schema + pgx-backed data access │ │ ├── schema.sql │ │ ├── store.go # EnsureSchema, RecordMessage, SearchHistory, GetConversation │ │ └── store_test.go │ ├── mcp/ # MCP tool registration │ │ ├── server.go # NewServer, register{RecordMessage,SearchHistory,GetConversation} │ │ └── server_test.go # in-process MCP round-trip │ └── telemetry/ # install_id + opt-out gate │ └── telemetry.go ├── server/telemetry-worker/ # Cloudflare Worker + D1 for the telemetry ingest ├── docs/ │ └── marketplace-submissions.md # Playbook: awesome-mcp / Smithery / PulseMCP / Glama ├── smithery.yaml # Smithery registry config (stdio start command) ├── .goreleaser.yaml # cross-platform build + Homebrew tap + rpm/deb via nfpm ├── .github/workflows/release.yml # tag push → goreleaser + gh-pages RPM repo publish ├── scripts/build-rpm-repo.sh # assemble zypper/dnf repo tree locally (uses createrepo_c via docker) ├── README.md ├── CLAUDE.md # in-repo dev docs, auto-loaded by Claude Code └── LICENSE # Apache-2.0

RequiresGo 1.26+and, for now, Homebrew's pgvector 0.8.5 (only if you want to refresh the vendored dylib — the committed copy is enough to build).

git clone https://github.com/sid077/chatmem cd chatmem go build ./...

To refresh the vendored pgvector artifacts:

# darwin (from Homebrew) — refreshes internal/pg/assets/darwin_arm64/ brew install pgvector cp /opt/homebrew/Cellar/pgvector/0.8.5/lib/postgresql@18/vector.dylib \ internal/pg/assets/darwin_arm64/vector.dylib cp /opt/homebrew/Cellar/pgvector/0.8.5/share/postgresql@18/extension/{vector.control,vector--0.8.5.sql} \ internal/pg/assets/darwin_arm64/extension/ # linux amd64/arm64 (from official PostgreSQL apt, pgdg11+1 for glibc 2.31 baseline) for arch in amd64 arm64; do curl -sSLo /tmp/pgv-$arch.deb \ "https://apt.postgresql.org/pub/repos/apt/pool/main/p/pgvector/postgresql-18-pgvector_0.8.3-1.pgdg11+1_${arch}.deb" tmp=$(mktemp -d); cd "$tmp"; ar x /tmp/pgv-$arch.deb; tar -xf data.tar.xz cp "$tmp/usr/lib/postgresql/18/lib/vector.so" internal/pg/assets/linux_${arch}/vector.so cp "$tmp/usr/share/postgresql/18/extension/"{vector.control,vector--0.8.3.sql} \ internal/pg/assets/linux_${arch}/extension/ done

The Linux.sois built against Debian 11's glibc 2.31 for maximum runtime compatibility — anything with glibc ≥ 2.31 works (Debian 11+, Ubuntu 22.04+, RHEL 9+, Alpine withlibc6-compat, etc.).

Every test that touches storage spins up a real embedded Postgres — expect ~7–10 s per test package on cold cache.

# unit + integration go test ./... -count=1 -timeout=240s # just the storage layer round-trip go test ./internal/store -count=1 -v # just the in-process MCP round-trip go test ./internal/mcp -count=1 -v # end-to-end: spawn the built binary as a subprocess, drive stdio MCP go test ./cmd/chatmem -run TestBinaryStdioMCP -count=1 -v

Tests use distinct hard-coded ports (54334,54335,54336) — run one test package at a time if you have a real chatmem daemon running on54329.
- Edit— code lives undercmd/chatmemandinternal/.
- Testgo test ./...after every change; add a test alongside any new store/MCP behavior.
- Update the docs on every functional change:

- README.mdfor user-facing changes (new tool, new command, changed defaults).
- CLAUDE.mdfor developer-facing changes (new package, new invariant, new gotcha).
- ~/.claude/skills/chatmem/SKILL.mdfor cross-session context (kept in sync automatically).

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.