Skip to content

How It Works

Maxim Mironenko edited this page Mar 16, 2026 · 16 revisions

How It Works

Architecture Overview

┌─────────────────────────────────────────────────┐
│            Claude Code Sessions                  │
│  Session A        Session B        Session C     │
└──────┬───────────────┬────────────────┬──────────┘
       │               │                │
       │  Stateless HTTP (MCP protocol) │
       └───────┬───────┘                │
               ▼                        │
    ┌──────────────────────┐            │
    │  MCP Streamable HTTP │            │
    │  Server (port 8765)  │◄───────────┘
    │                      │
    │  - MCP tools (/)     │
    │  - REST API (/api/*) │
    │  - WebSocket (/ws)   │
    └──────────┬───────────┘
               │
               ▼
    ┌──────────────────────┐
    │ MultiProjectGraphStore│
    │  - In-memory graphs  │
    │  - Write-through     │
    │  - Auto-compact      │
    └──────────┬───────────┘
               │
        ┌──────┴──────┐
        ▼             ▼
~/.knowledge-graph/
├── user.json          (global)
├── sessions.json
└── projects/
    ├── my-app/graph.json
    └── other/graph.json

Two-Level Model

Knowledge is split into two levels, all stored centrally:

Level What Goes Here Storage Location
user Cross-project wisdom: preferences, meta-learnings, architectural principles, personal patterns ~/.knowledge-graph/user.json
project Codebase-specific: architecture decisions, dependencies, debugging discoveries, conventions ~/.knowledge-graph/projects/<slug>/graph.json

The user graph is a singleton — one file, always loaded. Project graphs are loaded on demand when a session registers with a project_path.

Nodes and Edges

The graph has two entry types:

Nodes — Named concepts, patterns, or insights:

{
  "id": "auth-token-refresh",
  "gist": "JWT refresh uses sliding window; silent failure if session expired",
  "notes": ["discovered during auth debugging 2025-01"],
  "touches": ["src/auth/refresh.py"]
}

Edges — Relationships between nodes, files, or concepts:

{
  "from": "auth-module",
  "to": "session-handler",
  "rel": "requires-init",
  "notes": ["token validation assumes active session"]
}

Edges can reference node IDs or file paths directly — you don't need a node for every file.

Data Flow

  1. Session starts → Claude calls kg_register_session() then kg_read() which returns the full active graph (both levels)
  2. During work → Claude calls kg_put_node/kg_put_edge to capture insights
  3. Write-through → Every mutation saves to disk immediately (atomic write: temp file + rename)
  4. Background maintenance → Periodic thread (30s) runs compaction and orphan pruning
  5. Multi-session → Other sessions call kg_sync(session_id) to get changes since their start time

All operations go through the shared in-memory store. Write-through ensures disk is always up-to-date.

Auto-Compaction

When a graph exceeds 3000 tokens (estimated), the system archives low-value nodes:

  1. Score each node using three percentile-ranked signals:
    • Recency — When was it last updated?
    • Connectedness — How many edges + touches does it have?
    • Richness — How much content (gist + notes length)?
  2. Final score = recency × connectedness × richness (all percentiles, so 0.0–1.0)
  3. Archive lowest-scoring nodes until graph is under 90% of the token limit
  4. Grace period — Nodes updated in the last 3 days are never archived

Archived nodes get _archived: true. Their edges remain visible as memory traces — hints that related knowledge exists. Use kg_recall(level, id) to bring them back.

Orphan Cleanup

Archived nodes with no edges to active nodes are marked as orphaned (_orphaned_ts). After 30 days of being orphaned (configurable via KG_ORPHAN_GRACE_DAYS), they're permanently deleted along with their edges.

If an orphaned node gets reconnected (an active node links to it), the orphan timestamp is cleared.

Token Estimation

The system estimates token cost to decide when to compact:

Component Estimate
Base cost per node 20 tokens
Node text 1 token per 4 characters (gist + notes)
Per edge 15 tokens
Archived nodes Not counted toward limit

These are rough estimates — the goal is keeping the active graph small enough to fit in Claude's context window without being wasteful.

Transport: Stateless HTTP

The server uses MCP's Streamable HTTP transport in stateless mode. Each request is independent — no session IDs preserved between HTTP requests by the MCP layer.

This is because Claude Code's MCP client doesn't preserve session IDs between requests. The server was tested with stateful mode and it failed — Claude Code simply doesn't send the mcp-session-id header back.

Session tracking for sync (kg_sync) is handled at the application level, not the transport level.

Concurrency

  • Thread-safe via threading.RLock on the graph store
  • Multiple Claude Code sessions can read/write simultaneously
  • Last write wins — no conflict resolution beyond that
  • The kg_sync tool lets sessions pull changes made by other sessions

Clone this wiki locally