Custom Framework MCP
MCP-сервер для внутренней документации построенной на основе Markdown-документации. Строит легковесный индекс и предоставляет инструменты для поиска и объяснений.Так же включает crawler, который может скачивать документацию Xafari с официального сайта, извлекать чистый текст и примеры кода.
Зачем нужен MCP
MCP (Model Context Protocol) позволяет IDE и агентам обращаться к локальным даннымкак к «инструментам». Вместо ручного поиска по сайту документации, ассистентвызывает методы search_docs, get_page и get_examples, а сервер отвечаетструктурированными данными. Это ускоряет работу, дает воспроизводимые ответы иснижает зависимость от внешних источников.
Как MCP ускоряет работу:
- Меньше ручного поиска: вместо переходов по сайту и копипасты — один вызов
search_docs/get_page. - Мгновенная выборка: локальный индекс и кэш дают ответы быстрее, чем браузер + поиск.
- Точнее ответы: инструменты возвращают структуру (заголовки, ссылки, код), а не разрозненный текст.
- Повторяемость: один и тот же запрос даёт одинаковый результат — удобно для командной работы.
- Автодогрузка: если страницы нет в кэше, get_page может скачать её автоматически.
- Офлайн-режим: можно работать без доступа к сайту после первичного краула.
Как работает MCP в этом проекте
- Краулер
- Скачивает HTML, извлекает читабельный markdown и сохраняет в
data/pages. - Кэширует страницы и использует ETag/Last-Modified при повторных запусках.
- Из markdown строится индекс
pages.json+index.jsonдля быстрого поиска.
- Скачивает HTML, извлекает читабельный markdown и сохраняет в
- MCP-сервер читает индекс и отвечает на вызовы инструментов через stdio/http.
- IDE подключается к серверу и использует инструменты прямо в чате.
Быстрый старт
- Запустить краулер и собрать локальный индекс:
npm run crawl
- Запустить MCP-сервер (stdio):
npm run start
- Запустить HTTP-режим (опционально):
npm run start:http
- Пересобрать индекс без краулинга:
npm run reindex
Переменные окружения
DOCS_BASE_URL(например:https://documentation.galaktika-soft.com/xafari/)MAX_PAGES_PER_SESSION(по умолчанию:10000)FETCH_ON_MISS(по умолчанию:true, только если заданDOCS_BASE_URL)DATA_DIR(по умолчанию:./data)REQUEST_TIMEOUT_MS(по умолчанию:15000)USER_AGENTLOG_FILE(по умолчанию:logs/mcp.jsonl, путь относительноDATA_DIR)LOG_STDOUT(по умолчанию:false) — еслиtrue, логи дублируются в stdout (удобно в Docker)CODE_LANGUAGES(по умолчанию:cs,js,ts,json,yaml,xml,html,css)HTTP_PORT(по умолчанию:3333)TOOLS_PREFIX— если задана (непустая), инструменты будут иметь имена вида${TOOLS_PREFIX}search_docs,${TOOLS_PREFIX}get_page, ...
Примеры кода
- Примеры сохраняются только для языков из
CODE_LANGUAGES. - Языки нормализуются:
c#/csharp→cs,javascript→js,typescript→ts,yml→yaml. - Если язык не разрешен — блок кода не сохраняется.
Fetch on miss
get_page может автоматически догружать страницу, если ее нет в кэше.Управляется флагом FETCH_ON_MISS (по умолчанию true если задан DOCS_BASE_URL).
Подключение MCP в IDE на примере Cursor
stdio-режим (локальный запуск)
- Откройте настройки MCP в Cursor.
- Добавьте новый сервер со следующими параметрами:
name:custom-framework-mcpcommand:nodeargs:["C:\\Projects\\custom-framework-mcp\\src\\index.js"]cwd:C:\\Projects\\custom-framework-mcp
Пример ~/.cursor/mcp.json
{
"mcpServers": {
"custom-framework-mcp": {
"command": "node",
"args": ["C:\\Projects\\custom-framework-mcp\\src\\index.js"],
"cwd": "C:\\Projects\\custom-framework-mcp"
}
}
}
stdio-режим (через Docker)
Если сервис запущен в Docker, можно использовать docker exec:
{
"mcpServers": {
"custom-framework-mcp": {
"command": "docker",
"args": ["exec", "-i", "mcp-service", "node", "/app/src/index.js"]
}
}
}
HTTP-режим (SSE)
Если сервис запущен в HTTP-режиме (например, через docker compose), используйте SSE transport:
{
"mcpServers": {
"custom-framework-mcp": {
"url": "http://localhost:3333/sse"
}
}
}
Примечание: HTTP-режим требует, чтобы сервис был запущен с npm run start:http или через docker compose (который автоматически запускает HTTP-сервер).
- Перезапустите MCP-сервер в Cursor.
Чтобы проверить в окне чата напишите
list tools
Запросите какую-либо документацию с источника
Запуск через docker compose
git clone https://github.com/QuAzI/custom-framework-mcp.git
cd custom-framework-mcp
docker compose up -d
Запуск через npx
Локально в репозитории:
npm installnpx .— запустит MCP-сервер (stdio) черезsrc/index.js.
Чтобы запускать из любого каталога:
npm linknpx --no-install custom-framework-mcp
Запуск прямо из GitHub (без публикации в npm):
npx github:QuAzI/custom-framework-mcp
Опции краулера:
npm run crawl— по умолчанию скачивает только новые страницы.npm run crawl -- --force— перекачать все страницы.npm run crawl -- --no-only-new— отключить режим "только новые".
Примечание:
--no-only-newделает полный обход с кешем (ETag/Last-Modified), а--forceперекачивает все без учета кеша.
GitLab CI/CD (внешний репозиторий документации → индекс → деплой на VM)
В репозитории есть пример пайплайна .gitlab-ci.yml для сценария:
- скачать документацию из внешнего git-репозитория
- собрать индекс из markdown (
npm run reindex, данные вDATA_DIR/pages) - задеплоить на VM по SSH и перезапустить
docker compose, чтобы HTTP-сервер поднялся с актуальнымиpages.json/index.json
CI/CD variables (настраиваются в GitLab → Settings → CI/CD → Variables)
Переменные для скачивания документации:
DOCS_REPO_URL— URL внешнего репозитория документации (SSH или HTTPS)DOCS_REF— ветка/тег (по умолчанию:main)DOCS_SUBDIR— подкаталог в репозитории документации с markdown-деревом (по умолчанию:docs)DOCS_SSH_PRIVATE_KEY— опционально, SSH ключ для доступа к docs repo (еслиDOCS_REPO_URLпо SSH)DOCS_HTTP_TOKEN— опционально, токен для доступа по HTTPS (используется через~/.netrc)DOCS_HTTP_USER— опционально, логин для~/.netrc(по умолчанию:oauth2, удобно для GitLab)
Переменные для деплоя на VM:
DEPLOY_HOST— хост VM (DNS/IP)DEPLOY_USER— пользователь на VMDEPLOY_PATH— каталог на VM, где лежит сервисDEPLOY_SSH_PRIVATE_KEY— SSH ключ для деплояDEPLOY_SSH_PORT— опционально, порт SSH (по умолчанию:22)DEPLOY_COMPOSE_SERVICE— опционально, имя сервиса compose (по умолчанию:mcp-service)DEPLOY_HEALTHCHECK_URL— опционально, URL для проверки после деплоя (напримерhttp://<host>:3333/health)
Ожидаемая структура на VM (DEPLOY_PATH)
Пайплайн предполагает, что на VM уже есть каталог сервиса с docker-compose.yml и исходниками (а CI обновляет только data/):
DEPLOY_PATH/
docker-compose.yml
package.json
src/
data/ # обновляется из CI (pages/, pages.json, index.json, assets/)
После доставки data.tgz CI выполняет:
docker compose up -d --remove-orphansdocker compose restart mcp-service(чтобы сервер перечитал индекс, т.к. он кэшируется в памяти процесса)
Расписание (Schedule)
Чтобы документация/индекс обновлялись регулярно:
- GitLab → CI/CD → Schedules → Create a new schedule
- Выберите ветку
master - Добавьте нужные variables (например
DOCS_REPO_URL,DOCS_REF,DOCS_SUBDIR)
Job deploy_vm в .gitlab-ci.yml уже настроен так, чтобы запускаться только для master и schedule (и не запускаться в Merge Request pipeline).
MCP-инструменты
search_docs(query, limit?)- Ищет по индексу документации и возвращает список результатов с
title,url,excerpt,headings. limitограничивает количество результатов (1–20, по умолчанию 5).
- Ищет по индексу документации и возвращает список результатов с
get_page(slug | url)- Возвращает полный контент страницы (markdown-текст, headings, codeBlocks, links, breadcrumbs).
slug— короткая форма (например,doc_recursive_helper).url— полный адрес страницы документации.
get_examples(topic, limit?)- Ищет страницы по теме и извлекает фрагменты кода.
- Делает search_docs(topic, limit) по индексу.
- Берёт первые подходящие страницы и вытаскивает их codeBlocks.
- Возвращает список примеров с slug, title, url, code.
limitограничивает количество примеров (1–20, по умолчанию 5).
- Ищет страницы по теме и извлекает фрагменты кода.
explain_concept(name)- Возвращает краткое описание концепта и ссылку на наиболее релевантную страницу.
- Делает search_docs(name, 3).
- Берёт самый релевантный результат и возвращает:
- summary — это excerpt из результата,
- page — основная ссылка,
- related — оставшиеся 1–2 страницы.
- В
relatedдобавляет похожие разделы документации.
- Возвращает краткое описание концепта и ссылку на наиболее релевантную страницу.
stdio-режим
stdio — MCP-сервер общается с IDE через стандартные потоки ввода/вывода. Это нативный режим MCP: быстрее, проще в настройке, без сети и портов. Подходит для локального использования.
Транспорт и формат обмена:
- Общение по stdin/stdout.
- Формат: JSON-RPC 2.0, построчно или через Content-Length.
- Построчно — каждое сообщение это одна JSON-строка, разделенная \n. Сервер читает строки и парсит каждую как отдельный JSON-RPC запрос.
- Через Content-Length — перед сообщением идут заголовки (как в LSP). Сервер сначала читает длину, потом ровно столько байт JSON-тела.
Использовать можно не только из IDE, но и как локальную сервис-утилиту:
- CLI/скрипты: можно запускать сервер и слать ему JSON-RPC из скриптов (например, на CI или для массового прогрева кэша).
- Мост/прокси: stdio удобнее как backend для собственного HTTP-прокси — он проще, чем держать внутри сервера HTTP-слой.
- Интеграции: другой агент/процесс может общаться с MCP через pipe/stdin-stdout, без открытого порта.
- Безопасность: нет открытых портов, меньше требований к сетевой конфигурации.
Примеры stdio-запросов
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_docs",
"arguments": {
"query": "Performance Enhancement"
}
}
}
HTTP-режим
http — режим, в котором MCP-сервер поднимает HTTP-endpoint и принимает запросы по сети. Удобно для внешних клиентов и инструментов (например, HTTPYac), но требует поднять отдельный процесс и порт.
- Сервер поднимается командой
npm run start:http(портHTTP_PORT, по умолчанию3333). - Каждый инструмент доступен через
POST /tools/{toolName}с JSON-телом аргументов. - Для проверки доступен
GET /health.
Примеры HTTP-запросов
Подходят для инструментов типа HTTPYac, плагинов HTTP Request в IDE.
POST http://localhost:3333/tools/search_docs
Content-Type: application/json
{
"query": "Как работает модуль Performance Enhancement?",
"limit": 5
}
POST http://localhost:3333/tools/search_docs
Content-Type: application/json
{
"query": "подключение к DevExpress XAF",
"limit": 5
}
POST http://localhost:3333/tools/get_page
Content-Type: application/json
{
"slug": "doc_recursive_helper"
}
POST http://localhost:3333/tools/get_page
Content-Type: application/json
{
"url": "https://documentation.galaktika-soft.com/xafari/doc_recursive_helper"
}
POST http://localhost:3333/tools/get_examples
Content-Type: application/json
{
"topic": "Business Components",
"limit": 5
}
POST http://localhost:3333/tools/explain_concept
Content-Type: application/json
{
"name": "Security System"
}
Формат хранения
- Сырые страницы сохраняются в
data/pages/*.mdс метаданными в заголовке. pages.jsonформируется из markdown-файлов после завершения краулинга (по умолчанию это NDJSON: один JSON-объект на строку, чтобы файл можно было читать потоково даже при больших объёмах).- При сохранении учитываются breadcrumbs: страницы попадают в поддиректории по темам.
- Ассеты (PDF/картинки) сохраняются в
data/assets, ссылки в markdown остаются абсолютными.
Ручное пополнение документации
- Создайте
.mdвdata/pages(подкаталоги = breadcrumbs). - Добавьте YAML-front-matter с минимумом полей:
--- slug: my_custom_doc url: https://example.local/my_custom_doc title: Мой документ breadcrumbs: - Custom - Docs --- - Добавьте тело документа ниже фронт-маттера.
- Пересоберите индекс без краулинга:
npm run reindex
Логи
Структурированные логи пишутся в data/logs/mcp.jsonl (JSON Lines) при DATA_DIR=./data и дефолтном LOG_FILE=logs/mcp.jsonl.