websearch-mcp
by ceterizine74
README.md
# websearch-mcp
Локальный MCP-сервер веб-поиска. Без ключей, без платных API, без Docker.
Работает на Windows, macOS и Linux.
> **Лицензия:** PolyForm Noncommercial 1.0.0 — только для некоммерческого
> использования, при распространении и форках обязательны сохранение
> копирайта и текста лицензии. См. [LICENSE](LICENSE). Это **не** open source
> в смысле OSI.
Ротация бесплатных движков (DuckDuckGo → Mojeek → Brave → публичные SearxNG),
чтение страниц в markdown с сохранением блоков кода, обход документации в
пределах одного домена.
## Установка
Нужны Python 3.12+ и [uv](https://docs.astral.sh/uv/).
```bash
cd <путь-до-проекта>
uv sync
```
`lxml` даёт ускорение парсинга страниц, но не обязателен: если на вашей
платформе он не установился, сервер сам переключится на чисто питоновский
`html.parser`.
## Регистрация в Claude Code
Подставьте свой путь к проекту. Примеры для трёх платформ:
**Windows** (PowerShell):
```powershell
claude mcp add websearch -- uv run --project C:\path\to\websearch-mcp python -m websearch_mcp.server
```
**macOS / Linux**:
```bash
claude mcp add websearch -- uv run --project /path/to/websearch-mcp python -m websearch_mcp.server
```
Если в каталоге проекта уже есть `.mcp.json`, можно просто скопировать
`.mcp.json.example` в `.mcp.json` и вписать свой путь — тогда регистрация
не нужна.
## Инструменты MCP
| Инструмент | Что делает |
|---|---|
| `web_search(query, max_results)` | Поиск по вебу: заголовок, URL, сниппет |
| `fetch_page(url)` | Страница чистым markdown |
| `crawl_docs(start_url, depth, max_pages)` | Обход документации в пределах домена |
| `search_and_read(query, max_pages)` | Поиск + чтение топов одним вызовом |
| `cache_stats()` / `cache_clear()` | Статистика и очистка кэша |
## Команды CLI
```bash
websearch search "httpx timeout config"
websearch fetch https://example.com/docs
websearch crawl https://example.com/docs --depth 2
websearch read "httpx timeout config" --pages 3
websearch doctor # какие движки живы прямо сейчас
websearch cache stats
websearch cache prune # удалить только протухшее
websearch cache clear
```
## Ограничения нагрузки
Сервер рассчитан на локальный запуск и не должен ни висеть, ни расти в памяти:
| Ограничение | Значение | Зачем |
|---|---|---|
| Бюджет на один поиск | 30 с | цепочка из 7 хостов с backoff могла крутиться ~6 минут |
| Размер скачиваемой страницы | 8 MB | BeautifulSoup раздувает HTML в памяти ~20× |
| Суммарный объём `crawl_docs` | 300 тыс. знаков | иначе ответ не влезает в контекст клиента |
| Одна страница в markdown | 60 тыс. знаков | то же |
Превысившая лимит страница пропускается с пояснением, а не роняет вызов.
## Поведение при блокировке
Сервер определяет блокировку по содержимому ответа (HTTP 202, слова `challenge` /
`unusual traffic`, ноль распарсенных результатов), а не по коду ответа. При
блокировке — переход на следующий движок с экспоненциальной паузой и постоянным
набором cookie для каждого движка.
Если все движки отказали или кончился бюджет времени, возвращается **честная
ошибка** с причиной по каждому движку, а не выдуманные результаты.
## Кэш
Дисковый, TTL: поиск — 6 часов, страницы — 24 часа. Повторный запрос идёт из
кэша, в сеть не выходит. Протухшие записи вычищаются автоматически раз в
32 записи; вручную — `websearch cache prune`.
Расположение по платформам:
| ОС | Путь |
|---|---|
| Windows | `%LOCALAPPDATA%\websearch-mcp\cache` |
| macOS | `~/Library/Caches/websearch-mcp` |
| Linux | `$XDG_CACHE_HOME/websearch-mcp` или `~/.cache/websearch-mcp` |
Точный путь показывает `websearch cache stats`.
## Безопасность
Сервер скачивает URL, которые ему передаёт MCP-клиент. Содержимое чужой
страницы — недоверенные данные: страница может содержать инструкции для
модели (prompt injection), которые попытаются направить клиента на другой
адрес. Поэтому на стороне сервера:
**Защита от SSRF.** Все загрузки проходят через `netguard.validate_url()`:
заблокированы приватные, loopback, link-local, зарезервированные и
не-глобальные адреса, а также служебные имена (`localhost`,
`metadata.google.internal`, `169.254.169.254` и т. п.). Доменное имя
проверяется **после** резолва, иначе достаточно зарегистрировать
`evil.example` → `127.0.0.1`, чтобы обойти проверку литерала. Обход через
IPv4-mapped IPv6 (`::ffff:127.0.0.1`) тоже блокируется. Нерезолвимое имя —
блокировка (fail closed).
Известное ограничение: проверка идёт до установки соединения, поэтому
атака через подконтрольный DNS с разными ответами на проверку и на
подключение (TOCTOU) теоретически возможна. Полностью она снимается только
закреплением соединения на проверенном IP, чего `httpx` не предоставляет.
Для локального однопользовательского сервера остаточный риск приемлем.
**Чтение локальной документации.** Если вам нужно читать собственные доки на
`localhost:3000`, задайте `WEBSEARCH_ALLOW_LOCAL=1` — это осознанный opt-in,
по умолчанию он выключен. Он разрешает loopback и RFC1918, но **не**
link-local и **не** сервисы метаданных облаков: `169.254.169.254` и
`metadata.google.internal` блокируются всегда, потому что отдают
IAM-креденшелы. Включая флаг, вы принимаете, что вредоносная страница
теоретически сможет направить сервер на ваши локальные сервисы.
**Схемы.** Разрешены только `http` и `https` — `file://`, `gopher://` и
прочее отклоняются.
**Не рендерит HTML.** Страница конвертируется в markdown; `<script>`,
`<iframe>`, `<form>` и подобное вырезаются до извлечения текста. Сервер
ничего не исполняет и не отображает — он не браузер.
**Только локальный запуск.** Транспорт — stdio, сеть не слушается, портов
нет. Не запускайте этот сервер как общедоступный сервис: у него нет
аутентификации, изоляции и лимитов на число вызовов, и он не рассчитан
на работу от имени нескольких недоверяющих друг другу пользователей.
**Нет ключей и телеметрии.** Сервер не хранит секретов, никуда ничего не
отправляет кроме самих поисковых запросов и не ведёт статистику. Кэш лежит
в пользовательском каталоге и содержит только тексты скачанных страниц.
**Что остаётся на вашей стороне:** запросы уходят на публичные поисковики,
поэтому сами формулировки запросов видны этим сервисам. Не ищите через
сервер секреты и не передавайте их в URL.
## Тесты
```bash
uv run pytest
```
Тесты не требуют интернета: сетевые ответы подменяются, резолв DNS — тоже.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues