OnMind-RAG
A lightweight knowledge base MCP server that enables full-text search and retrieval of markdown documents from indexed sites using Orama BM25.
README
OnMind-RAG
A complementary and independent project from OnMind-PUB: a lightweight knowledge base (light RAG) that exposes an MCP server so an agent (MCP client) can query the content of one or more sites.
It is not part of the init → index → build → publish workflow. It only reads the _index.json already generated by a site and, on demand, the markdown.
Idea
- On startup, the server loads
docs/public/_index.jsonfrom each configured site. - Normalizes in memory (stable IDs, clean tags,
visibility, path to.md). - Body cache: reads each
.mdonce, storesbodyText(no frontmatter) and logs time/size to stderr. - Search engine: Orama BM25 full-text by default;
hybrid/vectormodes opt-in with embeddings. - Exposes compact MCP tools: summary → search (meta + body) → document reading.
No _catalog.json is written. If the site re-indexes externally, use the reload_catalog tool.
Requirements
- Node.js ≥ 18 (or Bun with Node compatibility)
- At least one site with
docs/public/_index.json(e.g.sites/knowfrom OnMind-PUB)
Setup
cd rag
cp .env.example .env # optional; you can also export vars
bun install # or: npm install
Variables (see .env.example):
| Variable | Meaning |
|---|---|
RAG_SITES |
Paths to site roots, comma-separated (required) |
RAG_SITE_NAMES |
Optional labels (same order) |
RAG_VISIBILITY |
public (default) | protected | all |
RAG_MAX_BODY |
Max chars returned by read_document (default 50000; increase for very long docs) |
RAG_CACHE_BODY |
1 (default) caches body at startup → Orama indexes body → full-text on markdown. 0 = metadata only (title/desc/tags) → no body search; read_document falls back to disk. |
RAG_SEARCH_MODE |
fulltext (default, BM25) | hybrid | vector (last two require RAG_EMBEDDINGS=1) |
RAG_EMBEDDINGS |
1 enables local hash embeddings for hybrid/vector; 0 (default) |
RAG_EMBED_DIMS |
Vector dimensions (default 384) |
RAG_SNAPSHOT |
1 saves/loads Orama snapshot to disk; 0 (default) |
RAG_SNAPSHOT_PATH |
Snapshot path (default ./data/orama-snapshot.json) |
Example:
export RAG_SITES=../sites/know
# or multiple:
# export RAG_SITES=../sites/know,../sites/andrey
Smoke test (no MCP)
RAG_SITES=../sites/know bun run smoke
With embeddings + hybrid:
RAG_SITES=../sites/know RAG_EMBEDDINGS=1 RAG_SEARCH_MODE=hybrid bun run smoke
With snapshot (2nd load ~100 ms):
RAG_SITES=../sites/know RAG_SNAPSHOT=1 bun run smoke # first: saved
RAG_SITES=../sites/know RAG_SNAPSHOT=1 bun run smoke # second: loaded
Start MCP server (stdio)
RAG_SITES=../sites/know bun run mcp
MCP Client (Cursor / Claude Desktop / Grok / Jan)
{
"mcpServers": {
"onmind-rag": {
"command": "bun",
"args": ["/absolute/path/to/pub/rag/src/server.js"],
"env": {
"RAG_SITES": "/absolute/path/to/pub/sites/know",
"RAG_VISIBILITY": "public",
"RAG_SEARCH_MODE": "fulltext",
"RAG_EMBEDDINGS": "0",
"RAG_SNAPSHOT": "0"
}
}
}
}
You can also use
nodeinstead ofbun
{
"command": "node",
"args": ["/absolute/path/to/pub/rag/src/server.js"],
"env": { "RAG_SITES": "/absolute/path/to/pub/sites/know" }
}
Tools
| Tool | Purpose |
|---|---|
list_sites |
Loaded sites, body-cache stats, search-engine status (Orama, embeddings, snapshot) |
catalog_summary |
Stats by category / language / tags (orientation) |
search_content |
Full-text Orama (BM25) by default on title/description/tags/body. mode: fulltext|hybrid|vector optional. Returns cards with match.score and match.snippet. |
get_entry |
One record by id (site:url) |
read_document |
Markdown body (preferably from cache; truncatable) |
list_series |
Series ordered by filename within a category |
reload_catalog |
Re-reads _index.json + body cache + rebuilds index |
Typical agent flow:
catalog_summary → search_content → get_entry / list_series → read_document
Relation to OnMind-PUB
| OnMind-PUB | onmind-rag |
|---|---|
Generates _index.json and the static site |
Only consumes it |
| Publish workflow | Outside that flow |
| Lives in monorepo for convenience | Movable: just point RAG_SITES to any folder with docs/public/_index.json |
Body cache (startup)
In stderr you'll see something like:
[onmind-rag] index: 289 entries from 1 site(s) in 3ms at ...
[onmind-rag] body cache: 289 docs, 3533.3 KiB in 59ms (missing path=0, read errors=0)
[onmind-rag] search engine: orama · mode=fulltext · embeddings=off · 289 docs · 450ms
With RAG_CACHE_BODY=1 (default): Orama indexes the body field → search_content performs full-text BM25 over the entire markdown. read_document answers from memory (fromCache: true).
With RAG_CACHE_BODY=0: Orama only indexes metadata (title, description, tags) → search_content does not find matches in the document body. read_document falls back to disk reads.
Disable only if RAM is very tight or corpus > 50 MB and you accept metadata-only search.
Search engine (Orama)
| Mode | What it does |
|---|---|
fulltext (default) |
BM25 with stemming, typo tolerance, field boosting. Fast, no external deps. |
hybrid |
BM25 + vector (cosine) — needs RAG_EMBEDDINGS=1 |
vector |
Vector similarity only — needs RAG_EMBEDDINGS=1 |
Embeddings (opt-in)
RAG_EMBEDDINGS=1 uses local feature-hashing (no TensorFlow, no API keys) — a portable baseline to exercise the vector/hybrid path. Not a SOTA semantic model. For real quality, replace embed.js with a provider (OpenAI, Xenova/transformers.js, etc.) keeping the embedText(text, { dims }) interface.
Snapshot (opt-in)
RAG_SNAPSHOT=1 serializes the Orama index to disk (data/orama-snapshot.json + .meta.json). On subsequent starts, if the corpus fingerprint (ids, titles, body length, hide, embeddings flag/dims) matches, it loads the snapshot in ~100 ms instead of re-indexing (~450 ms).
Limits (by design)
- Lexical/BM25 retrieval + hash vectors (no SOTA embeddings unless you plug them in).
- No knowledge graph / neighbors yet (next step: markdown links).
- Designed as a useful, portable base — not a managed vector engine.
Why Spanish tokenizer?
The corpus is predominantly Spanish (~80%). Orama's default English tokenizer treats short technical codes like "XDB", "IAM", "UCDM" as stop-words/noise. Setting language: 'spanish' in the tokenizer indexes them correctly.
Based on onmind-rag v0.2.0 — MCP server for knowledge retrieval with Orama BM25 + opt-in hybrid/vector + snapshot
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.