# Hippocampus Universal memory for AI. One server, every platform. Your AI shouldn't forget who you are just because you switched apps. --- Hippocampus is an open-source, self-hosted MCP memory server. Deploy it once. Connect it to Claude, ChatGPT, Gemini, Cursor, Perplexity — anything that speaks MCP. Tell one AI about a project decision, and every other AI already knows. ## The problem Every AI platform silos your context: - Claude.ai memory doesn't work in Claude Code - Claude Code's CLAUDE.md files don't work in ChatGPT - ChatGPT's memory doesn't work in Gemini - None of them talk to each other You repeat yourself constantly. Context gets lost. Continuity breaks every time you switch tools. ## How it works ``` Claude.ai ────────────┐ Claude Code ──────────┤ Claude Desktop ───────┤ ChatGPT ──────────────┼── MCP ──▶ Hippocampus Gemini CLI ───────────┤ (your server) Cursor / Windsurf ────┤ Perplexity ───────────┘ ``` [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) is the open standard every major AI platform has adopted. Hippocampus is a remote MCP server that exposes memory tools over Streamable HTTP — `remember`, `recall`, `forget`, and eight more. Any AI client that supports MCP can connect. Data model: knowledge graph with entities, observations, and relationships. Semantic search via local embeddings. Entire database encrypted at rest with SQLCipher (AES-256) — including embedding vectors, which [leak original text](https://arxiv.org/abs/2305.03010). ## Try it in 5 minutes You need Node.js 20+ installed. **1. Clone and install** ```bash git clone https://github.com/karrolcia/hippocampus.git cd hippocampus npm install ``` **2. Create your `.env` file** ```bash cp .env.example .env ``` Open `.env` and set your passphrase (this encrypts the database): ```env HIPPO_PASSPHRASE=any-secret-phrase-you-want ``` That's the only required value. Everything else has defaults. **3. Start the server** ```bash npm run dev ``` You should see: ``` Hippocampus starting on http://0.0.0.0:3000 MCP endpoint: http://0.0.0.0:3000/mcp ``` The embedding model (~80MB) downloads automatically on first run — this takes a minute the first time. **4. Verify it's alive** ```bash curl http://localhost:3000/health ``` Expected response: ```json {"status":"ok","version":"0.3.1"} ``` **5. Connect Claude Code** ```bash claude mcp add hippocampus --transport http http://localhost:3000/mcp ``` **6. Try it** Open a Claude Code session and say: > Remember that my preferred language is TypeScript and I use Hono as my web framework. Then in a new session: > What do you know about my tech preferences? If it comes back with TypeScript and Hono, it's working. Your AI now has persistent memory. ## Deploy to a server Local is great for trying it out. To use Hippocampus across all your AI tools — Claude.ai, ChatGPT, Gemini, mobile — you need it running on a public URL with HTTPS. ### What you need - A VPS (Hetzner CX22 at ~4 EUR/month is plenty — EU jurisdiction, GDPR) - A domain (or subdomain) pointed at the VPS - ~20 minutes ### Step 1: Set up the server SSH into your VPS and install Docker: ```bash # Firewall ufw default deny incoming && ufw default allow outgoing ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp ufw enable # Install Docker curl -fsSL https://get.docker.com | sh ``` ### Step 2: Point your domain Add an A record in your DNS provider: ``` Type: A Name: hippo (or whatever subdomain you want) Value: ``` DNS propagation usually takes a few minutes. Verify it resolves before continuing: ```bash dig hippo.yourdomain.com +short # Should return your VPS IP ``` ### Step 3: Configure ```bash git clone https://github.com/karrolcia/hippocampus.git cd hippocampus chmod +x setup.sh ./setup.sh ``` The script asks for your domain, username, and password, then writes `.env` and `Caddyfile` for you. No Node.js required. Caddy handles TLS certificates automatically via Let's Encrypt.
Manual setup (if you prefer) ```bash cp .env.example .env ``` Generate a passphrase and an OAuth password hash: ```bash # Generate a random passphrase — save this in your password manager openssl rand -base64 32 # Generate a hash of the password you'll use to log in # Replace 'your-password' with your actual password echo -n 'your-password' | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=' ``` Edit `.env` with all required values: ```env HIPPO_PASSPHRASE= HIPPO_OAUTH_ISSUER=https://hippo.yourdomain.com HIPPO_OAUTH_USER=admin HIPPO_OAUTH_PASSWORD_HASH= ``` Edit `Caddyfile` — replace the domain on the first line: ```caddy hippo.yourdomain.com { ```
### Step 4: Start and verify ```bash docker compose up -d ``` Wait ~30 seconds for the containers to start and Caddy to get a certificate, then: ```bash curl https://hippo.yourdomain.com/health ``` Expected response: ```json {"status":"ok","version":"0.3.1"} ``` If you get a certificate error, DNS might not have propagated yet. Wait a few minutes and retry. ### Step 5: Connect your AI tools Now that your server is live, connect each platform you use. **Claude Code:** ```bash claude mcp add hippocampus --transport http https://hippo.yourdomain.com/mcp ``` **Claude.ai (browser + mobile):** Settings > Integrations > Add custom integration > Enter your server URL: ``` https://hippo.yourdomain.com/mcp ``` You'll be redirected to log in with the username and password you configured in Step 3. **Claude Desktop:** Add to your MCP config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): ```json { "mcpServers": { "hippocampus": { "url": "https://hippo.yourdomain.com/mcp" } } } ``` **ChatGPT (web + mobile):** Settings > Apps > Developer Mode > Create a new app > Set server URL to `https://hippo.yourdomain.com/mcp` **Gemini CLI:** Add to `~/.gemini/settings.json`: ```json { "mcpServers": { "hippocampus": { "uri": "https://hippo.yourdomain.com/mcp" } } } ``` **Cursor / Windsurf / VS Code:** Add to your MCP configuration file: ```json { "mcpServers": { "hippocampus": { "url": "https://hippo.yourdomain.com/mcp" } } } ``` **Verify:** Open any connected platform and ask your AI to remember something. Switch to a different platform and ask it to recall. If it works across platforms, you're done. ### Alternative: Fly.io If you don't want to manage a VPS. ~$5/month. ```bash git clone https://github.com/karrolcia/hippocampus.git cd hippocampus fly launch # Creates app + Dockerfile detected automatically fly volumes create hippo_data --size 1 ``` Edit the generated `fly.toml` — add a volume mount so the database persists across deploys: ```toml [mounts] source = "hippo_data" destination = "/data" ``` Generate your secrets — save both values in your password manager: ```bash # Generate passphrase (save this — you lose your database without it) openssl rand -base64 32 # Generate hash of your login password (replace 'your-password') echo -n 'your-password' | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=' ``` Set them as Fly secrets and deploy: ```bash fly secrets set HIPPO_PASSPHRASE= fly secrets set HIPPO_OAUTH_ISSUER=https://.fly.dev fly secrets set HIPPO_OAUTH_USER=admin fly secrets set HIPPO_OAUTH_PASSWORD_HASH= fly deploy ``` Verify: ```bash curl https://.fly.dev/health ``` Fly handles HTTPS automatically. Connect your AI tools using `https://.fly.dev/mcp` as the server URL. ### Alternative: Home server + Cloudflare Tunnel Free, maximum control. Run Hippocampus on any machine at home and expose it via [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/) — no port forwarding, no static IP needed. ```bash # On your home machine git clone https://github.com/karrolcia/hippocampus.git cd hippocampus cp .env.example .env ``` Edit `.env` — set passphrase and OAuth variables (skip `setup.sh` here — it configures Caddy, which you don't need with Cloudflare Tunnel): ```bash # Generate values openssl rand -base64 32 # → HIPPO_PASSPHRASE echo -n 'your-password' | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=' # → HIPPO_OAUTH_PASSWORD_HASH ``` ```env HIPPO_PASSPHRASE= HIPPO_OAUTH_ISSUER=https://hippo.yourdomain.com HIPPO_OAUTH_USER=admin HIPPO_OAUTH_PASSWORD_HASH= ``` ```bash docker compose up -d hippocampus # only hippocampus — Caddy not needed # Install cloudflared and create a tunnel cloudflared tunnel create hippocampus cloudflared tunnel route dns hippocampus hippo.yourdomain.com ``` Create `~/.cloudflared/config.yml`: ```yaml tunnel: hippocampus ingress: - hostname: hippo.yourdomain.com service: http://localhost:3000 - service: http_status:404 ``` Start the tunnel: ```bash cloudflared tunnel run hippocampus ``` You're responsible for uptime and physical security. See [SECURITY.md](SECURITY.md) for trade-offs. ## Tools | Tool | Description | |------|-------------| | `remember` | Store a fact, preference, or piece of context. Optional `kind` classification (fact, decision, question, preference, or custom) and `importance` weighting. Reports overlapping observations so the AI can consolidate incrementally. Returns `version_hash` for cache invalidation. | | `recall` | Search memories by semantic similarity + keyword match. Filter by `type`, `kind`, `since`. Use `spread: true` to follow relationships and discover connected memories. Includes `version_hash` per entity in all formats. | | `get_observation` | Read ONE observation by its `observation_id` — the only id-taking tool that does not destroy anything. Use it to see what an id addresses before `merge` or `forget`. Bounded to the single row named; leaves `recall_count` untouched, unlike `recall`. | | `context` | Get everything about a topic — observations, relationships, related entities. Includes `version_hash`. | | `update` | Replace an existing observation with new content. Returns `version_hash`. | | `forget` | Permanently delete a memory or entity (secure deletion) | | `merge` | Merge multiple observations into one (atomic consolidation). Returns `version_hash`. | | `merge_entities` | Merge multiple entities into one — moves all data, deletes sources. Returns `version_hash`. | | `consolidate` | Find clusters of similar/duplicate memories, detect near-duplicate entities, surface contradictions, or run sleep mode for batch lifecycle analysis (compress/prune/refresh) | | `export` | Export as CLAUDE.md context file, readable markdown, JSON, wire format, or Obsidian vault | | `check_version` | "Did anything change?" — pass an entity name + cached hash, get back yes/no. No embedding computation, pure metadata. | | `onboard` | Bootstrap memory from a new AI session. Returns structured extraction instructions the AI follows to capture user context. | `since` is a UTC lower bound and accepts `YYYY-MM-DD`, `YYYY-MM-DD HH:MM:SS`, `2026-08-25T10:36:55` (no zone — read as UTC), and ISO-8601 with a zone (`2026-08-25T10:36:55Z`, `2026-08-25T13:36:55+03:00` — offsets are converted, not stripped). A value that can't be parsed is an **error**, never an empty result set: on a "what landed since my last run" query, silently returning nothing is indistinguishable from nothing having landed, and that's the one answer a memory server must never guess at. The AI calls these tools naturally. You don't manage memory manually — you just talk to your AI and it remembers. ### Spreading activation When you `recall` with `spread: true`, Hippocampus doesn't just return direct matches — it follows relationships one hop out from matched entities and scores their observations against your query. Related observations get a dampened score (0.5x decay), so they surface when relevant but don't drown out direct hits. Useful for questions that span multiple related topics. ### Contradiction detection `consolidate` with `mode: "contradictions"` finds observation pairs that talk about the same thing (high embedding similarity) but say different things (low word overlap). No LLM required — pure embedding math plus Jaccard comparison. Review the flagged pairs and decide what to keep. ### Novelty scoring Every `remember` call returns a `novelty` score (0–1) computed via SVD subspace projection. Pairwise cosine checks miss aggregate redundancy — five observations with moderate individual overlap can collectively explain a new observation entirely. Subspace projection compares against all existing observations simultaneously. When novelty drops below 0.1, the response warns that the information may already be captured. ### Near-match detection When `remember` stores a new observation, it reports existing observations that overlap (cosine similarity 0.5–0.85) — the zone between "clearly different" and "near-duplicate." The AI sees these in the response and can consolidate immediately instead of waiting for a batch `consolidate` pass. No extra computation: the dedup scan already compares against all entity embeddings. `near_matches` also carries any match above 0.85 that the dedup guards held back — an append-only entity, or an observation from a different UTC day (see [Append-safe writes](#append-safe-writes)). Overlap that isn't acted on is still overlap worth seeing. Each match's `content` is **capped at 200 characters** — anything longer comes back as a preview — plus, off append-only entities, the `observation_id` that addresses it. The cap bounds the response, not what you can do with it: quoting three overlapping observations in full could push a *successful* write past a client's token cap, and a caller that sees only a size error cannot tell a rejected response from a rejected write. Each non-append-only match also carries `created_at` and `kind`, because overlapping observations tend to share an opening and their previews can be identical. To consolidate, re-read the near match with `recall` (which returns `observation_id` alongside full content) and then `merge` it with the observation this response just stored — `merge` keeps only the content you pass it, so merging on the preview alone would discard the rest. The re-read returns whole rows, so it holds for telegraphic facts and few-KB observations and stops holding once a handful of rows runs to tens of thousands of characters; consolidation is report-only there. Append-only entities carry none of the three fields on purpose: an id is a deletion key, and their whole point is that nothing consolidates them. ### Append-safe writes Dedup on write is destructive: a >= 0.85 match whose stored content is shorter is deleted and replaced. That is right for a fact being restated with more detail, and wrong for a log — two dated entries sharing a template can clear 0.85 on the template alone. Two guards bound it: - **Append-only entities.** Entity names matching a prefix in `HIPPO_APPEND_ONLY_PREFIXES` (default `ops:daily-log:,ops:session-check,synthesis:`) are exempt from dedup entirely — nothing is skipped, nothing is deleted. The same setting governs all three protections — the dedup exemption here, the `consolidate` exclusion above, and the staleness suppression in [Reconsolidation hints](#reconsolidation-hints) — so turning it off re-enables all three. Set it to your own log namespaces. Leaving it unset or blank keeps the defaults — blank cannot mean "off", because a compose file forwarding an unset variable would otherwise silently disable the protection; to actually turn exemptions off, set it to the literal `none`. - **Same-day scoping.** Everywhere else, dedup only considers observations created on the same UTC calendar day. Entries written on different days can never evict each other, however similar they look. `consolidate` respects the same boundary: append-only entities are excluded from all four of its modes, so a log entity is never returned as a merge cluster, a `prune`/`compress`/`refresh` candidate, a contradiction pair, or an entity-resolution match. The count comes back as `excluded_append_only` and is named in the message — the drop is never silent. Pass `include_append_only: true` to inspect them anyway, which is worth doing occasionally to catch accidental double-writes, since these entities no longer dedup on write. Any write that did delete something says so in a top-level `replaced: true`, with the evicted text in `replaced_observation` and its id in `replaced_observation_id` — so a caller can detect (and undo) data loss without parsing the human-readable `message`. ### Sleep mode `consolidate` with `mode: "sleep"` runs batch lifecycle analysis — the overnight defrag for your knowledge graph. Uses SVD leverage scores combined with temporal signals to classify old observations into three categories: - **Compress**: redundant + old + recalled. Information captured elsewhere, safe to merge down. - **Prune**: never recalled + old. The synapse never fired — delete candidates. - **Refresh**: actively used + unique + old. The AI keeps serving these, but newer information exists on the entity. Reconsolidation candidates. Returns `information_rank` and `redundancy_ratio` per entity for structural diagnosis. The AI acts on results using existing tools — `merge` for compress, `forget` for prune, `update` for refresh. ### Reconsolidation hints When `recall` returns observations older than 30 days on an entity that has received newer information since, they're flagged `stale: true`. Lightweight date comparison on every retrieval — no embedding computation. The AI sees the flag and can decide whether to update or leave the observation as-is. Append-only entities are never flagged. "The entity has newer information since" is simply what an active log looks like, so the condition matched every historical entry — and since the flag's suggested remedy is `update`, which replaces an observation, the one place it fired hardest was the one place it must never fire. ### Cross-platform staleness detection You told Claude about your project stack on Monday. On Wednesday you switched to Gemini. Is Gemini's cached context still current? Every entity carries a `version_hash` — SHA-256 of its observation content. The AI caches this hash, and later calls `check_version` to ask "did anything change?" without re-fetching everything. One lightweight metadata call instead of re-reading the entire entity. All mutation tools return the new hash after writing. All read tools include it in the response. The AI always has a fresh hash to cache — no extra round trip. ### Onboarding New databases start cold — the `hippocampus://context` resource shows guidance prompting the AI to capture what it already knows about you (identity, projects, preferences). Once 5+ observations exist, the guidance disappears and the full knowledge graph takes over. For systematic first-session extraction, the `onboard` tool returns a structured prompt the AI follows — what to look for, what's already stored, what format to use. The tool stores nothing itself; it hands the AI a checklist and lets it do what it's good at. Each platform uses its own context for extraction. ## Configuration | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `HIPPO_PASSPHRASE` | Yes | — | Database encryption passphrase | | `HIPPO_DB_PATH` | No | `./data/hippocampus.db` | Database file location | | `PORT` | No | `3000` | Server port | | `HOST` | No | `0.0.0.0` | Bind address | | `HIPPO_TOKEN` | No | — | Bearer token for local dev (skip OAuth) | | `HIPPO_OAUTH_ISSUER` | No | — | Your server URL — enables OAuth | | `HIPPO_OAUTH_USER` | No | — | OAuth login username | | `HIPPO_OAUTH_PASSWORD_HASH` | No | — | SHA-256 hash of OAuth password | | `RATE_LIMIT_RECALL` | No | `60` | `/mcp` requests per minute per client address (all tools). Not forwarded by `docker-compose.yml`, so under compose the default applies | | `HIPPO_CONTEXT_MAX_OBSERVATIONS` | No | `100` | Max observations in `hippocampus://context` resource | | `TRANSFORMERS_CACHE` | No | System default | Embedding model cache directory | ## Security - AES-256 database encryption via SQLCipher (text, embeddings, indexes — everything) - OAuth 2.1 with PKCE for remote access - Input validation: 50,000 char/memory, 200 char/entity name - Rate limiting on `/mcp` and, more tightly, on the OAuth login, token and registration routes, plus a global cap on failed logins (limits in SECURITY.md) - 16 KB body cap on the OAuth routes - `PRAGMA secure_delete = ON` — forgotten memories are zeroed, not just unlinked - Non-root Docker, `cap_drop: ALL`, read-only filesystem - CORS restricted to known AI platform origins - No external API calls — embeddings run locally via Transformers.js See [SECURITY.md](SECURITY.md) for the full threat model and architecture. ## OAuth — what's happening under the hood When you connect Claude.ai, ChatGPT, or other browser-based platforms, they use OAuth 2.1 to authenticate with your server. Hippocampus includes a self-contained OAuth server — no external auth provider needed. Here's what happens when you click "Connect" in Claude.ai: 1. Claude.ai auto-registers as a client with your server (Dynamic Client Registration, RFC 7591) 2. You're redirected to a login page on your server 3. You enter the username and password from your `.env` 4. Your server issues a short-lived access token (1 hour) and a refresh token (30 days) 5. Claude.ai uses the access token for MCP requests, refreshes automatically when it expires The three `.env` variables that enable this: - `HIPPO_OAUTH_ISSUER` — your server's public URL (tells Hippocampus to turn on OAuth) - `HIPPO_OAUTH_USER` — your login username - `HIPPO_OAUTH_PASSWORD_HASH` — SHA-256 hash of your password (the server never stores your plaintext password) For local development, you can skip OAuth entirely by setting `HIPPO_TOKEN` in `.env` and passing it as a Bearer token. ## Contributing ### Architecture ``` src/ ├── index.ts # Hono server, MCP Streamable HTTP transport ├── config.ts # Environment config with Zod validation ├── mcp/ │ ├── server.ts # MCP tool registration (12 tools) │ └── tools/ # remember, recall, get_observation, forget, update, merge, merge_entities, context, consolidate, export, check_version, onboard ├── db/ │ ├── index.ts # SQLCipher initialization │ ├── schema.ts # Schema + migrations │ ├── entities.ts # Entity CRUD │ ├── observations.ts # Observation CRUD + keyword search │ └── relationships.ts # Relationship CRUD + BFS graph traversal ├── embeddings/ │ ├── embedder.ts # Local embeddings (all-MiniLM-L6-v2) + semantic search │ └── subspace.ts # SVD novelty scoring + redundancy analysis └── auth/ └── oauth.ts # Self-contained OAuth 2.1 server ``` **Stack:** Node.js, TypeScript, Hono, MCP SDK, SQLCipher, Transformers.js ### Running tests ```bash # Unit + integration tests npm test # Full end-to-end smoke test (real embeddings, temp encrypted DB) HIPPO_PASSPHRASE=test HIPPO_DB_PATH=/tmp/hippo-test.db npx tsx test-all-tools.ts ``` ## Platform compatibility | Platform | Remote MCP | How to connect | |----------|-----------|----------------| | Claude.ai (browser + mobile) | Yes | Custom integration | | Claude Code | Yes | `claude mcp add` | | Claude Desktop | Yes | Config file | | ChatGPT (web + mobile) | Yes | Developer Mode > Apps | | ChatGPT API | Yes | `server_url` in tools | | Gemini CLI | Yes | Settings file | | Gemini in Android Studio | Yes | Settings > MCP Servers | | Cursor / Windsurf / VS Code | Yes | MCP config file | | Perplexity Mac | Partial | Local MCP support, remote coming | ## License AGPL-3.0 — free to use, modify, and self-host. If you run a modified version as a network service, you must open-source your changes under the same license. ## Support If Hippocampus is useful to you, [buy me a coffee](https://buy.stripe.com/5kQ9AT4IydTz7h1apHb7y00). Open source. Free forever. No hosted version. No SaaS.