Horatio
A flight recorder and memory server that sits between MCP clients and Blender, recording interactions, distilling them into persistent per-file memory using NVIDIA Nemotron, and serving that memory to agents via MCP tools like recall and search_sessions.
README
Horatio
A flight recorder for MCP-driven Blender work. Horatio sits transparently between your MCP client (Claude Desktop / Claude Code / Cursor) and blender-mcp, records every message, distills the traffic with NVIDIA Nemotron into persistent per-file memory, and serves that memory back to agents over MCP — so every future session warm-starts instead of cold-starts.
Claude ──stdio──> tap ──stdio──> uvx blender-mcp ──tcp──> Blender
│
└──append──> raw.jsonl ──tail── distiller (Nemotron)
├── digest.jsonl (activity, live)
└── note.md + blends/<id>/memory.md
Claude ──stdio──> memory-server ──reads──> file memory + notes
(recall / search_sessions / log_decision)
Core invariant: raw.jsonl is the source of truth. Everything downstream is re-derivable (horatio rebuild). The only other source of truth is decisions.jsonl (agent-authored via log_decision).
Deeper docs: architecture & data flow · desktop app internals · why Nemotron · contributor guide (CLAUDE.md).
Why Nemotron
- Continuous ambient distillation (summarizing every session, all session long) is only economical on a fast, cheap, open model — trivial on
nvidia/nemotron-3-super-120b-a12b, absurd on frontier API pricing. - Its 1M context enables one-pass full-transcript synthesis experiments.
- Open weights mean the same system can run fully local (Nemotron Nano via Ollama) — always-on, private, zero token cost.
Setup
Desktop app (recommended)
The Electron app is the viewer + control plane: browse sessions (notes, digests, raw, artifacts), manage the NVIDIA API key, wrap/unwrap MCP servers, and fix store paths if a client is writing to the wrong home.
Build a downloadable macOS app (unsigned .dmg — right-click → Open the first time):
npm install && npm run desktop:install
npm run desktop:dist # → desktop/release/*.dmg
Or run from the repo (use a normal terminal, not an environment that sets ELECTRON_RUN_AS_NODE):
npm run desktop:install # once
npm run desktop # build + Electron hot reload
# or, after desktop:install:
cd desktop && npm start # production build of the UI, no Vite
Projects are Blender files, not arbitrary names. Sessions live in a flat sessions/ tree and are linked (never moved) to a blends/<id>/ bucket when a .blend path shows up in MCP traffic or you click Link .blend…. Memory hangs off that file; Reveal / Open jump to it on disk.
CLI
npm install && npm run build
cp .env.example .env # or create .env with: NVIDIA_API_KEY=nvapi-...
node dist/distill/cli.js install
node dist/distill/cli.js install --wrap blender
node dist/distill/cli.js uninstall
install --wrap sets HORATIO_HOME to the platform app-data folder (~/Library/Application Support/Horatio on macOS). Optional --project-dir <path> overrides to <path>/.horatio. Restart the MCP client after install so wraps pick up the new home.
Legacy FLIGHTREC_* env vars still resolve (with a deprecation warning). Older flightrec stores need a one-time horatio migrate.
install finds Claude Code, Claude Desktop, and Cursor configs automatically, wraps the named server with the tap, and registers the memory server alongside. Claude Code uses the official claude mcp CLI; Desktop and Cursor get surgical JSON edits with a timestamped backup and atomic writes. Wrapped entries are self-describing, so uninstall restores the originals without a state file. Use --client <claude-code|claude-desktop|cursor> to target one client, or --config <path> for any config with the standard mcpServers shape.
<details> <summary>Manual config (what install writes)</summary>
{
"mcpServers": {
"blender": {
"command": "node",
"args": ["/ABSOLUTE/PATH/Horatio/dist/tap.js", "--", "uvx", "blender-mcp"],
"env": {
"HORATIO_HOME": "/Users/you/Library/Application Support/Horatio"
}
},
"horatio-memory": {
"command": "node",
"args": ["/ABSOLUTE/PATH/Horatio/dist/memory-server.js"],
"env": {
"HORATIO_HOME": "/Users/you/Library/Application Support/Horatio"
}
}
}
}
</details>
The tap auto-spawns the activity follower per session (opt out with HORATIO_NO_ACTIVITY=1 / legacy FLIGHTREC_NO_AUTODISTILL=1 and run node dist/distill/cli.js watch latest in a terminal instead — nicer for demos).
Commands
node dist/distill/cli.js watch [session] # live activity → digests; auto-update memory at session end
node dist/distill/cli.js update [session] # session note.md + fold into blends/<id>/memory.md
node dist/distill/cli.js rebuild [session] # wipe digests, re-derive activity, then update memory
node dist/distill/cli.js export-memory [session] [--out path] [--assemble]
node dist/distill/cli.js migrate [--dry-run] # one-time flightrec (v1) → Horatio (v2) store
npm run desktop # viewer + control plane
Legacy aliases: distill --follow → watch, --save → update, --replay → rebuild.
update / “Update memory” in the app folds digests into durable file memory. Live capture mainly writes activity (Tier 1); synthesis runs at session end, on idle, or when you update manually. recall() returns existing notes/memory — it does not synthesize on demand.
export-memory writes agent-memory.md into the session folder (and optionally --out to your coding-agent project). It uses Nemotron to produce an agent-oriented brief from the session note + file memory; pass --assemble to skip the LLM and concatenate mechanically.
session = latest (default), a session id, or a path.
Store layout
Canonical app-owned store (default, v2):
~/Library/Application Support/Horatio/ # macOS
~/.config/horatio/ # Linux
%APPDATA%/Horatio/ # Windows
config.json # storeVersion: 2
.env # NVIDIA_API_KEY (optional; also loads repo .env)
live/<session-id>.json # per-tap live pointers
sessions/<ISO-id>/ # flat — sessions never move
raw.jsonl digest.jsonl note.md distill.log artifacts/
session.json link.json memory.json
agent-memory.md # optional export
blends/<basename>-<hash8>/ # one folder per Blender file
meta.json # { blendPath, name, … }
memory.md # durable cross-session file memory
decisions.jsonl
memory-history/ # recent memory.md archives
Project-local override (--project-dir or HORATIO_HOME=<workspace>/.horatio):
<workspace>/.horatio/
(same shape as above)
If an older wrap still points at a repo .flightrec / flightrec app-data path, use Preferences → Fix store paths (or install --repair-store / horatio migrate), then restart the client.
Memory tools (MCP)
recall— judgment-first warm start for the active.blend(decisions / constraints / failures / threads first; scene inventory last and marked historical). Warns if the.blendfile is newer than last memory update. Agents must re-check the live scene with Blender MCP. Resolves from optionalfilearg → live session link → most recently linked file.search_sessions(query)— substring search across notes, digest events, and file memory, with session/batch citations. No embeddings; at 1M context, brute force is the architecture.log_decision(text)— record a durable decision from the driving agent; memory is bidirectional.
Guarantees
- The tap is transparent: byte-for-byte forwarding, verified byte-identical against un-tapped runs; logging failures can never affect forwarding; base64 screenshots never reach the log.
- No speculation in distilled memory: every digest event cites the raw-record seq numbers supporting it; error strings are verbatim; hallucinated causality is prompt-forbidden and schema-policed — bad memory poisons future sessions, so incomplete beats invented.
- Batch boundaries are deterministic (record-timestamp gaps, not wall clock):
rebuildreproduces the same batches a live run made.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.