litedocs-mcp
Indexes a local folder of Markdown (and AsciiDoc) documentation into a SQLite FTS5 full-text index and exposes a search tool that returns the most relevant sections with file paths, line ranges, heading breadcrumbs and highlighted snippets, plus tools to list and incrementally reindex the documentation corpus.
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., "@litedocs-mcpsearch my docs for the retry backoff policy"
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.
litedocs-mcp
Search a folder of Markdown and AsciiDoc over MCP. SQLite FTS5, Python stdlib only, no embeddings, no Docker.
Point it at a documentation folder and your AI agent gets one precise search_docs tool instead of grepping its way through the repo. On a 220-file corpus it indexes in ~0.1s and answers queries in ~50ms.
agent ──MCP/stdio──▶ litedocs-mcp ──▶ SQLite FTS5 index ──▶ your docs folderWhy this exists
An agentic CLI pointed at a documentation repo will happily burn thousands of tokens on grep, then read whole file, then grep again. If your model access is metered — or the model is mediocre at long multi-step tool chains — that loop is expensive and unreliable.
litedocs-mcp replaces it with a single call that returns the five most relevant sections, each with a file path, a line range and a heading breadcrumb. The agent reads a full file only when it decides it needs to.
Most RAG servers solve this with embeddings, which means a vector database, a model download and often a Docker stack. For a few hundred text files that is the wrong weight class, and on a locked-down machine the model download is usually where the afternoon goes. litedocs-mcp uses SQLite's built-in FTS5 full-text index with BM25 ranking instead:
No dependencies. Python standard library only. Nothing to
pip install.No network. No model downloads, no API keys, no telemetry. Works fully offline.
No services. One process, one SQLite file. Nothing to keep running between sessions.
Readable. ~440 lines in a single file you can audit before pointing it at internal documentation.
On technical documentation — full of product names, service names, ticket IDs and domain jargon — keyword search is frequently better than naive embeddings, because those are exactly the tokens embedding models handle worst.
When you want something else
Reach for an embedding-based server (mcp-ragdocs, mcp-server-qdrant) when you need conceptual matching — finding the section about "rolling back a bad release" when the docs only ever say "reverting a deployment". litedocs-mcp will not find that; BM25 matches words, not meaning. The MCP interface is the same either way, so switching later costs you nothing.
Related MCP server: docs-mcp
Requirements
Python 3.9 or newer
SQLite built with FTS5 (true for essentially every CPython build; the server tells you clearly if yours is the exception)
That's it.
Install
Option 1 — just the file
curl -O https://raw.githubusercontent.com/gowthamraj07/litedocs-mcp/master/litedocs_mcp.py
python3 litedocs_mcp.py --docs ./docs --indexOption 2 — as a command
pip install git+https://github.com/gowthamraj07/litedocs-mcp.git
litedocs-mcp --docs ./docs --indexOption 3 — no install at all
uvx --from git+https://github.com/gowthamraj07/litedocs-mcp.git litedocs-mcp --docs ./docs --indexQuick start
Build the index and check that it found your documents:
$ python3 litedocs_mcp.py --docs ~/work/platform-docs --index
Indexed 214 files into 1893 sections (214 new, 0 updated, 0 removed).
Index: /home/you/work/platform-docs/.litedocs-index.sqlite3Try a query from the shell before wiring in any agent:
$ python3 litedocs_mcp.py --docs ~/work/platform-docs --search "retry backoff dead letter"
1 section(s) for 'retry backoff dead letter':
guides/payments.md:5-15
payments.md > Payment Service > Retry policy
## **Retry** policy Failed payment authorisations are **retried** three times
with exponential **backoff** starting at 500ms. After the third failure the
transaction moves to the **dead** **letter** queue for manual reconciliation. …
Read a file at the line range above for the full section.If that looks right, run it as an MCP server:
python3 litedocs_mcp.py --docs ~/work/platform-docsThe server speaks MCP over stdio. It logs to stderr and keeps stdout clean for protocol traffic — if your client starts the server but shows no tools, check stderr first, that is the usual cause.
Connecting an agent
Use absolute paths for both the script and the docs folder. Your client launches the server from an unpredictable working directory.
Claude Code (and its forks)
{
"mcpServers": {
"docs": {
"command": "python3",
"args": ["/abs/path/to/litedocs_mcp.py", "--docs", "/abs/path/to/your/docs"]
}
}
}Put this in .mcp.json at your project root (shared with the team) or in your user-level Claude Code config.
Claude Desktop
Same block, in claude_desktop_config.json:
macOS —
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows —
%APPDATA%\Claude\claude_desktop_config.jsonLinux —
~/.config/Claude/claude_desktop_config.json
OpenCode
opencode mcp add…or add it to opencode.json / ~/.config/opencode/opencode.jsonc as a local command.
Anything else
Any MCP client that can launch a local stdio subprocess works. Point it at python3 /abs/path/litedocs_mcp.py --docs /abs/path/docs.
The tools
Deliberately three. Tool descriptions consume context on every single turn, so a server with a dozen tools is working against the thing it is supposed to help with.
search_docs(query, limit=5)
Full-text search. Returns the best-matching sections as path, line range, heading breadcrumb and a snippet with matched terms marked.
Pass 3–6 distinctive keywords, not a whole sentence. The tool description tells the model this, because weaker models habitually dump the user's entire question into search tools, which wrecks BM25 ranking.
Wrap a phrase in double quotes for an exact match:
search_docs(query='"Idempotency-Key" header merchant')list_docs()
Every indexed document as path plus title. Use it when you want a map of the knowledge base rather than an answer — and run it once after your first index to spot files that came back suspiciously thin.
reindex_docs()
Rescan and refresh. Only files whose content actually changed get reprocessed, so this is cheap enough to run after every git pull.
The server also reindexes automatically on startup, so in practice you rarely need this one.
CLI reference
--docs PATH Folder of documentation to index. Required.
--db PATH Index file. Default: <docs>/.litedocs-index.sqlite3
--exts LIST Comma-separated extensions.
Default: .md,.markdown,.adoc,.asciidoc,.txt
--index Build the index and exit.
--force Reindex every file, ignoring content hashes.
--search QUERY Run one query and exit. Useful for tuning without an agent.
--limit N Max results for --search. Default: 5.With no --index and no --search, it runs as an MCP server on stdio.
How it works
Chunking. Documents are split at heading boundaries — # for Markdown, = for AsciiDoc — with a heading stack tracked so every chunk carries a breadcrumb like runbook.adoc > Incident Runbook > Escalation path. Headings inside fenced code blocks are ignored. Content before the first heading becomes a preamble chunk. Sections longer than 2000 characters are split at paragraph boundaries, with line numbers kept honest throughout. If a file uses the other syntax's heading style, the parser falls back to it rather than producing one giant chunk.
Indexing. Chunks go into a standalone FTS5 virtual table with the Porter stemmer, so "retry" matches "retried". The heading breadcrumb is weighted 10× the body in BM25 scoring — a section whose heading is about your query is almost always the one you want.
Incremental updates. Each file's content is SHA-256 hashed. On reindex, unchanged files are skipped, changed files have their chunks deleted and rebuilt, and deleted files are purged. A no-op reindex over 220 files takes ~50ms.
Query handling. The query is tokenised and tried as an AND match first, for precision. If that returns nothing it falls back to OR, so a long natural-language question still finds something instead of silently returning empty. Quoted phrases are extracted and tried first. Malformed FTS5 syntax can never crash the server.
Known limitations
AsciiDoc include:: is not resolved. Included files are indexed on their own, so no content is lost — but a master document that is mostly include:: directives will index as near-empty. Run list_docs after your first index to spot these.
AsciiDoc attribute substitution is not performed. If your docs use {product-name}, searching for the resolved value will not hit.
No conceptual matching. See When you want something else above.
The index file lives next to your docs by default, as .litedocs-index.sqlite3. Add it to .gitignore (this repo's own .gitignore covers it) or relocate it with --db.
Privacy
litedocs-mcp makes no network calls of any kind. It reads local files and writes one local SQLite file. There are no API keys, no model downloads, no telemetry, and no third-party dependencies that could add any of the above in a future version.
That is a deliberate design goal, not an accident — the intended use case is documentation that cannot leave the machine.
Searching history: litedocs-history
A second, separate server for a different question: what did the docs used to say?
If your documentation lives in git — a GitHub wiki is an ordinary git repo at https://github.com/OWNER/REPO.wiki.git — then every version of every page is still there. litedocs-history indexes all of them, so you can search text that has since been deleted and find out exactly when it was live and who changed it.
It is a separate entry point with its own index file, on purpose. Tool descriptions cost context on every turn, and history search is something you need rarely. Enable it when you are auditing; leave it off the rest of the time.
git clone https://github.com/OWNER/REPO.wiki.git ~/work/project.wiki
litedocs-history --repo ~/work/project.wiki --index$ litedocs-history --repo ~/work/project.wiki --search "three business days settle"
1 match(es) for 'three business days settle' across the full history:
Payments.md — Payments.md > Payments > Settlement
added 2026-01-15 by alice (efc8779e)
status REMOVED 2026-05-20 by carol (17fa2521)
## Settlement Funds **settle** within **three** **business** **days** of authorisation.That text exists nowhere in the working tree. Ordinary grep cannot find it.
Tools
Tool | Answers |
| "Did the docs ever say something like this, and when?" Fuzzy, ranked, finds deleted text. |
| "Which commit added this exact sentence, and which removed it?" Git pickaxe, literal match, author and date. |
| "How has this page changed?" Follows renames. |
The intended flow is two-stage: search_history to find the wording, when_documented to prove the provenance.
CLI
--repo PATH Clone of the docs/wiki git repo. Required.
--db PATH Index file. Default: <repo>/.litedocs-history.sqlite3
--index Build the index and exit (skipped if refs are unchanged).
--force Rebuild even if nothing has changed.
--search QUERY Search all history.
--only-removed Restrict to text that no longer exists.
--when TEXT Pickaxe a literal phrase. Needs no index.
--page PATH Revisions of one page. Needs no index.How it stays cheap
Indexing every version of every page sounds expensive. It isn't, because of two things.
Git stores one blob per distinct content, so a page edited fifty times contributes fifty blobs, not fifty copies of the repo. All of them are read in a single git cat-file --batch call.
More importantly, section lifetimes are collapsed. A section that survives twenty edits to other parts of its page becomes one row spanning the whole period it was live — not twenty near-identical search hits. So the index is sized by how much the docs actually changed, and results read as "this was true from January to May" rather than as a wall of duplicates.
Limits worth knowing
Git history is not tamper-evident. GitHub wikis, as far as I know, support neither branch protection nor required reviews, so anyone with write access can force-push. This tool reports faithfully what the history contains; it cannot tell you the history was never rewritten. If the audit trail genuinely matters, mirror the wiki on a schedule into a repo you control.
The repo is not a record of what anyone read. A commit date establishes what the docs said on a given day. It does not establish that a particular person saw it. Pair it with a ticket or a decision record for that.
Merge commits are skipped, as git log --raw does by default. Wiki histories are linear in practice; a heavily merged docs repo may miss changes that only exist in a merge resolution.
when_documented is a literal match. It is git's pickaxe — character for character, no stemming. Use search_history first to find the real wording.
Contributing
Issues and pull requests welcome. Two things worth preserving if you send a patch: zero runtime dependencies, and a single auditable file. Both are features.
License
MIT — see LICENSE.
Available Tools
3 toolslist_docsA
List every indexed document as path plus title. Use when you need a map of the knowledge base rather than an answer.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden. It does disclose scope ('every indexed document'), which tells the agent this is a complete, unfiltered enumeration, but it says nothing about result volume, pagination, or what happens on a very large knowledge base.
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, purpose first then usage, with no filler. Each sentence carries distinct information: what it returns and when to pick it.
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 listing tool with no output schema, the description supplies the essential facts (full enumeration, path+title format, use case). Only the lack of any volume/pagination note keeps it from being fully complete.
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 no parameters, so there is nothing for the description to clarify beyond the schema; baseline 4 applies. The description correctly implies no filtering input is accepted.
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 (List) and resource (every indexed document) and even names the return shape (path plus title). The contrast with a retrieval tool ('a map of the knowledge base rather than an answer') implicitly separates it from sibling search_docs.
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: use it when you need an overview/map rather than an answer. The alternative (search_docs) is implied by 'rather than an answer' but is not named explicitly, so it falls just 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.
reindex_docsA
Rescan the docs folder and refresh the index. Run after a git pull; it only reprocesses files whose content changed.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
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 it does disclose an important trait: incremental work ('only reprocesses files whose content changed'), which signals cost and safety. However, it says nothing about whether the operation blocks, how long it takes, concurrency safety, or permissions — meaningful gaps for a mutation tool with zero structured coverage.
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, front-loaded with the action and followed by the trigger condition and the incremental guarantee. No filler, no repetition of the tool 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 zero-param, no-output-schema maintenance tool, the description covers action, trigger, and scope of reprocessing. What is missing is operational context (duration, blocking behavior), which would be genuinely useful given the absence of annotations, but the essentials are present.
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 no parameters and the schema is empty, so there is nothing for the description to disambiguate. Baseline 4 applies for a zero-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?
States a specific verb ('Rescan') plus resources ('docs folder', 'the index'), which is unmistakably a maintenance/reindex operation and thus clearly distinct from the read-only siblings search_docs and list_docs. An agent can identify what this does 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 triggering condition ('Run after a git pull'), which is a real when-to-use signal rather than mere implication. It stops short of stating when NOT to run it or naming an alternative tool for index concerns.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_docsA
Full-text search over the local documentation knowledge base. Returns the best-matching sections with file path, line range, heading breadcrumb and a snippet. Prefer this over grepping or reading whole files: pass 3-6 distinctive keywords rather than a full sentence. Wrap a phrase in double quotes for an exact match.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max sections (default 5). | |
| query | Yes | Keywords to search for. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations and no output schema, the description carries the full burden and does disclose the return payload (best-matching sections with file path, line range, heading breadcrumb, snippet). It does not cover ranking behavior, index staleness, or what happens on an empty/degenerate query, which are the remaining behavioral gaps.
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 tight sentences with zero filler: purpose and return shape first, then the usage preference and query syntax. Nothing is repeated from the schema and no sentence could be dropped without losing 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?
For a simple two-parameter search tool with no annotations and no output schema, the description covers what it does, what it returns, how to phrase the query, and why to prefer it over alternatives. Nothing an agent needs 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 already 100%, so the baseline is 3. The description adds genuine value beyond the schema by specifying query formulation rules (3-6 distinctive keywords rather than a full sentence, double quotes for exact phrase matching), which meaningfully change how the query parameter should be populated.
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 ('Full-text search over the local documentation knowledge base') and even enumerates the shape of the result (path, line range, breadcrumb, snippet). It implicitly separates itself from list_docs and reindex_docs by being a matching/retrieval operation, but never names those siblings to disambiguate explicitly.
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 clear context for use ('Prefer this over grepping or reading whole files') plus concrete query-construction advice (3-6 distinctive keywords, quote phrases for exact match). It stops short of stating when NOT to use it or when to reach for list_docs instead, so no exclusions are provided.
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.
3 tool updates
v0.1.0- First observed
list_docs - First observed
reindex_docs - First observed
search_docs
TDQS
Scored across 3 tools
Each tool targets a distinct action on the docs index: search retrieves matching sections, list enumerates documents, reindex refreshes the index. Their boundaries are crisp and the descriptions reinforce when to prefer each over grep or file reads.
All three names follow the same verb_noun snake_case pattern (search_docs, list_docs, reindex_docs) with a shared '_docs' noun suffix. Fully predictable and readable.
Three tools is small but well-matched to a focused read-only documentation index; each tool clearly earns its place. It is on the lean side, with no room to trim and one arguably missing retrieval operation.
The surface covers search, listing, and index refresh, but there is no get_document tool to fetch a full document by path from a list_docs result. Agents can only see snippets or paths, creating a dead end when full content is needed.
Maintenance
Related MCP Connectors
Securely search and manage workspace context files for AI agents and teams.
Search and reason over your Obsidian-style Markdown vault, right from ChatGPT.
Open-source Obsidian for MDX - edit local docs with agent assistance
Persistent docs and memory for AI agents — read, write, organize & search a shared workspace.
Related MCP Servers
- AlicenseAqualityAmaintenanceEnables AI agents to search local Markdown documents using natural language, with automatic indexing and section-level retrieval.1012 npm1MIT
- AlicenseNot gradedqualityDmaintenanceGives AI agents full-text search over any Markdown/MDX documentation folder.10 npmMIT
- AlicenseAqualityBmaintenanceProvides fast, token-efficient search over coding agent documentation (e.g., Claude Code, Cursor) using local SQLite FTS5 indexing, with tools for searching snippets, reading pages, and grepping markdown.5MIT
- AlicenseAqualityBmaintenanceIndexes local documents (PDF, Word, Markdown, text) into a SQLite database for AI agents to search and retrieve bounded, source-located passages. Runs fully locally with optional OCR, preserving privacy.5MIT