RAGDown
RAGDown is a local MCP server over folders of Markdown files that embeds every section into a LanceDB index and gives agents tools to search, read, browse, write, and maintain those documents.
Search (
ragdown_recall): hybrid semantic + BM25 retrieval over Markdown sections, returning path, line range, heading breadcrumb, tags, similarity, and last-changed date; filters bypath_prefix,tag,written_by, format, andmax_chars.Inject context (
ragdown_context): hook-oriented tool that returns the sections most related to a prompt as a<ragdown-context>block (or Claude CodeadditionalContextJSON), gated bymin_score/min_ratio, skipping short prompts and slash commands, and never repeating a section persession_iduntilreset.Read (
ragdown_read_doc): read a file whole or by line range straight from disk, never clipped, or resolve an Obsidian wikilink target (Note,Note#Heading, alias); returns a hash for safe subsequent writes.Browse (
ragdown_list): list documents with title, tags, description, last change, author/session, and superseded status; filter by subfolder, tag, writer, or session, sorted by path or recency.Backlinks (
ragdown_backlinks): find documents linking to a document by wikilink, alias, or relative Markdown link, with the linking lines.Create notes (
ragdown_remember): write a new Markdown document with frontmatter, tags, session provenance, and optionalsupersedes; indexed immediately and never overwrites.Rewrite/append (
ragdown_write): replace or create a document, or append text (optionally to a section heading), withbase_hashprotecting against overwriting unread changes.Precise edit (
ragdown_edit): swap exactold_textpassages fornew_textwithout resending the whole document, all-or-nothing, with nearest-match feedback.Move/rename (
ragdown_move): relocate a document or subfolder and automatically rewrite every wikilink and relative link pointing at it.Delete (
ragdown_delete): permanently remove a document or subfolder (optionally recursive), with optional hash check; no trash or undo.Reindex (
ragdown_reindex): sync the index now, orfull: trueto re-embed everything.Stats (
ragdown_stats): report folder, index size, embedder, primary/reader role, sync state, and optionally list indexed files.Obsidian-compatible: each top-level folder is its own MCP server (opt-in), with tags, aliases, and wikilinks indexed and resolved per folder; read-only mode can hide write tools.
Serves as the document format the server indexes: it walks a folder of Markdown files recursively (.md, .markdown and .mdx), splits each file into sections at every heading, embeds each section with its heading breadcrumb, and keeps a local vector plus full-text index in sync as the files change. Agents can then run hybrid searches over the notes and read whole files or line ranges straight from disk, with each hit returning the path, line range and heading breadcrumb.
Supported alongside Markdown as an indexed file type — .mdx documents in the configured folder are walked, chunked by heading and embedded like any other note, so MDX content becomes searchable by agents.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@RAGDownsearch my notes for how to restore the database"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-ragdown
Website: cubicecho.github.io/mcp-ragdown: an overview, a walkthrough and a condensed reference.
An MCP server over folders of Markdown files. Point it at a directory and it embeds every section
into a local LanceDB index and keeps that index in sync as files change. Each top-level folder is
its own MCP server, off until you turn it on, and each one opens as an Obsidian vault. Agents get
search tools, and a hook that calls ragdown_context before each turn adds related documents to the
prompt. The tool layout follows mcp-zeromem, but the memory here is your Markdown files, not
conversation turns.
Everything an agent or a hook does goes through MCP. There is no hook command and no hook HTTP route; the only other routes feed the web UI.
Everything runs locally: the default embedder is granite-embedding-small-english-r2 on ONNX
Runtime. The Docker image has the model baked in; outside Docker it is downloaded once (about
50 MB) on first start. Two others are a setting away — see
Choosing an embedder.
Quick start
docker-compose.yml:
services:
ragdown:
image: vantreeseba/mcp-ragdown:latest
ports:
- '3300:3000'
volumes:
- ~/notes:/docs # one folder per directory in here; 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~/notes/work is now the folder work. It starts human-only: open http://localhost:3300, turn
MCP on for it under Settings → Folders (or put { "mcp": true } in ~/notes/work/.ragdown.json),
and Copy MCP config there gives you this line:
claude mcp add --transport http ragdown-work http://localhost:3300/mcp/work \
--header "Authorization: Bearer $RAGDOWN_TOKEN"The first index of a large folder takes minutes; searches answer from what is indexed so far. The
web UI lists each folder's files, renders them with their wikilinks and images, and searches them.
To add documents to every turn automatically, see Hooks in min-agent. Images
are published for linux/amd64 and linux/arm64 to Docker Hub and ghcr.io/cubicecho/mcp-ragdown.
Without Docker
npm install
RAGDOWN_DOCS_DIR=~/notes node src/cli.ts stdio # what an MCP client launches
RAGDOWN_DOCS_DIR=~/notes SECURE_LOCAL_NET=true node src/cli.ts serve # HTTP and the web UI on :3000Requires Node 26+, which runs the TypeScript directly.
Related MCP server: Knowledge MCP Server
Claude Code setup
Against the container, the claude mcp add --transport http line above. As a local process:
claude mcp add ragdown -e RAGDOWN_DOCS_DIR=$HOME/notes/work -- node /path/to/mcp-ragdown/src/cli.ts stdiostdio serves one folder: RAGDOWN_DOCS_DIR itself, whatever its .ragdown.json says (you chose
it by launching it). Point it at an Obsidian vault and the whole vault is the folder.
Claude calls ragdown_recall and ragdown_read_doc itself when the documents might help; the server's
instructions tell it to.
For related documents on every prompt without Claude asking, Claude Code's mcp_tool hooks call
ragdown_context on the server you added. In .claude/settings.json (or ~/.claude/settings.json):
{
"hooks": {
"UserPromptSubmit": [
{ "hooks": [ { "type": "mcp_tool", "server": "ragdown", "tool": "ragdown_context",
"input": { "prompt": "${prompt}", "session_id": "claude:${session_id}", "format": "claude-code" } } ] }
],
"SessionStart": [
{ "matcher": "compact", "hooks": [ { "type": "mcp_tool", "server": "ragdown", "tool": "ragdown_context",
"input": { "prompt": "", "session_id": "claude:${session_id}", "reset": true } } ] }
]
}
}serveris the name you gaveclaude mcp add.format: claude-codeis needed: Claude Code (2.1.292, where this was tried) calls the tool either way but only passes the result on to the model as a hook'sadditionalContext.The
SessionStarthook is for compaction, which keeps the session id and drops the sections Claude was given:resetlets them be sent again. Claude Code skipsmcp_toolhooks on theSessionStartat launch, which needs no reset.Claude Code caps a hook's output at 10,000 characters;
RAGDOWN_HOOK_MAX_CHARSdefaults to 6,000.
Folders
serve treats every top-level directory of RAGDOWN_DOCS_DIR as a folder, found as it
appears; the directories inside one are subfolders. All folders share one index, one model and
one watcher, but each is its own MCP server at /mcp/<folder>, named ragdown-<folder>, and sees
only its own documents: paths in and out are relative to it (backups.md, not work/backups.md), and
ragdown_read_doc refuses one that leaves it.
A folder's settings live in .ragdown.json at its root, and the web UI edits them:
{ "title": "Work notes", "mcp": true, "hook": { "top_k": 6, "min_score": 0.75 } }titleis shown in the web UI and the server's instructions; it defaults to the directory name.mcpdefaults to false. A folder is human-only until someone turns it on: indexed and searchable in the web UI, but no endpoint, no recall hits and no hook context./mcp/<folder>answers a human-only folder with the same 404 as a missing one.hookholds the folder's ownragdown_contextdefaults: any oftop_k,min_score,min_ratioandmax_chars. One it leaves out is the server'sRAGDOWN_HOOK_*value, and an argument on the call still wins over both. It is read on each call, so an edit applies without a restart.
Bare /mcp is a 404 that says to pick a folder, unless the root is turned on: see
Every folder at once. Markdown loose in RAGDOWN_DOCS_DIR, outside every
folder, is not indexed; the log and the web UI list it.
The web UI creates, edits, renames and deletes folders, and Copy MCP config on each one gives the
claude mcp add line and the JSON mcpServers entry for it. Under RAGDOWN_READ_ONLY a folder's
title, MCP switch and search defaults can still change — they are settings, not documents — but
creating, renaming and deleting cannot.
New document (the + beside Upload in a folder's list, or the button in an empty folder) asks
for a title and an optional subfolder, created if missing. It starts beside the open document, writes
# <title> to <subfolder>/<title>.md, and opens it in the editor. A name that is already taken
offers to open that document instead.
Edit on an open document swaps the preview for a Markdown source editor (CodeMirror, loaded on
first use), with a Write/Preview switch and Ctrl/Cmd+S to save. It edits the source, not rich
text, so wikilinks, embeds and front matter come back exactly as written. A save is made against
the version that was opened: if the file changed on disk meanwhile — in Obsidian, by an agent, by
git pull — the editor says so and asks whether to save over it or discard the edit, rather than
quietly losing either. Leaving with unsaved changes asks first.
A folder keeps an agent focused, not out: one token opens every folder with MCP on.
Every folder at once
With RAGDOWN_ROOT_MCP=true, or Serve every folder at /mcp under Settings → Server, bare
/mcp is one MCP server, named ragdown, over every folder with MCP on. It saves setting an agent
up once per folder, and it is the only place an agent can make a folder.
claude mcp add --transport http ragdown http://localhost:3300/mcp \
--header "Authorization: Bearer $RAGDOWN_TOKEN"The tools are the same, each with a
folderargument, and paths stay relative to that folder (folder: "work", path: "backups.md").ragdown_read_doc,ragdown_backlinks,ragdown_remember,ragdown_write,ragdown_edit,ragdown_moveandragdown_deleteneed one.ragdown_recall,ragdown_list,ragdown_contextandragdown_statscover every folder when it is left out, and each result then names its folder:[work] backups.md:7-9.A folder is still its own: wikilinks resolve inside it, a move stays inside it, and its
hooksettings apply to aragdown_contextcall that names it. A call that names none uses the server's defaults and keeps its own per-session memory.A human-only folder is not there: it is not searched, listed or counted, and naming it answers as a folder that does not exist would.
ragdown_folderslists the folders.ragdown_create_foldermakes one, with MCP on and"created_by": "ragdown_create_folder"in its.ragdown.json, which the web UI shows as Made by an agent. Agents cannot rename or delete a folder.
Each folder's own /mcp/<folder> endpoint is unchanged, with no folder argument: an agent that
should see one folder is still pointed at that one.
Subfolders
/mcp/<folder>/<subfolder...> narrows a folder's server to a subfolder, following the folder's MCP
setting:
claude mcp add --transport http work-alpha http://localhost:3300/mcp/work/projects/alpha \
--header "Authorization: Bearer $RAGDOWN_TOKEN"ragdown_recallandragdown_contextsearch only files under it, andpath_prefixnarrows further inside it. Paths are relative to the subfolder.ragdown_rememberwrites into the subfolder itself; on a folder's own endpoint it writes into<folder>/<RAGDOWN_NOTES_DIR>.ragdown_statscounts the subfolder's files and chunks, andragdown_context's per-session memory is kept separately for each endpoint.ragdown_reindexstill syncs everything.
A missing subfolder, a file, a symlink or a dot-folder is a 404.
Obsidian
Open a folder as an Obsidian vault and both see the same documents:
.obsidian/,.trash/and every other dot-folder are skipped, as is.ragdown.json.Tags from frontmatter (
tags: [a, b]ortags: a, b) and inline#tagsin the text (not in code) are indexed per document.ragdown_recall'stagfilter matches a tag and the tags nested under it:projectfinds#project/alpha. Every hit lists its document's tags.Aliases from frontmatter are found by keyword search and by wikilinks.
Wikilinks.
ragdown_read_doctakes a link target as well as a path —Note,Note#Heading,sub/Note, an alias — and resolves it the way Obsidian does, within the folder: an exact path, then a document whose name matches (the one beside the linking document, then the shortest path), then an alias. A heading narrows the text to that section. The web UI follows[[links]], shows![[embeds]]and images, lists the documents that link to the open one, completes[[in the editor with the folder's documents, and rewrites the links to a document when it is renamed or moved.
Tags and aliases get their own columns; they are not added to the text that is embedded, which was measured and made retrieval worse.
Hooks in min-agent
A min-agent hook calls a tool on a connected MCP server, so ragdown needs nothing beyond its MCP endpoint. Add a server under Settings → MCP:
{
"id": "ragdown",
"label": "Notes",
"transport": "http",
"url": "http://localhost:3300/mcp/work", // a folder with MCP on
// Stored as-is: min-agent does not expand env vars. Leave empty with SECURE_LOCAL_NET=true.
"headers": { "Authorization": "Bearer <RAGDOWN_TOKEN>" },
// The model has ragdown_recall; the context tool is for the hook only.
"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 } }
]
}Before each turn the hook sends the user's message. ragdown_context answers with the sections
whose cosine similarity reaches RAGDOWN_HOOK_MIN_SCORE, wrapped in <ragdown-context>, or with
empty text, which min-agent treats as nothing to add. It skips:
prompts under 12 characters and slash commands;
sections it already returned for the same
session_id, so a long chat pays for each document once.
max_chars of 3,000 keeps the block near min-agent's 800-token cap, so a section is not cut in the
middle. A hook that fails or takes longer than min-agent's 3 seconds only loses that turn's documents.
Tools
Tool | What it does |
| For hooks: the sections related to a |
| Hybrid search. Returns path, line range, heading breadcrumb, tags, similarity and the day the file last changed for each hit, and says when an agent wrote the document. Takes |
| Reads a file, or a line range of one, straight from disk. Never clipped. Also takes a wikilink target ( |
| The documents that link to a |
| Browses rather than searches. Returns a count, then one line per document: its path, title, tags, the day it last changed, the tool and session that wrote it if an agent did, what supersedes it, and its frontmatter |
| Folder, index size, embedder, role (primary or reader), whether a sync is running, and the last sync. |
| Writes a new document (with frontmatter) under |
| Rewrites a document at a |
| Changes part of a document at a |
| Renames or moves a document, or a subfolder with everything in it, from |
| Deletes a document or a subfolder at |
| Syncs now; |
| At |
| At |
The write tools (ragdown_remember, ragdown_write, ragdown_edit, ragdown_move,
ragdown_delete, ragdown_reindex, ragdown_create_folder) are not listed when
RAGDOWN_READ_ONLY=true. At /mcp every tool but ragdown_reindex also takes a folder: see
Every folder at once.
Every tool an agent is offered costs it context on every turn, used or not: the twelve descriptions
together are about 15,000 characters, and /mcp adds two tools and a folder argument to those. RAGDOWN_DISABLED_TOOLS leaves out the ones you name.
ragdown_context,ragdown_stats,ragdown_reindex is a fair set to drop for an agent that only reads
and writes documents: the first is for hooks, and the other two are for whoever runs the server.
Keep ragdown_context on a server a hook calls.
Configuration
Only RAGDOWN_DOCS_DIR is required. See .env.example.
Variable | Default | |
| — | For |
|
| The index. Deleting it only costs a rebuild. |
|
| Model cache, shared by every folder. |
|
| A local model — |
| OpenAI | For |
| half the cores | ONNX Runtime threads for the local model. |
|
| Watch the folder; without a watcher, sync on start and on |
|
|
|
|
| Hide the write tools, and refuse uploads, deletes and folder changes other than settings. |
| none | Tools to leave out, by name and comma-separated, such as |
|
| Where |
|
| Characters per hit in text output. Every cut names the |
|
| Most sections of one document a search returns and |
|
| How many of a search's best hits have the wikilinks in their own section followed: each linked document's two closest sections are placed right after the hit, within its folder and under the search's filters. |
|
| Log every search, hook call and |
|
|
|
| the embedder's own |
|
|
|
|
|
|
|
|
|
|
|
|
|
| — |
|
|
|
|
Under serve, the embedder, watching, the /mcp root, the characters per hit, the sections per
document, the query log and the four ragdown_context defaults can also be changed while the server runs, under Settings → Server in the web UI. What
is saved there is kept in .ragdown-server.json in the docs directory and wins over the variable of
the same setting, at once and after every restart, until it is cleared there; the variable is the
value it goes back to. A new embedder is loaded first (so one that cannot load changes nothing),
then the index is rebuilt with it in place. With no minimum score saved or set, the floor follows
whichever embedder is in use. The docs directory, read-only and the token stay variables only, and
stdio reads the variables alone.
Commands and HTTP
node src/cli.ts <command>: stdio (the MCP server a client launches) or serve (HTTP, what the
image runs). Searching, indexing and stats are MCP tools, not commands.
serve exposes these routes:
Route | Auth | |
| none | Liveness and index stats. |
| bearer | Streamable HTTP MCP, stateless, for a folder with MCP on. 404 otherwise. See Folders. |
| bearer | The same over every folder with MCP on, while |
| bearer | Each folder's |
| bearer | Create a folder: JSON |
| bearer | JSON |
| bearer | Delete a folder and everything in it. 400 unless |
| bearer | JSON |
| bearer | The query log summed up for Settings → Usage: |
| bearer | Delete one of the |
| bearer | The indexed files, of one folder or all: |
| bearer | One indexed file's text, read from disk, with its tags, aliases and |
| bearer | Upload a file: JSON |
| bearer | Delete a Markdown file. 404 when it is not there. |
| bearer | Hybrid search in one folder, human-only ones included. |
| bearer | A wikilink target, resolved from the document |
| bearer |
|
| bearer | The documents in the same folder that link to |
| bearer | Any file inside a folder — an image, a PDF — as raw bytes, sandboxed and |
| none | The web UI from |
Every path in /api includes the folder: work/notes/a.md. Writes answer after the index has
synced, so the next GET /api/docs already shows them, and uploads, deletes, and creating,
renaming or deleting a folder, and changing the server settings, are a 403 under RAGDOWN_READ_ONLY. They are for the web UI; agents
write with ragdown_remember, ragdown_write and ragdown_edit.
The web UI asks for the token once and keeps it in the browser's local storage. Its static files
hold no documents, so they need none; everything it shows comes from the bearer routes. It is built by
npm run build (the image does this) and only serve serves it.
Upgrading from 4.x
5.0 splits the docs directory into folders, and nothing is migrated for you:
Move loose Markdown into a folder. Files directly in
RAGDOWN_DOCS_DIRare no longer indexed byserve; the log and the web UI list them.Turn MCP on for each folder agents should reach, under Settings → Folders or with
"mcp": truein its.ragdown.json. Every folder starts human-only.Point clients at
/mcp/<folder>./mcpserves nothing unlessRAGDOWN_ROOT_MCPis on (6.2 and later: Every folder at once). A 4.x scope URL/mcp/<folder>/<sub>keeps working once<folder>has MCP on.RAGDOWN_NOTES_DIRis relative to each folder:notesnow means<folder>/notes.
stdio is unchanged in use: it serves RAGDOWN_DOCS_DIR as one folder. Both modes rebuild their
index once on first start, for the new tag and alias columns.
Design
Chunks follow headings. Every heading starts a section, and the section's breadcrumb
(Backups › Restore) is part of what gets embedded. That is how a paragraph that only says "run it
twice" is found by a question about restoring backups. A section longer than 1,500 characters
(about 400 tokens, inside the window of every embedder here) is packed paragraph by paragraph. Fenced code blocks are
never split. Line numbers refer to the original file, so a hit is always one ragdown_read_doc
call away from its surroundings.
Hybrid retrieval. Dense cosine search finds a paragraph that answers the question in different words. BM25 full-text search finds an exact error string or flag name that an embedding blurs. Both read the chunk with its breadcrumb in front, so a table of settings is still findable by the name of the service its heading names and never repeats. Indexing the body alone cost 22 points of top-1 recall on a benchmark of 210 questions, and left the fused ranking below dense search on its own. Both return a pool, and reciprocal rank fusion (k = 60) merges them. Ranking uses the fused score. Filtering uses cosine similarity, because a fused score is not comparable across queries.
A query that names something leans on BM25. A query holding a quoted command, a snake_case key,
a dotted or slashed path, a flag, a camelCase name, or a word mixing letters and digits is fused by
score instead: each side min-max normalised over its own pool, BM25 weighted 0.8. Rank fusion gives
the dense side an equal vote, and the dense side ranks a chunk about the name's topic above the
one chunk that holds it. On three generated corpora the chunk holding an identifier came first
26–36% of the time under RRF and 77–85% routed, and top-1 over all 270 questions rose 4–6 points,
with no question type worse. Plain prose (up-to-date, e.g., 10:30) is not routed. Weighting
every query towards BM25, or fusing every query by score, gained 1–3 points and cost table-cell
questions up to 10.
Choosing an embedder. RAGDOWN_EMBEDDER picks one of three local models. They were measured on
the same benchmark — 30 generated runbooks, 210 questions with a known answering section, identical
chunks throughout — so only the model differs. Top-1 is how often the right section ranked first;
the timings are one 8-thread laptop CPU indexing that corpus and embedding one query.
| Model | Dim | Top-1 | Recall@5 | Index | Per query |
|
| granite-embedding-small-english-r2 | 384 | 85.7% | 100% | 3.6 s | 9 ms |
|
| bge-small-en-v1.5 | 384 | 82.9% | 100% | 3.5 s | 9 ms |
|
| embeddinggemma-300m | 768 | 91.0% | 100% | 33 s | 260 ms |
|
granite-small is the default because it costs what bge-small costs — same dimensions, same
index, 9 ms a query — and was ahead of it on every measure here, though on 210 questions that gap
alone is not significant (p = 0.41). embeddinggemma is a real jump and a significant one
(p < 0.001), but 260 ms is paid on every search, and a hook searches every turn. Being the default
is also why granite-small is the model baked into the Docker image; the other two download on
first start. gte-small, snowflake-arctic-embed-s, mxbai-embed-xsmall, bge-base, granite's 149M
model and arctic-embed-m were measured too and beat the default on nothing — bigger was not better.
Changing the model rebuilds the index, and RAGDOWN_HOOK_MIN_SCORE moves with it. Cosine
is on each model's own scale, not a shared one. On the same corpus, the weakest on-topic question
and the strongest unrelated prompt ("weather in Paris", "a recipe for carbonara") scored:
on-topic (10th percentile) | unrelated (worst case) | |
| 0.86 | 0.75 |
| 0.70 | 0.60 |
| 0.68 | 0.53 |
The last column of the table above is the value that separates the two for each model, and it is
what RAGDOWN_HOOK_MIN_SCORE defaults to for that model. Borrow another model's number and the
hook either injects a pasta recipe into every prompt or drops the documents that answer the question:
a value left at 0.7 from bge-small lets granite-small answer "tell me a joke" with four
unrelated documents at 0.72. A floor at or under the model's unrelated score is flagged in the web UI's
settings, whether the variable or a folder's own settings set it, and the variable is also logged
at start. An openai:
model has no measured scale, so it gets the default model's number as a guess: set the variable.
A second, relative gate under that one. RAGDOWN_HOOK_MIN_RATIO drops a hit scoring less than
0.95 of the best hit for the same prompt, whatever MIN_SCORE let through. An absolute floor
answers "is this on topic at all"; the ratio answers "is this as on topic as the thing I already
found", and a chunk far below the best one is a distractor that costs accuracy, not just tokens.
Being a ratio it also survives a change of embedder, which MIN_SCORE does not. On the same
benchmark (granite-small, min_score 0.8, top_k 4, 210 questions plus 10 unrelated prompts):
ratio | recall | chunks/prompt | off-topic chunks |
| 84.3% | 1.00 | 0.16 |
| 94.8% | 1.62 | 0.67 |
| 99.0% | 2.67 | 1.68 |
| 99.0% | 3.17 | 2.18 |
| 99.0% | 3.99 | 3.00 |
Recall is flat from 0.96 down, so the default sits one step below the knee rather than on it: 0.95 keeps everything an ungated hook found while still cutting a fifth of the injected chunks.
A quoted name passes both gates. The hook re-sorts by cosine, so better fusion alone barely
reaches it: a chunk that holds the exact command a prompt quotes can sit below MIN_SCORE, since
everything around the name is prose the prompt does not share. So the best-ranked candidate holding
a name the prompt quotes — whole, case-insensitively — is injected first whatever its cosine, and
the gated hits follow it. Only one, and only a strong name: a plain word in backticks
(`typescript`) or an unquoted version number does not count. On the three corpora the
identifier's chunk was injected for 4–10% of identifier prompts before and 100% after; the 270
questions were unchanged, for about 0.15 more chunks per prompt, and prompts quoting names no
document holds got nothing more.
An alias passes both gates too. A document's frontmatter aliases are in its first chunk's lexical text and nowhere in the embedding, so a query that calls a document by an alias found the right section first for under a third of questions. Search adds one rank-1 RRF share to every candidate of a document whose alias the query holds — whole, case-insensitively, three characters or more — and the hook injects the best-ranked such candidate whatever its cosine, as it does for a quoted name. On three generated corpora, each document given a codename alias, the right section came first for 83–85% of alias questions (98–99% in the top 8) and the hook injected it for 86–89%, against 32–38%; questions by title were unchanged. A title needs no such help: it is in every chunk's breadcrumb, embedded and indexed. A query routed to score fusion is not boosted.
Sections per document, off by default. A long document on the topic can fill every result with
its own sections. RAGDOWN_MAX_PER_DOCUMENT caps them: past the cap, a document's sections move
behind every other document's, so a short result fills from the next-best documents and a corpus
with too few still fills it. The hook applies the cap again after its cosine sort. On three
generated corpora a cap of 2 cut the hook's off-target sections by about a fifth (2.31–2.40 to
1.83–1.89 a prompt), at about 2 points of the right section in the top 8 (88.9–89.3% to
86.7%). It did not find more of the documents a question spread over several needed, and nor did
maximal marginal relevance at λ 0.9–0.5, so neither is on by default
(diversity.ts). Turn the cap on when one document crowds out the rest.
Following links, off by default. A question can need a document it does not describe: "what
is the first command of the procedure the outage was recovered with?" matches the incident, and
the incident links the procedure. RAGDOWN_FOLLOW_LINKS follows the wikilinks in the own section
of a search's best hits: each linked document's two sections closest to the query are placed right
after the hit that links it, with that hit's score and their own cosine. They obey every filter of
the search, and a link resolves within the hit's folder. On four generated corpora, following the
first hit took the procedure's steps from 0% to 37–43% of the top 8 and an upstream service's
section from 0% to 63–83% of the top 4 and 93–100% of the top 8, at 3–6 points of top 4 on the
other questions and none of first place or top 8. Raising the linked documents' scores instead
cost first place and found nothing. The hook gains nothing either way: it gates on each chunk's own
cosine, which a linked section's is not near
(links.ts). Turn it on for a vault whose notes lean on their links.
The query log, off by default. Search is tuned on generated corpora; RAGDOWN_QUERY_LOG (or
Settings → Server) records how it does on real ones. Each ragdown_recall, ragdown_context
and web search is a JSON line in query-log.jsonl in the data dir — the query, its filters, each
hit's path, lines, similarity and score, the latency, and for the hook what every gate dropped —
and each ragdown_read_doc is one more. It holds queries, never document text, stays on the
server, and rotates at 10 MB to one .1 file. Settings → Usage sums it up: the gaps, the
share of prompts the hook injected into, and the reads that followed a search showing the same
document within 10 minutes. HTTP MCP has no session, so that last one is a hint that a hit was
used, not proof.
Superseding a document. A document whose frontmatter lists supersedes: hides the documents it names from
search and from hook context. Nothing is deleted or rewritten — the old file stays on disk and
ragdown_read_doc still opens it, saying which document replaced it, and the web UI
marks it superseded and links to the replacement. But a fact that changed stops coming back as
confident prose next to its replacement.
---
title: "Embedder"
date: 2026-09-15
supersedes: ["2025-04-02-embedder.md"]
created_by: ragdown_remember
---Paths are relative to the document's own folder, the way a Markdown link is, and one that climbs out of
the docs folder is ignored: frontmatter is data from a file, not a path the server should follow.
ragdown_remember writes the field for you from its supersedes argument and refuses a path that
names no document, so an agent that gets it wrong hears about it instead of believing it replaced
something. It also records created_by: ragdown_remember and the session_id, so a later reader —
person or model — can tell an agent's document from one the user wrote.
Who wrote a document. created_by and session are indexed with the document. Every search hit
from a document with created_by says an agent wrote it, in ragdown_recall and in the block
ragdown_context injects, so text an agent saved earlier does not come back looking like the user's
own. written_by: agent or user limits ragdown_recall and ragdown_list to one or the other,
and ragdown_list's session_id finds what one conversation wrote. Both fields are frontmatter:
what a file says about itself, which anyone who can edit the file can change, not proof.
ragdown_remember and ragdown_write write them on a document they create, and ragdown_write keeps
them when it replaces one. A document the user wrote stays the user's however an agent edits it.
What a document is, before reading it. A frontmatter description (one line, or a YAML block)
is indexed as metadata and shown by ragdown_list, so an agent browsing a folder can choose what to
read from more than a title. Like tags and aliases it is never added to the embedded text.
The index is derived data. meta.json records the embedder and chunker version, and a
mismatch drops and rebuilds the index instead of migrating it. Syncs are diffs:
Files whose size and mtime are unchanged are skipped without being read.
A content hash decides whether a changed file is re-embedded.
Files gone from disk are dropped.
A file's chunks are replaced with one delete and one add. Syncs never overlap: changes that arrive
during a sync share one follow-up. The watcher debounces for 750 ms and falls back to polling
every 60 s where recursive fs.watch fails.
One primary per index. Several Claude Code windows on the same folder must not all index it.
The process that binds <data dir>/primary.sock is the primary: it indexes and watches. The others
are readers. They search the same LanceDB table (which sees the primary's commits) and forward syncs to the primary. Holding the socket is the lock. A crashed primary
leaves a socket nobody listens on, and the next process takes it over; readers retry every 30 s.
Searches never wait for indexing. The first index of a large folder takes minutes, so until it
finishes, searches answer from whatever is indexed so far. ragdown_stats shows syncing.
Why TypeScript, not Rust. The plan allowed a Rust/napi port if it paid for itself. It does not. Measured on an i9-13900H:
Model load (warm cache) | ~300 ms |
Query embedding | ~23 ms |
First index, 105 files / 1,919 chunks | 180 s (~11 chunks/s) |
Re-sync with nothing changed | ~40 ms |
Almost all indexing time is the ONNX Runtime forward pass, which is native C++. Tokenizing takes
about 1 ms a chunk, and raw onnxruntime-node without transformers.js measured the same
throughput. A Rust port would call the same kernels. Two changes did help, and neither depends on
the language:
Batch chunks by length. A batch is padded to its longest member.
Use half the cores. Four threads beat eight on this P/E-core CPU.
For faster indexing, switch the embedder to a GPU-backed openai:<model> endpoint.
Development
npm run typecheck && npm run lint && npm test
npm run build # the web UI, into web/dist
npm run dev:web # Vite on :5173, proxying /api to a `serve` on :3000The web UI in web/ is React, TanStack Query and Router, Tailwind and shadcn components from the
cubeui registry (npx shadcn add @cubeui/<name> from web/).
The tests run against real LanceDB, the real socket, and MCP over the SDK's in-memory transport and
real HTTP. They use the hash embedder, so they need no model download.
The numbers quoted in Design come from scripts/bench — the
embedder comparison, the threshold and drop-off sweeps, the RRF check, and the retrieval and
answer-accuracy runs against a synthetic corpus. They are too slow for CI and some need a local LLM,
so they are run by hand; that README says how, and what each one found.
Available Tools
12 toolsragdown_backlinksDocuments linking hereARead-only
The documents that link to a document — by [[wikilink]], alias, or relative Markdown link — each with the lines the links are on. Use it to find what depends on or refers to a document, e.g. before changing or superseding it.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path of the document relative to the documents root |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true and openWorldHint=false, and the description adds real value beyond that: it specifies that results include the line numbers where links occur and clarifies the link-resolution semantics (wikilink, alias, Markdown). It does not discuss limits, ordering, or whether unresolved links appear, so it is not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no filler: the first defines the output precisely, the second gives the motivating scenario. Everything is front-loaded and each sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read tool with read-only annotations and no output schema, the description covers what the tool returns (documents plus link line numbers) and why one would call it. Missing only edge-case behavior such as whether non-existent paths error or return empty results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% with a single required 'path' parameter already documented as relative to the documents root. The description adds no further parameter detail (e.g. absolute vs relative handling, trailing separators), so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource (the documents that link to a given document) and enumerates exactly which link syntaxes count ([[wikilink]], alias, relative Markdown link), which no sibling does. An agent can distinguish it from ragdown_context or ragdown_read_doc immediately.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit use case — 'find what depends on or refers to a document, e.g. before changing or superseding it' — which is genuine when-to-use context tied to a workflow. It stops short of naming a sibling alternative or a when-not condition, so it is strong but not complete routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_contextContext for a promptARead-only
For hooks that run before a turn: the documents related to a user prompt, as a ready-to-inject block, or empty text when nothing is similar enough. Unlike ragdown_recall it filters by min_score, skips short prompts and slash commands, and never returns a section twice for the same session_id. reset: true first forgets what that session was given; call it with an empty prompt after the client compacts or clears its context. format: 'claude-code' wraps the block as the JSON a Claude Code UserPromptSubmit hook adds to the prompt.
| Name | Required | Description | Default |
|---|---|---|---|
| reset | No | Forget the sections already returned for session_id before answering, because the client no longer has them | |
| top_k | No | Default RAGDOWN_HOOK_TOP_K | |
| format | No | 'text' is the block itself. 'claude-code' is the block as a Claude Code hook's additionalContext, for an mcp_tool hook on UserPromptSubmit, which does not pass plain text on to the model | text |
| prompt | Yes | The user's prompt, verbatim | |
| max_chars | No | Most characters in the block. Default RAGDOWN_HOOK_MAX_CHARS | |
| min_ratio | No | Lowest share of the best hit's similarity a hit may have and still be included; 0 keeps every hit above min_score. Default RAGDOWN_HOOK_MIN_RATIO | |
| min_score | No | Lowest cosine similarity to include. Default RAGDOWN_HOOK_MIN_SCORE | |
| session_id | No | Stable id of the conversation; sections already returned for it are skipped |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnlyHint, not openWorld), but the description discloses non-obvious stateful behavior they cannot: per-session dedup ('never returns a section twice for the same session_id'), the reset-forgets-sections semantics, skip rules for short prompts/slash commands, and the empty-string return condition. It stops short of error/failure behavior or the exact shape of the returned block, so it is strong but not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences with no filler, and the primary purpose is front-loaded before the sibling comparison. The reset/format guidance reads as compressed notes rather than prose, which slightly hurts readability but not information density.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and does so (block vs. empty text). Given 8 parameters fully documented in the schema, the annotations covering safety, and the description covering purpose, routing, statefulness, and return semantics, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description nonetheless adds real meaning the schema lacks for reset (what is forgotten, and the empty-prompt idiom after compaction) and format (why 'claude-code' exists — the hook does not pass plain text through). The remaining numeric knobs are left entirely to the schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource (retrieve documents related to a prompt) plus the exact artifact returned (a ready-to-inject <ragdown-context> block or empty text) and the execution context (pre-turn hooks). It explicitly distinguishes itself from the sibling ragdown_recall, so an agent can route without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use it ('for hooks that run before a turn') and names the alternative plus the selecting conditions ('Unlike ragdown_recall it filters by min_score, skips short prompts and slash commands'). It also gives concrete invocation guidance for reset ('call it with an empty prompt after the client compacts or clears its context') and when to pick the claude-code format.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_deleteDelete a document or subfolderADestructive
Permanently delete a document, or a subfolder, from the documents folder and the index. There is no trash and no undo. Call ragdown_backlinks first: links to a deleted document are left as they are, pointing at nothing. When a document is out of date rather than unwanted, prefer ragdown_remember with supersedes, which keeps the old text readable. A subfolder that holds anything is deleted only with recursive: true. Pass base_hash to delete a document only if it is still the version you read.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path of the document or subfolder relative to the documents root, e.g. 'ops/pg.md' or 'ops' | |
| base_hash | No | For a document: the hash ragdown_read_doc returned. If the file changed since, nothing is deleted | |
| recursive | No | For a subfolder: also delete everything in it — documents, attachments and any other files |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and idempotentHint=false, but the description adds the decisive operational facts: no trash, no undo, backlinks are left dangling rather than fixed, and base_hash causes a no-op if the file changed. That is exactly the context an agent needs before invoking an irreversible operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Six tight sentences, front-loaded with the irreversible nature of the operation, and each sentence carries distinct routing or safety information. Slightly dense, but nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and a fully documented input schema, the description covers everything an agent must know: destructive outcome, missing safety net, sibling alternatives, and the conditional-delete semantics. Nothing material is left unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the parameters are already documented; the description nonetheless adds meaning the schema lacks, namely that recursive is required for any non-empty subfolder and that base_hash exists as an optimistic-concurrency guard tied to the version previously read.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: permanently delete a document or subfolder from the documents folder and the index. It names the exact scope and distinguishes itself from siblings such as ragdown_remember and ragdown_backlinks by explaining what those are for instead.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing: call ragdown_backlinks first to inspect incoming links, use ragdown_remember with supersedes when a document is merely out of date rather than unwanted, and use recursive: true only for a subfolder that holds content. Both the when and the when-not are spelled out with named alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_editEdit a documentADestructive
Change part of an existing document without sending the rest of it: each edit swaps one exact passage (old_text) for another (new_text). old_text must match the document character for character, as ragdown_read_doc returned it, and be there exactly once unless replace_all is true, so include enough of the surrounding text to make it unique. Edits are made in order, each on the result of the one before, and if any of them does not apply nothing is written. An empty new_text deletes the passage. A passage that is not found is answered with the nearest one the document does have.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path of a Markdown file relative to the documents root, e.g. 'projects/alpha.md' | |
| edits | Yes | ||
| base_hash | No | The hash from ragdown_read_doc. Optional: if the file changed since, nothing is written |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare destructiveHint=true and readOnlyHint=false; the description adds substantially more: edits are applied in order on the prior result, the operation is atomic ('if any of them does not apply nothing is written'), empty new_text deletes, and a missing passage returns the nearest match instead of silently failing. That is real behavioral context an agent cannot get from the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, purpose first, then matching rules, then ordering/atomicity, then edge cases. No filler; every sentence carries operational information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully covers failure behavior (nearest-passage response, all-or-nothing writes), but it never says what a successful edit returns — notably the updated hash implied by the base_hash parameter — leaving the agent to infer the success payload.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67%, and the description compensates: it explains the character-for-character match requirement, the exactly-once uniqueness constraint, the 'include enough surrounding text' remedy, and the delete-by-empty-new_text convention. It does not address the optional base_hash precondition, which only the schema documents.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Change part of an existing document') and the exact mechanism ('each edit swaps one exact passage (old_text) for another (new_text)'). This clearly distinguishes it from the sibling full-document writer ragdown_write and from ragdown_read_doc, which it names as the source of old_text.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Tells the agent how to obtain old_text ('as ragdown_read_doc returned it') and the condition under which replace_all is needed, and 'without sending the rest of it' implicitly routes the agent away from a full rewrite. It stops short of explicitly naming ragdown_write as the alternative or stating when not to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_listList documentsARead-only
Browse the documents rather than search them. Returns one line per document: its path, title, tags and last change, who wrote it and in which session when an agent did, and its description when its frontmatter has one. Optionally under a subfolder, with a tag, by who wrote it, or from one session. sort: 'recent' puts the most recently changed first.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | Only documents with this tag; 'project' also matches 'project/alpha' | |
| sort | No | path | |
| limit | No | ||
| session_id | No | Only documents written in this conversation, as ragdown_remember recorded it | |
| written_by | No | Only documents an agent wrote ('agent': frontmatter has created_by) or only the user's own ('user') | |
| path_prefix | No | Only documents under this subfolder, relative to the documents root, e.g. 'projects/' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered. The description adds substantial behavioral detail beyond annotations: one line per document, exact fields returned (path, title, tags, last change, author, session, frontmatter description), and what 'recent' sorting does. This is valuable given no output schema exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single tight paragraph front-loaded with purpose and usage contrast, followed by return format, optional filters, and sort behavior. Every sentence carries information; nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description thoroughly explains the return shape (one line per document and which fields appear). It covers filtering options, sorting semantics, and the safe-read nature is covered by annotations. The only minor gap is limit/pagination, which the schema already constrains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 67%. The description accounts for five of six parameters (tag, path_prefix, written_by, session_id, sort) and clarifies sort='recent' behavior that the schema's bare enum does not explain. It omits 'limit' entirely, and adds little beyond the schema for parameters that already have descriptions, so it is good but not exhaustive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Browse the documents') and immediately distinguishes it from search. It also lists the exact return format, so an agent knows this is a listing tool, not a retrieval or search tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The opening 'Browse the documents rather than search them' gives clear when-to-use context and implies a search alternative, but it does not name a specific sibling tool or state explicit exclusions. The optional filters are framed as conditions, which helps selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_moveMove or rename a document or subfolderA
Rename or move a document, or a subfolder with everything in it, to a new path inside the documents folder. Every link that pointed at what moved ([[wikilinks]] and relative Markdown links, in any document) is rewritten to still point at it, and the result names the documents that were updated. Never overwrites: if something is already at to, nothing moves. Use this rather than ragdown_write plus a delete, which would break the links.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | Its whole new path, name included: 'db/postgres.md', not 'db/'. Missing subfolders on the way are created. A document keeps a Markdown extension | |
| from | Yes | Path of the document or subfolder relative to the documents root, e.g. 'ops/pg.md' or 'ops' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only tell the agent it is not read-only, not destructive, and closed-world; the description adds substantive behavior beyond that: wikilink and relative Markdown links across any document are rewritten, nothing is overwritten on collision, and the result names the updated documents. These are the side effects an agent must know before calling a move tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with what moves and where, then the link-rewrite consequence, then the alternative. No filler and every clause carries decision-relevant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by stating what the result contains ('names the documents that were updated'), and it covers the collision case and the link side effects. Nothing needed to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and both params already document their path format, extension rules, and subfolder creation in the schema. The description adds only the destination-scoping phrase and the no-overwrite rule, which is baseline for a fully documented two-parameter schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb pair (rename/move) and resource (document or subfolder with its contents) and scopes the destination ('to a new path inside the documents folder'). It also explicitly contrasts itself with the naive ragdown_write + delete approach, so an agent can distinguish it from siblings without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when-to-use and a named alternative with the reason it is wrong: 'Use this rather than ragdown_write plus a delete, which would break the links.' It also states the no-overwrite precondition, which tells the agent what to expect when a destination already exists.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_read_docRead a documentARead-only
Read a Markdown file from the documents folder, whole or by line range, straight from disk. Never clipped. Use it to see the context around a ragdown_recall hit, or to follow a [[wikilink]] in a document. The first line is 'path:start-end of total_lines (hash …)', the hash being the whole file's, for base_hash; the text follows from the second line, exactly as it is in the file.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path relative to the documents root, as returned by ragdown_recall, or a wikilink target as written inside [[...]] ('Note', 'Note#Heading', 'sub/Note'); a heading narrows the text to that section | |
| end_line | No | ||
| start_line | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover only read-safety (readOnlyHint=true, openWorldHint=false), and the description adds genuinely new behavior: reads unclipped directly from disk and returns a header line 'path:start-end of total_lines (hash …)' where the hash is the whole file's base_hash, with raw file text from line two. That return contract is exactly what an agent needs and is absent from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads verb, resource and scope in the first sentence, then use cases, then output format. Three dense sentences, all load-bearing; the header/hash explanation is slightly run-on but is necessary since there is no output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-format burden and does so explicitly. Combined with path semantics for recall hits and wikilinks, an agent has enough to call it correctly; the only shortfall is line-range boundary semantics. Not requiring auth or error behavior is acceptable for a read-only disk tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 33%: path is documented in the schema, but start_line and end_line are bare. The description's 'whole or by line range' explains the intent of the two undocumented integers but not whether the range is inclusive, 1-based, or what happens on out-of-range values, so it only partially compensates.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read a Markdown file from the documents folder') plus scope ('whole or by line range, straight from disk'). An agent can distinguish this from ragdown_recall (search) and ragdown_context without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives two concrete triggers: inspecting context around a ragdown_recall hit and following a [[wikilink]]. It names the sibling workflow that leads here but never states when not to use it (e.g., vs. ragdown_context or ragdown_list), so guidance is strong but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_recallSearch documentsARead-only
Hybrid (semantic + keyword) search over the user's Markdown documents. Returns the most relevant sections with file path, line range, heading breadcrumb and cosine similarity (above ~0.8 is usually on topic) and the day the file last changed. A section of a document an agent wrote says so. Use it before answering anything the documents may cover; follow up with ragdown_read_doc for the surrounding text.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | Only search documents with this tag (frontmatter tags or inline #tags); 'project' also matches 'project/alpha' | |
| query | Yes | What to look for, as a question or keywords | |
| top_k | No | ||
| format | No | text | |
| max_chars | No | text format: characters per hit before it is clipped (0 = never) | |
| written_by | No | Only documents an agent wrote ('agent': frontmatter has created_by) or only the user's own ('user') | |
| path_prefix | No | Only search files under this subfolder, relative to the documents root, e.g. 'projects/' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint/openWorldHint already covering the safety profile, the description adds real behavioral context: what a hit contains, how to interpret the cosine similarity (~0.8 means on topic), that provenance is signaled when a section was written by an agent, and that results are clipped. Missing details are minor (ordering, dedup, empty-result behavior).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the search mechanism before return shape and usage routing. The middle sentence is dense but each clause (path, line range, breadcrumb, similarity, last-changed) earns its place by substituting for a missing output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter read tool with no output schema and no annotations carrying behavioral detail, the description supplies return fields and usage routing well. It leaves gaps on default result count, the text vs json format switch, and handling of empty results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 71%, and the description adds essentially no parameter guidance — tag, path_prefix, written_by, top_k, format and max_chars are documented only in the schema. The similarity-threshold hint is useful interpretation but does not map to an input parameter. Baseline 3 for moderate coverage with no compensating description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — hybrid semantic + keyword search over the user's Markdown documents — and immediately distinguishes itself from the browsing siblings (list, read_doc, backlinks). An agent can identify the retrieval tool without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use it ('before answering anything the documents may cover') and names a follow-up tool (ragdown_read_doc for surrounding text). It gives no when-not conditions or contrast against similar retrieval siblings such as ragdown_context, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_reindexReindex documentsA
Bring the index up to date with the folder now. Changes are normally picked up automatically within a second; use this after bulk edits made while no server was running, or full: true to re-embed everything.
| Name | Required | Description | Default |
|---|---|---|---|
| full | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this is a non-read-only, non-destructive, closed-world mutation, so the safety bar is partly covered. The description adds genuinely useful context beyond that: normal automatic pickup latency (within a second) and the fact that full: true re-embeds everything, which signals a materially heavier operation. It stops short of stating whether the call blocks or how long a full re-embed takes, so it is not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with zero filler, front-loading the core action and then the usage conditions. Every clause earns its place: the automatic-indexing note prevents unnecessary calls and the full flag guidance drives correct invocation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-optional-boolean, no-output-schema mutation tool, the description covers what it does, when to invoke it, and what the lone parameter changes. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the meaning of 'full' on its own, and it largely does: full: true re-embeds everything versus the implied incremental default that only picks up changes. The default value (false) and the incremental-path behavior are only implied rather than stated outright, keeping this from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action on a specific resource: bringing the folder's index up to date. It is immediately distinguishable from siblings like ragdown_recall, ragdown_edit, or ragdown_stats, which do not touch the index. No ambiguity about what the tool accomplishes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly frames the default behavior (changes are normally picked up automatically within a second), tells the agent when NOT to call it, and then names the two triggering conditions: bulk edits made while no server was running, or full: true to re-embed everything. This is a textbook when/when-not/alternative formulation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_rememberWrite a documentA
Save something worth keeping (a decision, a fix, a how-to) as a new Markdown document in the documents folder, indexed immediately so later searches find it. Never overwrites an existing file. When this document replaces an earlier one, pass that document's path as supersedes so searches stop returning the old version.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | File name under the documents folder, without .md; defaults to <date>-<title-slug> | |
| tags | No | ||
| title | Yes | ||
| content | Yes | Markdown body; the title and date go in frontmatter | |
| session_id | No | Stable id of the conversation, recorded in the document's frontmatter | |
| supersedes | No | Paths of documents this one replaces, as returned by ragdown_recall. They stay on disk and ragdown_read_doc still opens them, but search and hook context skip them. Use it when a fact changed, not when you are merely writing about the same topic. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare openWorldHint=false and non-destructive write. The description goes beyond that with three real behavioral facts: content is indexed immediately for later search, existing files are never overwritten, and superseding changes what search returns. It doesn't cover failure modes or name-collision handling, but the added context is substantial against a low annotation bar.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the write action, then the non-overwrite guarantee, then the conditional supersede workflow. No filler, no restatement of the title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, but this is a write tool whose result is largely confirmed by the immediate-indexing statement. With 6 parameters, the description covers purpose, safety, indexing and the one non-obvious parameter; only edge cases like name collisions or failed writes are left unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 67%, so the schema already documents name, content, session_id and supersedes in detail. The description restates the supersedes intent ('searches stop returning the old version') but adds no syntax or semantics the schema lacks, so it stays at the baseline for a partially-covered schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource ('Save something worth keeping ... as a new Markdown document in the documents folder') and immediately qualifies the scope with the indexing behavior. The 'Never overwrites an existing file' clause effectively separates it from ragdown_edit without the agent needing to open a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-to-use cues ('a decision, a fix, a how-to') and a conditional rule for the supersedes case ('When this document replaces an earlier one'). It never states when NOT to use it versus ragdown_edit or ragdown_recall, so it stops short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_statsIndex statusBRead-only
The documents folder, index size (files, chunks), embedder, whether this process is the indexing primary, and the last sync. include_files lists every indexed file.
| Name | Required | Description | Default |
|---|---|---|---|
| include_files | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds that include_files enumerates every indexed file, hinting at a potentially large response, but it never warns about output size or cost, so the addition is modest.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with no filler. Slight deduction because it opens with a bare list of returned fields rather than a framing clause like 'Returns index status', which would front-load the purpose more cleanly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully enumerates the return contents, which is what an agent needs from a status tool. It is adequate for a read-only one-parameter tool, missing only guidance on when to prefer it over other index-inspecting siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the sole parameter include_files has no schema description at all, so the description carries the full burden. It does define the parameter's effect ('lists every indexed file'), which is exactly the compensation needed for a single-parameter tool.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description enumerates exactly what the tool reports (documents folder, index size, embedder, primary status, last sync), so the resource is unambiguous even though it never states a verb like 'reports'. It does not distinguish itself from siblings such as ragdown_list or ragdown_reindex, which is the only real gap.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no statement of when to call this versus alternatives like ragdown_list or ragdown_reindex, and no prerequisites or context given. Usage is only inferable from the fact that it reports index state.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ragdown_writeRewrite or append to a documentADestructive
Rewrite or append to an existing document, or create one at a path you choose. To change part of a document, use ragdown_edit, which sends only the passages that change. By default text replaces the whole file (frontmatter included), which for an existing document needs base_hash: the hash ragdown_read_doc returned, so you never overwrite a version you have not read. A document this creates gets created_by: ragdown_write in its frontmatter, so the hash returned is of the file as written, not of text. append: true adds text at the end of the document, or with heading at the end of that section, leaving the rest as it is. If the file changed since base_hash, nothing is written: read it again and redo the edit.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path of a Markdown file relative to the documents root, e.g. 'projects/alpha.md' | |
| text | Yes | The document's new Markdown, or with append, what to add | |
| append | No | ||
| heading | No | With append: add to the end of the section under this heading | |
| base_hash | No | The hash from ragdown_read_doc; required to replace an existing document | |
| session_id | No | Stable id of the conversation, recorded in the frontmatter of a document this creates |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnly=false and destructive=true; the description goes well beyond by disclosing that default text replaces the whole file including frontmatter, that base_hash prevents overwriting an unread version, that a created document gets created_by stamped, and that a changed file results in a no-op. This is exactly the destructive-mutation context an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the edit-vs-write routing, and every sentence carries operational content. It is on the dense side, but the length is justified by the number of behavioral facts it must convey for a destructive mutation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter destructive tool with no output schema and no annotations beyond hints, the description covers conflict handling, hash semantics, frontmatter side effects, and append behavior. Only minor gaps remain, such as the exact shape of the returned hash on create vs replace.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 83%, so baseline is 3, but the description adds real meaning: base_hash origins from ragdown_read_doc and why it is required, the append+heading interaction (end of document vs end of section), and session_id's role via frontmatter recording of created documents.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States specific verbs (rewrite, append, create) and the resource (a document) with scope, and explicitly distinguishes itself from sibling ragdown_edit by naming it. An agent can pick between the two without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'To change part of a document, use ragdown_edit' gives an explicit alternative plus the condition that selects it. It also states when base_hash is required and what happens on conflict, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v6.6.0- Changed
ragdown_edit7 fields changed- removed
Input schema / properties / appendRemoved value: -{ - "default": false, - "type": "boolean" -} - changed
Input schema / properties / base_hash / descriptionPrevious value: -"The hash from ragdown_read_doc; required to replace an existing document"New value: +"The hash from ragdown_read_doc. Optional: if the file changed since, nothing is written" - added
Input schema / properties / editsAdded value: +{ + "items": { + "additionalProperties": false, + "properties": { + "new_text": { + "description": "What replaces it; empty to delete the passage", + "type": "string" + }, + "old_text": { + "description": "The passage to replace, exactly as it is in the document", + "minLength": 1, + "type": "string" + }, + "replace_all": { + "default": false, + "description": "Replace every occurrence of old_text rather than require one", + "type": "boolean" + } + }, + "required": [ + "old_text", + "new_text" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" +} - removed
Input schema / properties / headingRemoved value: -{ - "description": "With append: add to the end of the section under this heading", - "type": "string" -} - removed
Input schema / properties / session_idRemoved value: -{ - "description": "Stable id of the conversation, recorded in the frontmatter of a document this creates", - "type": "string" -} - removed
Input schema / properties / textRemoved value: -{ - "description": "The document's new Markdown, or with append, what to add", - "minLength": 1, - "type": "string" -} - changed
Input schema / requiredPrevious value: -[ - "path", - "text" -]New value: +[ + "path", + "edits" +]
- Added
ragdown_write
9 tool updates
v5.16.0- Changed
ragdown_backlinks1 field changed- changed
Input schema / properties / path / descriptionPrevious value: -"Path of the note relative to the notes root"New value: +"Path of the document relative to the documents root"
- Changed
ragdown_context2 fields changed- added
Input schema / properties / formatAdded value: +{ + "default": "text", + "description": "'text' is the block itself. 'claude-code' is the block as a Claude Code hook's additionalContext, for an mcp_tool hook on UserPromptSubmit, which does not pass plain text on to the model", + "enum": [ + "text", + "claude-code" + ], + "type": "string" +} - added
Input schema / properties / resetAdded value: +{ + "default": false, + "description": "Forget the sections already returned for session_id before answering, because the client no longer has them", + "type": "boolean" +}
- Added
ragdown_delete - Changed
ragdown_edit4 fields changed- changed
Input schema / properties / base_hash / descriptionPrevious value: -"The hash from ragdown_read_doc; required to replace an existing note"New value: +"The hash from ragdown_read_doc; required to replace an existing document" - changed
Input schema / properties / path / descriptionPrevious value: -"Path of a Markdown file relative to the notes root, e.g. 'projects/alpha.md'"New value: +"Path of a Markdown file relative to the documents root, e.g. 'projects/alpha.md'" - added
Input schema / properties / session_idAdded value: +{ + "description": "Stable id of the conversation, recorded in the frontmatter of a document this creates", + "type": "string" +} - changed
Input schema / properties / text / descriptionPrevious value: -"The note's new Markdown, or with append, what to add"New value: +"The document's new Markdown, or with append, what to add"
- Changed
ragdown_list4 fields changed- changed
Input schema / properties / path_prefix / descriptionPrevious value: -"Only notes under this subfolder, relative to the notes root, e.g. 'projects/'"New value: +"Only documents under this subfolder, relative to the documents root, e.g. 'projects/'" - added
Input schema / properties / session_idAdded value: +{ + "description": "Only documents written in this conversation, as ragdown_remember recorded it", + "type": "string" +} - changed
Input schema / properties / tag / descriptionPrevious value: -"Only notes with this tag; 'project' also matches 'project/alpha'"New value: +"Only documents with this tag; 'project' also matches 'project/alpha'" - added
Input schema / properties / written_byAdded value: +{ + "description": "Only documents an agent wrote ('agent': frontmatter has created_by) or only the user's own ('user')", + "enum": [ + "agent", + "user" + ], + "type": "string" +}
- Added
ragdown_move - Changed
ragdown_read_doc1 field changed- changed
Input schema / properties / path / descriptionPrevious value: -"Path relative to the notes root, as returned by ragdown_recall, or a wikilink target as written inside [[...]] ('Note', 'Note#Heading', 'sub/Note'); a heading narrows the text to that section"New value: +"Path relative to the documents root, as returned by ragdown_recall, or a wikilink target as written inside [[...]] ('Note', 'Note#Heading', 'sub/Note'); a heading narrows the text to that section"
- Changed
ragdown_recall3 fields changed- changed
Input schema / properties / path_prefix / descriptionPrevious value: -"Only search files under this subfolder, relative to the notes root, e.g. 'projects/'"New value: +"Only search files under this subfolder, relative to the documents root, e.g. 'projects/'" - changed
Input schema / properties / tag / descriptionPrevious value: -"Only search notes with this tag (frontmatter tags or inline #tags); 'project' also matches 'project/alpha'"New value: +"Only search documents with this tag (frontmatter tags or inline #tags); 'project' also matches 'project/alpha'" - added
Input schema / properties / written_byAdded value: +{ + "description": "Only documents an agent wrote ('agent': frontmatter has created_by) or only the user's own ('user')", + "enum": [ + "agent", + "user" + ], + "type": "string" +}
- Changed
ragdown_remember3 fields changed- changed
Input schema / properties / name / descriptionPrevious value: -"File name under the notes folder, without .md; defaults to <date>-<title-slug>"New value: +"File name under the documents folder, without .md; defaults to <date>-<title-slug>" - changed
Input schema / properties / session_id / descriptionPrevious value: -"Stable id of the conversation, recorded in the note's frontmatter"New value: +"Stable id of the conversation, recorded in the document's frontmatter" - changed
Input schema / properties / supersedes / descriptionPrevious value: -"Paths of notes this one replaces, as returned by ragdown_recall. They stay on disk and ragdown_read_doc still opens them, but search and hook context skip them. Use it when a fact changed, not when you are merely writing about the same topic."New value: +"Paths of documents this one replaces, as returned by ragdown_recall. They stay on disk and ragdown_read_doc still opens them, but search and hook context skip them. Use it when a fact changed, not when you are merely writing about the same topic."
3 tool updates
v5.9.0- Added
ragdown_backlinks - Added
ragdown_edit - Added
ragdown_list
2 tool updates
v5.2.0- Changed
ragdown_read_doc1 field changed- changed
Input schema / properties / path / descriptionPrevious value: -"Path relative to the notes root, as returned by ragdown_recall"New value: +"Path relative to the notes root, as returned by ragdown_recall, or a wikilink target as written inside [[...]] ('Note', 'Note#Heading', 'sub/Note'); a heading narrows the text to that section"
- Changed
ragdown_recall2 fields changed- changed
Input schema / properties / path_prefix / descriptionPrevious value: -"Only search files under this folder, relative to the notes root, e.g. 'projects/'"New value: +"Only search files under this subfolder, relative to the notes root, e.g. 'projects/'" - added
Input schema / properties / tagAdded value: +{ + "description": "Only search notes with this tag (frontmatter tags or inline #tags); 'project' also matches 'project/alpha'", + "type": "string" +}
2 tool updates
v4.0.0- Added
ragdown_context - Changed
ragdown_remember2 fields changed- added
Input schema / properties / session_idAdded value: +{ + "description": "Stable id of the conversation, recorded in the note's frontmatter", + "type": "string" +} - added
Input schema / properties / supersedesAdded value: +{ + "description": "Paths of notes this one replaces, as returned by ragdown_recall. They stay on disk and ragdown_read_doc still opens them, but search and hook context skip them. Use it when a fact changed, not when you are merely writing about the same topic.", + "items": { + "type": "string" + }, + "type": "array" +}
5 tool updates
v1.0.0- First observed
ragdown_read_doc - First observed
ragdown_recall - First observed
ragdown_reindex - First observed
ragdown_remember - First observed
ragdown_stats
TDQS
Scored across 12 tools
Each tool targets a distinct action (recall=search, list=browse, backlinks=referrers, context=hook injection), and descriptions carefully differentiate close pairs like recall vs context and write vs edit. The only mild overlap is remember vs write, since both create documents, though descriptions clarify remember never overwrites and is intent-specific.
Every tool uses the same ragdown_ prefix with snake_case, and verb-based names (read_doc, write, edit, move, delete, list, recall, remember) dominate. The few noun names (backlinks, context, stats) still fit the uniform convention.
12 tools is well within the ideal 3-15 range and each one earns its place in a document-RAG workflow. No redundant or filler tools are present.
Full lifecycle coverage exists: create (remember/write), read (read_doc/list), update (edit/write), delete, plus move with link rewriting, search (recall), backlinks, reindex, and stats. This covers the domain thoroughly with no obvious dead ends.
Maintenance
Related MCP Connectors
Markdown notes and whiteboards your AI agent can read and write.
Personal context for every AI: search, read, and write back to your private Markdown library.
Markdown notes in folders, with files, that your AI assistant can read, write and organise.
Markdown notes in folders, with files, that your AI assistant can read, write and organise.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceTurns a folder of Markdown notes into an agent-native knowledge base, providing long-term memory with provenance, token-budgeted retrieval, and safe write-back with versioning.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to search, read, create, and maintain a local-first knowledge base using hybrid retrieval and Markdown note management through the Model Context Protocol.01MIT
- AlicenseNot gradedqualityBmaintenanceLets AI agents retrieve source-linked passages from a local folder of Markdown notes or an Obsidian vault, optionally following the explicit links between notes, and can also fetch passages from pinned public GitHub files you configure. It returns bounded evidence packets with original text, file paths and hashes so agents can answer from your material rather than generating content themselves.1MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to search, read, and navigate a folder of Markdown notes as a knowledge base, with full-text search, backlinks, tags, metadata, and note resources for conversation attachment.1MIT