Skip to main content
Glama
README.md
# docsearch-mcp

Local document search with **cited sources**, as an [MCP](https://modelcontextprotocol.io) server
and a web app. It uses BM25 ranking implemented in pure Python: no API key, no model download,
no vector database.

![demo](docs/demo.png)

## Why

Agents need grounded context they can cite. This gives any MCP client two tools, `search_docs`
and `list_sources`, over your own folder of `.md`/`.txt`/`.rst` files, with the source and a
relevance score returned for every chunk so answers can be traced back.

## Run the demo

```bash
pip install -e ".[dev]"
docsearch-web                      # http://127.0.0.1:8000
```

The default corpus is the docstrings of ~70 Python standard-library modules (about 1,000 chunks),
so it works with nothing to configure. Try "run subprocess and capture output" or
"compute sha256 hash".

Search your own documents instead:

```bash
DOCSEARCH_DIR=./my-docs docsearch-web
```

## Use it from an MCP client

```json
{ "mcpServers": { "docsearch": { "command": "docsearch-mcp-server",
                                  "env": { "DOCSEARCH_DIR": "/path/to/docs" } } } }
```

| Tool | Returns |
| --- | --- |
| `search_docs(query, k=5)` | Top-k chunks: `source`, `chunk`, `score`, `text` |
| `list_sources()` | Number of chunks and the list of source documents |

## How it works

Documents are split into ~120-word chunks on paragraph boundaries. Chunks are tokenized
(lowercased, stopwords removed; the source name is indexed too) and ranked with Okapi BM25
(k1=1.5, b=0.75).

## Tests

```bash
python -m pytest        # 10 tests
ruff check src tests
```

## Limitations

- Lexical matching only: a query must share words with the text. It does not understand
  synonyms the way embedding search can.
- The index is rebuilt in memory at startup, which suits small and medium corpora.
- Plain-text formats only (no PDF or HTML parsing).

TDQS

B3.3/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: one searches content by relevance, the other enumerates indexed sources. There is no realistic way to confuse a BM25 search with listing sources.

Naming Consistency5/5

Both names follow the same verb_noun snake_case pattern (search_docs, list_sources). The convention is predictable and readable.

Tool Count3/5

Two tools is thin for a search server; useful capabilities like fetching a full document or filtering search by source are absent. Still, the count is not absurd for a narrowly scoped read-only index.

Completeness3/5

Core retrieval and source enumeration are covered, but there is no way to retrieve a whole document, filter searches by source, or manage the index. Agents hitting a chunk boundary or needing document-level context will dead-end.

Maintenance

ActivityMaintained
ResponsivenessNo issues