A personal claude.ai conversation archive β schema, ingest, embeddings, hybrid retrieval, and an MCP server that lets any Claude session search your own past conversations.
Status:working end-to-end. Used daily by the author. Not packaged for general consumption β seeCaveatsbelow.
You export your conversations from claude.ai, drop the ZIP into a watched directory, and within ~15 minutes your full archive is searchable from any Claude session via two MCP tools:
tinderbox_search(query, limit=10)
β hybrid semantic + full-text retrieval over every message and artifacttinderbox_get_conversation(export_id, max_messages=50)
β pull the full thread of any conversation surfaced by a search
Everything is local-ish β you bring your own Supabase free-tier project, your own Ollama install for embeddings, and a Mac (this is targeted at Apple Silicon).
The current author's archive: 676 conversations, 10,653 messages, 172 artifacts, 10,731 mxbai-embed-large vectors. Hybrid retrieval hits 68.7% top-1 / 88.7% top-10 on a frozen 150-query QA set generated by Haiku from the corpus itself (re-runs weekly via launchd).
Tinderbox stores statements, not facts. Every retrieval response renders the provenance inline β never
"X is true"
, always"on [date], in [conversation], [participant] said [content]"
. The corpus answerswhat was said when, by whom; neverwhat is true.
That's design principle #1 from the schema doc. Memorial archive, not extraction pipeline. Forward-linked when superseded, never backward-edited.
For context-window reasons it's also genuinely useful: a Claude session can look up its own past reasoning instead of re-deriving it.
Postgres (Supabase) holds 12 tables under a tinderbox
schema β schema versioning, ingest runs, conversations, messages, artifacts, attachments, embeddings (vector(1024)
- hnsw), enrichment, named_instances, query log, and a frozen QA test set. A Python parser stream-reads claude.ai export ZIPs and upserts everything idempotently. An embed worker batches messages and artifacts through Ollama (
mxbai-embed-large
, 1024-dim) and writes vectors back. A server-side Postgres function (tinderbox.hybrid_search
) ranks results by (1 - cosine_distance) + 0.5 * ts_rank_cd
. A small from-scratch JSON-RPC 2.0 MCP server exposes two tools over stdio. Three launchd daemons run the whole thing on a schedule: inbox watcher (15min), QA eval (Sundays 03:00), staleness alerter (daily 09:00 with cooldown + debounce).
macOS with Apple Silicon, Python 3.14 (or 3.12+ probably β author runs 3.14.3_1)Supabase free-tier projectβ $0/month for this scale. Optional$4/mo IPv4 add-onfor proper RLS scoping (stage 5b).** Ollama**running locally withmxbai-embed-large
pulled (ollama pull mxbai-embed-large
)- A claude.ai data export ZIP (Settings β Account β Export Data)
git clone <this repo> ~/tinderbox && cd ~/tinderbox
./parser/scripts/setup.sh
cp .env.example ~/.tinderbox.env
ls migrations/
ollama pull mxbai-embed-large
python3 -m venv parser/venv
parser/venv/bin/pip install supabase python-dotenv click httpx pydantic anthropic
mkdir -p inbox
mv ~/Downloads/data-*.zip inbox/
parser/venv/bin/python -m tinderbox.cli scan-inbox
parser/venv/bin/python -m tinderbox.cli embed
parser/venv/bin/python -m tinderbox.cli search "your test query"
Activate the daemons (optional but recommended):
launchctl load ~/Library/LaunchAgents/com.$USER.tinderbox.scan.plist
launchctl load ~/Library/LaunchAgents/com.$USER.tinderbox.qa.plist
launchctl load ~/Library/LaunchAgents/com.$USER.tinderbox.staleness.plist
.
βββ migrations/ # Numbered SQL β apply to your Supabase project in order
βββ parser/
β βββ tinderbox/ # Python package
β β βββ parser/ # ZIP β typed records (streaming JSON, content-block parsing, artifact versioning)
β β βββ ingest/ # Records β DB (upsert, retry, tombstone sweep, mass-tombstone canary)
β β βββ embed/ # mxbai-embed-large via Ollama, batched, idempotent, per-row fallback
β β βββ search/ # Hybrid retrieval + query logging
β β βββ qa/ # Frozen-query-set eval (Haiku-generated, scheduled)
β β βββ mcp/ # Minimal JSON-RPC 2.0 MCP server (no SDK dep)
β β βββ staleness.py # Daily check w/ cooldown + debounce
β β βββ cli.py # tinderbox <command>
β β βββ ...
β βββ tests/
β βββ scripts/ # Setup, MCP launcher, surgical recovery
β βββ launchd/templates/ # Plist templates filled by setup.sh
βββ docs/
β βββ STAGE_1_SCHEMA_PROPOSAL.md # Design principles + table-by-table rationale
β βββ STAGE_1_COMPLETION_REPORT.md # Bugs found + fixed during ingest
β βββ STAGE_2_COMPLETION_REPORT.md # Embed + hybrid retrieval shipped
β βββ STAGE_5_COMPLETION_REPORT.md # MCP server + IPv4 add-on
β βββ ACCEPTED_ADVISORIES.md # Supabase advisor findings + accepted/applied/deferred
β βββ MCP_INSTALL.md # Claude Code / Desktop config snippets
β βββ STAGE_2_HANDOFF.md # Inter-session handback (historical)
βββ README.md (this file)
Hardcoded paths. The author runs everything under~/tinderbox/
.parser/scripts/setup.sh
renders the launchd plists for your$HOME
/$USER
but the Python defaults still assume that root.TINDERBOX_*
env vars override every default β set them up in your env file.No tests for end-to-end MCP from a real client. The smoke test inparser/tests/test_mcp_smoke.py
spawns the server as a subprocess and exchanges minimal protocol. Real validation is "does Claude Code surface the tool" (verified) and "does the tool return useful results" (eyeballed).The MCP server still authenticates viaservice_role
bypass for stage-1.service_role
(RLS bypassed). Documented indocs/STAGE_5_COMPLETION_REPORT.md
β fine until you start differentiating privacy classes; then stage 5b auth swap totinderbox_owner
direct connection becomes urgent.No SQLite option. Supabase only. The free tier easily handles this scale; if you want 100% local, you'll need to translate the schema and rewrite the DB layer (~5-6 hrs of work β author chose not to).macOS only. launchd schedules, paths, and the.pth
venv bridge are macOS conventions. Linux would need systemd units and a different venv approach. Not difficult, just not done.Author's archive shape baked into a few decisions. The 5 MBRAW_CONTENT_BYTE_LIMIT
was chosen because the author's largest message is 57 MB. The stratified sampler inqa/sample.py
uses bucket sizes (long β₯20 msgs, short 3-10 msgs) tuned to the author's distribution. Both are easy to retune.
Pick one. The author is open to anything that lets people fork and adapt without obligation.
Built collaboratively across many Claude sessions over a few days in April 2026. Schema design β parser β embed β search β QA β MCP server, mostly autonomous, with the human (Lucky) stepping in at architecture decisions and pushing back when something didn't smell right.
The system can now query the very conversations that built it.