Rabbithole: an infinite canvas where every answer becomes the next document
An infinite canvas for learning — select text, ask, and answers branch out as documents. MCP server for Claude Code, Codex, and any agent.
At a glance
- What is it?
- Rabbithole turns reading into a branching tree: open a document, select a passage, ask a question, and the answer arrives as a child document you can branch from again. It runs either as a static web app or as a local MCP server that hands the canvas to Claude Code and Codex.
- Who is it for?
- Rabbithole fits readers who lose their place in long threads and want the trail preserved as structure. The branching model is the part worth judging first: if you keep re-asking a question because an earlier answer scrolled away, a canvas that turns each answer into a document solves a real problem.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 17, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What Rabbithole is
Rabbithole is a reading surface built around one idea: an answer should not end a thread, it should start another document. The README describes it as an infinite canvas for learning, where you open a document, ask at any point, and follow each answer into a new document. package.json puts it more plainly, as starting from a document, selecting text, asking, and having the answer branch out as a child document, recursing as far as you want to go.
The project ships as two hosts over one canvas. The static web app uses a model endpoint you choose, or the coding-agent subscription already signed in on your machine. The MCP server lets Claude Code, Codex, and other MCP clients produce the answers while the canvas, the storage, and the local transport stay on your machine.
The privacy posture follows from that design. There is no account, no telemetry, and no hosted document store. Web documents live in your browser, and MCP documents live under ~/.rabbithole/ unless the RABBITHOLE_DIR variable points somewhere else.
The document model behind the branching
ARCHITECTURE.md states that Markdown is the authoritative source for document content and that rendered HTML is derived from it. At runtime each canvas is a hole, represented as HoleState: document metadata plus a map of nodes. Hosts change that state through DocEvent values handled by reduceHoleEvent. The reducer returns the next state and effects, while the host keeps ownership of persistence, network traffic, generation, and scheduling.
That split explains why the same canvas behaves the same way in a browser tab and behind an agent. The durable piece is the versioned persisted-hole schema in src/core/schema.js, and both storage implementations conform to the port in src/core/store.js. The MCP host stores one JSON document per hole with binary assets below ~/.rabbithole/, and the web host stores documents and assets in IndexedDB.
The other formats are projections of that one document. A .rabbithole file wraps a persisted hole and its base64 assets in the versioned portable format, so it can be exported and re-imported. A frozen snapshot embeds a shareable projection, referenced assets, styles, sanitizer, and frozen client in a single inert HTML file. Schema validation happens at storage and import boundaries, and unsupported versions are refused rather than reconstructed lossily.
Two hosts, one core
The layer boundaries are written down and enforced. src/core is the base layer and must not import src/ui or src/node, and it must not depend on Node built-ins. src/ui may import src/core but stays browser-only and may not reach into either host. The Node and web hosts compose the lower layers and own environment-specific input and output, and the two hosts do not import one another. The check:purity script enforces the core and UI portions of those rules.
The tree matches that runtime map. src/core holds the model, reducer, schema, storage port, Markdown and content behaviour, prompts, and artifact projections. src/ui holds the browser reader, canvas, overlays, primitives, rendering, transport adapter, and snapshot controls shared by both hosts. src/node holds filesystem persistence, PDF ingestion, MCP tools, local HTTP and SSE sessions, and browser launch behaviour. src/web holds the static app shell, IndexedDB persistence, provider adapters, browser generation, settings, imports, and library navigation.
One component sits off to the side. workers/fetch-proxy is an optional allowlisted relay for URL ingestion when a source blocks browser CORS.
Using the web app
Visit rabbithole.ing, then paste a question or a URL, drop Markdown or a PDF, or import a Rabbithole file. The web app supports OpenRouter, local and custom OpenAI-compatible endpoints, and an optional subscription bridge:
npx @shlokkhemani/rabbithole bridgeThe bridge prints a private pairing link and connects the page to an installed, signed-in Claude Code or Codex CLI, and it binds only to loopback.
Key handling is where the web host draws its line. Provider keys stay in browser storage and are sent only to the configured provider origin, and credentials and provider preferences are device-local web state that never becomes part of the document or of any exported projection. Opening a different hole is an in-document transition: the app serializes hole changes, flushes pending UI and host saves, disposes the old UI and transport, releases asset object URLs, resets the canvas surface, and mounts the target.
Using the MCP server with Claude Code and Codex
The MCP path requires Node 18 or newer and a browser. Registration with Claude Code:
claude mcp add rabbithole -- npx -y github:shlokkhemani/rabbitholeAnd with Codex:
codex mcp add rabbithole -- npx -y github:shlokkhemani/rabbitholeThen start a fresh agent session and ask it to open this document in Rabbithole. The tool call stays pending while the agent listens for canvas asks, which is the part that surprises people. If a client enforces a short MCP tool timeout, raise that client's timeout. Saved asks survive disconnects and resume.
Under the hood, bin/mcp-server.js starts the stdio server in src/node/mcp/server.js. Tool calls create or resume a RabbitHoleSession that serves a self-contained canvas over a loopback HTTP server and pushes live changes over SSE. The server is passive about generation: the external agent receives a branch request and streams the answer back through answer_branch. Standard output is reserved for MCP protocol messages, and Node diagnostics go through the stderr logger.
What you carry out of a session
Two export paths matter in practice. The .rabbithole file is the portable format, wrapping a persisted hole and its base64 assets, and it is what you import again later. The frozen snapshot is the sharing format, a single inert HTML file that carries its projection, referenced assets, styles, sanitizer, and frozen client.
The repository treats self-containment as a hard constraint. A bundle contract test prevents frozen artifacts from importing live transport, snapshot-export, or web-host modules, and byte and snapshot budgets remain separate acceptance gates. Both a live MCP canvas page and an exported frozen snapshot are assembled as one self-contained HTML response, and ARCHITECTURE.md warns against introducing external runtime assets into either artifact. The hosted web app may consist of several static files, but the snapshots it exports stay self-contained.
The practical result is that a shared snapshot keeps rendering after the original session is gone, and it does not carry your provider credentials with it, because those never entered the document.
Building it and the checks it runs on itself
The source is plain ES modules and needs no runtime compilation. The build exists only to produce browser artifacts. The documented development sequence:
git clone https://github.com/shlokkhemani/rabbithole.git
cd rabbithole
npm install
npm run build
npm testbuild.mjs produces dist/client.js and dist/frozen-client.js for the local MCP host, along with built styles and inlined math fonts. web/dist holds the static browser application assembled from the web shell and the browser bundles, and publish holds the Cloudflare Pages payload produced by the build:publish script. dist is generated and ignored, and the prepare lifecycle builds it in source checkouts, GitHub installs, and before npm packs or publishes, which is why published tarballs include the assets and consumers need no build step.
package.json also carries a long row of check scripts covering types, CSS integrity, UI architecture, docs, design, and icons, plus live checks for the bridge, the install journey, isolation, and image generation.
Licence, maturity and how it compares
Rabbithole is MIT licensed and the repository carries a LICENSE file. package.json lists version 0.1.0 with Shlok Khemani as author, the repository has no published releases, and the last push in view is 2026-09-16. Treat it as early software and read the compatibility contract in docs/ before depending on the file formats.
Against a chat window with a document pasted into it, the difference is shape. A chat keeps questions and answers in one linear thread, and context thins out as the thread grows. Rabbithole makes the answer a document of its own, so you can branch from a passage inside that answer, and the tree records how you got there. Against a notebook, the difference is that the unit of work here is a document you branch from rather than a cell you execute.
The costs come with the design. The MCP flow depends on an agent session staying open, and clients with short tool timeouts need that timeout raised. The web flow depends on you supplying a provider and a key. If what you want is one person reading and branching through a document with an agent doing the writing, this fits. If you want a shared hosted workspace, the README states plainly that there is no hosted document store.
Editorial conclusion
Rabbithole fits readers who lose their place in long threads and want the trail preserved as structure. The branching model is the part worth judging first: if you keep re-asking a question because an earlier answer scrolled away, a canvas that turns each answer into a document solves a real problem. Check three things before committing. Confirm which host you want, since the web app needs a provider and a key while the MCP path needs Node 18 plus a browser and a signed-in agent. Confirm where your documents will live, because the two hosts store them in different places. And confirm the version, because the repository sits at 0.1.0 with no published releases, so check the .rabbithole format and the snapshot contract against docs/compatibility.md before you archive anything you cannot recreate.
Frequently asked questions
Do I need an account to use Rabbithole?
No. The README states there is no account, no telemetry, and no hosted document store. Web documents stay in your browser, and MCP documents are written under ~/.rabbithole/ unless RABBITHOLE_DIR points elsewhere.
Which agents can drive the Rabbithole MCP server?
Claude Code and Codex are documented directly, through claude mcp add and codex mcp add with the npx GitHub shorthand. The README describes the server as working with any MCP client, and the MCP path requires Node 18 or newer plus a browser.
How do I share a Rabbithole session with someone else?
Export a frozen snapshot. It embeds a shareable projection, referenced assets, styles, sanitizer, and frozen client in one inert HTML file, and it carries no provider credentials because those are never part of the document.
What licence does Rabbithole use?
MIT, with a LICENSE file in the repository and MIT recorded in package.json. The package is at version 0.1.0 and the repository has no published releases.
Community notes