Skip to main content
Glama
README.md
# sandbook

A multi-tiered memory subsystem for AI agents — markdown + vector + graph long-term memory — usable as a standalone **memory MCP server for Claude**, independent of any host application.

`sandbook` is a dependency-light Python package that works standalone: in a clean virtualenv, `from sandbook import Memory` works with no host application on the path.

## What you get

| Surface | Entry point | Transport |
|---|---|---|
| Python library (embedded) | `from sandbook import Memory, MemorySettings, MemoryEntry, …` | in-process |
| Standalone server | `python -m sandbook.mcp.standalone` | streamable-HTTP — stochastic MCP at `/mcp` **and** thin engine RPC at `/v1` |
| Engine RPC client | `from sandbook.client import MemoryClient, AsyncMemoryClient` | sync + async over `/v1` |
| Memory web UIs | graph explorer at `/graph`, timeline at `/memory/timeline` (on the standalone server) | HTML over HTTP |
| Claude Code plugin | `.claude-plugin/plugin.json` (uv-launched stdio) | stdio MCP |
| Anthropic `memory_20250818` adapter | `sandbook.adapters.anthropic_memory.AnthropicMemoryTool` | Messages API memory tool |

The embedded `Memory` and the `AsyncMemoryClient` both satisfy the `MemoryLike` operations protocol (`sandbook.operations`), so a consumer can run the engine in-process or talk to a warm server without changing call sites.

The public API (50 names) is whatever `sandbook/__init__.py` re-exports. Everything heavier — the MCP transports (`sandbook.mcp`), the on-disk config (`sandbook.config`), the timeline subsystem (`sandbook.timeline`), and the adapters (`sandbook.adapters`) — lives in sub-namespaces that are **not** auto-imported, keeping the core import light.

![Multi-surface architecture overview — four consumer surfaces (Python app, MCP client, /v1 HTTP client, Claude Code plugin) converge on one Memory facade (the MemoryLike protocol), which sits over the two-wheel split: sandbook-core, the always-present markdown floor backing the Markdown store and the SQLite timeline, and the sandbook engine — retrieval, storage, consolidation — backing the Chroma vector LTM and the Neo4j graph LTM.](docs/diagrams/multi-surface-overview/rendered/multi-surface-overview.svg)

## Pick your path

sandbook exposes three integration surfaces (Python API · MCP server · Claude Code plugin), plus the `/v1` RPC client and web UIs. Jump to the section that fits your use case:

- **[Plugin user](#install-as-a-claude-code-plugin)** — wire memory into Claude Code sessions via hooks; no Python code required.
- **[Library embedder](#use-as-a-library)** — `import sandbook` and drive the engine in-process.
- **[MCP server / remote client](#run-as-a-memory-mcp-server-for-claude)** — run a warm server and connect any MCP client or the `/v1` RPC to it.

For per-persona quickstarts with verify and troubleshoot steps, see [docs/integration.md](docs/integration.md).

## Requirements

- Python 3.11+
- pip or [uv](https://docs.astral.sh/uv/)

## Install

> [!NOTE]
> `sandbook` is alpha and not yet on PyPI — install from source today (see the `uv sync` commands below). The `pip install` lines describe the published-package shape for when the package reaches general availability.

`pip install sandbook` is the base install — it gives you the **base memory** layer with only light dependencies (`pydantic`, `loguru`, `pyyaml`), and `pip install "sandbook[…]"` layers on the heavier capability tiers. The import path is identical either way (`from sandbook import Memory`) — installing an extra upgrades the same import in place.

```bash
pip install sandbook                                # base memory — start here
pip install "sandbook[ltm-vector]"                  # + semantic (vector) long-term memory
pip install "sandbook[ltm-vector,ltm-graph,mcp]"    # + graph LTM + the MCP server

# With uv, from the sandbook/ checkout:
uv sync                                             # base memory only
uv sync --extra mcp                                 # + the server + web UIs (one extra)
uv sync --all-extras                                # everything, incl. the ML deps
```

> `pip install sandbook` pulls `sandbook-core` automatically (it is a dependency) — you almost never install `sandbook-core` by hand. See [Minimal footprint](#minimal-footprint-sandbook-core) below for the one case you might.

![Two-wheel install profiles — pip install sandbook-core gives the dependency-light markdown floor; pip install sandbook gives the engine wheel, which pulls sandbook-core. Additive extras layer on the engine: ltm-vector adds Chroma plus embeddings; ltm-graph adds Neo4j plus PPR and implies ltm-vector; mcp adds the servers, /v1, and web UIs; client adds the sync and async /v1 clients.](docs/diagrams/install-profiles/rendered/install-profiles.svg)

### Base memory (`pip install sandbook`)

The base install needs **no heavy backends** (no ChromaDB / Neo4j / ML deps). It composes six pieces:

| Piece | What it is | Why it's useful |
|---|---|---|
| **Markdown stores** | Human-readable `.md` files are the source of truth for every memory | Inspectable, diffable, portable; survives any backend swap; the floor every heavier tier rebuilds from |
| **Scratchpad** | A working-memory note the agent reads and writes within a session | Holds in-progress reasoning and intermediate state without polluting long-term memory |
| **Session log** | An append-only write-ahead log of the conversation turns | Lets recall reconstruct "what we just discussed" and feeds the consolidation pipeline |
| **Timeline (SQLite)** | A queryable event overlay over the markdown entries | Episodic "when did we discuss X" enumeration (`recall_events`) with no vector store required |
| **Event-family registry** | A typed registry for custom event kinds | A host registers and queries its own event families (`register_event_family` / `query_by_family`) |
| **`Memory` Python API** | The `from sandbook import Memory` facade — `remember` / `recall` / `recall_events` | One object, identical across every profile; degrades to the markdown floor when no engine tier is installed |

### Capability tiers (extras)

<details>
<summary>Full extras reference (ltm-vector · ltm-graph · mcp · client · anthropic / agent-sdk)</summary>

Each extra is **additive** — pick the smallest set that covers what you need. The LTM (long-term-memory) store tiers are named for what they *are*, not the generic "memory".

| Extra | Install | Adds | Why you'd add it |
|---|---|---|---|
| **ltm-vector** | `pip install "sandbook[ltm-vector]"` | ChromaDB vector store + sentence-transformers embeddings | Semantic recall over the whole corpus — find by *meaning*, not just keyword |
| **ltm-graph** | `pip install "sandbook[ltm-graph]"` | Neo4j knowledge graph + Personalized-PageRank retrieval (implies `ltm-vector`) | Multi-hop "what relates to X" recall over an entity/fact graph |
| **mcp** | `pip install "sandbook[mcp]"` | The stdio + HTTP MCP servers, the deterministic `/v1` engine RPC, and the graph + timeline web UIs | Serve memory to Claude (and any MCP client) as a tool surface; inspect it in a browser |
| **client** | `pip install "sandbook[client]"` | The sync + async `/v1` clients (`sandbook.client`) | Talk to a warm sandbook server over HTTP instead of embedding the engine — for hooks and non-MCP consumers |
| **anthropic** / **agent-sdk** | `pip install "sandbook[anthropic]"` | The Anthropic / Agent-SDK clients for LLM-backed consolidation + the `memory_20250818` adapter | Higher-quality reflection/consolidation when an LLM is available (degrades to deterministic when it isn't) |

</details>

### Minimal footprint: `sandbook-core`

sandbook ships as **two wheels under one `sandbook` import namespace** (dec-036). `pip install sandbook` is the engine wheel; it pulls `sandbook-core` automatically, so **most users never install `sandbook-core` directly** — exactly like `pip install langchain` pulls `langchain-core`. The one case for installing it alone is an **embedded, size-constrained consumer** that wants *only* the base-memory layer above with **zero engine modules on disk**:

```bash
pip install sandbook-core      # markdown floor only — no engine code, no extras
```

`from sandbook import Memory` is identical for both; the core-only profile degrades to the markdown floor. See [Install profiles (two wheels)](docs/host_integration.md#install-profiles-two-wheels) for the full contract and the degradation semantics.

## Run the parts (uv)

From the `sandbook/` directory, `uv` runs each part of the system through its console entry points. The heavy backends are optional extras, so pass them on the `uv run` line (or use `--all-extras`):

| Command | Runs | Extras |
|---|---|---|
| `uv run --all-extras sandbook-server` | HTTP MCP, the `/v1` engine RPC, **and** the web UIs at `:8765` (`/` home, `/mcp`, `/v1`, `/graph`, `/memory/timeline`) | `mcp ltm-vector ltm-graph` |
| `uv run --extra mcp --extra ltm-vector --extra ltm-graph sandbook-stdio` | stdio MCP server (the Claude Code plugin entry) | `mcp ltm-vector ltm-graph` |
| `uv run sandbook-evals` | deterministic memory-quality eval suite | core only |

Each entry point is also a plain module — `python -m sandbook.mcp.standalone`, `python -m sandbook.mcp.stdio`, `python -m sandbook.evals` — for non-uv environments.

## Run as a memory MCP server for Claude

The standalone server is **agentless**: it builds a `Memory` from the environment via `Memory.from_env()` and serves the full memory + graph tool set over streamable-HTTP at `/mcp`.

```bash
uv run --all-extras sandbook-server --host 0.0.0.0 --port 8765
# or, without uv:
python -m sandbook.mcp.standalone --host 0.0.0.0 --port 8765
```

The same server also hosts the memory **web UIs**, so it doubles as a memory-inspection dashboard. Open `http://localhost:8765/` for the home menu; a shared top-nav links the memory timeline (`/memory/timeline`), reflection history (`/memory/reflections`), and the knowledge-graph explorer (`/graph`).

The graph explorer mounts only when the graph tier is enabled. Set `SANDBOOK_GRAPH_ENABLED=true` and a reachable Neo4j (`SANDBOOK_GRAPH_NEO4J_URI`, `SANDBOOK_GRAPH_NEO4J_USER`, `SANDBOOK_GRAPH_NEO4J_PASSWORD`); otherwise the Graph entry is shown disabled and `/graph` returns 404. These can be passed as env vars or kept in a `.env` file (auto-loaded from the working directory, or `--env-file PATH`) — copy `.env.example` to `.env` and fill it in; `.env` is gitignored, so the password stays out of git. Resolution precedence is env vars > `.env` > `sandbook.config.toml [graph]` > defaults. (Live timeline SSE reflects this server's own activity; cross-process events appear as history on query.)

Connect Claude Code to it — via the CLI:

```bash
claude mcp add --transport http sandbook-memory http://localhost:8765/mcp
```

…or in a project / user `.mcp.json`:

```json
{
  "mcpServers": {
    "sandbook-memory": {
      "type": "http",
      "url": "http://localhost:8765/mcp"
    }
  }
}
```

Then `/mcp` inside Claude Code lists `sandbook-memory` and its tools.

### Other MCP clients

The same package ships a stdio entry point (`python -m sandbook.mcp.stdio`) that any stdio-MCP client launches as a subprocess. Each snippet below assumes `sandbook[mcp,ltm-vector,ltm-graph]` is installed in the Python environment that `python` resolves to.

**Claude Desktop** — add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "sandbook-memory": {
      "command": "python",
      "args": ["-m", "sandbook.mcp.stdio"],
      "env": { "SANDBOOK_MEMORY_DIR": "~/.sandbook/memory" }
    }
  }
}
```

**Cursor** — add to the project's `.cursor/mcp.json` (same shape):

```json
{
  "mcpServers": {
    "sandbook-memory": {
      "command": "python",
      "args": ["-m", "sandbook.mcp.stdio"]
    }
  }
}
```

**ChatGPT / Codex** — declare the same stdio command in the client's MCP config (the OpenAI MCP spec uses the same `command` + `args` shape).

**Gemini** — Gemini speaks MCP over HTTP. Run the standalone HTTP server (`uv run --all-extras sandbook-server`) and point Gemini's MCP bridge at `http://localhost:8765/mcp`.

## Install as a Claude Code plugin

sandbook ships a Claude Code plugin manifest (`.claude-plugin/plugin.json`) that launches its memory MCP server over stdio via `uv`. Claude Code clones the plugin and runs the server for you — no manual `pip install`, no separate server process.

The fully-local path (no marketplace, no publishing) loads the plugin straight from your checkout:

```bash
claude --plugin-dir /path/to/sandbook
```

Claude Code reads `.claude-plugin/plugin.json`, launches `sandbook-memory` via `uv run --project mcp/sandbook-memory python -m sandbook_memory_mcp` (which builds an isolated, cached venv from `mcp/sandbook-memory/pyproject.toml` on first launch), and the memory + graph tools appear under `/mcp`.

**Prewarm the plugin venv before first launch** to avoid a multi-minute download inside a live Claude session:

```bash
uv sync --project mcp/sandbook-memory
```

**First-launch timing.** On first use, `uv` downloads sentence-transformers and its transitive dependencies (several hundred MB, potentially 1–3 GB with CUDA wheels). This is a one-time cost — subsequent launches start in under a second from the cached venv.

**Data safety under uninstall.** Plugin memory lives at the configured `memory_dir` (default `~/.sandbook/memory`) — outside the sandbook checkout and outside Claude Code's plugin data directory. Uninstalling the plugin or deleting the repo does **not** remove your memories. To relocate or remove memory before uninstalling, move or delete your configured `memory_dir`. Note: `~/.sandbook/plugin` is the hook cursor-file root (`SANDBOOK_PLUGIN_DATA_ROOT`) and the legacy path used by pre-update installs; your actual memories live at `memory_dir`. See [Host Integration Contract § 4](docs/host_integration.md#4-per-surface-memory-dir-contract).

**Disable autospawn.** By default the hooks auto-start the sandbook daemon when it is not running. To manage the daemon yourself (e.g. with a process supervisor), set:

```bash
export SANDBOOK_HOOK_AUTOSPAWN=false
```

**Verify the end-to-end chain** with the built-in doctor command:

```bash
uv run sandbook-doctor
```

`sandbook-doctor` checks the hook registration, store-path coherence, daemon reachability, the `/v1/recall` contract, and LLM consolidation wiring — and prints a three-part fix for anything that is broken.

To publish sandbook for others via a marketplace, see [README_DEV.md](README_DEV.md#4-package-sandbook-as-a-claude-code-plugin).

## Use as a library

```python
from sandbook import Memory, MemoryCategory

memory = Memory.from_env()          # data root from SANDBOOK_MEMORY_DIR
await memory.startup_check()

await memory.remember("the user prefers dark mode", category=MemoryCategory.PREFERENCE)
response = await memory.recall("ui preferences")   # the semantic read → MemoryResponse
print(response.context)
```

The core verbs are **`remember`** (write) and **`recall`** (the semantic read); `recall_events(topic, since=…, until=…)` is the episodic "when did we discuss X" enumeration. To talk to a warm server instead of embedding the engine, swap `Memory` for `sandbook.client.AsyncMemoryClient` (same `recall`/`remember` surface over `/v1`).

## Configuration

Selected variables; the full model is `MemorySettings` over `sandbook.config.toml`:

| Env var | Default | Meaning |
|---|---|---|
| `SANDBOOK_MEMORY_DIR` | — (required; no default) | markdown data root for `from_env()`; raises `MemoryConfigError` if unset (conventional value `~/.sandbook/memory`) |
| `SANDBOOK_CONSOLIDATORS_ENABLED` | `true` | background consolidation |
| Neo4j / ChromaDB settings | see `sandbook.config.toml` | graph + vector backends |

> [!NOTE]
> All environment variables are `SANDBOOK_*`. `SANDBOOK_MEMORY_DIR` is **required** and has no default — `from_env()` raises `MemoryConfigError` when it is unset; `~/.sandbook/memory` is only the conventional location (and the plugin manifest's default). The other path settings *do* default under `~/.sandbook/` (e.g. `SANDBOOK_MEMORY_VECTOR_STORE_PATH` → `~/.sandbook/chroma`) — defaults, not hard constants — override them via environment, TOML, `MemorySettings`, or `Memory(memory_dir=...)`. Data written by earlier sandbook versions under the legacy `~/.francisco` data root can be moved with the one-shot `sandbook-migrate-chroma-path` script (dry-run by default; `--apply` performs the move).

## Evaluate memory quality

sandbook ships an independent eval suite that reports the **current state, quality, behaviour, and performance** of the memory system. It drives the public `Memory` API with synthetic scenarios and scores four groups — Correctness, Quality, Behaviour, Performance — fully deterministically (zero network/LLM). An optional LLM-judged Cognition group runs when you provide a callback.

> [!NOTE]
> The suite installs synthetic test entries, so it runs against an **isolated, fresh memory dir by default** — your real `~/.sandbook/memory` is never touched.

```bash
# With uv: `uv run sandbook-evals [args]`.  Without uv: `python -m sandbook.evals [args]`.

# Deterministic report (text)
uv run sandbook-evals

# JSON, one group, or a single scenario
uv run sandbook-evals --json
uv run sandbook-evals --group correctness
uv run sandbook-evals --scenario abstention

# Also run the LLM-judged cognition group (needs the anthropic extra + ANTHROPIC_API_KEY)
uv run --extra anthropic sandbook-evals --llm
```

Programmatic use (pass any `Memory`; inject `llm_callback` to enable cognition):

```python
from sandbook.evals import run_memory_evals, format_text_report

report = await run_memory_evals(memory)          # deterministic
print(format_text_report(report))
print(report.composite_score, report.below_threshold)
```

The exit code is non-zero when any scored scenario falls below its threshold (CI-friendly). An MCP tool `run_memory_eval` is also exposed by the standalone server (it runs against an isolated instance). Authoring new scenarios: drop YAML into `sandbook/evals/scenarios/` — see [README_DEV.md](README_DEV.md).

## Documentation

**Start here by goal:**

- [docs/integration.md](docs/integration.md) — per-persona quickstarts (plugin user / library embedder / remote `/v1` client): install → configure → verify → troubleshoot, plus the embed lifecycle.
- [docs/configuration.md](docs/configuration.md) — config precedence, per-topology patterns, the consolidation-ownership matrix, secrets, and reserved knobs.
- [docs/hooks.md](docs/hooks.md) — the four Claude Code plugin hooks, the six env toggles, the `:8765` daemon autospawn, and the opt-out path.
- [docs/v1-api.md](docs/v1-api.md) — the `/v1` engine RPC guide (quickstart + conventions); the generated per-route reference is [docs/openapi/v1-reference.md](docs/openapi/v1-reference.md).
- [docs/upgrading.md](docs/upgrading.md) — migration steps, `sandbook-migrate-*` commands, and the deprecation schedule.
- [docs/deployment.md](docs/deployment.md) — deployment and security: the per-surface auth posture and safe binding patterns.

**Reference and internals:**

- [docs/memory_system.md](docs/memory_system.md) — the memory system itself: five tiers, retrieval depths, consolidation pipeline, self-healing, knowledge graph, full configuration reference.
- [docs/host_integration.md](docs/host_integration.md) — the consumer contract: versioning, `/v1` health, CLI flags, env vars, authentication.
- [README_DEV.md](README_DEV.md) — development workflow, independence testing, and publishing sandbook as a Claude Code plugin via a marketplace.

Maintenance

ActivityMaintained
ResponsivenessSyncing