Get started

From folders of notes to agents that read them

This walkthrough starts with the smallest setup โ€” a local stdio server for Claude Code โ€” then moves to the Docker image with a token, the web UI and its folders, and ends with a hook that adds related notes to every turn. The screenshots show the web UI in light mode.

1

Run it locally with Claude Code

With Node 26+, which runs the TypeScript directly, clone the repo and register it as a stdio server. Point RAGDOWN_DOCS_DIR at one folder of Markdown โ€” an Obsidian vault works as it is. It is the only required setting:

git clone https://github.com/cubicecho/mcp-ragdown && cd mcp-ragdown
npm install
claude mcp add ragdown -e RAGDOWN_DOCS_DIR=$HOME/notes/work -- node $PWD/src/cli.ts stdio

On first start the default embedder (about 50 MB) downloads once, then the folder is indexed into ~/.cache/ragdown/. Claude now has ragdown_recall and ragdown_read_doc, and calls them itself when the notes might help. Ask it something your notes answer.

Over stdio, you chose the folder. The whole of RAGDOWN_DOCS_DIR is served, whatever its .ragdown.json says. Open as many Claude Code sessions on it as you like: the first process indexes and watches; the rest search the same index.

2

Run the Docker image with a token

To serve several folders, to other machines or agents, run serve, which the image does by default. It has the default model baked in, so it starts offline. Mount a directory whose top-level directories are your folders โ€” ~/notes/work, ~/notes/journal โ€” and save this as docker-compose.yml:

services:
  ragdown:
    image: vantreeseba/mcp-ragdown:latest
    ports:
      - '3300:3000'
    volumes:
      - ~/notes:/docs          # add :ro and RAGDOWN_READ_ONLY=true to forbid writes
      - ragdown-data:/data     # the index, so restarts only diff
    environment:
      RAGDOWN_TOKEN: ${RAGDOWN_TOKEN}
    restart: unless-stopped

volumes:
  ragdown-data:
export RAGDOWN_TOKEN=$(openssl rand -hex 32)   # keep it; clients need it too
docker compose up -d
curl localhost:3300/api/status                  # "ready": true, and the file and chunk counts

Without Docker, the same server is RAGDOWN_DOCS_DIR=~/notes RAGDOWN_TOKEN=โ€ฆ node src/cli.ts serve after npm run build (which builds the web UI).

No token? serve refuses to start without RAGDOWN_TOKEN unless SECURE_LOCAL_NET=true, which skips auth for a trusted network. Markdown sitting directly in ~/notes, outside every folder, is not indexed; the log and the web UI list it.

3

Open the web UI

Visit http://localhost:3300 and paste the token once; the browser keeps it. The sidebar lists your folders โ€” a plug for one agents can reach, a person for a human-only one. Pick one to see its files: filter by title, path or tag, switch to Search for a hybrid search of the folder, and click a file to read it rendered beside the list.

localhost:3300/f/work?doc=runbooks/backups.md
The work folder with runbooks/backups.md open: its tags, a wikilink and its size, line and chunk counts
[[Wikilinks]] are followed, embedded images are shown, and a tag opens the folder filtered by it. Every folder is searchable here, human-only ones included.
4

Turn MCP on and connect

Every folder starts human-only: indexed and searchable in the web UI, but no MCP endpoint, no recall hits and no hook context. Open Settings โ†’ Folders and flip Serve over MCP for the folders agents should read. The switch writes "mcp": true to the folder's .ragdown.json, so you can also set it by hand.

localhost:3300/settings
Settings, Folders: Journal human-only, Research served over MCP, each with Copy MCP config, Rename and Delete
Here you can also create, retitle, rename and delete folders. Deleting asks you to type the name.

Copy MCP config puts the line for that folder on the clipboard, with the token header when the server needs one:

claude mcp add --transport http ragdown-work http://localhost:3300/mcp/work \
  --header "Authorization: Bearer $RAGDOWN_TOKEN"

The same six tools as over stdio, with work as the root: search, read, stats, and โ€” unless read-only โ€” ragdown_remember, which writes into work/notes, and ragdown_reindex. A human-only folder answers /mcp/<folder> with the same 404 as one that does not exist.

The Server tab shows how the server is set up: the docs directory, embedder, role, read-only and watch flags, the last sync, and the search defaults a hook gets. Those come from environment variables at start, and each row names the one that sets it. This browser holds the theme and the stored token.

localhost:3300/settings?tab=server
Settings, Server: the docs directory, embedder, role, read-only and watch flags and last sync, each with the environment variable that sets it
Read-only: change a variable and restart the server to apply it.
5

Write, upload and delete notes

The + button above the list opens New note. Give it a title and, if you like, a subfolder โ€” prefilled with the one the open note is in, and created if it is missing. The note is saved as <title>.md with the title as its first heading, and opens in the editor.

New note
The New note dialog: title Replica failover, subfolder runbooks, saved as runbooks/Replica failover.md
A name that is already taken is refused, with a button that opens the note already there.

Edit on any previewed note opens the same editor: Markdown with syntax highlighting, a Preview tab, and Ctrl/โŒ˜+S to save. A save is made against the version you opened, so if the file changed on disk meanwhile โ€” in Obsidian, by an agent, by a git pull โ€” you choose between yours and theirs instead of one silently replacing the other. Leaving with unsaved changes asks first.

localhost:3300/f/work?doc=runbooks/backups.md&edit=true
runbooks/backups.md open in the editor with an unsaved line added at the end, and Write, Preview, Close and Save above it
Saves are written to the file and indexed before the editor says so.

The upload button above the list opens Upload Markdown. Pick .md, .markdown or .mdx files and optionally a subfolder of the open folder โ€” missing subfolders are created. The upload returns once the files are indexed. A file that already exists is not replaced unasked: it stays in the list with an Overwrite button.

Upload Markdown
The Upload Markdown dialog with the subfolder notes/reviews and one picked file
Uploads land in the folder on disk, so they are ordinary Markdown files from then on.

The trash button on a previewed file deletes it from disk โ€” not only from the index โ€” after a confirmation.

Delete this document?
The confirmation shown before deleting runbooks/backups.md
Under RAGDOWN_READ_ONLY=true new notes, edits, uploads, deletes and folder changes other than the MCP switch and title are refused.
6

Narrow an agent to a subfolder

/mcp/<folder>/<subfolder> serves the same tools with the subfolder as the root: searches stay inside it, paths are relative to it, and ragdown_remember writes into it. It follows the folder's MCP switch, with nothing else to configure:

claude mcp add --transport http work-alpha http://localhost:3300/mcp/work/projects/alpha \
  --header "Authorization: Bearer $RAGDOWN_TOKEN"

Focused, not fenced. A subfolder keeps an agent on its project's notes, but the same token opens every folder with MCP on. Keep what agents must never read in a human-only folder.

7

Open a folder in Obsidian

Point Obsidian's Open folder as vault at ~/notes/work and both see the same notes. .obsidian/ and .trash/ are skipped. Tags in frontmatter and inline #tags are indexed, so an agent can ask for one:

ragdown_recall({ query: "restore a backup", tag: "runbook" })   // also matches #runbook/db
ragdown_read_doc({ path: "Backups#Restore" })                   // a wikilink target: just that section

Wikilinks resolve the way Obsidian does, within the folder: an exact path, then a note by name (the one beside the linking note first), then an alias from frontmatter.

8

Add notes to every turn with a hook

For context without the model having to ask, a hook calls ragdown_context before each turn. It is an MCP tool like the others, so it needs a client whose hooks call MCP tools โ€” min-agent does. There is no hook command to wire into Claude Code; there, Claude calls ragdown_recall itself. In min-agent, add the server under Settings โ†’ MCP:

{
  "id": "ragdown",
  "label": "Notes",
  "transport": "http",
  "url": "http://localhost:3300/mcp/work",
  "headers": { "Authorization": "Bearer <RAGDOWN_TOKEN>" },
  "hiddenTools": ["ragdown_context"],
  "hooks": [
    { "id": "notes", "on": "beforeTurn", "tool": "ragdown_context", "inject": true, "maxTokens": 800,
      "args": { "prompt": "{{prompt}}", "session_id": "min-agent:{{session.id}}", "max_chars": 3000 } }
  ]
}

The hook sends the user's message. ragdown_context returns the sections that clear the similarity bar, wrapped in <ragdown-context>, or empty text when nothing fits. It skips short prompts and slash commands, and never returns the same section twice in one session, so a long chat pays for each note once. hiddenTools keeps the hook's tool away from the model, which has ragdown_recall.

That's it โ€” your notes are in the loop.

Keep writing Markdown; the index follows. For every tool, setting and route โ€” and how to pick an embedder โ€” read the docs.