mcp-docsearch

mcp-docsearch

Semantic search over any markdown corpus using local embeddings. Provides tools to search, reindex, and get index stats, with results including file paths, line numbers, and header breadcrumbs.

Category
Visit Server

README

mcp-docsearch

Semantic search over any markdown corpus, exposed as an MCP server.

Point it at a directory of markdown. It chunks the files, embeds them into a local ChromaDB vector store, and gives your MCP client — Claude Code, Claude Desktop, or anything else that speaks MCP — three tools: search_docs, reindex, and index_stats.

Results come back with file paths, line numbers, and section breadcrumbs, so the model can cite and open the source rather than paraphrasing from a soup of context.

Everything runs locally. No API keys, no documents leaving your machine.


Why

Grep finds the word you typed. It does not find the paragraph that means what you meant.

Once a corpus — design docs, a knowledge base, a wiki, a novel — grows past a few dozen files, an agent needs retrieval, not string matching. This is a small, dependency-light way to give it one, over a corpus you already have sitting on disk as markdown.

Install

git clone https://github.com/MrMattMunro/mcp-docsearch
cd mcp-docsearch
pip install -e .

Quick start

export DOCSEARCH_CORPUS=/path/to/your/markdown
mcp-docsearch-index          # build the index (first run embeds everything)

Then register the server with your MCP client. For Claude Code (.mcp.json):

{
  "mcpServers": {
    "docsearch": {
      "command": "mcp-docsearch",
      "env": {
        "DOCSEARCH_CORPUS": "/path/to/your/markdown",
        "DOCSEARCH_COLLECTION": "my_docs"
      }
    }
  }
}

Ask your model something like "search the docs for how authentication is handled" and it will call search_docs and get back ranked excerpts with line numbers.

In practice

This exists because I hit the problem from two directions at once: I wanted semantic search over a corpus of structured design docs and over a long-form fiction manuscript, and discovered that no single chunking strategy serves both. Splitting a novel on its headers gives you one useless mega-chunk per chapter. Sliding a fixed window over a reference doc slices sections in half and strands the heading from the thing it describes.

Hence two strategies. The examples below are the three corpus shapes I actually run it against.

A structured knowledge base

Design docs, runbooks, a team wiki. Sections are already self-contained ideas, and the header chain (Runbook > Deploys > Rollback) is a ready-made breadcrumb.

DOCSEARCH_CORPUS=~/notes/handbook
DOCSEARCH_COLLECTION=handbook
DOCSEARCH_CHUNK_MODE=header

A long-form manuscript

A novel, a screenplay, interview transcripts. Hundreds of pages, barely a header in sight — the kind of corpus where "which scene was it where they argued about the money?" is exactly the question you can't grep for.

DOCSEARCH_CORPUS=~/writing/manuscript
DOCSEARCH_COLLECTION=manuscript
DOCSEARCH_CHUNK_MODE=window
DOCSEARCH_WINDOW=120     # prose needs more context per chunk than reference docs
DOCSEARCH_OVERLAP=30     # a scene straddling a seam still lands whole in one window

A mixed working directory

Plans, notes and reference material side by side, with no consistent shape. Let auto decide per file, and use the group filter (the top-level folder) to scope a search to just plans/ or just reference/.

DOCSEARCH_CORPUS=~/workspace
DOCSEARCH_COLLECTION=workspace
DOCSEARCH_CHUNK_MODE=auto
DOCSEARCH_EXCLUDE=archive,drafts
search_docs("what did we decide about the migration?", group="plans")

One install, three corpora: give each its own DOCSEARCH_COLLECTION and they stay completely independent.

Configuration

All configuration is environment variables, so one install can serve many corpora.

Variable Default Meaning
DOCSEARCH_CORPUS cwd Root directory of the markdown corpus
DOCSEARCH_DB <corpus>/.docsearch Where the vector store is written
DOCSEARCH_COLLECTION docs Collection name (use one per corpus)
DOCSEARCH_CHUNK_MODE auto auto, header, or window
DOCSEARCH_WINDOW 80 Lines per window (window mode)
DOCSEARCH_OVERLAP 20 Overlapping lines between windows
DOCSEARCH_EXCLUDE Extra comma-separated directory names to skip

How it works

Chunking — the part that decides whether retrieval is any good

Chunk badly and no amount of embedding quality will save you: split an idea in half and neither half retrieves. So there are two strategies, chosen per file.

header — split on markdown headers, one section per chunk. Right for structured docs, where a section is already a self-contained idea. Each chunk carries its full header chain (Guide > Setup > Windows), which gives the model a breadcrumb for free and makes results readable at a glance.

window — fixed-size overlapping line windows. Right for long-form prose with few headers (a novel chapter, a meeting transcript), where header-splitting would yield one giant useless chunk. Windows overlap so an idea straddling a seam still appears whole in one of them. A runt tail is absorbed into the previous chunk rather than emitted as a two-line fragment that matches nothing.

auto (the default) picks per file: header split if the file has 2+ headers, else windows.

Incremental indexing

Re-embedding an entire corpus every time you fix a typo is slow and pointless. Each file's mtime is recorded, so a normal reindex only re-embeds files that actually changed. Chunks belonging to deleted files are removed from the store, so the index can't rot into a graveyard of stale references.

reindex(force=True) does a full rebuild when you want one.

Retrieval

Cosine similarity over the collection. Results carry file_path, start_line, end_line, and header_chain, and can be filtered by group (top-level directory) or path_prefix.

MCP tools

Tool Purpose
search_docs(query, n_results, path_prefix, group) Ranked excerpts with paths + line numbers
reindex(file_path, force) Incremental by default; one file or a full rebuild
index_stats() What's indexed, and the active configuration

A note on privacy

The vector store contains embedded copies of your documents. If your corpus is private, the index is private too — even if the code around it isn't. .docsearch/ and .chromadb/ are in .gitignore for exactly this reason. Don't commit an index.

Development

pip install -e ".[dev]"
pytest

Licence

MIT

Recommended Servers

playwright-mcp

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.

Official
Featured
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured