by shlokkhemani
Provides an infinite canvas for learning where any selected text can be queried and the answer appears as a fully‑rendered child document, working with Claude Code, Codex, or any MCP‑compatible agent.
Rabbithole creates a persistent, web‑based canvas that lets users drop in PDFs, Markdown files, URLs, or plain text. Selecting any piece of content opens a dialog with an AI agent; the response is stored as a new document linked to the original selection. Each “hole” (conversation thread) is saved locally and can be revisited indefinitely.
claude mcp add rabbithole -- npx -y github:shlokkhemani/rabbithole
or the equivalent for Codex. The server launches via bin/mcp‑server.js and opens a browser tab.~/.rabbithole/ (MCP host); exportable .rabbithole backups and self‑contained HTML snapshots.open_rabbithole, answer_branch, and list_rabbitholes enable seamless long‑polling loops.Q: Do my API keys leave my machine?
A: No. Keys are stored only in the browser’s local storage (or session storage) and are sent directly to the provider endpoint.
Q: Can I run the model on another device on my LAN?
A: Yes. Start Ollama with OLLAMA_HOST=0.0.0.0 OLLAMA_ORIGINS=https://rabbithole.ing ollama serve and point the Custom endpoint to the LAN IP.
Q: What happens if the agent times out?
A: Questions are saved locally and re‑queued when the agent reconnects; the MCP server returns keep_listening or session_closed as needed.
Q: How do I export my work?
A: Use the Download snapshot button for a self‑contained .html file, or Export Rabbithole to generate a portable .rabbithole JSON backup.
Q: Do I need to clone the repo to use the server?
A: Not at all. The npx -y github:shlokkhemani/rabbithole command fetches the repo at runtime. Cloning is only required for development or faster startup.
An infinite canvas for learning. Open a document, select any text, ask a question — and the answer opens as a fully-rendered child document. Follow whatever pulls at you, as deep as it goes. Every hole is saved and revisitable.
There are two ways in:
Open rabbithole.ing and start from anywhere:
drop in a PDF or Markdown file, paste a URL, import a .rabbithole or
snapshot .html — or just ask a question and let the answer become your
first document.
Three ways to run a model:
Keys never leave the browser: they're stored locally (or session-only, your choice) and sent exclusively to the provider origin you configure. Exports scrub anything credential-shaped.
The model doesn't have to run on the device you're reading on. Serve Ollama from a machine on your network and point Custom at it:
OLLAMA_HOST=0.0.0.0 OLLAMA_ORIGINS=https://rabbithole.ing ollama serve
Then open rabbithole.ing on the phone and set the
endpoint to http://<that-machine's-lan-ip>:11434/v1 — for example
http://192.168.0.198:11434/v1. The browser asks once whether the page may
reach other devices on your local network; allow it. OLLAMA_HOST=0.0.0.0 is
what makes Ollama listen beyond its own machine, and OLLAMA_ORIGINS is what
lets the page talk to it.
This needs a browser that implements Local Network Access — Chrome 142 or newer, on desktop and Android. Safari has no equivalent, so on iOS run the browser version from that same machine over plain http instead (below), which sidesteps the question entirely.
Requires Node 18+:
git clone https://github.com/shlokkhemani/rabbithole.git
cd rabbithole
npm install
npm run build
npx -y serve web/dist
Open http://localhost:3000 (or the URL printed by
serve). serve also prints a network URL — open that one from a phone or
tablet on the same network and both the page and your model are plain http, so
nothing has to be allowed or tunneled. The local browser build has the same OpenRouter and
OpenAI-compatible local-model options as rabbithole.ing,
and its documents and provider settings stay in that browser's local storage.
Holes persist in IndexedDB, and each document gets a memorable local URL such
as rabbithole.ing/curious-teacup-abcdef. That path names a record in your
browser's database — it is not a sharing link. To move a hole between
machines, export the .rabbithole file; to share something readable,
download a snapshot.
Self-hosting is static: run npm run build and serve web/dist from any
host. The optional workers/fetch-proxy Cloudflare Worker enables URL
ingestion for sources that block browser CORS (set RABBITHOLE_PROXY_URL at
build time to point the app at your relay). Serve index.html as the
fallback for unknown single-segment paths so document URLs survive a direct
visit or refresh.
Requires Node 18+ and a browser. Pick your agent:
Claude Code
claude mcp add rabbithole -- npx -y github:shlokkhemani/rabbithole
Codex
codex mcp add rabbithole -- npx -y github:shlokkhemani/rabbithole
Then raise the tool timeout in ~/.codex/config.toml — Codex's 60-second
default is shorter than Rabbithole's blocking wait, and codex mcp add
cannot set this field:
[mcp_servers.rabbithole]
command = "npx"
args = ["-y", "github:shlokkhemani/rabbithole"]
tool_timeout_sec = 600
Any other MCP client — most accept this shape in their MCP config:
{
"mcpServers": {
"rabbithole": {
"command": "npx",
"args": ["-y", "github:shlokkhemani/rabbithole"]
}
}
}
Then start a new agent session and say:
open this document in rabbithole
A browser tab opens. Select any text on the page, ask a question (or tap a lens — Explain · ELI5 · Example · Go Deeper), and the answer streams in as a new document. Keep going.
If a host ever reports a tool timeout, nothing is lost — questions are saved and re-queued the next time the agent listens.
git clone https://github.com/shlokkhemani/rabbithole.git
cd rabbithole && npm install
claude mcp add rabbithole -- node "$(pwd)/bin/mcp-server.js"
Follow these steps exactly:
node --version must be ≥ 18.claude mcp add … for Claude Code, codex mcp add …
for Codex, or write the JSON block into the client's MCP config file).
The npx -y github:shlokkhemani/rabbithole form needs no clone and no
install step — npx fetches this repo and runs bin/mcp-server.js over stdio.
For Codex, also add tool_timeout_sec = 600 under
[mcp_servers.rabbithole] in ~/.codex/config.toml; codex mcp add cannot
set that field.open_rabbithole, answer_branch, list_rabbitholes. (In Claude Code,
claude mcp list should show rabbithole as connected. The server itself
also responds to a standard MCP initialize with server name rabbithole.)open_rabbithole
with { title, content } (or { title, file_path }; add base_url when
the markdown came from a URL or repo; add assets for local images referenced
as asset:name.png), which blocks until they select text and ask. Answer
with answer_branch (stream chunks with partial: true, finish with a
titled final call) and keep looping until status='session_closed'. If a
long wait returns status='keep_listening', immediately call
open_rabbithole with the returned { hole_id } and do not re-send content.Notes for you: the tool call blocks by design (long-poll) — that is normal, not
a hang. The first npx run fetches this repo, so allow it time; later runs
are cached. If the browser must not auto-open (headless), set
RABBITHOLE_NO_BROWSER=1 in the server's env.
| Tool | What it does |
|---|---|
open_rabbithole |
Open a doc ({ title, content } / { title, file_path }, optional base_url, optional assets) or resume one ({ hole_id }). A PDF file_path opens natively: rendered pages, selectable text, and box-select — no markdown authoring needed (title optional; PDF metadata or filename is used). Opens the canvas in the browser and blocks until the human asks something. |
answer_branch |
Answer a pending branch request → a child document. Stream with partial: true chunks, then finish with a normal call carrying the node title; use base_url for fetched markdown and assets for local images referenced as asset:name.png. A branch_request from a PDF may include region.image_path — read that image before answering. Also streams "Convert to document" transcriptions when a convert_request arrives. |
list_rabbitholes |
List saved holes to resume by id. |
The loop: open_rabbithole → branch_request → answer_branch → branch_request → … → session_closed.
Long waits may return keep_listening; immediately call open_rabbithole
again with the returned hole_id. If the host reports a tool timeout, do the
same — questions are saved.
For research PDFs, page renders are the dependable figure source. For arXiv
links, prefer fetching the HTML version and opening that content with
base_url instead of ingesting the PDF.
mermaid diagrams, bespoke show visuals, URL-based resolution
for relative links/images, and local image assets via asset:name.png;
source stays as Markdown for copy/export,
while frozen snapshots inline assets into the HTML.j/k walk marks, ↵ opens, ⌫ jumps back up, ⌘K
searches the whole hole..html — data, assets, and a
read-only client in one file anyone can open; Export Rabbithole (web
app) produces a .rabbithole backup for device transfer — MCP holes are
already plain JSON on disk; or ask the agent for a synthesis of the whole
journey.~/.rabbithole/; resuming
restores the doc, scroll position, mode, and canvas framing.The MCP host stores each hole as a JSON file directly under ~/.rabbithole/
(RABBITHOLE_DIR overrides the base directory) and assets under the matching
asset directory. The web .rabbithole file is the same persisted hole JSON
wrapped as { format: "rabbithole", format_version: 1, hole, assets }, with
assets base64-encoded into the single JSON file for portability.
Use an ordinary Mermaid code fence; Rabbithole renders it in live canvases, the hosted web app, and self-contained offline snapshots:
The bundled runtime supports flowchart, sequence, class, state, and
entity-relationship diagrams. Mindmaps, architecture diagrams, and
Mermaid-side KaTeX are not included; use a show visual or regular Rabbithole
math for those cases. Mermaid runs in strict mode, its SVG is sanitized again
before mounting, and invalid diagrams fall back to their original source.
| Env var | Effect |
|---|---|
RABBITHOLE_DIR |
Override the storage directory (default ~/.rabbithole/). |
RABBITHOLE_NO_BROWSER=1 |
Don't auto-open the browser (headless/testing). |
RABBITHOLE_MAX_BLOCK_MS |
Max time for one blocking MCP wait before returning keep_listening (default 240000). |
RABBITHOLE_PROXY_URL |
Build-time: URL of your fetch-proxy relay for the web app (empty string disables the default). |
bin/mcp-server.js — entry point (stdio MCP server)src/core/ — host-independent document engine, rendering, artifacts, and contractssrc/ui/ — shared live/frozen browser runtimesrc/node/ — MCP host, filesystem storage, local HTTP/SSE, and PDF ingestionsrc/web/ — static BYOK browser host and IndexedDB storagebuild.mjs — builds the committed MCP bundles and the static web appdist/ — committed browser bundles used by GitHub npx installsweb/dist/ — generated static web app (untracked build output)scripts/ — reproducibility checks and publish assemblytest/ — capability-oriented unit, contract, integration, end-to-end,
performance, and packaging suitesworkers/fetch-proxy/ — optional allowlisted URL-ingestion relaywebsite/public/ — public deployment assets consumed by build:publishSee CONTRIBUTING.md for the development workflow and
ARCHITECTURE.md for system boundaries. Compatibility,
testing, and interface rules live under docs/. The browser runtime
source lives in src/ui/ and is bundled into committed artifacts under dist/.
When editing the UI, run:
npm run build
npm run check:dist
Commit both the source changes and dist/. There is no prepare build step;
GitHub npx installs use the committed artifacts.
The Deploy Cloudflare Pages workflow
runs the complete test suite and deploys publish/ to the rabbithole Pages
project on every push to main. It can also be rerun manually from GitHub
Actions. Each Cloudflare deployment is tagged with the exact Git commit.
The workflow requires:
CLOUDFLARE_ACCOUNT_ID;CLOUDFLARE_API_TOKEN, scoped to Account → Cloudflare
Pages → Edit.Configure them with gh variable set CLOUDFLARE_ACCOUNT_ID and gh secret set CLOUDFLARE_API_TOKEN; both commands prompt without committing credentials.
MIT
Please log in to share your review and rating for this MCP.
Explore related MCPs that share similar capabilities and solve comparable challenges
by modelcontextprotocol
A basic implementation of persistent memory using a local knowledge graph. This lets Claude remember information about the user across chats.
by topoteretes
Provides dynamic memory for AI agents through modular ECL (Extract, Cognify, Load) pipelines, enabling seamless integration with graph and vector stores using minimal code.
by basicmachines-co
Enables persistent, local‑first knowledge management by allowing LLMs to read and write Markdown files during natural conversations, building a traversable knowledge graph that stays under the user’s control.
by agentset-ai
Provides an open‑source platform to build, evaluate, and ship production‑ready retrieval‑augmented generation (RAG) and agentic applications, offering end‑to‑end tooling from ingestion to hosting.
by smithery-ai
Provides read and search capabilities for Markdown notes in an Obsidian vault for Claude Desktop and other MCP clients.
by chatmcp
Summarize chat messages by querying a local chat database and returning concise overviews.
by dmayboroda
Provides on‑premises conversational retrieval‑augmented generation (RAG) with configurable Docker containers, supporting fully local execution, ChatGPT‑based custom GPTs, and Anthropic Claude integration.
by qdrant
Provides a Model Context Protocol server that stores and retrieves semantic memories using Qdrant vector search, acting as a semantic memory layer.
by doobidoo
Provides a universal memory service with semantic search, intelligent memory triggers, OAuth‑enabled team collaboration, and multi‑client support for Claude Desktop, Claude Code, VS Code, Cursor and over a dozen AI applications.
{
"mcpServers": {
"rabbithole": {
"command": "npx",
"args": [
"-y",
"github:shlokkhemani/rabbithole"
],
"env": {}
}
}
}claude mcp add rabbithole npx -y github:shlokkhemani/rabbithole