knowledge
Uses an OpenAI-compatible embeddings API to generate vector embeddings for Markdown chunks, enabling semantic search alongside keyword search. The base URL, model, dimensions, batching, and API key source are configurable.
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., "@knowledgesearch my notes for the deployment checklist"
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.
rag-mcp
Search over a folder of Markdown files. The .md files stay the source of truth and
remain ordinary files you can edit in Obsidian or any editor. Next to them sits
index.db, a SQLite database holding a full-text index (FTS5) and embedding
vectors. Delete it and it rebuilds from the files.
Two ways in: the markdown-rag command line tool, and an MCP server so an
MCP-capable client can search the vault instead of reading the whole folder.
The repository is named rag-mcp; the Python package is markdown-rag. Names
inside still say "knowledge" — config knowledge.toml, KNOWLEDGE_* variables,
MCP server knowledge, tools knowledge_search and so on. Same thing: the
markdown-rag tool serves a knowledge base.
One dependency: numpy. There is no vector database. Twelve thousand chunks are
scanned by multiplying matrices in tens of milliseconds.
Requirements
Python 3.11 or newer (
tomllib)numpyAn OpenAI-compatible embeddings API — optional, but without it only keyword search works
Related MCP server: Brain MCP
Install
python -m pip install -e .
markdown-rag init # writes knowledge.tomlOr run without installing, from the repository root:
python -m markdown_rag.cli statusConfigure
One file, knowledge.toml. It is looked up in this order: --config,
KNOWLEDGE_CONFIG, the current directory, ~/.config/knowledge/.
[vault]
path = "~/Obsidian/MyVault" # required
db = "index.db" # relative to the config file
[tiers]
names = ["CONTEXT.md", "CONTEXT-*.md", "MEMORY.md", "INDEX.md"]
dirs = ["Memory", "Notes"]
[projects]
root = "Projects"
[embed]
base_url = "https://api.openai.com/v1"
model = "text-embedding-3-small"
dim = 1536
batch = 32
timeout = 300
api_key_env = ["OPENAI_API_KEY"]
credentials_file = "~/.secrets.yaml"Only vault.path is required. tiers, projects and embed all have defaults.
Without vault.path the tool reports what is missing and exits with code 2.
api_key_env lists environment variables in priority order; credentials_file is
the fallback, a plain file of KEY=value or KEY: value lines. The key is only
needed for embeddings.
Any value can be overridden by an environment variable: KNOWLEDGE_VAULT,
KNOWLEDGE_DB, KNOWLEDGE_BASE_URL, KNOWLEDGE_EMBED_MODEL, KNOWLEDGE_EMBED_DIM,
KNOWLEDGE_BATCH, KNOWLEDGE_TIMEOUT, KNOWLEDGE_CREDENTIALS,
KNOWLEDGE_TIER_NAMES, KNOWLEDGE_TIER_DIRS, KNOWLEDGE_API_KEY. A .env next
to the config is loaded too and never overwrites what is already set.
Commands
markdown-rag index # incremental reindex
markdown-rag index --full # reread everything
markdown-rag embed # compute missing embeddings
markdown-rag embed --limit 500 # in batches
markdown-rag search "query"
markdown-rag search "query" --tier 1 # overviews and memory only
markdown-rag search "query" --mode fts # no embeddings
markdown-rag search "query" --project p # one project
markdown-rag read "Projects/p/CONTEXT.md" --start 1 --end 40
markdown-rag context p # project entry point
markdown-rag projects # list projects
markdown-rag status # index stateGlobal flags --config, --vault, --db go before the subcommand.
How search works
Files are cut into chunks on headings and paragraphs — around 600 tokens, never
more than 1000. Small tails are glued to the previous chunk (MIN_FLUSH = 200):
30-token chunks ranked worse than nothing because they carried no context.
Every chunk goes into two searches at once: FTS5 over words (with ё→е
normalisation and prefix matching) and cosine similarity over vectors. Results are
merged with Reciprocal Rank Fusion.
RRF_K = 10, not the 60 from the original RRF paper. With K=60 the weight
distribution is nearly flat — rank 1 is only 1.6× rank 40 — so an item ranked 40th
by FTS and 15th by vector scored 1/100 + 1/75 = 0.0233 and beat the genuine
leaders of both lists at 1/61 = 0.0164. Mediocre in two lists beat a leader in
one. With K=10 the spread is 7.5×, leaders surface, and the bonus for two lists
agreeing remains. The candidate pool is 20, not 40: the tail of the list is noise.
The symptom that catches this: the top results are off-topic even though
--mode fts and --mode vector each return the correct leaders on their own.
Tiers
tier | what lands there | why |
1 |
| entry points and overviews |
2 | everything else, meaning the conversation archive | details |
Tier 1 gets a 1.3 multiplier on equal score. Names and directories are set in
[tiers], so a different vault layout needs no code change.
[projects] root defines where a project name comes from: Projects/p/... →
project p. knowledge_projects and --project use it.
MCP
markdown-rag-mcp (or python -m markdown_rag.mcp) is a stdio server speaking
JSON-RPC 2.0, with no third-party MCP dependency. Tools:
knowledge_search(query, project?, tier?, limit?, mode?) hybrid search
knowledge_read(path, start_line?, end_line?) read plus hash
project_context(project) entry point
knowledge_write(path, content, expected_hash?) write with a guard
knowledge_append(path, text) append
knowledge_commit(title, body, project?) note
knowledge_status() state
knowledge_projects() list projects
knowledge_reindex(full?) rebuildBefore every call the server catches the index up by mtime — an editor changes
files directly and that is picked up without a daemon or a file watcher. The
catch-up runs at most once every 10 seconds so it does not pay for stat on every
call.
A write indexes the file and computes its embeddings immediately; otherwise a fresh note would be findable by words but not by meaning. An API failure does not break the write — the chunk stays queued and goes out on the next call. The catch-up computes at most 64 chunks per call so edits do not hang a response for minutes.
Registering with a client
The server talks over stdio and starts as an ordinary process. The exact format depends on the client; the meaning is always a command plus arguments:
{
"command": "markdown-rag-mcp",
"args": ["--config", "/path/to/knowledge.toml"]
}Without --config the server looks up the config the usual way. Clients start
servers from their own working directory, so passing the config path explicitly
avoids surprises.
CLI and MCP share one index, so markdown-rag status and knowledge_status report
the same state.
The skills/ directory holds an optional hint for clients that support skills; the
server works the same without it.
Tests
python -m unittest discover -s tests -t .No network and no API: embeddings are replaced by a deterministic stub, and each
test gets a temporary database. Covered: chunker, schema and FTS, classification
and incremental indexing, RRF merging, reading and writing with expected_hash,
configuration, and the MCP layer.
There is a dedicated regression test for RRF_K: it asserts that K=60 breaks the
ranking order and K=10 does not. 157 tests run in under a second.
Limitations
Vector search is a full in-memory scan. Fine for 12 thousand chunks; not for tens of millions.
An external embeddings API is required for semantic search.
Tier classification and project names depend on the path layout: the
Memory/,Notes/andProjects/<name>/rules work while the vault is organised that way.Relevance is tuned on one real vault. Expect to adjust
RRF_K,TIER_BOOSTand the tier lists for your own data.
Layout
markdown_rag/
store.py schema, connection, FTS normalisation
chunker.py Markdown to chunks on headings and paragraphs
indexer.py incremental indexing, tier and project classification
embed.py OpenAI-compatible embeddings, batching, retries
service.py search, RRF, tiers, read and write
mcp.py MCP server over stdio
cli.py command line
mcp_server.py entry point wrapper used when a client calls the file directly
skills/ optional client hint
tests/ 157 tests, unittest, no networkCode comments and in-code docs are in Russian.
What is missing
Reindexing on a schedule or an editor hook.
A question set for measuring retrieval quality.
Sources other than Markdown.
This server cannot be deployed
Maintenance
Related MCP Connectors
Serve a folder of Markdown notes as an MCP server: hybrid search, reading, and sourced answers.
Markdown-based note-taking with a hosted MCP server. Your notes serve you and your AI.
Notes, files, GitHub, and Drive through one MCP connection.
Notes, files, GitHub, and Drive through one MCP connection.
Related MCP Servers
- AlicenseAqualityAmaintenanceA generic Markdown vault MCP server with FTS5 full-text search, semantic vector search, frontmatter-aware indexing, incremental reindexing, and non-markdown attachment support that exposes search, read, write, and edit tools.44191 PyPI36MIT
- AlicenseBqualityCmaintenanceEnables hybrid dense and BM25 search over a markdown vault, with tools for reading, writing, grepping, and managing notes through any MCP-compatible client.12MIT
- FlicenseNot gradedqualityAmaintenanceEnables users to create, link, search, explore, and synthesize atomic notes through MCP-compatible clients, with Markdown file storage and a SQLite index for efficient graph traversal.-
- FlicenseNot gradedqualityAmaintenanceEnables users to create, link, explore, and synthesize atomic notes through MCP-compatible clients, using Markdown files as the source of truth with a searchable SQLite index.-