MCP-сервер и редакторы
Работа с elemctl из ИИ-агента по MCP, плюс плагины и расширение VS Code.
MCP-сервер
Сервер отдаёт операции платформы как MCP-инструменты (транспорт stdio):
pip install "elemctl[mcp]"
claude mcp add elemctl -- elemctl mcp
Реквизиты подключения сервер берёт из тех же переменных ELEMENT_* / .env.
Инструменты
| Инструмент | Что делает |
|---|---|
list_apps |
список приложений; удалённые скрыты, пока их не попросят (include_deleted), name фильтрует по подстроке имени на клиенте, status – по статусу целиком; в ответе счётчики и строка summary рядом с applications, brief (по умолчанию) оставляет от карточки id, имя, статус, uri и применённую версию |
find_app |
найти приложение по точному имени: id и признак found; удалённые пропускаются, если не задан include_deleted |
get_app |
карточка приложения: статус, uri, фактическая версия проекта; в applied-build – ветка и коммит сборки, на которой оно работает, из карточки сборки или из локального реестра загрузок |
create_app |
создать приложение; по одному project_id источником берётся последняя сборка проекта. В ответе sign-in – способ войти; verify дожидается приложения и проверяет, на какой сборке оно правда работает |
ensure_app |
создать приложение по имени, только если его ещё нет; существующее не пересоздаётся (created: false); verify проверяет, на какой сборке приложение правда работает |
start_app |
запустить приложение |
stop_app |
остановить приложение |
delete_app |
удалить приложение. Отменить это нельзя: данные теряются, а пересозданное получит другой URL |
list_app_tasks |
задачи приложений; app_id – необязательный фильтр |
debug_info |
данные сессии отладки: debug-token и debug-address (нужна включённая отладка на сервере) |
list_spaces |
список пространств |
list_projects |
список проектов; name фильтрует по подстроке имени на клиенте, удалённые скрыты, пока не задан include_deleted; brief (по умолчанию) – id, имя, вид проекта, пространство, счётчик приложений, признак удаления |
list_builds |
сборки проекта, свежие первыми; ответ – объект {total, shown, summary, builds}; limit (по умолчанию 10, 0 – все), brief (по умолчанию) оставляет ид, версии, дату, ветку и коммит и называет, откуда взяты ветка и коммит: из карточки или из локального реестра загрузок |
get_build |
карточка одной сборки целиком; version – это версия сборки (1.0-42), ид тоже принимается и разрешается по перечню |
build_assembly |
локально собрать архив .xasm/.xlib из исходников (к платформе не обращается) |
inspect_assembly |
разобрать готовый архив: манифест, свойства проекта, подсистемы и глобальные типы с полными именами (локально) |
deploy |
полный цикл из исходников с проверкой того, что сборка применилась; итог – поле ok, детали – problems и log; allow_data_loss пропускает то, что отклоняет сторож схемы (сужение, смену типа, снятый целиком справочник), как --allow-data-loss; сервер, который ещё стартует, инструмент пережидает не дольше server_start_timeout секунд (по умолчанию 900); необязательные expected_app_name / expected_app_uri отклоняют неверную цель до сборки |
probe |
проверить компиляцию серверным компилятором, не трогая рабочее приложение; ошибки с файлом, строкой и колонкой, за собой убирает; с keep отчёт называет команду, которая уберёт оставленное |
probe_cleanup |
убрать пробник, оставленный на стенде ключом keep или оборвавшейся уборкой, по его приложению: приложение, сборку пробника в его собственном проекте и проект, если в нём ничего не осталось; пробник со своими именем и версией узнаётся по локальному реестру загрузок, он же называет сборки, выкаченные в пробник; чужое приложение отклоняется; app_id может быть списком, и тогда пробники убираются по очереди, у каждого свой отчет, а отказ одного не останавливает остальных |
apply_build |
применить загруженную сборку к приложению по её ид |
verify_deploy |
проверить фактическое применение: задачи с ошибками, сверка примененной сборки (сборки расширения - по расширениям приложения), доступность uri |
list_user_lists |
списки пользователей; name фильтрует по подстроке представления |
configure_user_list |
самостоятельная регистрация и вход по паролю; без флагов только показывает состояние |
list_app_users |
кто подключён к приложению: список пользователей, ид, представление, администратор ли и есть ли доступ по токену, со строкой итога; логина платформа здесь не отдаёт |
token_access |
доступ пользователя к HTTP-сервисам приложения по токену – тот признак, без которого вызов получает 500 “Token access is denied”; показывает его, а с enabled переключает и перечитывает; пустой user – учётная запись, под которой работает elemctl; user может быть списком, и тогда у каждого пользователя своя запись, а сбой одного не останавливает остальных |
export_app |
записать в файл сборку, на которой работает приложение; расширений, примененных в приложении, в архиве нет, их записывает export_extension; пустой output – текущий каталог сервера и имя {Имя} {Версия}.xasm; сервер без этого метода получает понятный отказ |
export_extension |
записать в файл сборку расширения, примененного в приложении (Console API 2.1); extension – ид расширения, ид его проекта, имя или представление проекта, а промах называет расширения, которые в приложении есть; пустой output – текущий каталог сервера и имя {Имя} {Версия}.xasm; сервер без этого метода получает понятный отказ |
list_branches |
список веток среды разработки; фильтры project_id и name необязательны |
merge_branch |
принять изменения ветки среды разработки |
debug_adapter |
путь к debug-адаптеру платформы из плагина; отсутствие плагина – это ответ (found: false), а не ошибка (локально) |
Рядом с ними встают инструменты, которые приносят плагины, о них ниже.
Одним окружением дело не ограничено. У каждого инструмента, который обращается
к платформе, есть необязательный env_file – путь к .env другого стенда. Так
один сервер работает и с облаком, и с локальной установкой, а перезапускать его
с другими реквизитами не нужно.
list_apps по умолчанию отдаёт краткие карточки: id, имя, статус, uri и
применённую версию. Полные карточки пространства – это десятки тысяч символов в
ответе агенту, за ними ходят с brief=false. Параметр name фильтрует по
подстроке имени без учёта регистра и делает это на клиенте, потому что платформа
query-параметр игнорирует. Параметр status фильтрует по статусу целиком.
Удалённые приложения скрыты, пока их не попросят через include_deleted: на
стенде, живущем не первый месяц, в перечне сотни карточек, из которых живых
единицы. Молча ответ не режется. В нём лежат счётчики total (сколько карточек
отдала платформа), live (сколько из них не удалено) и shown, готовая строка
summary вида “живых 7 из 324”, а сами карточки – в applications.
list_projects ведёт себя так же и отдаёт id, имя, вид проекта, пространство,
счётчик приложений и признак удаления. name у него тоже фильтрует по подстроке,
а проекты с признаком удаления скрыты, пока не задан include_deleted: на стенде,
живущем не первый месяц, их в перечне сотни против единиц живых.
list_builds тоже отвечает краткими карточками – ид, версии, дата, ветка,
коммит – и десятью свежими сборками, пока limit не скажет иного; значение 0
снимает срез. Карточки лежат в builds, рядом с ними счётчики total (сколько
сборок отдала платформа) и shown, а также строка summary. Перечень платформы –
не вся история проекта: платформа сама удаляет сборки, которыми
никто не пользуется, когда приложение проекта заканчивает применение сборки, и возраст
тут ни при чем. Остаются сборка работающего приложения, сборка с наибольшим номером
базы, релизная и загруженные без ид проекта, среди них первая сборка проекта. Страниц у перечня нет, и summary прямо
говорит, что перед читателем остаток истории. Считает эта строка по фактам
ответа: номера сборок одной базовой версии платформа выдаёт подряд, поэтому
номер, которого в перечне нет, – это сборка, уже снятая уборкой, если это не скачок.
Сборка, загруженная без ид проекта, сохраняет номер архива, и счет идет от
него; пропуск под такой сборкой этой машины summary называет скачком, а не
удалением. Пропуск, внутри которого есть загрузка этой машины в проект, – и скачок, и
удаление сразу, и summary называет и то и другое. Проект-расширение уборка не
трогает вовсе: его сборки остаются, пока их не удалят вручную, пропуск там и есть
такое удаление, и summary так и говорит.
get_build отдаёт одну карточку целиком и адресуется версией сборки. Ид тоже
принимается и находится по перечню, потому что адресом метод считает версию, а не
ид карточки.
get_app, delete_app, start_app, stop_app и debug_info принимают ид
приложения (UUID) либо его точное имя. Значение, не похожее на UUID, разрешается
по списку. Несколько совпадений дают ошибку, а не догадку.
create_app и ensure_app добавляют к ответу поле sign-in – адрес и учётную
запись, которой входят в только что созданное приложение. Запись эта от панели
управления, учётные записи других приложений там не работают, а агент видит один
только JSON. Параметр verify у обеих команд дожидается приложения и проверяет,
что оно правда работает на запрошенной сборке: неудачное применение платформа
откатывает молча, а созданное приложение раньше считали готовым на веру. Отчёт
попадает в поле verify ответа. Ожидание стоит минут, поэтому такой вызов лучше
запускать фоновой командой elemctl. Оборванное ожидание приложение не теряет:
ответ сохраняет его ид, а поле wait-error говорит, что оборвалось.
version_id до создания ищется в перечне сборок проекта. Проект – project_id, а
при пустом параметре – ELEMENT_PROJECT_ID стенда. Сборку, которой в этом проекте
нет, инструмент ищет в других проектах стенда: сборка, загруженная без ид проекта,
лежит в проекте, который ей выбрал сервер. Проект стенда уступает проекту сборки, а
явный project_id получает отказ, который называет проект, где сборка лежит.
Сборку, которой нет ни в одном проекте, инструмент отвергает до создания: платформа
удаляет сборки, которыми никто не пользуется, и на создание из удаленной сборки
отвечает голым 400. Отказ называет эту причину и сборку, на которой работает
приложение проекта.
Плагины
elemctl подхватывает внешние пакеты через точки расширения importlib.metadata.
В своём pyproject.toml он о плагинах ничего не объявляет и читает их при
обращении. Так непубликуемые вендорские артефакты живут отдельным пакетом, а ядро
elemctl остаётся чистым и общедоступным.
elemctl.debug_adapter: пакет-плагин объявляет каталог debug-адаптера
платформы. Это проприетарные jar 1С, в состав elemctl они не входят. Значением
точки расширения бывает путь либо функция без аргументов, возвращающая путь. Путь
указывает на каталог с подкаталогом repo/, где и лежат jar-файлы адаптера.
elemctl.commands: пакет-плагин приносит собственные команды. Значением точки
расширения бывает Command, их список либо функция без аргументов, возвращающая
одно из двух. Одного объявления хватает на обе поверхности: elemctl строит по нему
подкоманду CLI и инструмент MCP с настоящей схемой, а о том, что команда делает,
ничего не знает. Там и место команде, которая знает про ваше окружение: внутренние
контуры, смежные системы, ваши стенды. В публичном ядре такой команде жить нельзя.
# pyproject.toml пакета-плагина
[project.entry-points."elemctl.debug_adapter"]
имя = "мой_пакет:adapter_root" # () -> Path на каталог, содержащий repo/
[project.entry-points."elemctl.commands"]
имя = "мой_пакет.commands:commands" # () -> list[Command]
# мой_пакет/commands.py
from elemctl.plugins import Argument, Command
def warm_up(context, stand="", force=False):
context.log(f"прогреваем {stand}") # прогресс: stderr в CLI, поле log в MCP
# context.surface называет, кто позвал: "cli" или "mcp"
card = context.client.get_app(stand) # клиент строится при первом обращении
return {"ok": True, "status": card.get("status")}
def commands():
return [Command(
name="warm-up",
help="открыть админку свежего стенда",
handler=warm_up,
arguments=[Argument("--stand", help="приложение"), Argument("--force", type=bool)],
)]
Результат обработчика обязан быть JSON-сериализуемым: CLI его печатает, инструмент
MCP возвращает. Из результата CLI берёт и код возврата. Целое от 0 до 255 в поле
exit-code становится кодом возврата как есть, и команда с тремя исходами отдаёт
сценарию “различий нет”, “есть различия” и “сбой шага” кодами 0, 1 и 2. Без этого
поля словарь с "ok": false завершается кодом 1, по тому же соглашению, что у
отчётов deploy и probe. Когда поле расходится с ok, решает поле. Строку, true
или 300 CLI кодом не считает и смотрит на ok. Инструмент MCP возвращает поле вместе
с остальным результатом. Вызывать sys.exit в обработчике ради кода нельзя: та же
функция работает внутри сервера MCP, где вызов остался бы без ответа, а сервер
остановился бы. Типы аргументов – str, int, float и bool для флага. Поле
env_file к инструменту MCP добавляет сам elemctl, поэтому команда плагина
дотягивается до других окружений так же, как инструменты ядра. Занять имя, которое
уже есть у ядра, команда не может: такая команда остаётся за бортом. За бортом
остаётся и плагин, который не загрузился, например написанный под более новое ядро.
elemctl называет такие плагины в stderr и в elemctl plugins и работает с остальными.
Поле context.surface говорит, откуда пришел вызов: "cli" у подкоманды и "mcp"
у инструмента. Команда, которая отвечает на двух поверхностях по-разному, например
кратко в инструменте и целиком в терминале, берет ответ оттуда, а не угадывает по
поведению context.log. В старом ядре такого атрибута нет, поэтому читайте его как
getattr(context, "surface", None).
Позиционный аргумент может завести синоним-ключ, который действует только в CLI:
Argument("page", cli_alias="--page") принимает и elemctl wiki-get 123, и
elemctl wiki-get --page 123. Схема инструмента MCP от этого не меняется –
у неё как был, так и остаётся один параметр page: cli_alias меняет только то,
что принимает разборщик CLI, и никак не затрагивает объявленные аргументы. Обе
формы взаимно исключают друг друга: разборщик отказывает и при обеих сразу, и при
отсутствии обеих у обязательного аргумента. Плагин, не объявивший cli_alias,
ведёт себя как раньше.
Опция может принимать несколько значений: Argument("--file", multiple=True)
принимает elemctl wiki-attach --file a.png --file b.png, и обработчик получает
["a.png", "b.png"] в порядке командной строки. Без ключа он получает объявленное
умолчание списком либо []. Инструмент MCP получает параметр-массив объявленного
типа. Опция без multiple принимает одно значение, и повтор с другим значением
CLI отвергает, как у любого ключа с одним значением. Объявляйте multiple там,
где пользователь повторит ключ. Флаг
повторяемым быть не может, а умолчание повторяемого аргумента – список или None. В
старом ядре такого поля нет, и плагин остается за бортом целиком, поэтому плагин,
которому приходится работать с обоими ядрами, сначала проверяет
hasattr(Argument, "multiple").
Несколько значений может принимать и позиционный аргумент:
Argument("page", multiple=True, cli_alias="--page") принимает и
elemctl wiki-get 123 456, и elemctl wiki-get --page 123 --page 456, а обработчик
в обоих случаях получает ["123", "456"]. Формы по-прежнему исключают друг друга,
так что значения одного вызова приходят из одной формы. С required=True аргументу
нужно хотя бы одно значение, а необязательный без значений передает объявленное
умолчание списком либо []. Инструмент MCP получает тот же параметр-массив, что и у
опции, а второго параметра для ключа у него нет. Старое ядро, которое уже знает
multiple, принимает его только у опции: повторяемый позиционный аргумент там –
PluginError, и плагин остается за бортом целиком. Проверка
hasattr(Argument, "multiple") на таком ядре тоже проходит, поэтому плагин, которому
приходится работать с обоими ядрами, читает признак из elemctl.plugins:
getattr(plugins, "POSITIONAL_MULTIPLE", False) истинен только там, где позиционный
аргумент может быть повторяемым.
# путь к адаптеру от установленного плагина (для расширения VS Code):
# {"path": "...", "found": true} либо {"path": null, "found": false}
elemctl debug-adapter
# что приносят плагины - каталоги адаптера и команды
elemctl plugins
Сам адаптер, те самые проприетарные jar 1С, достаёт из дистрибутива платформы
скрипт tools/extract_adapter.py. Полученный каталог потом указывают вручную в
xbsl.debug.adapterPath либо собирают из него пакет-плагин. В дистрибутив пакета
скрипт не входит.
Обнаружение плагинов отключает переменная ELEMCTL_NO_PLUGINS=1: тогда работают
только штатные возможности ядра.
VS Code
С редактором elemctl связывает расширение-спутник:
- XBSL (проект
xbsl) – подсветка, линтер, конструктор форм и дерево
метаданных. Кнопка
XBSL: деплой на стендзапускаетelemctl deployтерминальной задачей и проверяет применение. Там же отладка приложений 1С:Элемента штатным DAP-адаптером платформы, данные сессии для которой даётelemctl apps debug. Раньше отладка была отдельным расширением XBSL Debug в репозитории elemctl. С версии XBSL 0.57 она часть одного расширения, а в репозитории осталась только сторона elemctl.
Оно публикуется и в Open VSX.