My long-term memory for AI agents, as one MCP server that every agent connects to: Claude Code on both of my operating systems, Claude on the web and phone, and ChatGPT. It holds four things:
- Notes: short, dated facts, decisions, gotchas, preferences and open loops. Agents write them as they work, without being asked (see docs/memory.md). Every write is versioned and can be reverted.
- Archive: about 21,000 imported files (old chats, exports, docs) split into passages and searchable with SQLite FTS5. Agents cite the source and passage.
- Vault: credentials encrypted with AES-256-GCM. Agents see names only, and reading a value needs a stated reason that is logged.
- Projects: folders and git remotes mapped to a project, so a session started in a repo gets that project's notes at boot.
Python 3.11, the official mcp SDK over Streamable HTTP, SQLite. One small VPS runs it behind nginx. Nothing here phones home.
Claude Code ──Bearer──┐
claude.ai ───OAuth────┼──► nginx (TLS) ──► memory-mcp serve (127.0.0.1:8091) ──► /srv/memory-mcp/data
ChatGPT ─────OAuth────┘ memory-mcp dash (127.0.0.1:8092) ──┘ (notes, archive, vault,
browser ──Cloudflare Access──► nginx ──► dashboard SPA + /api oauth state)
inbox (scp) ──► memory-mcp-import.timer (every 2 min) memory-mcp-backup.timer (daily)
- Claude Code authenticates with a static Bearer token (
MEMORY_TOKEN). - claude.ai and ChatGPT use OAuth through the SDK: dynamic client registration, PKCE, a consent page with CSRF protection, a scrypt-hashed owner password, rate-limited sign-in, one-hour access tokens, rotating 30-day refresh tokens. Notes marked private never reach OAuth clients.
- The dashboard is a React app plus a small JSON API, reachable only through Cloudflare Access. The API checks the Access JWT itself, and the Origin header on writes.
- systemd hardening: its own system user,
ProtectSystem=strict,ProtectHome,NoNewPrivileges, a memory cap, and write access only to the data folder.
boot, search_memory, read_source, list_sources, read_note, list_notes, remember, verify_note, archive_note, restore_note, relabel_note, list_changes, revert_change, list_projects, set_project, delete_note, forget_source, memory_status, list_credentials, get_credential, put_credential, delete_credential, credential_audit, plus a boot_prompt prompt and a plain /boot route the Claude Code hooks use.
The server's instructions tell every client to call boot first. On the next note it saves, a client gets back a receipt line ("saved: … · undo #812"), which it shows to the user.
You need a Linux server with a public address (any small VPS), a domain, Python 3.11+, nginx and certbot. Node is only needed to build the dashboard.
-
Pick your names. This copy uses
mcp.example.orgfor the server anddash.example.orgfor the dashboard. Search and replace them with yours acrossops/andsrc/(auth.pyhas the issuer,server.pythe allowed hosts and origins). -
Create the secrets. These are local and never committed:
pip install -e . # gives the memory-mcp command memory-mcp vault new-key # MEMORY_VAULT_KEY python -c "import secrets; print(secrets.token_urlsafe(48))" # MEMORY_TOKEN (Claude Code) python -c "from memory_mcp.auth import password_hash; print(password_hash('...'))" # MEMORY_PASSWORD_HASH
Put them, with
MEMORY_USERNAMEandMEMORY_DATA=/srv/memory-mcp/data, inmemory-mcp.env. For the dashboard addmemory-dash.envwithCF_TEAM_DOMAIN,CF_ACCESS_AUD,OWNER_EMAIL,DASH_SECRETandDASH_ORIGINS. -
DNS. Point both names at the server (
ops/create-dns.pydoes it through Cloudflare's API). -
Build and ship.
python ops/build.pybuilds the dashboard and packsapp.tar.gz. Copy it, the two env files andops/install.shto the server, then runsudo bash install.sh. It does all of this:- creates the
memory-mcpsystem user and/srv/memory-mcp/{app,data,inbox}; - builds a venv from the hash-pinned
requirements.lock; - installs the env files (never overwriting a live one, since it holds the vault key);
- installs the systemd units and timers, and adds nginx vhosts for both names.
ops/enable-tls.shthen gets the certificates. - creates the
-
Lock down the dashboard. Put it behind a Cloudflare Access application (
ops/cf-dash.py) and use an Access origin certificate. -
Check it. Run
ops/verify-remote.py(health and tools),ops/verify-oauth.py(the whole OAuth flow) andops/verify-browser-login.py. -
Connect clients.
- Claude Code:
claude mcp add --scope user --transport http watchthelight https://mcp.example.org/mcp --header "Authorization: Bearer <MEMORY_TOKEN>".ops/configure-claude-code.pydoes the same. - claude.ai: Settings > Connectors > Add custom connector, URL
https://mcp.example.org/mcp, sign in once. - ChatGPT: the same URL as a custom connector.
- Claude Code:
-
Feed it. Drop
.md,.txt,.json,.jsonlor.csvfiles in an inbox folder, andops/sync-inbox.pyuploads only new or changed ones over SSH. The import timer indexes them within two minutes.ops/import-credentials.pyloads a credentials file into the vault.
Run the tests with pip install -e ".[dev]" && pytest. The fixtures in tests/ use made-up secrets on purpose.
- Make the agent save notes itself and show the receipt. Memory that depends on you remembering to save it stays empty.
- Keep notes small and dated, with a review date. Stale facts are worse than none, so the server flags them and agents check before acting.
- Never let a credential value enter a chat. The vault plus "names only unless there's a logged reason" works.