For the complete documentation index, see llms.txt. This page is also available as Markdown.

Доработка MCP: система плагинов

MCP-серверы поставляются готовыми Docker-образами, внутри которых почти всё скомпилировано. Чтобы их можно было адаптировать под конкретную конфигурацию, компанию и терминологию без пересборки образа, в новых beta-сборках прикладных серверов встроена единая система плагинов.

Плагин — это один Python-файл в каталоге плагинов сервера. Ни базового класса, ни декоратора, ни регистрации, ни манифеста, ни шага сборки. Пустой файл — валидный плагин, который ничего не меняет.

Эта статья и её подстатьи написаны в первую очередь для ИИ-агента (Cursor, Claude Code, e-agent), которому поручили доработать MCP-сервер. Всё, что нужно для написания плагина, лежит внутри образа в читаемом виде: /app/plugin_api.py (полный справочник; у CodeMetadataSearchServer — /app/src/plugin_api.py, у 1CCodeChecker — /app/MCP_1copilot/plugin_api.py), /app/plugins/AGENTS.md (короткая версия) и /app/plugins/example.py (пример со всеми хуками). Документация ниже — карта этих файлов и различий между серверами.

Модель в пяти строках

  • Один плагин — один .py файл в каталоге плагинов. Импорты не нужны, чтобы написать рабочий плагин.

  • Пустой файл — валидный плагин. Хука нет — hook не срабатывает.

  • Применяется только возвращённое значение. Хук получает копию payload: изменить её и забыть return — значит не изменить ничего.

  • Опечатка в имени хука — громкая ошибка, а не тишина. Имя вне объявленного набора отклоняет загрузку файла и печатает список разрешённых имён.

  • Упавший хук не роняет сервер. Исключение перехватывается, пишется в журнал с именем файла, хуком и трассировкой, вызов отвечает так, будто плагина нет.

Какие серверы поддерживают плагины

Сервер
Плагины
Идентификатор продукта
Каталог плагинов
Включение

Да

1c-help

/app/plugins (PLUGIN_DIR)

Всегда включены

Да

ssl-search

/app/plugins (PLUGIN_DIR)

Всегда включены

Да

bsl-syntax-check

/app/plugins (PLUGINS_DIR)

Всегда включены

Да

template-search

/app/plugins (PLUGIN_DIR)

Всегда включены

Да

graph-metadata-search

/app/plugins (GRAPH_PLUGINS_DIRECTORY)

GRAPH_PLUGINS_ENABLED=true

Да

code-metadata-search

/app/plugins (PLUGIN_DIR)

Всегда включены

Да (beta)

onec-code-checker

/app/plugins (PLUGIN_DIR)

Всегда включены

У CodeMetadataSearchServer справочник лежит в /app/src/plugin_api.py, у 1CCodeChecker — в /app/MCP_1copilot/plugin_api.py. Проверка только /app/plugin_api.py даст для них ложное «нет плагинов».

Два слоя: контракт хоста и набор хуков

Система намеренно разделена надвое, потому что обобщается только одна половина:

Слой
Что в нём
Где описан

Контракт хоста

Обнаружение и загрузка файлов, передача аргументов, семантика возврата, изоляция сбоев, порядок выполнения, перезагрузка, эпохи, интроспекция, версии, dry-run

Правила хоста — одинаковы во всех серверах семейства

Набор хуков

Какие хуки существуют, что им передаётся, когда они срабатывают, какие декларативные таблицы принимаются

Справочник хуков по серверам — свой у каждого сервера

Практический вывод: правила («только возврат применяется», «опечатка отклоняет файл», «падение изолируется») выучиваются один раз и работают везде. Различаются только имена хуков и форма payload — их и нужно смотреть в plugin_api.py конкретного образа.

Версионируются слои раздельно: HOST_CONTRACT_VERSION — общий контракт, HOOKS_VERSION — набор хуков конкретного продукта, PRODUCT — сам продукт. Плагин может закрепить их через REQUIRES, и тогда при несовпадении он не загрузится с явным сообщением, где названы обе стороны:

Классификация хуков: чем платят за правку

Каждый хук объявлен одним из двух классов, и это не косметика.

Класс
Что делает
Цена правки

call-scoped

Влияет на один вызов и ни на что, что его переживает

Правка + перезагрузка каталога → действует со следующего вызова. Ничего не пересобирается

derived-state

Формирует то, что сервер строит один раз и переиспользует: индекс, векторную коллекцию, граф

Исходник файла входит в отпечаток годности состояния. Правка делает сохранённое состояние устаревшим, и следующий старт пересобирает его

Что именно пересобирается при правке 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С:Напарнике

Декларативные таблицы вместо кода

Если ответ не зависит от вызова, продукт предлагает не хук, а таблицу — модульную константу, которую сервер читает как данные. Таблица не может упасть, на затронутом ею пути не выполняется никакой код плагина, и её проще проверить глазами. Некорректная запись отклоняет загрузку файла с указанием записи и причины.

Таблица
Серверы
Что делает

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

Код диагностики → причина, по которой она не попадает в отчёт

Жизненный цикл доработки

  1. Прочитать контракт. /app/plugin_api.py в образе нужного сервера (у CodeMetadataSearchServer — /app/src/plugin_api.py, у 1CCodeChecker — /app/MCP_1copilot/plugin_api.py) — полный справочник: payload каждого хука, какие поля применяются, рабочий пример.

  2. Скопировать example.py. В /app/plugins/example.py объявлены все хуки и таблицы продукта, закомментированные и безвредные.

  3. Написать один файл. Имя файла задаёт порядок: файлы выполняются в отсортированном порядке имён, каждый хук — конвейер.

  4. Прогнать dry-run. Одна команда в пустом контейнере: без смонтированных данных, без построенного индекса, без запущенного сервера.

  5. Смонтировать каталог плагинов томом в контейнер.

  6. Перезагрузить и проверить состояние: что загружено, что отключено и почему.

Подробно, с командами для каждого сервера: Как написать плагин.

Что система гарантирует

Плагин выполняется внутри процесса сервера, поэтому система не обещает защитить реализацию от автора плагина. Она обещает другое — что плагин не сможет:

  • уронить сервер — исключение перехватывается, call-scoped хук отключается до следующей перезагрузки;

  • испортить производное состояние — упавший derived-state хук по умолчанию оставляет свою единицу необработанной и считается в сводке сборки, а в строгом режиме роняет сборку, и обслуживающее состояние продолжает отвечать;

  • нарушить опубликованный контракт ответа — пределы измеряются после последнего хука, счётчики пересчитываются из фактически возвращённого, ответ проверяется по схеме, а результат, который её не проходит, отбрасывается в пользу неизменённого;

  • скрыть подмену от вызывающей стороны — если хук изменил аргументы, ответ сообщает и исходные аргументы, и фактически использованные, и имя файла плагина;

  • обойти правила журналирования — секреты (лицензионный ключ, ключ embedding-API, операторский токен, пароль Neo4j) приходят в on_startup заменёнными или в виде булева «задано / не задано»;

  • добраться до внутренностей — в хук передаются только простые данные: примитивы, списки и словари, без ссылок на хранилище, модель, соединение или объект сервера.

Куда дальше

Как написать плагинПравила хостаСправочник хуков по серверамРецептыСерверы без плагинов

Last updated