Graph-Memory
# Graph Memory
[](https://glama.ai/mcp/servers/stevepridemore/graph-memory)
A personal knowledge graph for Claude that survives across sessions, devices, and tools. Built on Neo4j with semantic embeddings, OAuth-secured for use from Claude Code, Claude Desktop, and claude.ai web — all hitting the same graph.
https://github.com/user-attachments/assets/826e5f5a-5759-4b31-83dd-6bd7e0e044b8
*Asked from my phone. Pulls a decision made days ago on my laptop, citing the commit hash.*
> **No external API keys or per-token costs by default.** Entity extraction runs inside your Claude sessions on your Claude plan. Embedding runs locally via [bge-small-en](https://huggingface.co/BAAI/bge-small-en-v1.5). Everything stays on your hardware unless you choose to expose it. If you'd rather pay per token, an optional runner moves the nightly dream and weekly maintenance to an Anthropic API key (see [API-key runner](docs/DREAM_PROCESS.md#api-key-runner)).
## Why a graph
Built-in memory in Claude Code is "append facts to markdown, grep later." That gets you 80% there but breaks at scale: no relationships, no confidence, no decay, no contradiction detection, no temporal awareness. Two memories that reinforce each other look identical to two memories that contradict each other.
This project replaces flat keyword matching with weighted, relationship-aware retrieval:
- **Weighted edges with configurable decay** — frequently-confirmed knowledge stays strong; stale information fades naturally on per-type half-lives (preferences ~693 days, events ~99 days)
- **Bi-temporal validity** — separate `valid_at` (when fact was true), `invalid_at` (when superseded), `ingested_at` (when learned). Old facts get marked invalid rather than deleted
- **Semantic + structural search** — vector embeddings find conceptually similar entities; graph traversal then expands through real relationships
- **Project-context affinity** — when you're working in a specific project, related entities surface first
- **Contradiction detection** — conflicting facts are flagged, not silently coexisting
- **Full provenance** — every edge traces back to the conversation, transcript, or document that sourced it
- **Dream process** — a scheduled Claude session reviews recent transcripts and ingest documents overnight, extracts new knowledge, applies decay, and writes a changelog
## Architecture
```
Claude Code Claude Desktop claude.ai web
│ │ │
└────────── OAuth 2.1 Bearer ─────────┘
│
https://your-host.example/mcp
│
Cloudflare Tunnel
│
docker-compose
┌────────────┴────────────┐
▼ ▼
graph-memory-mcp graph-memory-neo4j
(Node 22 + jose) (Neo4j 5.20 + APOC)
port 3847 bolt://neo4j:7687
│ │
└─── bolt-internal ───────┘
```
Two Docker services, talking over the compose network (plus an optional third, `graph-dream`, covered below). The MCP server is the only thing that touches Neo4j directly — it implements OAuth 2.1 itself (RS256 JWTs, public clients with PKCE-S256, RFC 7591 dynamic client registration, RFC 7009 revocation), validates bearer tokens for `/mcp` calls, and exposes Cloudflare Access only on `/oauth/authorize` for the actual user login. The Neo4j instance has no external listeners.
The dream process is just another Claude session that runs on a schedule, reads transcripts, and calls the same MCP tools any client would call — there's no separate extraction pipeline.
By default that session is a Claude Code scheduled task on your Claude plan. If you'd rather bill it to an Anthropic API key, the optional dream runner (`src/dream-runner/`, the `graph-dream` compose service under the `dream` profile) runs the same nightly and weekly prompts through the Messages API, with a per-run cost cap and an email summary of each run's cost. The same runner can run your other scheduled prompts on the host with only the commands, URLs and MCP tools you allow (`dist/dream-runner/task.js`). See [API-key runner](docs/DREAM_PROCESS.md#api-key-runner).
Two more opt-in pieces use [TypeSafe's Jev](https://docs.typesafe.ai), a model that returns typed judgments for a fraction of a cent each. With `JEV_VERB_SELECT=on`, an edge that would be stored as `RELATED_TO` gets a documented verb when Jev is confident, and stays `RELATED_TO` otherwise. With `JEV_DUPLICATE_CHECK=on`, the nightly runner judges `graph_merge_suggestions` candidates and writes them to a log for you to review; it never merges. Both are off by default and capped on cost. See [Jev judgments](docs/DREAM_PROCESS.md#jev-judgments-optional).
## Schema
**Node types (19):** `Person`, `Organization`, `Project`, `Feature`, `Concept`, `Technology`, `Decision`, `Reasoning`, `Preference`, `Event`, `Fact`, `Artifact`, `Object`, `Resource`, `Infrastructure`, `Alias`, `Location`, `Issue`, `Task`.
**Edge verbs (50):** grouped into people and roles (`WORKS_ON`, `REPORTS_TO`, `KNOWS`, …), knowledge and decisions (`PREFERS`, `DECIDED_FOR`, `LED_TO`, …), tech and dependencies (`USES_TECH`, `DEPENDS_ON`, `IMPLEMENTS`, …), composition (`PART_OF`, `CONTAINS`, `INSTANCE_OF`, …), reference (`ABOUT`, `DESCRIBES`, `DOCUMENTS`), authorship, lifecycle (`SUPERSEDES`, `REPLACES`, `BLOCKS`, …), place, events, and identity (`ALIAS_OF`, `CONTRADICTS`). `RELATED_TO` is the fallback when no specific verb fits, with a `relationship_type` subtype property. Writes are checked against this vocabulary; `SCHEMA_ENFORCEMENT` controls whether unknown verbs are coerced (default) or rejected.
Every node and edge carries:
- `weight` (0.0–1.0) — decays over time on per-type half-lives
- `confidence` — separate from weight, tracks the source's certainty
- `tenant_id` — multi-tenant isolation (single-user by default; multi-user-ready via OAuth email claim)
- `embedding` (nodes) — 384-dim vector for semantic search
- `valid_at` / `invalid_at` / `ingested_at` (edges) — bi-temporal tracking
Concise vocabulary in [`GRAPH_SCHEMA.md`](GRAPH_SCHEMA.md). Full reference (weights, decay, validity windows, init Cypher) in [`docs/GRAPH_SCHEMA_REFERENCE.md`](docs/GRAPH_SCHEMA_REFERENCE.md).
## Tools
The MCP server exposes 24 tools across these categories:
| Category | Tools |
|---|---|
| Query | `graph_query`, `graph_search` (semantic), `graph_entities`, `graph_contradictions`, `graph_communities`, `graph_build_context` |
| Write | `graph_relate` (single + batch), `graph_boost`, `graph_weaken`, `graph_delete`, `graph_merge`, `graph_unmerge`, `graph_alias` |
| Maintenance | `graph_decay`, `graph_prune`, `graph_validate`, `graph_reembed`, `graph_merge_suggestions` |
| Operational | `graph_stats`, `graph_export`, `graph_audit`, `graph_ingest`, `graph_read_transcript`, `graph_cypher` (admin only) |
Slash-command wrappers (`/graph`, `/graph-ask`, `/graph-find`, `/graph-stats`, `/graph-boost`, `/graph-briefing`, `/graph-dream`, `/graph-backup`, `/graph-bootstrap`, `/graph-capture`, `/ingest`, `/ingest-audio`) install into `~/.claude/skills/`. Full reference: [`docs/SKILLS.md`](docs/SKILLS.md).
`/graph-capture` is the manual companion to the nightly dream: the dream extracts knowledge from Claude Code transcripts in `~/.claude/projects/`, but cannot see claude.ai web conversations or Claude Desktop chats (those live server-side or in Electron app data). Run `/graph-capture` at the end of a substantive claude.ai or Desktop conversation to commit any new entities, decisions, or facts to the graph. Claude Code cloud sessions are different: a collector prompt (`prompts/collect-cloud-sessions.md`) queues their text into the ingest backlog through `graph_ingest`, and the nightly dream extracts it (see [Cloud sessions](docs/DREAM_PROCESS.md#cloud-sessions-and-claude-code-projects)).
## Prerequisites
**Required:**
- **Node.js 22+** and npm
- **Docker** (Desktop on Windows/macOS, or Docker Engine on Linux) with Docker Compose v2
- **Claude Code** and/or **Claude Desktop** with a Claude plan that covers Claude Code access — Pro, Max, Team, Enterprise, or Console all work (the free Claude.ai plan does not include Claude Code). Pro is fine for light/exploratory use; **Max is recommended for daily-use deployments** because the nightly dream process can be transcript-heavy on a busy day, and an unattended run that exhausts Pro's 5-hour window will abort mid-extraction and skip that night. Running the dream on an Anthropic API key instead avoids the plan limits.
- A few hundred MB of disk for Neo4j + embeddings model
**Optional:**
- **[MarkItDown](https://github.com/microsoft/markitdown)** (`pip install "markitdown[pdf,docx,xlsx,pptx]"`) — enables ingesting binary documents (`.pdf`, `.docx`, `.xlsx`, `.pptx`, `.epub`, `.msg`, `.csv`, `.xml`, `.png`, `.jpg`). Without it, ingest is limited to `.md`, `.txt`, `.json`, `.html`, `.srt`, `.vtt`.
- **[yt-dlp](https://github.com/yt-dlp/yt-dlp)** — convenient way to grab YouTube/web video subtitle files for ingestion. `yt-dlp --write-auto-sub --sub-lang en --skip-download <url>` writes a `.vtt` you can drop into `ingest/pending/`. Not a runtime dependency; just a tool that produces files graph-memory can already eat.
- **[cloudflared](https://github.com/cloudflare/cloudflared)** + a Cloudflare account — only needed for the multi-device / claude.ai web setup described in [`docs/REMOTE.md`](docs/REMOTE.md). Local-only deployments don't need it.
- **Anthropic API key** — only for the optional [API-key runner](docs/DREAM_PROCESS.md#api-key-runner).
- **TypeSafe API key** — only for the optional [Jev judgments](docs/DREAM_PROCESS.md#jev-judgments-optional) (verb selection and the duplicate check).
- **Python 3.10+** — required only by MarkItDown and by `scripts/sync-dream-skill.py`.
## Install
graph-memory has exactly one "primary device" — the machine that **runs the two Docker containers** (Neo4j + the MCP server) **and** runs the nightly dream + weekly maintenance scheduled tasks. Every other device is a "secondary device" that talks to the primary over HTTPS + OAuth — secondaries don't run their own containers and don't run their own dream process. Pick the install path that matches the role of the device you're sitting at right now.
### Install — Primary Device (this device runs the containers)
Use this on the machine that will host Neo4j + the MCP server. This is also where the nightly dream and weekly maintenance scheduled tasks run, so the Claude Code transcripts you want extracted should live on this device.
**Linux / macOS / Windows with Git Bash or WSL:**
```bash
curl -fsSL https://raw.githubusercontent.com/stevepridemore/graph-memory/v0.6.0/scripts/install-primary.sh \
| bash -s v0.6.0
# edit ~/graph-memory/.env (NEO4J_PASSWORD, GRAPH_MEMORY_HOME, CLAUDE_PROJECTS_DIR)
cd ~/graph-memory && docker compose up -d
```
**Windows PowerShell (no bash needed):**
```powershell
$v = 'v0.6.0'
iwr "https://raw.githubusercontent.com/stevepridemore/graph-memory/$v/scripts/install-primary.ps1" -UseBasicParsing -OutFile $env:TEMP\gm-install.ps1
& $env:TEMP\gm-install.ps1 -Version $v
# edit $HOME\graph-memory\.env
cd $HOME\graph-memory; docker compose up -d
```
Verify with `/graph-stats` in any Claude Code session.
Optional: see [`docs/REMOTE.md`](docs/REMOTE.md) for the Cloudflare Tunnel + Access setup that lets secondary devices and claude.ai web reach this graph remotely.
### Install — Secondary Device (this device just talks to the primary)
Use this on every additional laptop, work computer, or phone. No Docker, no Neo4j — just the slash commands and an MCP client config pointed at the primary device's Cloudflare Tunnel URL. The primary device must already have the tunnel set up per [`docs/REMOTE.md`](docs/REMOTE.md).
**Linux / macOS / Windows with Git Bash or WSL:**
```bash
curl -fsSL https://raw.githubusercontent.com/stevepridemore/graph-memory/v0.6.0/scripts/install-secondary.sh \
| bash -s v0.6.0 your-tunnel-host.example.com
```
**Windows PowerShell (no bash needed):**
```powershell
$v = 'v0.6.0'
iwr "https://raw.githubusercontent.com/stevepridemore/graph-memory/$v/scripts/install-secondary.ps1" -UseBasicParsing -OutFile $env:TEMP\gm-install.ps1
& $env:TEMP\gm-install.ps1 -Version $v -TunnelHost your-tunnel-host.example.com
```
First `/graph-stats` call triggers the OAuth browser flow once; subsequent calls use the cached bearer token.
### Install — Developer (build from source)
Use this if you want to modify graph-memory itself. Requires Node 22+ and Docker.
```bash
git clone https://github.com/stevepridemore/graph-memory
cd graph-memory
cp .env.example .env # edit as above
npm install && npm run build
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
```
The `docker-compose.dev.yml` override switches the MCP service from the published GHCR image to a local `build: .` so your edits get picked up on rebuild.
## Multi-device / claude.ai web access
To use the same graph from claude.ai web, your office laptop, your phone, etc., expose the MCP server through Cloudflare Tunnel + Access. The auth flow is OAuth 2.1 with Cloudflare's IdP doing the actual user login.
Step-by-step in [`docs/REMOTE.md`](docs/REMOTE.md). The setup is one-time:
1. Cloudflare Tunnel with `cloudflared` pointing at `https://localhost:3847`
2. A single Cloudflare Access application scoped to `/oauth/authorize` (everything else is public + bearer-token-protected)
3. Server generates an RSA keypair on first run, persists it, exposes via `/oauth/jwks`
4. Claude clients hit `https://your-host.example/mcp`, get a 401 with proper `WWW-Authenticate: Bearer ... resource_metadata="..."`, walk the OAuth flow, store the bearer token, and call subsequent requests with it
This makes the graph reachable from any device or AI tool that speaks MCP + OAuth 2.1.
For Claude Code on remote machines, [`.mcp.json.remote.example`](.mcp.json.remote.example) is the matching client template — copy it to `~/.claude/.mcp.json` (or a project-local `.mcp.json`) and replace `your-host.example` with your tunnel hostname:
```json
{
"mcpServers": {
"graph-memory": {
"type": "http",
"url": "https://your-host.example/mcp"
}
}
}
```
Claude Code walks the OAuth flow on first call and caches the bearer token. claude.ai web uses its own custom-connector UI — the URL is the same.
If you use Claude Code on more than one PC and want a single dream process to ingest transcripts from all of them, see [Multi-PC transcript sharing](docs/REMOTE.md#multi-pc-transcript-sharing) — that's a separate concern from the OAuth multi-device story above, with a one-time sync setup.
## Document ingestion
Drop files into `~/graph-memory/ingest/pending/`, or call `graph_ingest` with a file path or, from a remote caller that can't reach the server's disk, with inline text (`content` + `file_name`, up to 2 MB). The next dream run extracts entities and relationships into the graph. Native support for `.md`, `.txt`, `.json`, `.html`, `.srt`, `.vtt`. With [MarkItDown](https://github.com/microsoft/markitdown) installed (`pip install "markitdown[pdf,docx,xlsx,pptx]"`), also handles `.pdf`, `.docx`, `.xlsx`, `.pptx`, `.epub`, `.msg`, `.csv`, `.xml`, `.png`, `.jpg`, etc. — converted to Markdown first, then extracted. Original files archive to `ingest/originals/<date>/`.
## Privacy
The graph stores personal information — names of colleagues, decisions, preferences, project details. Treat the database with the same care as a private journal:
- Default deployment is local-only (Docker on `localhost`); nothing leaves your machine
- The optional Cloudflare Tunnel exposure adds OAuth + Cloudflare Access in front
- All data lives under a directory you control (default `~/graph-memory/`)
- A `graph_export` tool produces portable JSONL backups; `~/graph-memory/backups/` is auto-rotated
- Embedding model runs locally — no text leaves the machine for vector search
- Entity extraction runs in your Claude sessions; same trust boundary as Claude itself. With the optional API-key runner, transcripts go to the Anthropic API under your API account instead
- API keys, passwords, and secrets are explicitly excluded from extraction (see `prompts/dream-nightly.md`)
## Tech stack
| Component | Technology |
|---|---|
| Language | TypeScript / Node.js 22 |
| Graph DB | Neo4j Community 5.20 (Docker) with APOC |
| Embedding model | `@huggingface/transformers` running bge-small-en-v1.5 (384-dim, ONNX) |
| Driver | `neo4j-driver` |
| MCP framework | `@modelcontextprotocol/sdk` |
| Auth | `jose` for JWT signing/verification (RS256) |
| API-key runner (optional) | `@anthropic-ai/sdk` |
| Typed judgments (optional) | TypeSafe Jev over HTTPS |
| Tunnel (optional) | Cloudflare Tunnel (`cloudflared`) + Cloudflare Access |
| Testing | Vitest |
## Status
All planned phases shipped:
- ✅ Phase 0–3: MCP server, dream process, SessionStart hook, slash commands
- ✅ Phase 4: Bootstrap complete (graph populated from transcripts and memory files)
- ✅ Phase 5: bi-temporal modeling, Reasoning entity type, semantic/vector search, community detection, build_context meta-tool
- ✅ Multi-tenant infrastructure (single-user by design, multi-user-ready)
- ✅ OAuth 2.1 + Cloudflare Tunnel for multi-device access
- ✅ Aura → local Neo4j migration with full data preservation
- ✅ OAuth 2.1 hardening: PKCE-S256 mandatory, public clients only, RFC 7009 revocation, jti tracking, refresh-token TTL 30d, redirect-URI allowlist, optional email allowlist, body-size caps (64 KB OAuth / 4 MB MCP), structured event logging
- ✅ Internal threat model fully resolved (16 of 16 findings closed)
- ✅ npm audit clean (0 vulnerabilities)
- ✅ Pre-built GHCR images + curl-pipeable installers (no clone or local build required for end users)
Current release: [`v0.6.0`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.6.0).
Currently steady-state. Active development is opportunistic; the system runs unattended via the nightly dream process.
## Releases
Newest first. Each tag publishes `ghcr.io/stevepridemore/graph-memory-mcp:<tag>` and moves `:latest`.
| Version | Date | Summary |
|---|---|---|
| [`v0.6.0`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.6.0) | 2026-10-27 | `graph_prune` execute now spares edges on rule nodes, as its preview already said. Tool descriptions for `graph_decay`, `graph_validate`, `graph_search` and `graph_communities` match what the tools return. Opt-in TypeSafe Jev features: verb selection for edges that would be stored as `RELATED_TO`, and a log-only duplicate check after the nightly dream. |
| [`v0.5.0`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.5.0) | 2026-10-27 | Optional API-key runner: the nightly dream and weekly maintenance can run on an Anthropic API key (`graph-dream` compose service, `dream` profile) with per-run cost caps and an email summary of each run's cost. A general task runner (`dist/dream-runner/task.js`) runs other scheduled prompts on the same key with only the commands, URLs and MCP tools a task file allows. `graph_ingest` accepts inline content so Claude Code cloud sessions can feed the nightly dream. Adds the `plain-tech-docs` skill for checking docs. |
| [`v0.4.0`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.4.0) | 2026-10-11 | Security and data-integrity release. Fixes Cypher injection through relation and type names, and a `tenant_id` overwrite through caller-supplied properties. Fixes compounding decay, makes batch writes atomic, routes every write through one entity resolver, and adds the `graph_alias` tool. Types and relations are now checked against the documented vocabulary (see `SCHEMA_ENFORCEMENT`). Transcript format detection no longer misreads transcripts that open with metadata records. |
| [`v0.3.1`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.3.1) | 2026-05-17 | Rule subtype for permanent preferences, exempt from decay and pruning. `.gitattributes` forces LF on shell scripts and the Dockerfile so a Windows clone builds a working image. |
| [`v0.3.0`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.3.0) | 2026-05-10 | Curl-pipeable primary/secondary device installers + pre-built GHCR image. Multi-stage Dockerfile, auto-cert generation on first run, vendored slash commands. End users no longer need to clone or build from source. |
| [`v0.2.1`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.2.1) | 2026-05-09 | STRIDE threat model fully closed (16 of 16 findings). Hardens OAuth 2.1: PKCE-S256, RFC 7009 revocation, jti tracking, refresh-token TTL, redirect-URI allowlist, email allowlist, body-size caps. |
| [`v0.2.0`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.2.0) | 2026-05-09 | OAuth 2.1 security hardening pass. Public clients only, mandatory PKCE, body-size caps, structured event logging. |
| [`v0.1.1`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.1.1) | 2026-05-08 | Decay correctness + test coverage. Vitest in CI, decay function bug fixes. |
| [`v0.1.0`](https://github.com/stevepridemore/graph-memory/releases/tag/v0.1.0) | 2026-05-07 | Initial public release. MCP server, dream process, slash commands, bi-temporal modeling, semantic search, OAuth multi-device. |
## Documentation
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — system design, data flows, component responsibilities
- [`GRAPH_SCHEMA.md`](GRAPH_SCHEMA.md) — concise vocabulary (node types + edge verbs) for both agent and humans
- [`docs/GRAPH_SCHEMA_REFERENCE.md`](docs/GRAPH_SCHEMA_REFERENCE.md) — full reference (decay functions, weight semantics, validity windows, example queries)
- [`docs/MCP_SERVER.md`](docs/MCP_SERVER.md) — every MCP tool with input/output schemas
- [`docs/DREAM_PROCESS.md`](docs/DREAM_PROCESS.md) — extraction pipeline, manifest format, changelog structure, API-key runner
- [`docs/BACKUP.md`](docs/BACKUP.md) — backups and restore
- [`docs/SKILLS.md`](docs/SKILLS.md) — slash command definitions
- [`docs/REMOTE.md`](docs/REMOTE.md) — exposing graph-memory via Cloudflare Tunnel + Access
- [`docs/MULTI_TENANT.md`](docs/MULTI_TENANT.md) — tenant isolation model
- [`docs/TESTING.md`](docs/TESTING.md) — running the test suite locally and in CI
- [`CLAUDE.md`](CLAUDE.md) — retrieval and chat-write guidelines for Claude
## License
MIT — see [`LICENSE`](LICENSE).
TDQS
Scored across 24 tools
Most tools are cleanly distinct, and the descriptions actively steer between the few lookalikes (graph_query for canonical names vs graph_search for natural language vs graph_entities for catalog browsing vs graph_cypher for raw queries). The main residual overlaps are graph_stats versus graph_build_context (which explicitly bundles stats) and the decay/prune/delete trio, but in each case the docs spell out when to use which.
Every tool is snake_case with the same graph_ prefix, and nearly all are verb-led (graph_query, graph_merge, graph_reembed, graph_build_context). No camelCase or style mixing anywhere in the set.
24 tools is at the heavy end for this surface, but the domain genuinely spans CRUD, entity resolution, decay/pruning, ingest, and dream-process maintenance, and each tool maps to a real operation rather than a convenience duplicate. A few specialist tools (graph_audit, graph_read_transcript, graph_cypher) are narrow but justified by admin/maintenance workflows.
Lifecycle coverage is strong: create/strengthen (graph_relate), read (query/search/entities/cypher), modify (boost/weaken/alias/merge/unmerge), delete (graph_delete/graph_prune), plus maintenance (decay/validate/reembed/export/stats) and ingest. Minor gaps: no direct delete-single-edge-by-id operation (weakening to 0.0 or deleting the entity is the workaround) and no in-band schema/type listing, which is deferred to an external GRAPH_SCHEMA.md.