Доработка MCP: система плагинов
MCP-серверы поставляются готовыми Docker-образами, внутри которых почти всё скомпилировано. Чтобы их можно было адаптировать под конкретную конфигурацию, компанию и терминологию без пересборки образа, в новых beta-сборках прикладных серверов встроена единая система плагинов.
Плагин — это один Python-файл в каталоге плагинов сервера. Ни базового класса, ни декоратора, ни регистрации, ни манифеста, ни шага сборки. Пустой файл — валидный плагин, который ничего не меняет.
Модель в пяти строках
Один плагин — один
.pyфайл в каталоге плагинов. Импорты не нужны, чтобы написать рабочий плагин.Пустой файл — валидный плагин. Хука нет — hook не срабатывает.
Применяется только возвращённое значение. Хук получает копию payload: изменить её и забыть
return— значит не изменить ничего.Опечатка в имени хука — громкая ошибка, а не тишина. Имя вне объявленного набора отклоняет загрузку файла и печатает список разрешённых имён.
Упавший хук не роняет сервер. Исключение перехватывается, пишется в журнал с именем файла, хуком и трассировкой, вызов отвечает так, будто плагина нет.
Какие серверы поддерживают плагины
Да
graph-metadata-search
/app/plugins (GRAPH_PLUGINS_DIRECTORY)
GRAPH_PLUGINS_ENABLED=true
У Graph Metadata Search подсистема плагинов выключена по умолчанию. Без GRAPH_PLUGINS_ENABLED=true каталог не читается вообще, а reload_plugins отвечает, что подсистема отключена.
Два слоя: контракт хоста и набор хуков
Система намеренно разделена надвое, потому что обобщается только одна половина:
Контракт хоста
Обнаружение и загрузка файлов, передача аргументов, семантика возврата, изоляция сбоев, порядок выполнения, перезагрузка, эпохи, интроспекция, версии, dry-run
Правила хоста — одинаковы во всех серверах семейства
Набор хуков
Какие хуки существуют, что им передаётся, когда они срабатывают, какие декларативные таблицы принимаются
Справочник хуков по серверам — свой у каждого сервера
Практический вывод: правила («только возврат применяется», «опечатка отклоняет файл», «падение изолируется») выучиваются один раз и работают везде. Различаются только имена хуков и форма payload — их и нужно смотреть в plugin_api.py конкретного образа.
Версионируются слои раздельно: HOST_CONTRACT_VERSION — общий контракт, HOOKS_VERSION — набор хуков конкретного продукта, PRODUCT — сам продукт. Плагин может закрепить их через REQUIRES, и тогда при несовпадении он не загрузится с явным сообщением, где названы обе стороны:
Классификация хуков: чем платят за правку
Каждый хук объявлен одним из двух классов, и это не косметика.
call-scoped
Влияет на один вызов и ни на что, что его переживает
Правка + перезагрузка каталога → действует со следующего вызова. Ничего не пересобирается
derived-state
Формирует то, что сервер строит один раз и переиспользует: индекс, векторную коллекцию, граф
Исходник файла входит в отпечаток годности состояния. Правка делает сохранённое состояние устаревшим, и следующий старт пересобирает его
Что именно пересобирается при правке derived-state хука:
HelpSearchServer
on_document
Индекс справки: сборка в staging-поколение, текущее продолжает отвечать
CodeMetadataSearchServer
on_source_file, on_chunk, on_metadata_object
Векторные коллекции, SQLite-под-индексы и хранилище объектов метаданных: сборка нового поколения, опубликованное продолжает отвечать
SSLSearchServer
on_entry
Полное переэмбеддирование выбранной базы БСП bases/<версия>.db
TemplatesSearchServer
on_template, on_memory
Векторные коллекции шаблонов и заметок (index_meta.json хранит отпечаток)
Graph Metadata Search
on_source_unit, on_metadata_object, on_routine, on_embedding_document
Проект пересобирается: граф, полнотекстовый и векторный маршруты
SyntaxCheckServer
нет
Ничего: сервер не хранит состояния между вызовами, все хуки call-scoped
1CCodeChecker
нет
Ничего: все хуки call-scoped, анализ выполняется в 1С:Напарнике
Пересборка индекса на полном корпусе — это десятки минут, а на конфигурации — часы. Прежде чем править derived-state хук, убедитесь, что нужного эффекта нельзя добиться call-scoped хуком или декларативной таблицей.
Декларативные таблицы вместо кода
Если ответ не зависит от вызова, продукт предлагает не хук, а таблицу — модульную константу, которую сервер читает как данные. Таблица не может упасть, на затронутом ею пути не выполняется никакой код плагина, и её проще проверить глазами. Некорректная запись отклоняет загрузку файла с указанием записи и причины.
ALIASES
HelpSearchServer, Graph Metadata Search
Дополнительные имена, которые разрешаются в существующие сущности
QUERY_ALIASES
SSLSearchServer, TemplatesSearchServer, CodeMetadataSearchServer
Замены терминов в нормализованном запросе до поиска
TOOL_PRESETS
HelpSearchServer, Graph Metadata Search, CodeMetadataSearchServer, 1CCodeChecker
Новые MCP-инструменты как пресеты существующих с зафиксированными аргументами
CYPHER_TEMPLATES
Graph Metadata Search
Дополнительные read-only Cypher-шаблоны для run_graph_cypher_template
SUPPRESSED_DIAGNOSTICS
SyntaxCheckServer
Код диагностики → причина, по которой она не попадает в отчёт
Жизненный цикл доработки
Прочитать контракт.
/app/plugin_api.pyв образе нужного сервера (у CodeMetadataSearchServer —/app/src/plugin_api.py, у 1CCodeChecker —/app/MCP_1copilot/plugin_api.py) — полный справочник: payload каждого хука, какие поля применяются, рабочий пример.Скопировать
example.py. В/app/plugins/example.pyобъявлены все хуки и таблицы продукта, закомментированные и безвредные.Написать один файл. Имя файла задаёт порядок: файлы выполняются в отсортированном порядке имён, каждый хук — конвейер.
Прогнать dry-run. Одна команда в пустом контейнере: без смонтированных данных, без построенного индекса, без запущенного сервера.
Смонтировать каталог плагинов томом в контейнер.
Перезагрузить и проверить состояние: что загружено, что отключено и почему.
Подробно, с командами для каждого сервера: Как написать плагин.
Что система гарантирует
Плагин выполняется внутри процесса сервера, поэтому система не обещает защитить реализацию от автора плагина. Она обещает другое — что плагин не сможет:
уронить сервер — исключение перехватывается, call-scoped хук отключается до следующей перезагрузки;
испортить производное состояние — упавший derived-state хук по умолчанию оставляет свою единицу необработанной и считается в сводке сборки, а в строгом режиме роняет сборку, и обслуживающее состояние продолжает отвечать;
нарушить опубликованный контракт ответа — пределы измеряются после последнего хука, счётчики пересчитываются из фактически возвращённого, ответ проверяется по схеме, а результат, который её не проходит, отбрасывается в пользу неизменённого;
скрыть подмену от вызывающей стороны — если хук изменил аргументы, ответ сообщает и исходные аргументы, и фактически использованные, и имя файла плагина;
обойти правила журналирования — секреты (лицензионный ключ, ключ embedding-API, операторский токен, пароль Neo4j) приходят в
on_startupзаменёнными или в виде булева «задано / не задано»;добраться до внутренностей — в хук передаются только простые данные: примитивы, списки и словари, без ссылок на хранилище, модель, соединение или объект сервера.
Куда дальше
Как написать плагинПравила хостаСправочник хуков по серверамРецептыСерверы без плагиновLast updated