RootMind
RootMind’s MCP server lets you search and read your locally indexed documents through read-only tools.
Search indexed files with fast SQLite FTS5 full-text/prefix matching and optional file-type filters.
Run typo-tolerant trigram fuzzy search when exact search finds nothing or terms may be misspelled.
List documents and metadata, filtering by file type or filename substring.
Read a document or a character window (up to 50,000 characters per call) by document ID.
Get collection statistics: document count, file types, and total size.
Search results include snippets, known locations such as page/slide/sheet, and suggested read windows; the server does not index or modify files through MCP.
RootMind
English | Deutsch
Install · Connect MCP clients · How it works · Why a tool loop · Security · Limitations
Ask your documents a question. Get the answer with the page it came from.
RootMind indexes the folders you choose on your own computer (PDF, Word, Excel, PowerPoint, CSV, Markdown and text files). When you ask a question, a language model searches that index and reads the passages it asks for, then answers with sources down to the page, slide or sheet. There is no embedding model and no vector database: indexing is plain full-text search in SQLite, and the model drives the retrieval.
https://github.com/user-attachments/assets/d3cc0249-a550-4de5-ae6b-55c1291a282f
Cited answers. Every answer names its files and, where it can be resolved, the page, slide or sheet.
Your files stay on your computer. Indexing and search are local and need no LLM and no API key. Answering a question sends the question and the excerpts the model reads to the provider you configure (Anthropic, OpenAI or xAI). With a local Ollama server as the provider, nothing leaves your computer (see Local models with Ollama).
It reads, it does not stuff. Instead of pasting whole documents into a chat, the model searches and reads targeted windows, and every step is a visible tool call. The cost is more model round trips than classic RAG; see Why a tool loop instead of embedding RAG.
Usable from your other tools. The same read-only tools are served over MCP, for Claude Desktop, Claude Code, n8n and other agents.
Windows and Linux. The desktop app is in English and German; the license is MIT.
Try it in a few minutes
Download RootMind-Setup-x64.exe (Windows 10 or 11) or the .deb, .rpm or AppImage (Linux).
Start RootMind and add a folder with your documents under Folders.
Open Chat, tick Search only and search. This needs no API key. For answers, add a key under Settings and untick Search only.
The Quickstart has the details.
What it is
The repository has two parts:
Backend (Python, FastAPI): indexer, full-text search, the agent loop, a localhost REST API, and MCP servers over stdio and HTTP.
Desktop app (Tauri 2 with React and TypeScript, plus a Rust shell): a chat and search UI for Windows and Linux, in English and German. On Windows its installer sets up the backend as a Windows service; on Linux the app sets it up as systemd user services of your account. The app talks to that service.
Status: this is a personal project. It is not commercially maintained and comes with no support commitment. Issues and pull requests are welcome and get a best-effort response.
Related MCP server: Claude RAG MCP Pipeline
Install
Each release has these downloads for Windows (x64); the Linux packages are described in Install on Linux:
RootMind-Setup-x64.exe, for most users, on Windows 10 or 11: the desktop app and its Windows service. Run it and approve the administrator prompt once; no Python is needed. It installs the app to
%ProgramFiles%\RootMindand sets up the RootMind service for the account that is signed in to Windows.rootmind-backend-windows-x64.zip, for the command line and servers: the backend alone, without the app (see Install for developers and servers).
rootmind.mcpb, RootMind for Claude Desktop, to install after the app (see Use RootMind in Claude Desktop).
Installing needs an administrator account. The service belongs to the account
that uses RootMind, so the installer sets it up only when it runs as the
account signed in to Windows. The install folder is always
%ProgramFiles%\RootMind, a folder that only administrators can change; the
installer refuses another one (for example with /D=, exit code 10).
Silent and managed installs. winget, Intune, Configuration Manager and
similar tools often run the installer silently (/S) as SYSTEM. Then, and
when an administrator approves the prompt while another account is signed in,
the installer installs the app but not the service, and exits with 0. The
service is set up when the account that uses RootMind first opens it: RootMind
says "The RootMind service is not set up yet", and Set up sets it up for
that account with one administrator approval. A standard account cannot set it
up; it needs an administrator account that uses RootMind itself. If the
installer could not set the service up for another reason, it also exits with
0 and RootMind shows the problem and Repair. The outcome is recorded in
the uninstall entry HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\RootMind
(RootMindServiceSetup: installed, skipped or failed, with
RootMindServiceSetupReason and, for a failure, RootMindServiceSetupCode).
Updates run as SYSTEM update an existing service in place.
The installer is not code-signed yet. Windows SmartScreen may show "Windows protected your PC": choose More info, then Run anyway. If Smart App Control is on, it may block the installer or RootMind's service program without that choice. Smart App Control has no exception for a single app; signed releases, which are planned, will avoid this (see the code signing policy).
Then start RootMind and add a folder under Folders. Indexing starts within about a minute, and search works without an API key once the folder is indexed. To ask questions, add an API key for Anthropic, OpenAI or xAI under Settings and save it, or choose Ollama to use a local model. The service keeps the index up to date while the app is closed.
The app follows the Windows display language (English or German); Settings → Appearance → Language changes it. Settings → About shows the app and backend versions and links to releases and issues.
Install on Linux
Each release also has the app for Linux on x86-64 with glibc 2.35 or later (Ubuntu 22.04, Debian 12, Fedora 38, openSUSE Tumbleweed and newer). Pick the package for your distribution:
Distribution | Download | Install |
Ubuntu, Debian and their derivatives |
| |
Fedora, openSUSE Tumbleweed |
| |
Any other distribution |
|
The packages install the app and its backend to /usr/lib/RootMind and pull
in WebKitGTK 4.1; only that step needs root. The AppImage needs no
installation; it needs FUSE 2 (the package libfuse2, on Ubuntu 24.04
libfuse2t64). Text recognition (OCR, per folder) uses the distribution's
Tesseract: the packages recommend it, so apt installs it along; with the
AppImage, install tesseract-ocr and tesseract-ocr-deu (Fedora:
tesseract, tesseract-langpack-deu) yourself. The Windows installer
brings its own. Check the
download with sha256sum --check --ignore-missing SHA256SUMS.txt (see
Verify downloads).
Start RootMind from your app menu, or in a terminal with:
rootmind-desktopThe .deb and .rpm print this command when they are installed. The AppImage starts when you run the file.
Windows Subsystem for Linux. On Windows 11, RootMind runs in WSL too, and
its window opens on the Windows desktop (WSLg). WSL needs systemd for the
service: add the following to /etc/wsl.conf, then run wsl --shutdown in
Windows and open the distribution again. Without systemd, RootMind says
that it cannot set up its service.
[boot]
systemd=trueWhat happens on the first start. RootMind sets up its background service
for your account, without a question and without root: two systemd user
services, rootmind-backend.service (REST API and indexing) and
rootmind-mcp-http.service (MCP over HTTP), in ~/.config/systemd/user/.
They run a copy of the backend in ~/.local/share/rootmind/runtime/, so the
service keeps working when the AppImage is closed or moved, and keep their
data in ~/.local/share/rootmind. When a newer version of the app starts, it
updates the service to its own version. If setting it up fails, RootMind says
why, and Set up (or Repair) tries again; Settings → RootMind
service → Details shows the service's state and the end of its
logs/install.log. Then add a folder under Folders, as on Windows.
The services start when you log in and stop when your last session ends. To keep them running while nobody is logged in, for example for n8n on the same computer, turn on lingering for your account once:
loginctl enable-linger "$USER"Update by installing the package of the new version (as above, or
sudo dnf upgrade ./RootMind-x86_64.rpm) or by replacing the AppImage;
RootMind updates its service when it starts.
Uninstall the service first, then the app:
# the .deb and .rpm (from the AppImage: ~/.local/share/rootmind/runtime/*/rootmind-backend)
/usr/lib/RootMind/backend/rootmind-backend service uninstall
sudo apt remove root-mind # or: sudo dnf remove root-mindservice uninstall stops and removes the two user services and the program
copy, and keeps your settings, API keys, index and MCP token in
~/.local/share/rootmind; service uninstall --purge deletes them as well.
Chats are in ~/.local/share/io.github.cgaeking.rootmind.
Upgrade from 1.x
RootMind 1.x was installed per account and, unless the background service was turned on, ran its backend inside the app. The 2.0 installer removes a 1.x installation of your account on its own. The folders, settings, API keys and index of the 1.x built-in backend are not carried over: add your folders and your API key again. If the background service was on in 1.x, it is updated and keeps its folders, settings and index. Chats stay in both cases.
Verify downloads
Each release lists the installer's size in its notes and ships SHA256SUMS.txt
and a build provenance attestation for every file. The hash must match the
file's line in SHA256SUMS.txt:
(Get-FileHash .\RootMind-Setup-x64.exe -Algorithm SHA256).Hash.ToLower()sha256sum --check --ignore-missing SHA256SUMS.txt
gh attestation verify RootMind-Setup-x64.exe --repo cgaeking/rootmindUpdate and uninstall
To update, run the installer of the new version and approve the prompt. It replaces the installed version in place, without further questions, and updates the service; ports, tokens, settings, index and chats are kept. If the app is running, the installer asks to close it first.
Uninstall RootMind from Windows Settings → Apps. This removes the RootMind
services. Their data (settings, API keys, index, tokens and logs) stays in
%ProgramData%\RootMind unless you tick Delete the application data,
which deletes it and your chats as well. A silent uninstall (/S, as package
managers run it) also removes the services but never deletes data. To delete
the data later, remove %ProgramData%\RootMind from an elevated prompt.
The RootMind service
RootMind's backend runs as the Windows service RootMind-Backend. It keeps
the index up to date while the app is closed and starts with Windows; the app
is a client of it. Settings → RootMind service shows its state, address
and version, offers Set up when the service is not set up yet (after a
silent or managed install) and Repair when it is stopped or does not
answer (one administrator approval each). When the service runs an older version than
the app, Update service brings it to the app's version. Both run the
backend installed with the app in %ProgramFiles%\RootMind\backend, which
only administrators can change.
The service belongs to the account that installed RootMind. Other accounts on the same computer cannot use it; to move RootMind to another account, uninstall it and install it again from that account.
It runs as LocalSystem, but it reads only folders and files your account can open without administrator rights. Network drives and shares, system folders and other users' profiles are not available to it, and the Folders screen marks a folder it cannot read.
Its REST API requires a token. The token is in
%ProgramData%\RootMind\data\rest_token.txt, which only your account, SYSTEM and administrators can read. The app uses it only after the service on the port proved that it knows the token (without the token being sent).A second service,
RootMind-MCP-HTTP, serves MCP over HTTP on 127.0.0.1 with its own bearer token (see Connect MCP clients). It is on by default; the checkbox Serve MCP over HTTP turns it off and on again (one administrator approval). Below it, Remote access (ngrok) gives it a public HTTPS address for cloud assistants (off by default; see Remote access).The services run from their own copy of the program under
%ProgramFiles%\RootMind Service\, which only administrators can change, and keep their data in%ProgramData%\RootMind.
On Linux the same service is the two systemd user services of your account (see Install on Linux), and nothing about it needs root or an approval:
It runs as your account and reads what your account can read, mounted network shares included. Other accounts on the computer get services of their own (on other ports) when they start RootMind.
Its REST token is in
~/.local/share/rootmind/data/rest_token.txt, which only your account can read (mode 0600, in folders of mode 0700). The app uses it only after the service on the port proved that it knows the token.MCP over HTTP and remote access work as on Windows; Serve MCP over HTTP adds or removes
rootmind-mcp-http.servicewithout a prompt.systemctl --user status rootmind-backendandjournalctl --user -u rootmind-backendshow the unit and its systemd log; RootMind's own logs are in~/.local/share/rootmind/logs.
Settings → Advanced connects the app to a RootMind backend at another address instead. SECURITY.md describes the security model.
Connect MCP clients
MCP clients get the same read-only tools as the app's agent and use their own model. Connect them to the service's MCP over HTTP: Settings → RootMind service shows the MCP endpoint and copies it and the token (Copy token). For Claude Desktop there is a bundle that needs neither (see Use RootMind in Claude Desktop). Claude Code:
claude mcp add --transport http rootmind http://127.0.0.1:<port>/mcp \
--header "Authorization: Bearer <token>"The port is 3000 when it is free, otherwise one from 18400–18499. The token is kept across updates, and the server picks up folder changes without a restart.
Use RootMind in Claude Desktop
Download rootmind.mcpb from the latest release.
Double-click it. Claude Desktop opens it, shows RootMind's tools and installs it once you confirm.
There is nothing to configure. The extension finds the RootMind service on
this computer, reads the MCP port and token from it and connects to MCP over
HTTP on 127.0.0.1. It needs RootMind installed with Serve MCP over HTTP
on (the default; Settings → RootMind service), and Claude Desktop must run
under the account that uses RootMind, since only that account can read the
token. On Linux it finds the service through
~/.local/share/rootmind/service.json, which names the program copy the
services run (no secret). When something is missing, the extension's error says
what to do, for example to install RootMind or to turn MCP over HTTP on;
once that is fixed, the next request works without reinstalling the
extension. After a repair it follows the service to a new port by itself. To
update it, install the rootmind.mcpb of a newer release. The bundle is not
signed yet, so Claude Desktop marks it as unsigned. Its source and how it
works: packaging/mcpb.
The bundle is also listed in the official
MCP Registry as
io.github.cgaeking/rootmind. Every release (not a prerelease) updates the
entry with its download URL and SHA-256, so clients that install from the
registry get the current bundle.
Claude Desktop starts only local (stdio) MCP servers; the extension is such
a server and forwards to the service's HTTP endpoint. The program in the
app's folder (rootmind-backend.exe mcp) cannot serve as a stdio server for
the installed app: it cannot open the service's index. The stdio server is
meant for a source or uv setup with its own config.yaml (see
3. Use the tools from an MCP client).
Reach MCP from n8n
Use n8n's MCP Client Tool node with the values that Settings → RootMind service shows; Copy next to n8n (MCP Client Tool) copies them with the token filled in:
Field | Value |
Endpoint |
|
Server Transport | HTTP Streamable |
Authentication | Header Auth |
Header Name |
|
Header Value |
|
n8n on the same machine: use the endpoint as shown.
n8n in Docker on the same machine: inside a container,
127.0.0.1is the container itself. Usehttp://host.docker.internal:<port>/mcp. Docker Desktop forwards that name to the host. If the connection is refused, run a reverse proxy on the host that listens on an address the container can reach and forwards to127.0.0.1:<port>, and keep that address closed to other machines.n8n on another machine: only through a TLS-terminating reverse proxy or a tunnel (such as Remote access) in front of
127.0.0.1:<port>. Never expose the plain HTTP port: the server speaks plain HTTP, and anyone with the token can read every indexed document. See Reach RootMind from cloud assistants.
Reach RootMind from cloud assistants
Assistants that run on someone else's servers, such as Grok Bot, a cloud-hosted
n8n or other web agents, cannot reach 127.0.0.1 on your computer. They need a
public HTTPS address that forwards to the MCP endpoint, which a tunnel on your
computer provides: RootMind's built-in Remote access (ngrok), or a tunnel
you run yourself (Tailscale Funnel, the ngrok agent). Either way the tunnel
forwards to MCP over HTTP only, and the client sends the same token.
Remote access
RootMind can run the tunnel itself. The RootMind service's MCP server
(RootMind-MCP-HTTP) opens it with ngrok's open-source SDK, so no ngrok
program needs to be installed. It is off by default.
Create a free account at ngrok.com and copy your authtoken from the dashboard (Your Authtoken). Under Domains you can reserve a free fixed domain; without one, ngrok assigns an address that can change.
In Settings → RootMind service, under Remote access (ngrok), paste the authtoken and, if you have one, the domain, then choose Save. The authtoken is never shown again: the field then only says Saved (Replace and Remove change it).
Tick Reach MCP from the internet through ngrok and confirm. The status changes to Connected and shows the Public MCP endpoint (
https://<domain>/mcp) with Copy. Use it with the MCP token (Copy token) in the assistant, see In the assistant.
Remote access needs Serve MCP over HTTP; turning that off turns remote access off too. Changes need no administrator approval, take effect within a few seconds and survive restarts and updates: the tunnel starts with the service. Unticking the box closes the public address at once. When the connection to ngrok drops, the service reconnects; a refused connection (a wrong authtoken, a domain that is not yours or already in use) appears as the status with ngrok's message and is retried with growing pauses (up to five minutes).
ngrok allows one endpoint per domain. If you already run your own ngrok tunnel on the same domain, stop it before you turn remote access on.
The authtoken is stored in
%ProgramData%\RootMind\data\ngrok_authtoken.txt, which only SYSTEM, administrators and the MCP service account can read; it is never written toconfig.yaml, never returned by the app's API and never logged. Uninstalling without Delete the application data keeps it.Free ngrok accounts have traffic limits; see ngrok's pricing page.
Your own tunnel
With the ngrok agent (free account, one fixed free domain):
Create an account at ngrok.com, then install the agent and add your authtoken:
winget install ngrok.ngrok ngrok config add-authtoken <your-ngrok-authtoken>In the ngrok dashboard under Domains, note your free domain, then start the tunnel to the MCP port (3000 unless Settings shows another):
ngrok http 3000 --url https://<your-free-domain>To keep it running after a restart, define the tunnel in ngrok's configuration file and install it as a service (
ngrok service install; see the ngrok documentation).
With Tailscale Funnel (free Tailscale account and app):
tailscale funnel --bg 3000The command prints the public address (https://<computer>.<tailnet>.ts.net)
and keeps the funnel running in the background, also after a restart. The first
time, it asks you to allow Funnel for your tailnet.
In the assistant
Add a custom MCP connector (or n8n's MCP Client Tool) with:
Field | Value |
URL |
|
Transport | HTTP Streamable |
Authentication | header |
Clients that only offer an OAuth sign-in and cannot send a bearer token cannot connect this way.
Keep in mind:
Anyone with the token can read every indexed document through the tunnel. Keep it secret; stopping the tunnel (or turning remote access off) closes the public access.
What a cloud assistant reads through RootMind (search results and document text) goes to that assistant's provider.
RootMind refuses requests that a web page sends from a browser (requests with a non-local
Originheader); server-to-server requests from the assistant are not affected.
How it works
flowchart LR
desktop["Desktop app<br/>Tauri + React"]
mcpclient["MCP client<br/>e.g. Claude Desktop, Claude Code, n8n"]
subgraph backend["Backend (Python)"]
rest["REST API<br/>127.0.0.1 + bearer token"]
mcp["MCP servers<br/>stdio, HTTP + bearer token"]
engine["Agent loop<br/>rlm_engine/engine.py"]
tools["Tool layer<br/>tools.py"]
repo[("SQLite<br/>FTS5 + trigrams")]
indexer["Indexer + scheduler"]
end
folders[("Your folders")]
llm(["LLM provider<br/>Anthropic, OpenAI or xAI"])
desktop -- "/query, /search, /config" --> rest
mcpclient --> mcp
rest --> engine
rest -- "search, documents" --> repo
engine <--> llm
engine --> tools
mcp --> tools
tools --> repo
indexer -- "read files" --> folders
indexer -- "extracted text" --> repoIndexing uses no LLM. A connector walks each configured folder. Parsers extract text from PDF, DOCX, XLSX, PPTX, CSV, Markdown and other text files. The text goes into SQLite: a documents table, an FTS5 index and a trigram table for typo-tolerant search. PDF pages, slides and sheets keep markers in the text, so a hit can be cited by location. Unchanged files are skipped by content hash.
The model chooses what to read. For
POST /query, the engine offers the model five read-only tools:search_documents,search_fuzzy,list_documents,read_documentandget_statistics. A typical run is: search, read the window around a hit, perhaps search again with other words, then answer.The loop is bounded. The loop makes at most 10 model calls that may request tools. If the model is still searching after that, one final call with tools disabled asks it to answer from what it has found. At most 8 tool calls run per model turn, and the tool results of one question are capped at 200,000 characters; calls beyond that get an error result instead of running. Every LLM call has a timeout, a read returns at most 50,000 characters, tool errors go back to the model as error results instead of ending the loop, and an answer cut off by the output token limit says so.
Answers carry sources. The response lists the documents the model read, with page, slide or sheet where it can be resolved, every tool call it made, and why each LLM call stopped.
POST /query/streamreturns the same answer and sends every model and tool call as it starts and ends (server-sent events), so a client can show what the agent is doing; closing that connection stops the question.MCP clients bring their own model. Over MCP, RootMind serves the same tool definitions and the client's model does the deciding. Search and indexing never need an API key; only questions (
POST /query), chat titles and the model list in the settings call the provider.
The desktop app talks to the RootMind service on the same computer, or to a custom backend at an address you enter under Settings → Advanced. Development builds do not use the service and ask for a backend address. ARCHITECTURE.md has the details.
Why a tool loop instead of embedding RAG
A typical RAG pipeline splits documents into chunks, embeds them, retrieves the top matches by vector similarity and puts those chunks into the prompt. The model sees only what that retrieval step picked.
RootMind keeps each document's full text in a full-text index and lets the
model drive retrieval. It can search, look at the snippets, read the text
around a hit, and search again with different terms when the first query
misses. This is sometimes called RLM-style or agentic retrieval. The engine
module is named rlm_engine after that idea; in this code base it is a bounded
tool-use loop, not a model that calls itself recursively.
Works well | Works less well |
Exact terms: invoice numbers, product codes, names, amounts | Synonyms and paraphrases: "vacation" does not match "paid time off" unless the model tries the other word |
No embedding model or vector store; indexing only parses text and works offline | Questions in a different language than the documents |
The model reads the context around a hit, such as the table header above a number, instead of an isolated chunk | More LLM round trips per question, so more latency and token cost |
Every retrieval step is visible as a tool call | Answer quality depends on the model's search strategy |
Trigram fuzzy search covers typos, not synonyms. Eval case c09 (the synonym trap) keeps that weakness visible.
Install for developers and servers
With uv (any OS with Python 3.12 or newer; uv installs it if needed):
uv tool install "git+https://github.com/cgaeking/rootmind@v2.8.0#subdirectory=backend"
rootmind --help # api, index, mcp, mcp-http, service, status, folders, search, ask, ...This installs the commands rootmind, rootmind-mcp and rootmind-mcp-http.
On Windows, rootmind service status and rootmind service uninstall work
from such an install, but rootmind service install is refused and prints the
download address of the release build. The service runs as SYSTEM, so it may
only run a program from a folder that administrators alone can change;
service install copies the release build into such a folder, while a uv or
pip environment lives in a folder your account can change.
Windows server or a machine without the app. The release also contains
the backend alone, rootmind-backend-windows-x64.zip. Download and check it
in PowerShell:
$base = "https://github.com/cgaeking/rootmind/releases/latest/download"
Invoke-WebRequest "$base/rootmind-backend-windows-x64.zip" -OutFile rootmind-backend-windows-x64.zip -UseBasicParsing
Invoke-WebRequest "$base/SHA256SUMS.txt" -OutFile SHA256SUMS.txt -UseBasicParsing
$line = Select-String -Path SHA256SUMS.txt -SimpleMatch "rootmind-backend-windows-x64.zip"
$expected = ($line.Line -split '\s+')[0]
$actual = (Get-FileHash rootmind-backend-windows-x64.zip -Algorithm SHA256).Hash.ToLower()
if ($actual -ne $expected) { throw "Checksum mismatch: $actual" }Then, from an elevated PowerShell of the account whose folders RootMind should
index (it must be a local administrator), unzip it into a folder that only
administrators can change and install the service from there.
service install refuses, with exit code 10, a program in a folder that other
accounts can change, such as a download folder:
$dir = Join-Path $env:ProgramFiles "RootMind Backend"
Expand-Archive rootmind-backend-windows-x64.zip -DestinationPath $dir -Force
$exe = Join-Path $dir "rootmind-backend\rootmind-backend.exe"
# install, or update an installed service
& $exe service install
# state, port, version and token files (works without elevation)
& $exe service status
# remove the service; --purge also deletes its data
& $exe service uninstallservice install copies the program to
%ProgramFiles%\RootMind Service\<version>-<id>\, so the unzipped folder is
not needed afterwards. It uses port 8000 and, for MCP over HTTP, port 3000;
--port auto and --mcp-port auto fall back to 18300–18399 and 18400–18499
when those are taken. MCP over HTTP is on for a new install; --no-mcp-http
leaves it out. Running service install again updates the service and keeps
its ports, MCP setting, tokens and data; service install --help lists every
option. The REST token is in %ProgramData%\RootMind\data\rest_token.txt and
the MCP token in %ProgramData%\RootMind\data\api_token.txt.
A new service starts without folders. Add them with
& $exe folders add D:\Shares\Projects (see Command line,
no elevation needed), or under connectors in
%ProgramData%\RootMind\config.yaml (as in
backend/config.example.yaml; the file can
only be changed from an elevated prompt) followed by
Restart-Service RootMind-Backend.
The zip also runs without a service, for example
rootmind-backend.exe api --require-token or rootmind-backend.exe mcp-http.
Linux server or a machine without the app. The release contains the Linux
backend alone, rootmind-backend-linux-x64.tar.gz (x86-64, glibc 2.35 or
later). As the account whose folders RootMind should index (not root), unpack
it into a folder that only you can change and install the service from there:
base=https://github.com/cgaeking/rootmind/releases/latest/download
curl -L -O "$base/rootmind-backend-linux-x64.tar.gz" -O "$base/SHA256SUMS.txt"
sha256sum --check --ignore-missing SHA256SUMS.txt
mkdir -p ~/opt && tar -xzf rootmind-backend-linux-x64.tar.gz -C ~/opt
exe=~/opt/rootmind-backend/rootmind-backend
"$exe" service install # install, or update the installed services
"$exe" service status # state, port, version and token files
"$exe" service uninstall # remove them; --purge also deletes the data
loginctl enable-linger "$USER" # keep them running without a loginservice install copies the program into
~/.local/share/rootmind/runtime/<version>-<id>/ and writes the two units to
~/.config/systemd/user/; it needs a systemd user manager for the account,
which a desktop or SSH login starts (exit code 11 without one, for example
under su or sudo). The ports, options and tokens are those of Windows; the
data is in ~/.local/share/rootmind (config.yaml, data/rest_token.txt,
data/api_token.txt). Add folders and the LLM key with "$exe" folders add
and "$exe" config llm (see Command line); after editing
config.yaml by hand, restart the service with
systemctl --user restart rootmind-backend.
Command line
The same program that runs the service also manages it. These commands talk to the RootMind service of your account through its REST API, on Windows and Linux alike, and never need root or an administrator prompt:
rootmind status # service, index, folders, MCP endpoint, remote access
rootmind folders list
rootmind folders add /srv/shares/projects --name projects [--rescan-minutes 10]
rootmind folders remove projects
rootmind index [--full] [--folder projects] [--wait]
rootmind search "invoice telekom" [--limit 10] [--snippets]
rootmind ask "What does the contract with Fjordhaus say about notice periods?"
rootmind config llm # provider, model, which keys are saved
rootmind config llm --provider anthropic --model claude-sonnet-5-5 --key-file ~/anthropic.key
rootmind mcp tokens list # names, folders, fingerprints
rootmind mcp tokens create alice --folders projects --out alice.token
rootmind mcp tokens revoke alice
rootmind remote-access on|off|status # the ngrok tunnelrootmind is the command of a uv or pip install. With the app or the
backend archive, call the backend itself: rootmind-backend (Linux:
/usr/lib/RootMind/backend/rootmind-backend from the .deb and .rpm,
~/opt/rootmind-backend/rootmind-backend from the tar.gz) or
& "$env:ProgramFiles\RootMind\backend\rootmind-backend.exe" on Windows.
Every command takes --json for scripts and --help.
How it finds the service. Like the app, it asks
service statusfor the port and the token file, lets the service on that port prove that it knows the token, and only then sends the REST token. The token is read fromdata/rest_token.txtin the data folder, which only the account that set up the service can read (on Windows also SYSTEM and administrators from an elevated prompt). From another account, or withsudo, the commands stop with exit code 4 and say why.Secrets stay off the command line.
config llmreads the API key from a file (--key-file FILE) or from stdin (--key-file -, which asks without echo in a terminal), never from an argument, and never prints a key, not even masked;--remove-keyremoves it.remote-access on --authtoken-file FILEsaves the ngrok authtoken the same way. Tokens are never printed;mcp tokens listshows the fingerprint the MCP service logs, andmcp tokens createwrites a new token to a new file (mode 0600 on Linux, readable by your account alone on Windows;--out FILE, or--out -to print it instead).Another backend.
--url http://127.0.0.1:18000 --token-file FILE(orRLM_URLandRLM_TOKEN_FILE) uses that REST API instead, for example a development backend (rootmind api). Plainhttp://is accepted only for127.0.0.1,localhostand::1; usehttps://or an SSH tunnel for anything else.indexin this process.rootmind index --localindexes the folders ofconfig.yaml(inRLM_HOMEor the working folder) in the command's own process, asrootmind indexdid before 2.5.--localis implied by a global--config, byRLM_HOME, and when your account has no RootMind service (then a note says so).Exit codes: 0 done, 1 the service refused or failed the request, 2 wrong usage, 3 no service found, stopped or not answering (or another program on its port), 4 the token cannot be read or was refused.
Run RootMind on a server. A Linux server can index shared folders
(--rescan-minutes re-scans mounted network shares) and serve MCP to other
machines over TLS or behind a reverse proxy, with one named token per person
or client, each limited to some folders (mcp tokens create --folders). The
guide is docs/SERVER.md; read the
threat model for server use first: the folder scope
of a token is the only access control, share permissions are not checked per
user.
Local models with Ollama
Choose Ollama under Settings → LLM (or provider: ollama under llm
in config.yaml) to run the question loop against a model on your own
machine. There is no API key; the base URL defaults to
http://localhost:11434 and can point at another computer on your network.
llm:
provider: ollama
model: qwen3:8b # any model you pulled that supports tools
base_url: http://localhost:11434What stays local: with Ollama on this computer, the question, the excerpts the model reads and the answer never leave it. With a base URL on another computer, they go to that computer, unencrypted over plain
http://.Tool support is required. Pick a model Ollama lists as supporting tools (
ollama show <model>). A model without it fails with a clear message.Context length: the loop reads document windows, and Ollama's default context is small. Raise it (for example
OLLAMA_CONTEXT_LENGTH=16384for the server, or a Modelfile withPARAMETER num_ctx 16384), or long documents are cut off without an error.Search first: Ollama models are not forced to call a tool on the first turn, since not every model supports that. If the first answer never looked at the documents, RootMind asks once more with an explicit instruction.
How well a local model follows the tool loop depends on the model. Measured with
granite4.1:8bon an 8 GB graphics card, on the eval set of ten short fictional documents: 15 of 16 cases passed in all three runs, mean pass rate 96%; the one flaky case is an unanswerable question whose correct "not found" answers were sometimes worded in a way the check does not accept. Large documents and long contexts are not covered by that measurement (details inbackend/evals). Measure the one you use withscripts/run_evals.py --mode live --provider ollama --model <name>(seebackend/evals).
Tracing the agent loop
Tracing is off by default. Turned on, it records every step of a question: each model call (purpose, tokens, stop reason, the tools it asked for, time) and each tool call (arguments, result size, the documents a search returned, the windows a read returned), and the answer.
Evals:
scripts/run_evals.py --trace <folder>writes a JSONL file and a readable Markdown summary per eval run (seebackend/evals).Backend:
tracing: {local: true}inconfig.yamlwrites the same files underlogs/traces/in RootMind's data folder. Nothing leaves the computer.Langfuse (optional):
tracing: {langfuse: true}, or--langfusefor the eval runner, exports traces to a Langfuse server. It needs thetracingextra (uv sync --extra tracing; not part of the packaged backend) andLANGFUSE_PUBLIC_KEY,LANGFUSE_SECRET_KEYandLANGFUSE_BASE_URLin the environment. A self-hosted Langfuse keeps traces on your network; Langfuse Cloud is a third party.
Traces contain your questions, the model's tool arguments and the document text it read. They never contain API keys. See SECURITY.md.
Run from source
You need uv; it installs Python 3.12 if necessary. The desktop app also needs Node 24, pnpm 11, Rust 1.96 or newer and, on Windows, the MSVC build tools and WebView2.
1. Index and search without an API key
cd backend
uv sync
cp config.example.yaml config.yaml # indexes the fictional demo corpus in evals/corpus
uv run rootmind index --local # builds data/index.db; no LLM involved
uv run rootmind api # REST API on http://127.0.0.1:8000, no tokenIn a second terminal:
curl "http://127.0.0.1:8000/search?q=total%20due&limit=3&snippets=true"The first hit is invoice_INV-2026-0142.pdf with "page": "Page 2" and a
snippet around the match. Without snippets=true the search returns only the
ranked list, which is faster on large indexes.
The interactive API docs are at http://127.0.0.1:8000/docs. While the API
runs, it re-indexes about 30 seconds after start and then every 24 hours
(indexer.sync_interval_hours). To index your own files, add entries under
connectors in config.yaml. Use absolute paths: relative connector paths
resolve against the working directory.
rootmind api started this way requires no token. To require one,
set RLM_API_TOKEN or pass --require-token, which creates
data/rest_token.txt, and send Authorization: Bearer <token> with each
request. The docs pages are off while a token is required.
2. Ask a question
Set a key for one provider (ANTHROPIC_API_KEY, OPENAI_API_KEY or
XAI_API_KEY; Ollama needs none, see
Local models with Ollama) in the environment or in backend/.env (see
.env.example). Choose the provider and model under
llm in config.yaml, restart the API, then:
curl -X POST http://127.0.0.1:8000/query \
-H "Content-Type: application/json" \
-d '{"question": "What is the total amount due on invoice INV-2026-0142?"}'The response contains answer, sources (with page where known),
tool_calls and tokens_used. The question, search snippets and document
excerpts go to the provider you configured; see SECURITY.md.
3. Use the tools from an MCP client
The MCP servers read the same config.yaml and database as the REST API. They
only serve and never index, so keep the index current with rootmind index --local or
a running REST API.
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"rootmind": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/rootmind/backend", "run", "rootmind-mcp"]
}
}
}If Claude Desktop cannot find uv, put the absolute path of the uv
executable into command. On Windows, write the paths with forward slashes or
escaped backslashes (C:\\...).
Claude Code:
claude mcp add --transport stdio rootmind -- \
uv --directory /absolute/path/to/rootmind/backend run rootmind-mcpOver HTTP, for MCP clients that cannot start a local process:
cd backend
uv run rootmind-mcp-http # http://127.0.0.1:3000/mcpOn first start the server generates a token and saves it to
data/api_token.txt (under RLM_HOME, or the working directory when that is
unset); alternatively, set MCP_API_TOKEN. The server prints where the token
came from and a short fingerprint of it, never the token itself. Then:
claude mcp add --transport http rootmind http://127.0.0.1:3000/mcp \
--header "Authorization: Bearer <token from data/api_token.txt>"The stdio server reads the folder list at startup, so restart it (the MCP
client) after adding or removing folders. The HTTP server re-reads it when
config.yaml changes. The HTTP server binds to 127.0.0.1 and speaks plain
HTTP; to reach it from another machine, put it behind a TLS-terminating proxy
or tunnel and never expose the plain port. rootmind-mcp-http --index also
runs the background indexer in that process. Use it only when no REST API
indexes the same database: every indexing process removes the documents of
folders that are missing from its own copy of the config.
In a LangChain or LangGraph agent, load the same tools through
langchain-mcp-adapters: examples/langchain_mcp
starts rootmind-mcp over stdio and runs LangChain's stock agent with a local
Ollama model.
4. Run the desktop app (development)
# terminal 1: backend, with the Vite dev origin allowed by CORS
cd backend
RLM_DEV=1 uv run rootmind api # PowerShell: $env:RLM_DEV=1; uv run rootmind api
# terminal 2: desktop
cd desktop
pnpm install
pnpm tauri devA development build does not use the RootMind service, so the app asks for a
backend address: confirm http://127.0.0.1:8000. More in
desktop/README.md.
Build the installer yourself
On Windows, from the repository root:
# 1. Backend: PyInstaller onedir build -> backend/dist/rootmind-backend/
cd backend
uv sync --locked --no-dev --group build
uv run --no-sync pyinstaller packaging/rootmind-backend.spec --noconfirm --clean --distpath dist
cd ..
# 2. Copy it to desktop/src-tauri/binaries/rootmind-backend/ and check it
python scripts/prepare_backend.py
# 3. Installer with the release overlay, which adds the backend
cd desktop
pnpm install
pnpm tauri build --config src-tauri/tauri.release.conf.jsonThe installer is written to desktop/src-tauri/target/release/bundle/nsis/.
Run uv sync in backend/ afterwards to get the development tools back.
Without the overlay, pnpm tauri build builds an app without a backend.
Repository layout
backend/
src/rootmind/
rlm_engine/engine.py agent loop
tools.py tool registry, argument validation, result formatting
tracing/ optional tracing of the agent loop: local JSONL, Langfuse export
mcp_stdio.py MCP server over stdio
mcp_http.py MCP server over HTTP with a bearer token
api/ REST API (FastAPI)
auth.py, access.py REST token handling; folder access policy interface
winaccess.py folder access policy of the Windows service (AuthZ)
service/ background service: install, uninstall, status, service host;
Windows services and Linux systemd user units (linux*.py, systemd.py)
database/ SQLite models, FTS5 and trigram search
indexer/ parsers, indexer, sync scheduler
connectors/ local folder connector
tests/ offline test suite with fake LLM clients; e2e/ service test
evals/ fictional corpus and eval cases; baselines/langgraph: the
LangGraph baseline (own environment)
scripts/ eval runner and corpus generator
packaging/ PyInstaller spec, smoke test, tree hash
deploy/windows/ run-backend.bat: the REST API from source in a console window
desktop/
src/ React + TypeScript UI
src-tauri/ Rust shell: service connection and commands, chat history;
installer/ with the NSIS template and the service hooks;
linux/ and tauri.linux.conf.json for the Linux service and packages
scripts/ installer smoke test
packaging/winget/ winget manifest templates
packaging/mcpb/ Claude Desktop bundle (rootmind.mcpb): stdio-to-HTTP bridge, manifest, tests, MCP Registry template
examples/langchain_mcp/ RootMind's MCP server as tools in a LangChain agent (own environment)
scripts/ version check, release notes, backend preparation, winget rendering,
NSIS template refresh
.github/workflows/ CI and releaseDesign decisions, guardrails and trade-offs are described in ARCHITECTURE.md, the threat model in SECURITY.md, and the changes per version in CHANGELOG.md.
Tests and evals
Both run offline and need no API key:
cd backend
uv sync
uv run pytest -q # fake LLM clients, generated test corpus
uv run python scripts/run_evals.py # retrieval regression: scripted policy, no modelTests. An autouse fixture replaces the Anthropic and OpenAI client
constructors with functions that raise and removes API keys from the
environment. Tests inject scripted fake clients
(tests/fakes.py) through the engine's client
parameter. Every test gets its own RLM_HOME and working directory. Tests
that need documents get a small generated corpus with one PDF, DOCX, XLSX,
PPTX, CSV, Markdown and text file each. Tests of the Windows folder access
policy and of the service's folder permissions run on Windows only, those of
the Linux file modes on Linux only (the Linux service logic itself, with a
fake systemctl, runs everywhere); installing real services is left to the
CI end-to-end tests.
Evals. scripts/run_evals.py indexes the
fictional corpus in backend/evals/corpus/ with the
real indexer and runs 16 questions through the real engine.
Live mode is the eval: it runs a real model several times per case and reports pass rate (expected source cited, required strings present, forbidden strings absent), tool-path variance, tool calls, tokens, latency and stop reasons. It reads keys only from environment variables and never runs in CI.
Dry mode is a retrieval regression test, not an eval of the agent: a scripted policy replaces the model, searches with a query written by hand for each case (
dry_searchin the dataset), reads the suggested window and answers with the excerpt. It checks that the index and the tool layer still put the right document first (hit@1), among the first five hits (hit@5), and cite the right page, slide or sheet. With hand-picked queries 1.0 on the 15 gated cases is the expected result; c09, the synonym trap, is the known miss. CI fails if hit@5 drops below 1.0. It says nothing about answer quality.--agent langgraphruns the same cases with the same tools, system prompt and scoring in LangChain's stock LangGraph agent, from its own environment (the backend does not depend on LangChain). Withgranite4.1:8bthrough Ollama, 3 runs per case, both agents passed 98% (15 of 16 cases every run) with about the same tool calls, tokens and latency; the edges where the loops differ (iteration limit, search nudge) did not occur in those runs. Hosted models were not compared.
Details: backend/evals/README.md.
CI. GitHub Actions workflows with actions pinned by commit SHA and no secrets. Only the release job can write to the repository:
Workflow | What it runs |
ruff check and format check; the version check across backend, desktop and the Claude Desktop bundle; the tests of the release scripts on Ubuntu and Windows; pytest with coverage on Ubuntu (Python 3.12 and 3.14) and Windows (Python 3.12); the dry retrieval regression | |
The PyInstaller onedir builds on Windows and on Ubuntu 22.04, a smoke test of each executable (REST, MCP over HTTP and stdio, indexing of every file type) and their tree hashes; then end-to-end tests of the service: on the elevated Windows runner the Windows services (install, permissions, tokens, read confinement, update and uninstall), on Ubuntu the systemd user units of the runner's account (refusals, install, file modes, tokens, indexing, restarts, update, MCP off and on, uninstall). On pull requests the service tests run only when service-related code changed | |
ESLint, TypeScript typecheck, vitest, Vite build, | |
For a version tag | |
The Claude Desktop bundle: the bridge's | |
detect-secrets over all tracked files, against the audited baseline |
Dependabot (.github/dependabot.yml) proposes
monthly dependency updates for all four ecosystems.
Security model in short
RootMind is built for one person on their own computer. The REST API binds to 127.0.0.1, refuses connections from other machines, rejects foreign
Hostheaders and allows cross-origin requests only from the desktop app.The RootMind service requires a bearer token on its REST API.
rootmind apifrom source runs without a token unless you ask for one; without a token, any local process can call it.The service runs as LocalSystem but reads only what the installing account can read without administrator rights. Installing and repairing it needs one administrator approval. On Linux it runs as systemd user services of your own account, without root; file modes keep its data and tokens private to the account.
The MCP HTTP server is on by default; it requires a bearer token and binds to 127.0.0.1. Remote access (ngrok) is off by default; turned on, anyone with the MCP token can read every indexed document through the public address.
Indexing stays on your machine. Answering a question sends the question, search snippets and document excerpts to the LLM provider you configured.
Indexed documents are untrusted input. All tools are read-only, document text is marked as data, and a question's tool use is capped, but prompt injection is not a solved problem.
The full threat model is in SECURITY.md.
Limitations
The desktop app and the RootMind service run on Windows and on Linux (x86-64); there is no macOS build. The backend is cross-platform Python; CI runs its tests on Ubuntu and Windows.
The release binaries are not code-signed yet (see Install). RootMind is not in the winget repository yet; the first submission is under way.
Text recognition (OCR) for scanned PDFs and images is off by default and turned on per folder. It recognizes one page at a time in the background, so a large archive of scans takes hours before all of it is searchable.
Legacy
.docand.xlsfiles go to the DOCX and XLSX parsers and usually fail to parse.No streaming: the desktop app waits for the complete answer.
Full-text search has no synonym or cross-language matching (see the trade-off table above).
On Windows, installing needs an administrator account, and the service serves one account per computer: the one that installed RootMind. On Linux every account that starts RootMind gets services of its own.
The Windows service cannot index network drives or shares.
On Linux the service runs only while you are logged in, unless lingering is on (
loginctl enable-linger).Only one process per database should index. The MCP servers only serve by default; set
RLM_DISABLE_SCHEDULER=1on any additional REST API that shares the database.The MCP stdio server reads the list of folders at startup; restart it after changing folders.
An incremental sync hashes every file in every configured folder, which is slow for very large folders.
Contributing
See CONTRIBUTING.md for the development setup, checks, conventions and the release steps. Please report security issues privately as described in SECURITY.md.
License
MIT, copyright (c) 2026 Christian Gäking.
The packaged backend and the installer bundle third-party dependencies, which keep their own licenses.
Code signing policy
Releases are not code-signed yet; each release ships checksums and build provenance attestations instead (see Verify downloads). CODE_SIGNING.md describes what will be signed, the team roles and the privacy policy.
Available Tools
5 toolsget_statisticsA
Show statistics about the document collection (number of documents, file types, total size).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries the burden. It discloses the content of the result (document count, file types, total size) but never states that the operation is read-only with no side effects, nor anything about cost or freshness of the statistics.
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?
A single front-loaded sentence with no filler; the resource is named first and the return contents follow immediately.
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 zero-parameter read tool with no output schema, the parenthetical enumeration is the only source of return-value information and it covers the essentials. It could go further on scope (whole collection vs. filtered) but is adequate for correct invocation.
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?
The tool takes zero parameters, so the baseline is 4. Nothing in the description misrepresents the input surface, and no parameter semantics are needed.
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 ("Show statistics about the document collection") and enumerates the returned metrics in parentheses. It is clearly distinguishable from the sibling list/read/search tools, though it doesn't explicitly contrast itself with them.
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?
Usage is only implied: an agent can infer this is for getting a collection overview rather than retrieving documents. There is no explicit when-to-use statement and no reference to alternatives such as list_documents.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_documentsA
List indexed documents with metadata (no content). Use it for an overview of the available files or to find a file by name.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of documents (1-200, default 50). | |
| file_type | No | Optional: filter by file type, e.g. pdf, docx, xlsx, pptx, md, txt, csv | |
| search_filename | No | Optional: substring to look for in file names. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It usefully discloses that only metadata is returned and no content is included, but it does not state read-only behavior, pagination behavior, permissions, or response shape beyond the metadata/content distinction.
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 short sentences, front-loaded with the core behavior and followed by practical usage guidance. There is no wasted text.
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 simple, three-optional-parameter listing tool with full schema coverage, the description is largely complete. The main remaining gap is that no annotations exist and no output schema is present, so slightly more could be said about the returned metadata or pagination behavior.
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%, so all three parameters are fully documented in the schema itself. The description adds no parameter-specific meaning beyond that, which is acceptable at baseline 3 when the schema does the heavy lifting.
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: 'List indexed documents with metadata (no content).' It clearly distinguishes this from content-reading tools by emphasizing that no content is returned, though it does not explicitly differentiate itself from search_documents or search_fuzzy.
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 gives clear usage contexts: an overview of available files or finding a file by name. It does not explicitly name alternatives or exclusions, such as using search_documents for content-based search or read_document for file contents.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_documentA
Read the text of a document, or a character window of it. At most 50,000 characters are returned per call; for large documents read the window around a known hit instead of paging through everything.
| Name | Required | Description | Default |
|---|---|---|---|
| doc_id | Yes | Document ID (from search_documents or list_documents). | |
| end_char | No | Optional: end position (exclusive). Capped at start_char + 50,000. | |
| start_char | No | Optional: start position (character offset, default 0). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full disclosure burden, and it does surface the key behavioral trait: a hard 50,000-character cap per call and the recommended workaround. It leaves out other behaviorally relevant facts — whether truncation is signaled in the response, what happens for an invalid doc_id, or whether the text is returned raw or as structured blocks — which matter for a read tool with no safety 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?
Two sentences, both load-bearing: the first defines the operation, the second defines the constraint and the preferred strategy. Front-loaded and free of restatement of the name.
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 three-parameter read tool with full schema coverage and no output schema, the description covers purpose, the size limit, and the recommended access pattern. The remaining gap is the response shape — whether the returned text is truncated silently and how the agent should detect it — which an agent would likely want before paging.
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% and each parameter (doc_id, start_char, end_char) is already described in the schema, including the 50,000-character cap on end_char. The description reinforces the windowing concept but adds no format or edge-case detail beyond the schema (e.g., negative offsets, out-of-range clamping), so it sits at the baseline.
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 ("Read the text of a document") and immediately qualifies the scope with the optional character-window form, so an agent knows exactly what comes back. It does not name or contrast with the sibling readers (search_documents, list_documents), so differentiation relies on the schema and the doc_id hint rather than the description itself.
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 gives concrete usage context: for large documents, read the window around a known hit rather than paging through everything — a real strategy cue that steers the agent toward search-then-window instead of sequential paging. It stops short of naming the alternative tool or stating exclusions (e.g., when to prefer search_documents alone), so it is clear context without explicit routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_documentsA
Search the user's own files on this computer (the local folders indexed by RootMind). Fast full-text search (SQLite FTS5) over all indexed documents. Matches exact words and word prefixes. USE THIS FIRST: it is fast and cheap. Returns matching documents with text snippets, the location (page/slide/sheet) when known, and a suggested read_document call around each hit.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (1-50, default 10). | |
| query | Yes | Search term(s). Matches exact words and prefixes (e.g. 'invoice' also finds 'invoices'). | |
| file_type | No | Optional: filter by file type, e.g. pdf, docx, xlsx, pptx, md, txt, csv |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and discloses meaningful details: the SQLite FTS5 backend, exact-word/prefix matching, and the return structure including snippets, location, and suggested read_document calls. It still omits permission requirements, result ordering/ranking, and empty-result behavior, which keeps it below 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?
Front-loads the core purpose, then adds backend, matching semantics, usage priority, and return behavior in tight, non-redundant sentences. Every sentence contributes useful information without bloat.
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?
Because there is no output schema, the description appropriately explains what results look like: matching documents, snippets, locations, and suggested read_document calls. It is nearly complete for a three-parameter search tool, though it could mention result ordering or behavior when no documents match.
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%, so the schema already documents query, limit, and file_type fully. The description's matching-semantics sentence largely repeats what the query parameter schema already says, adding little parameter-specific meaning beyond the 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 specific verb and resource ('Search the user's own files') plus the index scope ('local folders indexed by RootMind'). The exact-word/prefix matching detail implicitly differentiates it from search_fuzzy, and it names read_document in the return behavior, but it does not explicitly name the closest sibling alternative.
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 explicit priority guidance with 'USE THIS FIRST: it is fast and cheap,' which tells the agent when to prefer it. However, it does not state when not to use it or name search_fuzzy as the alternative for typo/fuzzy matching, so the guidance is clear but incomplete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_fuzzyA
Trigram-based fuzzy search that tolerates typos. USE THIS ONLY when search_documents finds nothing or the search term may be misspelled (e.g. 'lantren' finds 'lantern').
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (1-50, default 10). | |
| query | Yes | Search term, typos allowed. Finds similar words. | |
| file_type | No | Optional: filter by file type, e.g. pdf, docx, xlsx, pptx, md, txt, csv | |
| min_similarity | No | Minimum similarity 0.1-1.0 (default 0.3). Higher means fewer but closer matches. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does disclose the matching mechanism (trigram), typo tolerance, and that this is a fallback rather than a primary search. It omits result ordering and any performance/cost characteristics, which keeps it short of 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 sentences, zero waste. The capability statement comes first and the restrictive usage rule second, which is the right order for an agent deciding whether to call.
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?
A four-parameter read-only search with full schema documentation and no output schema needs exactly this much: what it does, when to prefer it, and how matching behaves. Nothing required for correct invocation 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 coverage is 100%, and every parameter including min_similarity and file_type is documented in the schema, so the baseline is 3. The description's typo example adds a little intuition about the query parameter but no syntax or format beyond what the schema already provides.
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 mechanism and resource: trigram-based fuzzy search that tolerates typos. It explicitly contrasts with the sibling search_documents, so an agent can distinguish the two without inspecting 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?
Gives an explicit gating rule ('USE THIS ONLY when search_documents finds nothing or the search term may be misspelled') plus a concrete motivating example ('lantren' finds 'lantern'). Both the when and the fallback alternative are named.
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.
5 tool updates
v2.6.1- First observed
get_statistics - First observed
list_documents - First observed
read_document - First observed
search_documents - First observed
search_fuzzy
TDQS
Scored across 5 tools
Each tool has a clearly distinct role: listing metadata, reading content, exact full-text search, fuzzy fallback search, and collection statistics. The descriptions explicitly guide when to use search_documents versus search_fuzzy, eliminating ambiguity between the two search tools.
All names use consistent snake_case with a leading verb (list_, read_, search_, get_). The only slight variation is search_fuzzy using an adjective modifier rather than a noun, but it still follows the same verb-led convention and is clearly readable.
Five tools is well within the ideal range and each tool covers a distinct, necessary operation for a local document search server. There is no redundancy or filler.
The surface covers the core lifecycle: discover documents, read them, search exactly or fuzzily, and inspect collection stats. Minor gaps exist, such as no filtering of searches by metadata or file type, and no dedicated single-document metadata lookup beyond the list overview.
Maintenance
Related MCP Connectors
Read and search folders on your own computer with explicit consent. Files are never uploaded.
Private persistent memory for Claude, ChatGPT & Gemini via MCP - semantic search, zero-code setup.
Search and reason over your Obsidian-style Markdown vault, right from ChatGPT.
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceThis is a connector to allow Claude Desktop (or any MCP client) to read and search any directory containing Markdown notes (such as an Obsidian vault).979 npm1,352AGPL 3.0
- AlicenseNot gradedqualityDmaintenanceEnables Claude Desktop to search and query personal document collections (PDF, Word, Markdown, text) using semantic search and conversational AI with full context preservation across exchanges.MIT
- AlicenseNot gradedqualityBmaintenanceEnables Claude to read, search, and analyze your entire knowledge vault locally via MCP tools like search, drafting, and linting.88 npm5MIT
- AlicenseNot gradedqualityBmaintenanceProvides persistent long-term memory and local RAG document search for Claude Desktop, fully offline. Enables memory save/search and document retrieval via MCP.MIT