Persistent memory for AI assistants, self-hosted and provider-portable.
Named after Munin, one of Odin's two ravens — the one responsible for memory.
AI assistants forget everything between conversations. The context you build up — project decisions, personal preferences, how your systems work — evaporates when the session ends.
Some providers offer built-in memory features, but that context lives on their servers, in their format, under their control. If you switch providers, or they change their terms, or they shut down — your accumulated context goes with them.
Munin Memory is a different approach: a lightweight MCP server that stores your AI's memory in a SQLite database you own and control. It runs on a Raspberry Pi, a VPS, or your laptop. Any MCP-compatible AI client can connect — today that's Claude across all platforms, tomorrow it could be any provider that supports the protocol.
The underlying principle: your AI's memory is your data, and it should live on your infrastructure.
For the full argument, see Resilient and Sovereign AI.
- 9 MCP tools for reading, writing, searching, and organizing memories
- Two memory types: state entries (mutable, current truth) and log entries (append-only, chronological history)
- Hierarchical namespaces (e.g.
projects/website,people/alice,decisions/tech-stack) - Three search modes: keyword (FTS5), semantic (vector embeddings), and hybrid (both combined via Reciprocal Rank Fusion)
- Content security: writes are heuristically scanned for common secrets — obvious API keys, tokens, and inline passwords are rejected before storage
- Dual auth: Bearer token (simple) + OAuth 2.1 (for web/mobile clients)
- Two transports: stdio (local) and Streamable HTTP (network)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Claude Code │ │Claude Desktop│ │ Claude Web │
│ (Bearer) │ │ (Bearer) │ │ (OAuth) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└─────────────────┼─────────────────┘
│ HTTPS
┌──────────┴──────────┐
│ Reverse proxy │
│ (Cloudflare Tunnel, │
│ nginx, etc.) │
└──────────┬──────────┘
│
┌─────────────────────────┴───────────────────────────┐
│ Munin Memory MCP Server │
│ Node.js · TypeScript · Express · HTTP transport │
├─────────────────────────────────────────────────────┤
│ SQLite + FTS5 (keyword search) │
│ sqlite-vec + Transformers.js (semantic search) │
└─────────────────────────────────────────────────────┘
Also supports stdio transport for local use (Claude Code
or Claude Desktop running on the same machine as the server).
Key technology choices:
- SQLite — single-file database, no server process, runs everywhere including ARM64
- FTS5 — built-in full-text search, no external service needed
- sqlite-vec — vector similarity search for semantic queries
- Transformers.js — local embedding generation (all-MiniLM-L6-v2), no API calls
- MCP — open protocol, not locked to any provider
- Node.js 20+
- npm
git clone https://github.com/Magnus-Gille/munin-memory.git
cd munin-memory
npm install
npm run buildThe simplest way to run — Claude Code connects directly via stdin/stdout:
# Register with Claude Code
claude mcp add-json munin-memory \
'{"command":"node","args":["'$(pwd)'/dist/index.js"]}'For remote access — run as a service, connect from any device:
# Generate an API key
export MUNIN_API_KEY=$(openssl rand -hex 32)
# Start the server
MUNIN_TRANSPORT=http MUNIN_API_KEY=$MUNIN_API_KEY node dist/index.js
# Register with Claude Code
claude mcp add --transport http \
-H "Authorization: Bearer $MUNIN_API_KEY" \
-s user munin-memory http://localhost:3030/mcpWhen running in HTTP mode, the server exposes OAuth 2.1 endpoints. Configure your MCP client with the server URL and the OAuth flow handles authentication automatically. For public deployments, OAuth consent is now fail-closed: you must configure a trusted proxy-authenticated header/value pair for /authorize and /authorize/approve, or the server will refuse to serve public consent. See the OAuth section in CLAUDE.md for endpoint details.
All configuration is via environment variables. Copy .env.example for a starting point.
| Variable | Default | Description |
|---|---|---|
MUNIN_TRANSPORT |
stdio |
Transport: stdio or http |
MUNIN_HTTP_PORT |
3030 |
HTTP server port |
MUNIN_HTTP_HOST |
127.0.0.1 |
HTTP bind address |
MUNIN_API_KEY |
— | Bearer token (required for HTTP mode) |
MUNIN_MEMORY_DB_PATH |
~/.munin-memory/memory.db |
Database file location |
MUNIN_EMBEDDINGS_ENABLED |
true |
Enable semantic search |
MUNIN_HYBRID_ENABLED |
false |
Enable hybrid search (FTS5 + vector) |
MUNIN_OAUTH_ISSUER_URL |
http://localhost:3030 |
OAuth issuer (set to your public URL) |
MUNIN_OAUTH_TRUSTED_USER_HEADER |
— | Trusted header name required for public OAuth consent |
MUNIN_OAUTH_TRUSTED_USER_VALUE |
— | Exact trusted header value required for public OAuth consent |
MUNIN_OAUTH_ALLOW_LOCALHOST_CONSENT |
true |
Allow consent on loopback-only local development |
See .env.example for the full list.
This is how I run it — a Pi 5 on my desk, accessible from anywhere via a Cloudflare Tunnel. The general pattern:
- Deploy the code —
./scripts/deploy-rpi.sh <your-pi-hostname> - Install the systemd service — see
munin-memory.serviceas a template - Set up a reverse proxy — Cloudflare Tunnel, Tailscale, WireGuard, nginx, or whatever fits your setup
- Configure OAuth — set
MUNIN_OAUTH_ISSUER_URLto your public domain and configureMUNIN_OAUTH_TRUSTED_USER_HEADER+MUNIN_OAUTH_TRUSTED_USER_VALUEso browser consent is only available to your authenticated user
The deploy script and service file are tailored to my setup. You will likely need to adjust paths, usernames, and network configuration for yours.
Every major feature was designed through structured adversarial debates between Claude (Opus) and Codex (GPT-5.3). One AI proposes, the other critiques, they iterate, and the resolution becomes the implementation spec.
The summaries of these debates are in the debate/ directory:
debate/resolution.md— v1 core design (schema, queries, tools)debate/expansion-resolution.md— v2 features (tunnel, semantic search, OAuth)debate/tunnel-security-summary.md— 5-layer security architecturedebate/conventions-summary.md— memory conventions and session protocol
See CLAUDE.md for the full technical reference, including architecture details, spec amendments from the debates, and implementation notes.
npm test # 255 tests, single run
npm run test:watch # Watch modeThis is a personal project. It works well for my use case — Claude across 4 platforms (CLI, Desktop, Web, Mobile) sharing persistent memory through a Raspberry Pi on my desk.
It is not designed as a polished product for general consumption. The deployment scripts assume my hardware, the security model assumes a single user, and the documentation assumes familiarity with MCP, systemd, and reverse proxies. You are welcome to use it, fork it, or learn from it — but expect to adapt it to your own setup.
Built by Claude (Opus 4.6) and Magnus Gille, adversarially reviewed by Codex (GPT-5.3).