rag-mcp
The rag-mcp server provides a single tool, search_knowledge(query, k), for semantic search over a local knowledge corpus. It uses local ONNX-based embeddings, returning cited passages (source file, heading, chunk index) for full traceability. k (1–20, default 5) controls the result count; empty queries are rejected and out-of-range values are clamped. The tool is auth-scoped to the configured corpus root, blocking path‑escape attempts, and includes fail‑soft handling (structured errors for store issues). Ideal for documentation QA, knowledge‑base lookup, and RAG pipelines.
rag-mcp
A minimal, honest RAG-over-a-corpus MCP retrieval tool. One tool,
search_knowledge(query, k), that embeds a query, vector-searches a local corpus, and returns passages with citations (source + heading + chunk index) so answers are traceable.
Built to slot into the mcp-factory manifest model. Fully local + $0 (no paid embedding API).
Why it's safe to put in front of a real corpus
Cited - every hit carries
source+heading+chunk_index.Auth-scoped - results are confined to the configured corpus root; sources that escape it (absolute paths,
..traversal) are refused.Fail-soft - a down or empty store returns a structured error, never an exception that crashes the calling agent.
Bounded -
kis clamped to[1, 20]; empty queries are rejected.Version-pinned deps (
requirements.txt).
Related MCP server: ragi
Stack
Layer | Choice |
Embeddings | local ONNX |
Vector store | ChromaDB embedded |
Server |
|
Protocol revision
Pinned to mcp==2.0.0, the first SDK release implementing MCP protocol revision
2026-07-28. The server serves both eras on the same stdio connection -- the
client's first frame picks:
Client opens with | Negotiated revision | Notes |
a per-request |
| stateless per-request envelope; no |
the classic |
| handshake era caps here -- expected, not a downgrade |
2026-07-28 is not reachable via the initialize handshake; it is a "modern"
revision reached through server/discover or an inline _meta version stamp. Era
selection is automatic and per-connection -- there is no server-side flag.
tests/test_protocol_version.py asserts both paths end-to-end, so a dependency
rollback that silently drops the server to an older revision fails CI instead of
passing quietly.
Quick start
python -m venv .venv && .venv/Scripts/python -m pip install -r requirements.txt
# Ingest a corpus (markdown). Incremental by default: only files whose content
# changed since the last run are re-embedded.
python -m rag_mcp.cli ingest path/to/docs --db ./store.chroma
# Force a rebuild in place (ignore the manifest, re-embed everything)
python -m rag_mcp.cli ingest path/to/docs --db ./store.chroma --full
# One-off query (corpus root = the auth scope)
python -m rag_mcp.cli query "your question" --db ./store.chroma --corpus path/to/docs -k 5
# Run as an MCP server (stdio); configure via env first
# RAG_MCP_CORPUS_ROOT, RAG_MCP_DB_PATH, RAG_MCP_COLLECTION, RAG_MCP_EMBEDDER
python run_server.py # operational entrypoint (referenced by mcp.yaml)
python -m rag_mcp # same server, via the packaged console entry point
rag-mcp # after `pip install jaimenbell-rag-mcp` -- console scriptKeeping the index fresh (incremental ingest)
Ingest is incremental by default. A manifest inside the store dir records a SHA-256 of each file's decoded text; a run re-embeds only what actually changed, and prunes what upsert alone never could (chunks of deleted/renamed notes, and trailing chunks of notes that got shorter).
Measured on a live 2808-file / 26.6 MiB corpus (bge, CPU):
Run | Cost |
tick with no changes | ~0.7s (walk + read + hash everything) |
full re-embed | ~2h33m (50,109 chunks at ~5.5 chunks/sec) |
That is what makes a frequent schedule affordable: reingest.bat is meant to run
every 15 minutes instead of once daily at 03:00, which had left a note written
at 03:05 invisible to search_knowledge for nearly 24 hours.
The manifest is only trusted when the run identity matches -- embedder, embedding dimension, collection and chunking parameters. Change any of them and every file is re-embedded, so an embedder swap can never be silently half-applied. A missing, corrupt, or mismatched manifest, or a manifest against an empty store, all degrade to a full rebuild; nothing degrades to a wrong skip.
Snapshot de-duplication
The manifest's skip is a whole-file hash, so it cannot see the duplication that
actually hurts retrieval: a daily snapshot series (fleet-health-2026-07-23.md and
friends) repeats yesterday's paragraphs verbatim inside a file whose hash still
changed. Measured on the live vault, one ## RED Bots status line took five
distinct values across fifteen consecutive files and crowded a top-10 with
byte-identical copies of itself, burying the document that explained it at rank 16.
Ingest therefore also de-duplicates at chunk level, but only within a dated
series and only against the immediately preceding snapshot. The first occurrence
is always embedded and keeps its own date as its source; later verbatim repeats
are not embedded, and instead extend the survivor's repeat_dates metadata, which
search_knowledge returns as snapshot_date / also_unchanged_on /
snapshots_covered. So "what did this say on date X" is still answerable -- that is
why the series is de-duplicated rather than excluded. A value that changes and later
returns is kept, because it is a new fact rather than a repeat.
Scope is narrow and stated with the rule in rag_mcp/snapshots.py: filename ending
in -YYYY-MM-DD, at least 3 such files sharing a directory and stem, byte-identical
under an identical heading. On the live corpus that is 316 of 2,814 files and
collapses 842 of 50,428 chunks (17.5% of series chunks, 1.67% corpus-wide) while
touching zero ordinary notes. Disable with --no-snapshot-dedupe.
--full rebuilds in place (ignores the manifest, keeps the store); --clean
deletes the store first. Both still WRITE a manifest, so the next run is cheap.
reingest-clean.bat (weekly) remains a belt-and-braces reset.
As an MCP server
Register via mcp.yaml (validated against mcp-factory's Manifest loader). The tool is
search_knowledge(query, k); it reads the store configured by the RAG_MCP_* env vars.
Tests
python -m pytest # 149 passedLayout
rag_mcp/
chunking.py heading-scoped, overlapping markdown chunks
store.py VectorStore (Chroma) + Embedder protocol (MiniLM default + BgeEmbedder opt-in + offline HashEmbedder)
ingest.py idempotent ingest pipeline with source/heading/chunk-index metadata; incremental by default
manifest.py per-file content hashes -> skip unchanged files, prune stale chunks
search.py search_knowledge: cited, auth-scoped, fail-soft, bounded
server.py MCP stdio server exposing search_knowledge
config.py env-driven Config
cli.py ingest + query CLI
__main__.py console entrypoint (`python -m rag_mcp` / `rag-mcp` script); fails loud on missing config
run_server.py operational MCP entrypoint (referenced by mcp.yaml)
mcp.yaml manifest (mcp-factory model)Commercial support
Maintained by Jaimen Bell. For production MCP integrations, custom servers, or agent-reliability work, see jaimenbell.dev.
Building your own MCP server? The MCP Starter Kit has templates, a build playbook, and packaging war-stories from shipping this one.
mcp-name: io.github.jaimenbell/rag-mcp
Available Tools
1 toolsearch_knowledgeA
Retrieve the most relevant passages from the configured knowledge corpus for a natural-language query. Returns the passage text plus a CITATION (source file + heading + chunk index) for each hit so answers are traceable. Auth-scoped to the corpus root and fail-soft: a down/empty store returns a structured error, never an exception. Use when asked to look something up in the knowledge base / docs.
| Name | Required | Description | Default |
|---|---|---|---|
| k | No | Max passages to return, 1-20 (default 5). | |
| query | Yes | Natural-language search query. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses behavior: auth-scoping to the corpus root, fail-soft behavior (structured error on down/empty store), and the output format. This is comprehensive and goes beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and includes only high-value sentences: return format, auth/fail-soft behavior, and usage guidance. No redundancy or filler.
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?
Given the simple 2-parameter tool with no output schema, the description covers purpose, return format, error behavior, and invocation context. It leaves no critical gaps for an agent to understand the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds no extra semantics about how 'query' or 'k' should be used 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?
The description opens with a specific verb ('Retrieve'), resource ('knowledge corpus'), and purpose ('for a natural-language query'). It further clarifies the return value (passage text + CITATION) making its function unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides usage guidance: 'Use when asked to look something up in the knowledge base / docs.' This gives clear invocation context, even though no alternative tools are listed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
TDQS
The single tool has a clear, distinct purpose (retrieving relevant passages). With no other tools to differentiate, there is no possibility of ambiguous selection.
The tool name 'search_knowledge' follows a clear verb_noun convention, which would be consistent even if more tools were added. As a single tool, it sets a predictable pattern.
With only one tool, the server feels too thin for the 'rag' domain, which typically requires additional operations like listing sources or ingesting content. The single tool is not trivial but is insufficient for a well-scoped RAG server.
The tool surface covers only search/retrieval. There are no tools for managing the knowledge corpus (e.g., list sources, add/update/delete documents), leaving significant gaps that would require external intervention for many workflows.
Maintenance
Related MCP Connectors
Self-hosted AI-native knowledge workspace with hybrid search, GraphRAG, and MCP.
Personal knowledge base MCP server with semantic search, auto-categorization, metadata extraction
Knowledge base MCP for AI agents on iknow.dev. Search, read, and maintain via OAuth.
Make your knowledge agent-ready. One MCP endpoint, 5 connectors, 3 search modes.
Related MCP Servers
- AlicenseAqualityDmaintenanceLocal-first RAG indexing and semantic search MCP server. Enables document retrieval and context-aware queries using local embedding models.314MIT
- AlicenseNot gradedqualityBmaintenanceEnables semantic search over a local knowledge base using MCP tools, allowing AI clients to retrieve relevant document chunks via the search_knowledge tool.152MIT

devitway-rag-starterofficial
FlicenseNot gradedqualityCmaintenanceMinimal local RAG stack with an MCP server that provides document search for any agent.1
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jaimenbell/rag-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server