Docs Brain
Provides searchable, citable access to the FastAPI documentation: hybrid semantic + keyword search across indexed doc pages and code examples, retrieval of full pages as Markdown, and a section listing — each result returns structured citations with section anchors. Retrieved text is sanitized for prompt-injection attempts (with path-traversal protection) before being returned.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Docs Brain

A real stdio session with the server (recorded by scripts/make_demo_gif.py):
paraphrased questions find the right sections with citation URLs, and a path-traversal attempt
is refused.
An MCP server that gives any AI client (Claude Desktop, Cursor, ...) searchable, citable access to the FastAPI documentation. It combines semantic and keyword search (hybrid retrieval with reciprocal rank fusion), returns short structured results with source URLs, and filters prompt-injection attempts out of the docs before they reach the model. It runs locally on a CPU with no API keys and no LLM inside the server: the client's own model writes the answer.
Built to practice the three things AI-engineering roles ask for: RAG, MCP, and evaluation.
Tools
Tool | Input | Returns |
|
| Ranked results: |
|
| Full page as Markdown (max 20k chars), |
| none | Every indexed page grouped by section, so the model knows what it can ask about |
Also exposed: the resource docs://index (same data as list_sections) and the prompt
explain_concept(concept), which tells the model to search, read and cite.
All tools are read-only and return structured output (JSON schema), so clients get typed results instead of a text dump.
Related MCP server: FastAPI Docs MCP Server
Quickstart (Windows, CPU)
git clone <this repo> docs-brain && cd docs-brain
python -m venv .venv
.venv\Scripts\activate
pip install torch --index-url https://download.pytorch.org/whl/cpu # CPU-only torch, much smaller
pip install -r requirements.txt
pip install -e .
# Get the docs (only the docs folder and its code examples)
git clone --depth 1 --filter=blob:none --sparse https://github.com/fastapi/fastapi.git data/raw/fastapi
git -C data/raw/fastapi sparse-checkout set docs/en/docs docs_src
python -m docs_brain.ingest # ~3 min: 149 pages -> 1,261 chunks
python eval/run_eval.py # results table below
python scripts/demo_client.py "run code after the response is sent" # end-to-end MCP checkOn macOS/Linux use .venv/bin/activate and .venv/bin/python instead.
Keep the project in a short path such as
C:\Users\you\Projects\docs-brain. PyTorch files pass Windows' 260-character path limit if the folder is nested deeply.
Connect to Claude Desktop
Edit %APPDATA%\Claude\claude_desktop_config.json (Claude Desktop > Settings > Developer >
Edit Config) and restart Claude Desktop:
{
"mcpServers": {
"docs-brain": {
"command": "C:\\Users\\HP\\Projects\\docs-brain\\.venv\\Scripts\\python.exe",
"args": ["-m", "docs_brain.server"],
"env": { "DOCS_BRAIN_HOME": "C:\\Users\\HP\\Projects\\docs-brain" }
}
}
}Or let the script do it. Quit Claude Desktop fully first (system tray > Quit), because a running Claude can rewrite its config from memory and drop the entry. Then run this from a normal PowerShell window:
.venv\Scripts\python.exe scripts\install_claude_desktop.pyIt finds the config (including the %LOCALAPPDATA%\Claude-Data\... location some installs
use), backs it up and adds the entry with absolute paths.
Use absolute paths. A relative path, or plain python that resolves to a different
interpreter, is the most common reason a server doesn't show up. Cursor uses the same block in
~/.cursor/mcp.json (see examples/).
Inspect with MCP Inspector
Needs Node.js:
npx @modelcontextprotocol/inspector .venv\Scripts\python.exe -m docs_brain.server(mcp dev src/docs_brain/server.py also works if you have uv installed.)
Settings
Env var | Default | Meaning |
|
|
|
|
| Cross-encoder used by |
| repo root | Where |
Results
40 questions written by hand, in a developer's words rather than the docs' headings (see
eval/questions.jsonl). Broad questions ask which page covers a
task ("the browser blocks calls from my React app"). Specific questions ask about one fact
inside a page ("what status code does RedirectResponse use by default?"). A hit means the
expected page is among the top 5 chunks returned.
Mode | Recall@5 (all 40) | Recall@5 broad | Recall@5 specific | Recall@1 | MRR | Median latency |
Dense only (bge-small) | 95% | 96% | 93% | 75% | 0.83 | 26 ms |
BM25 only | 90% | 84% | 100% | 72% | 0.79 | 3 ms |
Hybrid (dense + BM25, RRF) | 98% | 96% | 100% | 80% | 0.86 | 31 ms |
Hybrid + bge-reranker-base | 98% | 96% | 100% | 72% | 0.82 | 10,906 ms |
Hybrid + MiniLM-L6 reranker | 95% | 96% | 93% | 72% | 0.82 | 1,757 ms |
CPU only (4 threads). Reproduce with python eval/run_eval.py --misses. Every label was checked
by hand against the docs text; eval/LABELS.md lists the rule and the four
labels the audit corrected.
What the numbers say
Hybrid wins on every quality metric: recall@5 from 95% to 98%, recall@1 from 75% to 80%, MRR from 0.83 to 0.86, for 5 ms more than dense. Each retriever covers the other's misses: dense search missed "mount an existing Flask app" (it returned Sub Applications), and BM25's exact match on Flask brought the WSGI page back. BM25 alone missed "I don't want the client to wait for the email", where no keyword matches, and dense search found Background Tasks. BM25 is perfect on the specific questions (exact names like
convert_underscores) and weak on broad paraphrases, and dense search is the reverse. That's the case for fusing them.Reranking didn't help, so it's opt-in. Neither cross-encoder beat hybrid on recall@5, both lowered recall@1 from 80% to 72%, and they cost 55-350× the latency on CPU.
bge-reranker-basepushed the correct CORS page from #1 out of the top 5. MiniLM dropped the XML answer. General-purpose rerankers trained on web search don't know that "the browser blocks my React app" means CORS.Caveat: with 40 questions, one question is 2.5 points, so gaps of one or two questions could be noise. The clearest gap is hybrid vs. the rerankers on recall@1 (3 questions). A larger, harder set is the next step.
Security: prompt-injection guard
Documentation is untrusted input. Anyone who gets text into the docs (a merged PR, a poisoned
mirror) can address the AI that reads them. Every snippet and page goes through
guard.py before it is returned:
Attack | Example | Handling |
Instruction override | "Ignore all previous instructions and..." | Line replaced, |
Role / mode switch | "You are now in developer mode" | Line replaced, |
Hidden HTML comment |
| Comment removed, |
Zero-width / bidi characters |
| Characters removed, |
Base64-encoded instruction |
| Decoded, checked, line replaced |
Exfiltration / system-prompt probes | "send the conversation to...", "reveal your system prompt" | Line replaced, |
Path traversal |
| Rejected: only indexed pages can be read |
Flagged text is replaced with [removed: possible prompt injection] and the result carries
flagged: true with reasons, so nothing is dropped silently and the client can tell the user.
The server's MCP instructions also tell the model that retrieved text is reference material,
never instructions.
False positives: 0 of 1,261 real FastAPI chunks are flagged (checked by a test). One early
false positive, the zero-width joiner inside the 👨🍳 emoji in async.md, is now allowed.
Try it: python -m docs_brain.ingest --with-demo-poison indexes a planted page
(tests/fixtures/poisoned.md). Ask "how should I set cache
headers?" (or run python scripts/injection_demo.py) and the poisoned page ranks first, with
the attacks removed and the legitimate text kept:
#1 Response Caching > Cache headers flagged=True ['hidden-html-comment', 'ignore-instructions']
Set the `Cache-Control` header on the response to let browsers and proxies cache it.
[removed: possible prompt injection]
[removed: possible prompt injection]
Use `max-age` to control how long a response stays fresh.
#2 Response Caching > Invalidation flagged=True ['base64:ignore-instructions',
'base64:system-prompt-probe', 'role-override', 'zero-width-characters']
[removed: possible prompt injection]
[removed: possible prompt injection]
A normal paragraph about cache keys, based on the request path and query parameters.Run python -m docs_brain.ingest again afterwards to go back to the clean index.
Design choices
Chunking by heading (H1-H3), capped at ~400 tokens (1,600 chars) with a one-paragraph overlap. Each chunk keeps
path, pagetitle,heading_pathand the headinganchor, so every result links to the exact section (.../tutorial/cors/#use-corsmiddleware). Code fences are never split mid-block, and#comments inside code aren't mistaken for headings.Code examples are inlined. FastAPI's docs reference code with
{* ../../docs_src/x.py *}. Ingestion pastes the file in (respectingln[...]ranges, capped at 60 lines), because code is what developers search for.MkDocs syntax is removed: admonitions, tabs and
:::API-reference stubs. The 740 KB changelog and site-meta pages are excluded so they don't crowd out real answers.Embedding text = heading path + chunk. "Dependencies > First Steps" gives an isolated chunk the context it would otherwise lack. Queries get bge's retrieval instruction prefix.
RRF (k=60) over the top 50 of each retriever. No score normalization needed between cosine similarity and BM25.
BM25 tokenizer keeps underscores, so
response_modelstays one token.Small outputs. 800-char snippets,
top_k≤ 10, and pages capped at 20k chars: big dumps waste the client's context window.Fast startup. Indexes and models load in a background thread, so the MCP handshake answers immediately, and models load from the local cache, so the server works offline.
Logs go to stderr only, because stdout carries the stdio protocol.
Project layout
src/docs_brain/
config.py paths, models, settings
ingest.py clean Markdown, split by heading, build Chroma + chunk store
retrieve.py dense, BM25, RRF fusion, cross-encoder reranking
guard.py prompt-injection detection and sanitizing
server.py MCP server: tools, resource, prompt
eval/ questions.jsonl, run_eval.py, results.md
scripts/ demo_client.py, injection_demo.py: real MCP clients over stdio
tests/ 31 tests (no models needed; run in CI)Limitations and next steps
One library, English only. Swapping libraries means changing the paths and the cleanup rules in
ingest.py.Small eval set. 40 questions, so ±1 question is ±2.5 points. Most questions are single-page lookups, and both the questions and their expected pages were labeled by one person. Next: 100+ questions, multi-page answers, and graded relevance (nDCG).
The guard is heuristic. Regex catches common, low-effort attacks. A paraphrased or translated injection can get past it. Next: add a small classifier (e.g. Prompt Guard) as a second layer, and scan at index time too.
Reranking is slow on CPU. A GPU, ONNX/quantized cross-encoders, or reranking only the top 10 would make it practical.
No incremental indexing. Re-ingesting rebuilds everything (~3 min).
The API reference pages are thin, because their content is generated from docstrings at build time and isn't in the Markdown.
License
MIT
Related MCP Connectors
Search and read Vector Panda docs: API operations, pricing, storage tiers, measured benchmarks.
Versioned documentation registry and semantic search for AI tools and coding assistants.
Read-only search over the Vocenya AI receptionist API docs and endpoints, with code samples.
Read-only search over the public Sikaru documentation: managed agents, inference, evals and traces.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to fetch, index, and perform semantic RAG-based searches on API documentation from various sources. It provides tools for hybrid search and collection management, allowing users to access up-to-date documentation from projects like Gemini and FastMCP.-
- AlicenseAqualityBmaintenanceProvides AI assistants with real-time access to FastAPI documentation, enabling search, browsing, code examples, and comparisons.64MIT
- AlicenseNot gradedqualityDmaintenanceEnables LLMs to search and retrieve information from large technical documentation (OpenAPI specs, markdown) via intelligent chunking and semantic search.MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to perform semantic, hybrid, and filtered search on indexed local documentation with RAG capabilities.2MIT