docs-search-engine
# Documentation Search Engine
A custom Model Context Protocol (MCP) server that acts as a documentation search engine.
This project attempts to build a simple, personal clone of [Context7](https://context7.com), unlocking the capability to access up-to-date documentation from GitHub repositories and web pages directly within your AI assistant's context.
## š ļø Tech Stack
- **Python**: Core programming language.
- **FastMCP**: Framework for building MCP servers easily.
- **minsearch**: Lightweight, in-memory full-text search engine.
- **uv**: Fast Python package and environment manager.
- **Jina Reader**: For turning web pages into LLM-friendly markdown.
- **requests**: For handling HTTP requests and downloading zip files.
- **pytest**: For comprehensive testing.
## š Project Structure
```
docs-search-engine/
āāā main.py # Entry point: Defines MCP tools and server configuration
āāā search.py # Core logic: Zip download, extraction, indexing, and search
āāā scrape_web.py # Web scraping functionality (using Jina Reader)
āāā test_search.py # Tests for search functionality
āāā test_scrape_web.py # Tests for web scraping
āāā pyproject.toml # Project dependencies and configuration
```
## š Workflow
1. **Ingestion**: The server downloads documentation source code (e.g., as a `.zip` from GitHub).
2. **Indexing**: Markdown content (`.md` and `.mdx`) is extracted and indexed in-memory using `minsearch`.
3. **Caching**: Indexes are cached by URL to ensure fast subsequent searches without re-downloading.
4. **Retrieval**: Users query the system via MCP tools (`search_docs`, `scrape_web`), and relevant context is returned to the LLM.
## āļø MCP Configuration
Add the following configuration to your MCP client settings (e.g., `mcp_config.json` in Google Antigravity):
```json
{
"mcpServers": {
"docs-search-engine": {
"command": "uv",
"args": [
"run",
"--directory",
"C:/Users/username/path/to/docs-search-engine",
"main.py"
]
}
}
}
```
**Note**: Replace `C:/Users/username/path/to/docs-search-engine` with the actual absolute path to your project directory.
## š” Example Usage
Once the MCP server is connected to your AI assistant (e.g., VSCode, Claude, Cursor, Antigravity), you can use natural language to interact with it.
**1. Search Documentation**
```text
"Search for 'context' in the FastMCP docs."
```
```text
"Find information about 'indexing' in the minsearch docs (https://github.com/alexeygrigorev/minsearch)."
```
**2. Scrape Web Pages**
```text
"Scrape the content of https://example.com/blog/article and summarize it."
```
**3. Count Word Occurrences**
```text
"Count how many times the word 'LLM' appears on https://example.com/ai-trends."
```
## š» Setup & Execution
### Prerequisites
- Python 3.13+
- `uv` installed (recommended)
### Installation
1. Clone the repository and navigate to the directory:
```bash
cd docs-search-engine
```
2. Install dependencies:
```bash
uv sync
```
### Running Locally
To run the server manually for debugging:
```bash
uv run main.py
```
### Testing
Run the comprehensive test suite to ensure everything is working correctly:
```bash
# Run all tests
uv run pytest -v
# Run specific test files
uv run pytest test_search.py -v
uv run pytest test_scrape_web.py -v
# Run only integration tests
uv run pytest -m integration -v
```
TDQS
Scored across 3 tools
scrape_web and count_word_occurrences both involve scraping a URL, so there is slight overlap, but count_word_occurrences is clearly a specialized analysis and search_docs is distinct. An agent can generally select the right tool from the stated purpose.
All three tool names use a consistent verb_noun snake_case pattern (scrape_web, count_word_occurrences, search_docs), making the set predictable and easy to navigate.
Three tools is a small but reasonable surface for a focused toolkit; only one tool actually performs documentation search, which feels slightly lean for a server named docs-search-engine, but no tool is redundant.
The core function of searching a GitHub repository's docs is implemented, but cache management and indexing of arbitrary scraped pages are not supported. These gaps are minor and can be worked around by passing a new zip_url or relying on the automatic index.