corpus-mcp
corpus-mcp
Локальный MCP сервер, который предоставляет агенту чистый и эффективный доступ к локальному корпусу знаний из офлайн-ZIM-архивов — Википедия, медицинская вики (MDWiki), документация разработчика (DevDocs) и Stack Exchange — через единый интерфейс. Без интернета, без эмбеддингов, без векторной базы данных: полнотекстовый поиск libzim плюс детерминированная очистка контента на стороне сервера.
Публичный интерфейс MCP — ровно два инструмента:
search(query, limit?)
fetch(ref, sections?)Настроенный корпус — это забота оператора, а не агента. Агент только:
discover → search()
select → fetch()Семейства корпусов
Корпус |
| Документ | Модель секций |
Wikipedia, MDWiki |
| статья | дерево заголовков (h2+), вводная секция с id |
DevDocs (C, CMake, Python) |
| страница документации | дерево заголовков; внутристраничные оглавления и навигационные элементы удаляются |
Stack Exchange |
| вопрос + ответы | синтетические секции: |
Вся идентификация корпуса, маршрутизация, доступ к ZIM, интерпретация HTML, очистка, ранжирование, обработка редиректов и нормализация остаются обязанностями сервера. Агенту никогда не требуется разбирать HTML, обрабатывать редиректы, конструировать или разбирать ссылки, а также знать что-либо о libzim, пространства имён ZIM или внутреннем устройстве хранения корпуса.
Related MCP server: mcpzim
Ссылки
Результаты search() содержат непрозрачный ref (например, corpus://Wikipedia/Bell_test); fetch() использует его. Агент никогда не должен создавать, разбирать или изменять ref или выводить корпус из него:
search() produces ref fetch() consumes refАрхитектура
Local agent
│ MCP / Streamable HTTP → http://127.0.0.1:8000/mcp
▼
┌──────────────────────────────────────────────┐
│ Corpus MCP Server │
│ search() fetch() │
│ ├─ CorpusManager (routing, cache, │
│ │ bounded-concurrency fan-out) │
│ ├─ federated ranking (RRF + lexical title │
│ │ reranking + diversity) │
│ ├─ adapters: mediawiki / devdocs / │
│ │ stackexchange │
│ ├─ HTML cleaner → Markdown, section trees │
│ └─ GlobalRef codec (opaque refs) │
└─────────────┬────────────────────────────────┘
▼
per-library ZIM service (only libzim touchpoint,
one search lock per archive)
▼
corpus/ (read-only volume, N .zim archives)
corpus.toml (manifest: name, adapter, path)Слой MCP не раскрывает никаких концепций libzim: никаких пространств имён, идентификаторов кластеров, сырых записей, MIME-типов или сырого HTML.
Предварительные требования
Docker + Docker Compose
Архивы ZIM (см. ниже)
Для запуска набора тестов локально: Python 3.12 и
uv(или pip)
Расположение корпуса
Сервер никогда не скачивает архивы сам — сбор корпуса намеренно отделен от запуска приложения. Структура по умолчанию:
corpus/
wikipedia/wikipedia_en_all_nopic_*.zim
medical/mdwiki_en_all_maxi_*.zim
devdocs/devdocs_en_cpp_*.zim
devdocs/devdocs_en_cmake_*.zim
devdocs/devdocs_en_python_*.zim
stackexchange/stackoverflow.com_en_all_*.zim
stackexchange/security.stackexchange.com_en_all_*.zim
stackexchange/softwareengineering.stackexchange.com_en_all_*.zim
corpus.tomlcorpus.toml описывает каждую библиотеку, ее адаптер и путь (относительно корня корпуса):
version = 1
[[library]]
name = "Wikipedia"
path = "wikipedia/wikipedia_en_all_nopic_2026-06.zim"
adapter = "mediawiki"
[[library]]
name = "CMake-Docs"
path = "devdocs/devdocs_en_cmake_2026-08.zim"
adapter = "devdocs"Правила проверки: уникальные имена, известные адаптеры, пути должны оставаться внутри корня корпуса. Проверьте корпус перед запуском сервера:
make validate-corpus # opens every archive, reports metadata
make corpus-list # list configured librariesЗапуск / остановка
make start # build + start (docker compose, detached)
make logs # tail logs
make ps # container status
make stop # stop (keep containers)
make down # stop + remove
make restart
make buildПосле этого конечная точка MCP доступна по адресу http://127.0.0.1:8000/mcp (Streamable HTTP). Порт хоста по умолчанию привязан только к loopback; контейнер слушает адрес 0.0.0.0:8000 внутри.
Если какой-либо указанный ZIM не может быть открыт, сервер не запускается и указывает проблемную библиотеку — частично работоспособного режима не существует.
Схемы инструментов
search(query: str, limit?: int)
Ищет полнотекстовый индекс каждой настроенной библиотеки (ограниченная параллельность, один рабочий процесс на архив), объединяет ранжированные списки с помощью Reciprocal Rank Fusion, упорядочивает конкурирующих кандидатов из разных корпусов по лексическому совпадению заголовков, применяет детерминированную логику разнообразия и возвращает чистые результаты. limit по умолчанию равен 5; сервер накладывает жёсткий максимум (SEARCH_MAX_LIMIT, по умолчанию 10).
{
"results": [
{
"ref": "corpus://Wikipedia/Bell_test",
"library": "Wikipedia",
"kind": "article",
"title": "Bell test",
"snapshot": "2026-06",
"snippet": "To close the detection loophole, an apparatus with a high detection efficiency is needed.",
"relevant_sections": [
{ "id": "Notable_experiments", "title": "Notable experiments" },
{ "id": "Loopholes", "title": "Loopholes" }
]
}
]
}ref— непрозрачный глобальный идентификатор; передайте его обратно вfetch().library/kind/snapshot— происхождение: какой архив, какой документ, и снимок корпуса (полученный из метаданных архива).relevant_sections— 0–3 детерминированных лексических подсказки (пусто, если ни одна секция явно не соответствует). Идентификаторы секций генерируются сервером; агент не должен их восстанавливать.
Одна неисправная библиотека ухудшает качество поиска (остальные по-прежнему отвечают), но никогда не останавливает его.
fetch(ref: str, sections?: list[str])
Возвращает очищенный документ в виде структурированного Markdown.
Без параметра
sections: весь документ (ограниченMAX_FETCH_CHARS;truncated: true, если обрезан на границе секции).С параметром
sections: только указанные секции (включая вложенные). Идентификаторы секций берутся изsearch()подсказок или изavailable_sections. Вводная секция имеет идентификатор"". Для тем секции —question,accepted-answerиanswer-<id>; их полеmetadataсодержит оценку, флаг «принято» и теги.
{
"ref": "corpus://Wikipedia/Bell_test",
"library": "Wikipedia",
"kind": "article",
"title": "Bell test",
"snapshot": "2026-06",
"sections": [
{ "id": "Loopholes", "title": "Loopholes", "content": "## Loopholes\n\n..." }
],
"available_sections": [
{ "id": "", "title": "Bell test" },
{ "id": "Background", "title": "Background" },
{ "id": "Loopholes", "title": "Loopholes" }
],
"truncated": false
}Ошибки — краткие и конструктивные:
{ "error": "invalid_ref", "message": "invalid reference: ..." }
{ "error": "not_found", "message": "Document not found in Wikipedia: Foo_bar" }
{
"error": "section_not_found",
"missing_sections": ["Experiments"],
"available_sections": [ { "id": "Loopholes", "title": "Loopholes" }, "..." ]
}Пример рабочего процесса агента
search("Bell experiment loopholes")
↓
fetch("corpus://Wikipedia/Bell_test", ["Notable_experiments", "Loopholes"])Конфигурация
Переменные окружения (показаны значения по умолчанию в контейнере):
Variable | Default | Meaning |
|
| Корень корпуса внутри контейнера (обязательно) |
|
| Путь к манифесту внутри контейнера (обязательно) |
|
| Адрес прослушивания внутри контейнера |
|
| Порт прослушивания внутри контейнера |
|
| Значение |
|
| Жёсткий максимум для |
|
| Бюджет размера вывода для полученного контента |
|
| Параллельный поиск по архивам при рассылке |
|
| Логика разнообразия: максимум подряд идущих результатов из одной библиотеки |
|
| Записывать текст поискового запроса (приватность) |
|
| Выполнять полную проверку контрольных сумм libzim при старте (затрагивает весь корпус: опционально, медленно для больших архивов) |
Переменные Compose на стороне хоста: CORPUS_ROOT (по умолчанию ./corpus) и CORPUS_CONFIG (по умолчанию ./corpus.toml).
Сервер быстро завершится при недопустимой конфигурации.
Тесты
make test # unit + integration + MCP surface tests (needs .venv)
make lint
make formatНастройка для локального запуска тестов:
uv venv .venv --python 3.12
uv pip install -e . --python .venv/bin/python
uv pip install --python .venv/bin/python pytest pytest-asyncio ruff
make testТесты создают небольшие самостоятельные ZIM-файлы с помощью writer из libzim (по одному на семейство корпусов); внешний корпус не требуется. Регрессионный тест поверхности MCP проверяет, что сервер предоставляет ровно два инструмента — search и fetch — и никаких prompts или resources.
Модель безопасности
Сервис локальен изначально: привязка к хосту по умолчанию только к loopback, тома корпуса только для чтения, контейнер запускается от непривилегированного пользователя, без привилегированного режима, без Docker-сокета, без произвольного файлового доступа, без извлечения URL, без выполнения команд оболочки. Ни один инструмент не принимает файловые пути, URL, команды или исполняемое содержимое — ref является лишь непрозрачным идентификатором корпоративной базы.
This server cannot be deployed
Maintenance
Related MCP Connectors
Shared, peer-validated knowledge archive for AI agents — search, contribute, and validate via MCP
Agentic search over your Dewey document collections from any MCP-compatible client.
Scrape, crawl and search the web for AI agents via MCP.
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Related MCP Servers
- AlicenseAqualityAmaintenanceEnables AI models to access and search offline Wikipedia and other knowledge bases stored in ZIM format files. Provides intelligent content retrieval, structured browsing, advanced search capabilities, and metadata extraction for comprehensive offline knowledge access.8185 PyPI145MIT
- AlicenseAqualityBmaintenanceAn MCP server that provides offline access to ZIM file archives, including Wikipedia, medical knowledge, and maps. It dynamically exposes tools like search, article retrieval, and driving route planning based on available ZIM files.4MIT
- AlicenseNot gradedqualityDmaintenanceEnables offline CRUD and semantic search on Wikipedia ZIM archives via MCP tools for reading, writing, editing, deleting, and searching articles.1MIT
- AlicenseNot gradedqualityDmaintenanceProvides offline search and retrieval of Wikipedia articles using Kiwix .zim files, enabling LLMs to access full Wikipedia content without internet.2MIT