chatmem MCP server
Commit 5. Nov. 2026Erfasst und speichert den lokalen LLM-Chatverlauf in einer eingebetteten Postgres-Datenbank, um relevanten Kontext aus der Vergangenheit via MCP an AI-Clients bereitzustellen.
Auch bekannt als sid077/chatmem
- Stars
- 2
- Tools
- 15
- Downloads, 30 Tage
- —
- Neueste Version
- —
- Letzter Commit
- 5. Nov. 2026
- Lizenz
- Apache-2.0
Was ist chatmem MCP?
chatmem ist ein MCP server. Erfasst und speichert den lokalen LLM-Chatverlauf in einer eingebetteten Postgres-Datenbank, um relevanten Kontext aus der Vergangenheit via MCP an AI-Clients bereitzustellen. Er stellt 15 Tools bereit, etwa conversation_id, role, content.
Zusammenfassung generiert aus dem README des Projekts
Über chatmem MCP server
chatmem ermöglicht es Entwicklern, die Tools wie Claude Code, Cursor oder aider nutzen, den lokalen LLM-Chatverlauf in einer eingebetteten Postgres-Datenbank zu erfassen und zu speichern. Dieses Single-Binary-Utility fungiert als lokale Memory-Layer und nutzt pgvector, um Konversationen auf deinem Rechner zu speichern, sodass AI-Clients bei Bedarf relevanten Kontext aus der Vergangenheit abrufen können. Es arbeitet als eigenständiger stdio MCP server, der seine eigenen Datenbankprozesse verwaltet.
Nutzer können den Lebenszyklus ihres Chatverlaufs über verschiedene Commands steuern, einschließlich der Initialisierung der Datenbank, des Imports bestehender JSONL-Transkripte oder der Installation eines Hintergrund-Daemons via systemd oder launchd, um einen sofortigen Start zu gewährleisten. Das System priorisiert Privatsphäre, indem es sicherstellt, dass der Nachrichteninhalt die lokale Maschine nie verlässt und nur anonyme Telemetrie gesendet wird. Für eine bessere Organisation enthält es eine Notion-Integration, die Konversationen automatisch in strukturierte Lern- oder Debugging-Seiten mit Mermaid-Diagrammen und faktenbasierten Zusammenfassungen umwandeln kann.
Was du damit machen kannst
- 1Vergangenen Kontext aus Konversationen abrufenBitte deinen AI-Client, mithilfe der Volltextsuche spezifische Informationen aus früheren Chat-Sessions zu finden.
- 2Historische Chat-Daten nachpflegenLade bestehende Chat-Transkripte aus JSON-Dateien in deine lokale Datenbank, um eine vollständige Historie aufzubauen.
- 3Erstellung von Lernnotizen automatisierenTransformiere lange technische Diskussionen in organisierte Notion-Seiten, die Zusammenfassungen und Diagramme enthalten.
- 4Einen dauerhaft aktiven Datenbank-Service bereitstellenRichte einen Hintergrund-Service ein, damit dein Chatverlauf sofort verfügbar ist, wenn du einen MCP-Client startest.
Geschrieben aus dem README des Projekts. Jeder Use Case wurde gegen ein Zitat daraus geprüft.
chatmem MCP tools 15
Erkannt aus dem READMEconversation_idrolecontentmodelproviderclient_idtoken_countquerytop_ktoken_budgetsinceuntil
| conversation_id | — | |
| role | — | |
| content | — | |
| model | — | |
| provider | — | |
| client_id | — | |
| token_count | — | |
| query | — | |
| top_k | — | |
| token_budget | — | |
| since | — | |
| until | — |
Alle 15 Tools anzeigenWeniger Tools anzeigen
conversation_idslimitafter
| conversation_ids | — | |
| limit | — | |
| after | — |
Automatisierte Checks
Geluften am 14. Nov. 2026Vollständiger Bericht →Noch nicht verifiziert. Ein Server benötigt einen Score von 80, keine kritischen Befunde und genug Daten für unsere Prüfung. Wie die Checks funktionieren · Was du für mehr Punkte verbessern kannst
- Source Repository verknüpft und erreichbarsid077/chatmem is public and reachablegithub.com/sid077/chatmem ↗Bestanden
- Paket nennt dieses Repository als QuelleNicht geprüft
- Build-Provenienz (Sigstore-Attestierung)Nicht geprüft
- Open Source Lizenz (OSI)Apache-2.0spdx.org/licenses/Apache-2.0.html ↗Bestanden
- Commit oder Release im letzten JahrLast commit or release 2026-11-05github.com/sid077/chatmem ↗Bestanden
- OpenSSF ScorecardNicht geprüft
- Package-Name ähnelt keinem populären PackageNicht geprüft
- Keine kritischen Advisories im Paket oder seinen AbhängigkeitenNicht geprüft
- Keine versteckten Anweisungen an das Modell15 tool descriptions and the README read; no override phrases, credential paths or hidden charactersBestanden
- Destruktive Tools sind markiertNo tool named like delete, drop or execBestanden
- Remote-Endpoint beantwortet den MCP-HandshakeNicht geprüft
README
Auf GitHub ansehenchatmem
Local LLM chat history, served over MCP.
chatmem is a single-binary utility that captures your LLM conversations via Model Context Protocol tools any client can call (Claude Code, Cursor, aider, custom SDKs), stores everything in an embedded Postgres 18 database with pgvector on your machine, and serves relevant past context back to any LLM on demand.
Only anonymous telemetry (install ID, aggregate counters, opt-in crash reports) leaves your machine — message content never does.
Table of contents
- Status
- Quickstart
- MCP tools reference
- Commands
- Data & config paths
- Telemetry
- Architecture
- Repository layout
- Building from source
- Testing
- Development workflow
- Troubleshooting
- Uninstall
- Roadmap
- License
Status
Pre-alpha (v0.0.1-dev). Fully working platforms:
| Platform | pgvector | Distribution | Verified |
|---|---|---|---|
darwin/arm64 |
0.8.5 | Homebrew tap | Live use |
linux/amd64 |
0.8.3 | RPM (zypper/dnf) + DEB + tarball | Debian 12 container + openSUSE Leap 15.6 container |
linux/arm64 |
0.8.3 | RPM (zypper/dnf) + DEB + tarball | Debian 12 container + openSUSE Leap 15.6 container |
darwin/amd64 |
— | — | Needs an Intel-Mac pgvector.dylib |
windows/amd64 |
— | — | Needs a pgvector.dll + service story |
The version skew between macOS and Linux is intentional (Homebrew ships 0.8.5, official Postgres apt ships 0.8.3 for PG18). Both are wire-compatible for our use — same operators, same HNSW support.
Working today:
| Command | Purpose |
|---|---|
chatmem init |
Provision the local database, apply schema, print MCP client config. |
chatmem mcp |
Self-contained stdio MCP server (starts + manages embedded Postgres). |
chatmem daemon |
Long-lived Postgres process. Normally not invoked directly — chatmem install wraps this in a launchd/systemd service so it starts at login and stays warm. |
chatmem install / uninstall |
Install / remove the user-level background service (launchd on macOS, systemd --user on Linux) that runs chatmem daemon. Keeps embedded Postgres warm so MCP clients get instant startup instead of the 6-8s cold boot. |
chatmem start / stop / restart |
Start / stop / restart the background service. |
chatmem status |
Show whether the service is installed + whether PG is listening. |
chatmem doctor |
Print a self-diagnostic report — HOME, EUID, data/cache paths, port availability, telemetry state, ingest reachability, Notion status. Run this first if anything's weird. |
chatmem telemetry {enable,disable,status,dump} |
Manage anonymous telemetry; honors CHATMEM_TELEMETRY=0. |
chatmem notion {connect,status,disconnect,list,resync,sample} |
Manage Notion integration for auto-synthesizing conversations into study/debug pages. See Notion synthesis below. |
chatmem import |
Bulk-load an existing chat transcript (JSONL or JSON array) into chatmem. Great for backfilling conversations that happened outside a chatmem-connected client. See Import an existing chat below. |
Quickstart
# --- macOS or Linuxbrew (Homebrew tap — ships as a cask) ---
brew tap sid077/chatmem
brew install --cask chatmem
# --- openSUSE / SUSE (zypper self-hosted repo) ---
sudo zypper ar https://sid077.github.io/chatmem/chatmem.repo
sudo zypper --gpg-auto-import-keys refresh
sudo zypper in chatmem
# --- Fedora / RHEL (dnf, same repo) ---
sudo dnf config-manager --add-repo https://sid077.github.io/chatmem/chatmem.repo
sudo dnf install chatmem
# --- Debian / Ubuntu (direct .deb download; APT repo TBD) ---
# curl -sSLo /tmp/chatmem.deb https://github.com/sid077/chatmem/releases/latest/download/chatmem_<ver>_<arch>.deb
# sudo apt install /tmp/chatmem.deb
# --- direct download (any Linux) ---
# curl -sSL https://github.com/sid077/chatmem/releases/latest/download/chatmem_Linux_x86_64.tar.gz \
# | tar -xz && sudo mv chatmem /usr/local/bin/
# 2) bootstrap the local database — prints the JSON snippet to paste into your MCP client
chatmem init
# 3) paste into ~/.claude/mcp.json (or ~/.cursor/mcp.json), restart the client
# 4) verify tools appear
# In Claude Code, ask: "list your MCP tools" — you should see
# record_message, search_history, get_conversation from the 'chatmem' server.
Claude Code (terminal claude, 2.1.x+):
claude mcp add --scope user chatmem /opt/homebrew/bin/chatmem mcp
claude mcp list # should show: chatmem ✓ Connected
Claude Desktop app — merge into ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"chatmem": {
"command": "/opt/homebrew/bin/chatmem",
"args": ["mcp"]
}
}
}
Fully quit + relaunch the app after editing.
Codex — merge into ~/.codex/config.toml:
[mcp_servers.chatmem]
command = "/opt/homebrew/bin/chatmem"
args = ["mcp"]
startup_timeout_sec = 60
Cursor / Windsurf / other MCP clients — same JSON block as Claude Desktop but in their MCP config file. Consult the client's docs for the path.
MCP tools reference
All three tools are registered by internal/mcp/server.go. Every write conveys model/provider/client_id explicitly — the daemon never infers them — so multiple LLM clients writing into the same database stay cleanly attributed.
record_message
Store a single chat message. Opens a new conversation when conversation_id is empty (then model/provider/client_id are required).
| Field | Type | Required | Notes |
|---|---|---|---|
conversation_id |
UUID string | if continuing a conversation | Omit to open a new one |
role |
user | assistant | system | tool |
yes | |
content |
string | yes | Message text |
model |
string | when opening a new conversation | e.g. claude-opus-4-7 |
provider |
string | when opening a new conversation | e.g. anthropic, openai |
client_id |
string | when opening a new conversation | e.g. claude-code, cursor, aider |
token_count |
int | no | Optional metadata |
Returns:
{ "message_id": "<uuid>", "conversation_id": "<uuid>" }
search_history
Search stored chat history. MVP uses Postgres full-text ranking (to_tsvector + plainto_tsquery); semantic re-ranking via ONNX embeddings is the next milestone. Returns one hit per conversation (MMR-lite), packed to token_budget. The first 100,000 characters of each message are searchable; longer messages are stored in full but matched only on that prefix.
| Field | Type | Required | Notes |
|---|---|---|---|
query |
string | yes | Free-text query |
top_k |
int | no | Default 10, max 100 |
token_budget |
int | no | Total snippet tokens (default 4000) |
model |
string | no | Filter by model id |
client_id |
string | no | Filter by client id |
since |
RFC3339 string | no | Lower bound on created_at |
until |
RFC3339 string | no | Upper bound on created_at |
conversation_ids |
array of UUIDs | no | Restrict to these conversations |
Returns both a rendered text block (visible to any MCP client) and a structured payload for programmatic use:
Rendered text (Content):
2 hit(s) for "kafka retention"
── hit 1 ──
role: user
conversation: 8a2f…
message: def6…
created: 2026-07-20T10:15:32Z
score: 0.2341
snippet:
kafka retention is set at 7 days for the ingest topic…
Structured (StructuredContent):
{
"hits": [
{ "message_id": "<uuid>", "conversation_id": "<uuid>", "role": "user",
"snippet": "...", "score": 0.147, "created_at": "2026-07-20T10:15:32Z" }
]
}
(Before v0.0.2 Content was a bare hit count — Windsurf/Cascade and any client that ignores StructuredContent would show no snippet text.)
get_conversation
Fetch a conversation and its messages, ordered by created_at ascending (insertion order breaks ties). Cursor-paginated: pass the returned next_after back as after.
| Field | Type | Required | Notes |
|---|---|---|---|
conversation_id |
UUID string | yes | |
limit |
int | no | Default 100, max 500 |
after |
string | no | next_after from the previous page (<RFC3339>|<seq>), or a plain RFC3339 timestamp to return messages strictly after it |
Returns both a rendered text block and a structured payload.
Rendered text (Content):
conversation 8a2f…
model: anthropic / claude-opus-4-7
client: claude-code
started: 2026-07-20T10:15:32Z
updated: 2026-07-20T10:20:15Z
messages: 3
── user @ 2026-07-20T10:15:32Z ──
hello from chatmem
── assistant @ 2026-07-20T10:15:35Z ──
hi back
Structured (StructuredContent):
{
"conversation": {
"id": "<uuid>", "client_id": "...", "model": "...", "provider": "...",
"title": null, "started_at": "...", "updated_at": "..."
},
"messages": [
{ "id": "<uuid>", "role": "user", "content": "...", "token_count": 0, "created_at": "..." }
],
"next_after": null
}
Notion synthesis (v0.3.0 — multi-pass with coverage guarantee)
After each conversation, chatmem publishes a structured, concept-organized Notion page — a study-mode page for learning conversations, or a debug-mode page for problem-solving sessions. The pages are optimized for revision: TL;DR at the top, Mermaid diagrams for anything with structure or flow, citations back to the original messages, full transcript collapsed at the bottom.
v0.3.0 quality guarantees: the LLM extracts atomic facts from every message first, then composes the Summary from that inventory. Chatmem refuses to write to Notion unless the Summary cites ≥ 95% of the messages that carry non-trivia facts. Result: no message silently dropped, even in a 4-hour, 200-message session.
suggest_synthesize=true triggers the client-side LLM to run:
┌─ Phase 1: extract facts ──────────────────────────┐
│ get_extraction_prompt → chunk of unextracted │
│ record_facts → store, return remaining │
│ loop until extraction_complete=true │
└──────────────────────┬────────────────────────────┘
▼
┌─ Phase 2: compose Summary ────────────────────────┐
│ get_synthesis_prompt → transcript + facts + │
│ schema + quality rules │
└──────────────────────┬────────────────────────────┘
▼
┌─ Phase 3: publish (coverage-gated) ───────────────┐
│ synthesize_to_notion → validates + writes to │
│ Notion, OR returns list │
│ of missed msg uuids for │
│ the LLM to fix and retry │
└───────────────────────────────────────────────────┘
Setup (once)
- Create an internal Notion integration at https://www.notion.so/my-integrations. Copy the secret token (starts with
ntn_orsecret_). - Create a Notion page that will hold your chatmem pages. Share it with the integration (⋯ → Add connections → your integration).
- On your machine:
chatmem notion connect ntn_XXXXX --parent <page-url-or-id> chatmem notion status # verify token + parent
Restart your MCP client (Windsurf/Cursor/Claude Code). Auto-synthesis fires when a conversation crosses 20 messages, or the client-side rule triggers on suggest_synthesize=true in record_message responses.
To turn auto-synthesis off, set "auto_synthesize": {"enabled": false} in ~/.local/share/chatmem/notion.json. Fields you leave out of auto_synthesize take their defaults (idle_minutes 10, message_threshold 20, min_messages 4).
The coverage gate requires 95% of messages with non-trivial facts to be cited. synthesize_to_notion's min_coverage can raise that bar but not lower it. record_facts rejects message ids that aren't in the conversation. Each re-synthesis that changes the page bumps the version shown in the page header.
System-prompt rule for your MCP client
Paste this into your Windsurf / Cursor / Claude-Code rules so auto-fire lands:
Whenever a
record_messagetool response includessuggest_synthesize: true, immediately (a) callget_synthesis_promptfor the referencedconversation_id, (b) compose the Summary object per the schema in that prompt, (c) callsynthesize_to_notionwith{conversation_id, summary}. Do this once per conversation per trigger, before continuing the user's turn.
Page types
- Study mode (concept-heavy conversations): TL;DR · Prerequisites · Core Concepts (definition callout + body + example + why-it-matters + citations) · Mermaid diagrams · Key Insights · Code · Further Study · References · Full Transcript.
- Debug mode (fixing a broken thing): TL;DR · Status callout (resolved / partial / unresolved) · Timeline Mermaid diagram · What I Tried (attempts) · Root Cause · Resolution · Prevention · Full Transcript.
Session type is auto-classified per conversation. Diagrams are required (validation enforced) — Mermaid timeline for debug, appropriate diagram type for study when concepts warrant one.
Preview the templates without touching Notion
chatmem notion sample --type=study # prints Summary JSON + rendered blocks JSON
chatmem notion sample --type=debug
Inspect coverage
chatmem notion coverage <conversation_id>
# → total messages, messages with facts, category breakdown,
# msgs without any fact yet
Reliability
Failed Notion writes are persisted to ~/.local/share/chatmem/notion-pending/<conversation-id>.json (owner-only, since it holds the transcript) and retried automatically on the next chatmem mcp start, or manually with chatmem notion resync. Only the newest failed write per conversation is kept, and any later successful synthesis discards it. If a page was created before the failure, the retry updates that page; it never creates a second one.
Long content is split to fit Notion's API limits (2000 characters per text run, 100 blocks per request). Each transcript turn on the page shows at most 100 KB; longer messages end with a note pointing at the full text in chatmem.
Disconnect
chatmem notion disconnect # removes notion.json; published pages are untouched
Import an existing chat (v0.2.1)
Got a transcript from a chat that happened outside chatmem — ChatGPT web export, a Claude.ai conversation you saved, an aider log, etc.? Load it into chatmem with chatmem import. Then either the LLM auto-synthesizes it to Notion (if past the threshold) or you nudge it manually.
Input formats
Two formats accepted; auto-detected by the first non-whitespace character:
JSONL (one JSON object per line):
{"role":"user","content":"what is hnsw"}
{"role":"assistant","content":"a graph-based ANN index"}
{"role":"user","content":"pgvector defaults?"}
JSON array:
[
{"role":"user","content":"what is hnsw"},
{"role":"assistant","content":"a graph-based ANN index"}
]
Extra fields on each message (timestamps, source ids, tool_calls) are silently ignored — chatmem generates its own ids and timestamps.
Commands
# From a file, opening a new conversation:
chatmem import -f ./chatgpt-export.jsonl \
--model gpt-5 --provider openai --client-id chatgpt-web
# Piping from stdin:
cat ./transcript.jsonl | chatmem import --stdin \
--model claude-opus-4-7 --provider anthropic --client-id claude-web
# Appending to an existing chatmem conversation (e.g. resume a partial capture):
chatmem import -f ./followup.jsonl \
--conversation-id 8a2f... \
--model claude-opus-4-7 --provider anthropic --client-id claude-code
chatmem import attaches to a running chatmem instance's Postgres if one is up (Claude Code / Windsurf / etc. session live). Otherwise it starts its own embedded PG briefly. Either way it's safe to run.
On success it prints the new conversation UUID + a hint to synthesize it in your LLM client:
imported 47 messages into conversation 8a2f...
Next steps:
chatmem notion status
# From an LLM session with chatmem:
# "call get_synthesis_prompt for conversation 8a2f..., then synthesize_to_notion"
Converting from real chat exports
- ChatGPT
.zipexport → JSONL:jqrecipe (adjust for your export format):jq -c '.[0].mapping | to_entries | map(select(.value.message != null)) | sort_by(.value.message.create_time) | .[] | {role: .value.message.author.role, content: (.value.message.content.parts | join("\n"))}' \ < conversations.json > out.jsonl - Claude.ai export → JSONL:
jq '.[] | {role: .sender, content: .text}' < conversation.json - Anything text-based: turn each turn into
{"role":"...","content":"..."}and you're done.
Commands
Every subcommand has --help. Run chatmem with no arguments to see the top-level list.
chatmem init
Provisions the persistent data directory, extracts embedded Postgres, installs pgvector into the runtime, and applies the schema. On completion prints a ready-to-paste MCP JSON snippet with the actual binary path (os.Executable()).
Safe to re-run — all provisioning steps are idempotent.
chatmem mcp [--port <n>]
Runs a stdio MCP server for a single client. Starts embedded Postgres for the process lifetime and stops it cleanly on stdin close or SIGTERM.
Concurrent MCP clients cannot share one embedded Postgres in the MVP — each chatmem mcp process opens its own PG on the port supplied. See Roadmap for the daemon+shim architecture.
chatmem daemon [--port <n>]
Runs the long-lived Postgres foundation for the future daemon+shim architecture. Not required for MVP usage. Ends cleanly on SIGINT/SIGTERM.
chatmem telemetry {enable|disable|status}
Manage the anonymous telemetry setting. status prints the effective state, the precedence source (env | config | default), and the install id.
Data & config paths
| Kind | macOS | Linux | Env override |
|---|---|---|---|
| Data | ~/.local/share/chatmem/ |
~/.local/share/chatmem/ |
CHATMEM_HOME |
| Cache | ~/Library/Caches/chatmem/ |
~/.cache/chatmem/ |
CHATMEM_CACHE |
| Install id | <data>/install_id |
<data>/install_id |
— |
| Telemetry cfg | <data>/telemetry.json |
<data>/telemetry.json |
— |
| Postgres data | <data>/pgdata/ |
<data>/pgdata/ |
— |
| Postgres runtime | <cache>/pg-runtime/ |
<cache>/pg-runtime/ |
— |
Telemetry
chatmem can send anonymous usage pings to help track adoption. Message content, query strings, and filenames are never sent, ever. What is sent when enabled:
- install UUID (generated locally on first run, in
<data>/install_id) chatmemversion- counters for 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 percentiles per operation — p50/p95/p99 in ms
Modes
The client operates in three modes depending on config:
| Mode | Behavior |
|---|---|
| Disabled (env or config) | Nothing accumulated. Aggregator.Record* still runs but Flush is a no-op. |
| Enabled, no ingest URL | Accumulate + periodic flush → slog.Info("telemetry flush (local-only, no ingest URL set)", ...). Local observability only. |
| Enabled, ingest URL set | Accumulate + flush → POST to <URL>/v1/ping with 3-attempt exponential backoff. Failed sends persist to <data>/pending/*.json and get drained on the next successful flush (24h TTL). Release binaries have this URL baked in (points at the maintainer's Worker); CHATMEM_TELEMETRY_URL overrides it, and chatmem telemetry status prints the effective value. |
Precedence (highest wins)
CHATMEM_TELEMETRY=0(alsofalse,off) — hard off<data>/telemetry.json({"enabled": true|false}) — persistent choicechatmem telemetry {enable|disable}— writes the above- Default: enabled
chatmem init prompts on first run (interactive TTY only) and writes the config. Non-TTY inits print a notice and leave the default; opt out non-interactively with chatmem telemetry disable or the env var.
Commands
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)
Standing up your own ingest
The client posts to any endpoint that speaks POST /v1/ping with the payload documented in internal/telemetry/client.go:Payload. A ready-to-deploy Cloudflare Worker + D1 lives in server/telemetry-worker/ — one wrangler deploy and you have an endpoint. See that README for the 5-command setup.
chatmem MCP Fragen
Welche Tools bietet chatmem MCP?
Er stellt 15 Tools bereit, darunter conversation_id, role, content, model, provider.
Ist mein Nachrichteninhalt privat?
Ja, der Nachrichteninhalt verlässt deine Maschine nie; es werden nur anonyme Telemetriedaten wie Install-IDs und aggregierte Zähler gesendet.
Kann ich bestehende Chat-Transkripte importieren?
Ja, du kannst bestehende Chat-Transkripte in den Formaten JSONL oder JSON-Array per Bulk-Load in chatmem laden.
Wird eine Notion-Integration unterstützt?
Ja, es kann Konversationen automatisch in strukturierte Notion-Seiten für Lernzwecke oder zum Debugging umwandeln.