Первая — Analyzer 1C, статический анализ и граф вызовов конфигураций, он уже доступен в маркетплейсе Infostart. Analyzer отвечает на вопрос «как устроена конфигурация», а герой этой статьи — за то, чтобы у AI-инструментов появился доступ к живой модели конфигурации. И тут мы пришли к решению, которое кажется очевидным только задним числом: MCP-сервер должен жить прямо внутри 1C:EDT — в плагине. EDT открыта — у агента есть доступ к актуальной модели конфигурации на чтение и запись. EDT закрыта — на машине не остаётся ничего: ни процесса, ни службы, ни открытого порта.
В статье — архитектура такого плагина, протокол MCP «на пальцах», система защиты от самодеятельности агента (ни одна правка не проходит молча), встроенная в EDT вкладка Analyzer — граф вызовов и роль метода следуют за кареткой автоматически — и коллекция граблей, на которые мы наступили: закрытые API EDT, ловушка p2/dropins и то, как доставить данные агенту на сервере, не открыв на рабочей станции ни одного порта наружу.
Что такое MCP и при чём тут EDT
MCP (Model Context Protocol) — открытый протокол подключения инструментов к LLM, де-факто стандарт: его понимают Claude Code, Cursor, VS Code Copilot и далее по списку. Идея простая: вы поднимаете «сервер инструментов», клиент спрашивает у него tools/list (что умеешь?) и дальше зовёт tools/call (сделай). Всё — JSON-RPC 2.0 поверх HTTP или stdio.
Для 1С-разработчика это означает: если рядом с EDT есть MCP-сервер, который умеет «прочитай модуль по FQN», «покажи методы», «добавь метод» — любой современный AI-инструмент получает эти способности одной командой подключения, без всякой доработки под 1С.
Вопрос только в том, где этому серверу жить.
Почему именно внутри плагина
Первая версия у нас была «как у всех»: Java-плагин с HTTP-мостом + отдельный Python-шлюз, который транслирует MCP в вызовы моста. Работает. Но посмотрите на это глазами пользователя, которому вы отдаёте дистрибутив:
- на рабочей станции нужен Python (или замороженный exe);
- нужно запускать и держать отдельный процесс;
- процесс нужно обновлять синхронно с плагином;
- служба безопасности спрашивает, что это за процесс и почему он слушает порт.
Каждый пункт — минус желающий попробовать. Поэтому шлюз мы упразднили в тот же день, когда осознали альтернативу: у плагина уже есть HTTP-сервер (embedded com.sun.net.httpserver из JDK — ни одной внешней зависимости), и MCP — это просто ещё один обработчик на нём. Итоговый принцип:
Всё в одном плагине. EDT открыта — MCP есть. Закрыли EDT — на станции ничего нет, кроме jar-файла в dropins.
Плагин поднимает на 127.0.0.1:39721 (порт настраивается) четыре эндпоинта:
| Эндпоинт | Назначение |
|---|---|
POST /rpc |
JSON-RPC мост: module.*, method.*, symbol.rename, editor.open, patch.* |
GET /health |
версия, статус EDT, список поддерживаемых операций |
POST /mcp |
MCP-сервер: initialize / tools/list / tools/call |
GET /mcp |
витрина инструментов — страница для браузера: список инструментов и форма «дёрнуть вручную» |

Витрина на GET — незаслуженно редкий приём. MCP-эндпоинт обычно «немой»: открыл в браузере — получил ошибку парсинга. А тут открываешь http://127.0.0.1:39721/mcp — и видишь живой список инструментов с описаниями и можешь вызвать любой прямо со страницы. Для отладки и для «показать коллеге за 30 секунд» — бесценно.
MCP «на пальцах»: stateless-сервер — это просто
Отдельный миф, который хочется развеять: «реализовать MCP — это подключить SDK и месяц разбираться». Для инструментального сервера достаточно stateless streamable-http диалекта — это четыре метода JSON-RPC:
initialize -> представиться (имя, версия, capabilities)
ping -> пустой ответ
tools/list -> массив инструментов с JSON-схемами аргументов
tools/call -> вызвать инструмент, вернуть content
Плюс одно неочевидное правило: уведомления (запросы без id, например notifications/initialized) требуют ответа HTTP 202 без тела — если вернуть 200 с JSON, часть клиентов сочтёт сервер сломанным.
Ответ tools/call — конверт с массивом content:
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"content": [{ "type": "text", "text": "{ ...результат... }" }],
"isError": false
}
}
Весь наш MCP-слой — один Java-класс-обработчик поверх уже существующего диспетчера моста. Никаких SDK, никаких сессий, никакого SSE. Реестр инструментов — обычная Map, где каждый инструмент описан четвёркой: метод моста, описание, маппинг аргументов, обязательные поля.
11 инструментов: что агент умеет делать в EDT
| Инструмент | Что делает |
|---|---|
edt_health |
мост жив? версия? |
edt_project_active |
открытые проекты 1С: имя, путь, UUID конфигурации |
edt_list_modules |
перечислить модули проекта |
edt_read_module |
исходник модуля по FQN (CommonModule.усАлгоритмы) |
edt_list_methods |
методы модуля: имя, экспортность, параметры, строка |
edt_open_in_editor |
открыть модуль/метод в EDT (навигация для человека) |
edt_add_method |
write: добавить метод в конец модуля |
edt_replace_body |
write: заменить метод целиком |
edt_rename_symbol |
write: переименовать символ по ссылкам |
edt_apply_patch |
закрепить отложенную правку |
edt_drop_patch |
отменить отложенную правку |
Обратите внимание на гранулярность. Мы сознательно не дали агенту «запиши файл по пути» — только операции уровня модели: метод, модуль, символ. Во-первых, LLM гораздо надёжнее оперирует понятиями предметной области, чем смещениями в файле. Во-вторых, это позволяет повесить политику записи на осмысленные единицы (об этом ниже).
И про FQN: в 1С тёзки — реальность (справочник «Номенклатура» и документ «Номенклатура»), поэтому везде, где имя неоднозначно, инструмент принимает kind — тип метаданных. Внутренний ключ у нас всюду имя::тип_метаданных; ключ без типа рано или поздно попадает в ложное совпадение.
Ни одна правка — молча: политика записи
Самый частый вопрос про write-доступ агента: «а он мне конфигурацию не разнесёт?». Ответ должен быть архитектурным, а не «мы ему в промпте написали быть осторожным». У нас защита двухслойная:
Слой 1 — политика записи. Каждая write-операция идёт через режим:
diff(по умолчанию) — правка не применяется, а открывается в штатном Compare-редакторе EDT: слева как было, справа как будет. Человек смотрит и подтверждает. Это тот же UX, что при merge — разработчику знаком.dry_run— правка откладывается в стор сpatch_id; применить её можно отдельным явным вызовомedt_apply_patch(или отбросить).auto— применить сразу. Включается только явно и точечно.
Режимы настраиваются per-проект файлом .edt-mcp/policy.yaml с glob-правилами по FQN — например, «в ОбщийМодуль.ус* можно auto, во всё остальное — только diff». Глобальный колпак может политику лишь ужесточить, ослабить — нет.
Слой 2 — рубильник. Переменная окружения-выключатель write-операций целиком: не выставлена — мост принимает только чтение, что бы ни просил агент и что бы ни разрешала политика.

Безопасность транспорта: loopback и ничего кроме
Мост слушает только 127.0.0.1 — это не настройка по умолчанию, а свойство: наружу порт не открывается вовсе. Дальше два стандартных рубежа:
X-Bridge-Token— заголовок с токеном из Preferences; без него мост отвечает 401.- CORS-whitelist для браузерных потребителей. Нюанс, который стоил нам вечера: у потребителей порты переменные (dev-серверы фронтенда живут где попало), поэтому whitelist поддерживает маску порта — запись
http://127.0.0.1:*разрешает любой порт, но только точный хост. Wildcard*целиком запрещён осознанно.
Установка — запустил батник, и всё встало
Архитектура «всё в одном плагине» окупается в момент установки. В комплекте — один jar и пара скриптов; никакого инсталлятора, служб и «требуется Python 3.x». На Windows: распаковали архив, дважды кликнули install.bat — скрипт сам находит все установленные версии EDT и кладёт jar в их dropins. Перезапустили EDT — в меню Window появился пункт «Effector MCP Bridge»: по умолчанию мост выключен, включается кликом по этому пункту. А если хотите, чтобы он поднимался сам при каждом старте EDT, — в Preferences есть галка «Автозапуск моста». Права администратора не нужны (EDT при стандартной установке живёт в профиле пользователя). Для macOS/Linux — тот же сценарий с install.sh.
Батник не обязателен: весь его смысл — положить jar в папку dropins внутри каталога EDT, так что при желании можно просто перенести файл туда руками и перезапустить EDT.
Проверка — одной строкой:
curl http://127.0.0.1:39721/health
Удаление столь же скучное: uninstall.bat — и в системе не остаётся ничего.
Почему dropins, а не plugins
Коротко: dropins/ и plugins/ в Eclipse — это разные механизмы установки, и dropins/ выбран намеренно.
-
plugins/— это «внутренняя кухня» рантайма Eclipse/EDT. Бандлы там регистрируются через p2 (менеджер установки) и файлconfiguration/org.eclipse.equinox.simpleconfigurator/bundles.info, где для каждого бандла прописан путь и стартовый уровень. Просто скопировать jar вplugins/недостаточно — Eclipse его не увидит, пока он не прописан вbundles.info. А если прописать руками — легко разъехаться с p2-состоянием. -
dropins/— это специально придуманная папка «положил jar → перезапустил → подхватилось». Eclipse при старте сканируетdropins/, сам разбирается с зависимостями и регистрирует бандл. Никаких правок конфигов, никаких прав администратора (при обычной установке EDT в%LOCALAPPDATA%). Ровно то, что нужно клиентскому установщику на двойной клик. По умолчанию ее в EDT нет, надо создать, если хотите руками без bat.
Подключение Claude Code — одна команда
Собственно, ради этого всё и затевалось:
claude mcp add --transport http effector http://127.0.0.1:39721/mcp
После этого в Claude Code доступны все 11 инструментов, и диалог выглядит так: «прочитай ОбщийМодуль.РаботаСЗаказами, найди, где считается скидка, и перепиши расчёт с учётом накопительной программы» — агент сам читает модуль, сам пишет новый метод, а вам в EDT открывается diff на подтверждение.
Cursor и прочие MCP-клиенты подключаются аналогично — указанием URL.

Analyzer прямо в окне EDT: граф вызовов следует за кареткой
MCP — не единственный потребитель моста. Тот же плагин умеет поднимать вкладку Analyzer внутри самой EDT: встроенный браузер (SWT Browser на WebView2), в котором открывается веб-интерфейс Analyzer в embed-режиме. И вот тут мост начинает работать в обратную сторону — не «инструмент лезет в EDT», а «EDT рассказывает инструменту, где сейчас разработчик».
Работает это так. Плагин инжектирует во встроенную страницу объект window.__edtBridge = {url, token} — адрес и токен локального моста. Дальше две синхронизации, обе автоматические, без единого клика:
- Каретка в коде. Ставите курсор внутрь любой процедуры или функции BSL-модуля — вкладка Analyzer тут же показывает граф вызовов этого метода и его роль: кто его вызывает, кого вызывает он, что это за метод в архитектуре конфигурации. Передвинули курсор в соседний метод — граф перестроился следом. Никаких «выделить → ПКМ → показать граф»: ручной пункт меню мы в итоге просто удалили за ненадобностью.
- Дерево метаданных. Выбрали объект в навигаторе EDT — вкладка показывает его окружение в графе: справочник, документ, регистр — с учётом типа метаданных (тёзки, опять же, различаются).
Обратный переход тоже есть: из графа Analyzer — «Открыть в EDT», и мост командой editor.open разворачивает нужный модуль на нужном методе. Получается замкнутая петля навигации: читаешь код → видишь его место в архитектуре → щёлкнул по соседнему узлу графа → оказался в его коде.

Важно: всё описанное — вкладка, автонавигация по каретке и дереву, переходы «граф ↔ код» — работает и с недорогой Community-версией Analyzer, полная версия для этого сценария не требуется.
Вкладка включаемая: по умолчанию выключена (Analyzer есть не у каждого пользователя плагина), включается галкой «Включать окно Analyzer» плюс URL в Preferences. Именно ради этой вкладки в CORS-whitelist моста и появилась маска порта — dev-сервер фронтенда живёт на произвольном порту, а wildcard целиком мы принципиально не разрешаем.
Отдельная задача: агент живёт на сервере, а EDT — на станции
Пока MCP-клиент локальный (Claude Code на той же машине) — всё просто: loopback есть loopback. Но в линейке есть и серверный агент — Pilot 1C, третья часть 1C Intelligence Suite, её «мозг»: LLM-агент, который ведёт задачу от постановки до правки, заземляя свои рассуждения на граф Analyzer и живую модель конфигурации. Он работает в Docker на сервере и общается с пользователем через веб-окно. Как ему достучаться до моста на рабочей станции? Открывать порт наружу — нельзя (см. предыдущий раздел, и служба безопасности снова передаёт привет). VPN/туннель — это опять «поставьте на станцию ещё одну штуку».
Решение — браузерное реле. Единственный, кто легально достаёт и до сервера, и до loopback-моста одновременно — это браузер пользователя, в котором открыто окно агента:
агент (сервер) --событие edt_call--> окно в браузере --fetch--> мост 127.0.0.1
агент (сервер) <--POST /edt-result-- окно в браузере <--ответ-- мост 127.0.0.1
Сервер кладёт запрос в long-poll очередь окна; страница делает fetch к локальному мосту и возвращает результат серверу обычным POST. Оба соединения — исходящие от клиента: станция не открывает наружу ни одного порта, файрволы и NAT довольны, а канал жив ровно столько, сколько открыто окно — что честно отражает физику происходящего: нет пользователя за станцией — нет и «рук».
Из неожиданно важного: write-операции через реле должны ждать дольше (таймаут 10 минут против 45 секунд у чтения) — потому что на том конце человек смотрит diff, а человек думает медленнее HTTP.
Грабли, о которых стоит знать заранее
Это, пожалуй, самая полезная часть — то, чего нет в документации.
1. Закрытые API EDT: не трогать, пока можно
Модельные API EDT (com._1c.g5.v8.dt.*) закрыты и меняются между версиями. Первый рефлекс — добыть модель через IV8ProjectManager/IBmModelManager — мы подавили. Для чтения модулей и списка методов достаточно стабильного Eclipse Resources API: раскладка src/ в проекте EDT детерминирована, модуль по FQN находится по пути, методы — парсингом заголовков Процедура/Функция (на верхнем уровне BSL-модуля вложенных методов не бывает, так что «парсер» — это один регэксп).
Правило, к которому мы пришли: к закрытым API прикасаться только там, где иначе нельзя (reveal к символу через Xtext, формы, СКД) — и всегда с мягкой деградацией: API недоступен → плагин честно возвращает отказ, а не портит артефакт «текстовым способом».
2. Многостраничные редакторы: докопаться до ITextEditor
Задача «узнай, на каком методе стоит каретка» (на ней держится вся автонавигация вкладки Analyzer) упирается в то, что редактор модуля в EDT — многостраничный (DtGranularEditor*), и до настоящего ITextEditor нужно ещё докопаться. Штатный getAdapter(ITextEditor.class) срабатывает не всегда; дальше — перебор известных имён методов-обёрток (getEmbeddedEditor, getXtextEditor, …) через рефлексию, рекурсивно. Некрасиво, но устойчиво: нет метода — идём к следующему кандидату.
И ещё: подписываться на движение каретки надо на StyledText (CaretListener), а не на selection provider — встроенный Xtext-редактор EDT не шлёт selection-события на чистое перемещение курсора.
3. Ловушка p2/dropins: «я обновил jar, а плагин исчез»
Классика, на которой мы потеряли полдня — дважды. Eclipse хранит в bundles.info путь к jar-файлу. Если положить в dropins обновлённый jar под другим именем файла, не изменив Bundle-Version, — p2 не переустановит бандл: старый путь мёртв, новая версия «та же самая», бандл просто исчезает. Симптом: «плагин не видно», в Error Log — тишина.
Правило: подменять содержимое jar в dropins можно только под тем же именем файла, либо поднимать Bundle-Version.
4. Мелочи протокола, которые ломают клиентов
- Уведомления MCP (без
id) → строго HTTP 202 без тела. - В JSON-ответах —
serializeNullsиdisableHtmlEscaping(иначе<и&в исходниках BSL превращаются в кашу, а отсутствующие поля роняют строгие парсеры). - Установщик на Windows — тонкий
.bat, который запускает.ps1(вся логика там: поиск EDT, копирование, русские сообщения). Две грабли с кодировками: кириллица в теле.batломает парсерcmdна клиентских кодовых страницах (cp866/cp1251) — тело.batдержим строго ASCII; а.ps1наоборот — обязателен UTF-8 с BOM, иначе PowerShell 5.1 читает файл как ANSI и кириллица рвёт парсинг.
Что дальше
Текущий плагин закрывает модульный код. Дальше по плану — то, что в 1С болит сильнее всего:
- формы и СКД модельно:
form.addAttribute,dcs.addField— со структурным diff «было/стало» вместо текстового; - rename через Xtext-рефакторинг с распространением по ссылкам;
- связка со статическим анализом: переход «нашёл проблему в графе вызовов → открыл метод в EDT → попросил агента поправить → принял diff».
Выводы
- MCP-сервер для 1С не требует ни SDK, ни отдельного процесса: stateless диалект — это 4 метода JSON-RPC, которые помещаются в один класс внутри плагина EDT.
- «Всё в одном плагине» — не только про удобство: это модель безопасности. Нет процессов — нечего администрировать; EDT закрыта — атаковать нечего.
- Write-доступ агента без diff-подтверждения по умолчанию — безответственно. Политика + рубильник, обе защиты архитектурные, не промптовые.
- Стабильные API прежде закрытых: 80% пользы достигается на Resources API, не трогая
com._1c.g5.v8.dt.*вовсе. - Если серверному агенту нужен доступ к станции — посмотрите на браузерное реле, прежде чем открывать порты наружу или городить туннели.
Плагин — вторая часть линейки Intelligence Suite; первая — Analyzer 1C (статический анализ и граф вызовов), третья — Pilot 1C, серверный LLM-агент, «мозг» линейки. Плагин распространяется за стартмани, а купившим Analyzer — даже Community-версию — достаётся бесплатно; работает он в том числе с Community-версиями остальных инструментов линейки. Если тема интересна — расскажу в следующих статьях подробнее про политику записи и браузерное реле; вопросы и скепсис приветствуются в комментариях.
Плагин собран на EDT Ruby 2025.2.5, на более ранних, вероятнее всего, тоже запустится, если нет - отписывайте в комментах, сделаем под требуемую версию.
Проверено на следующих конфигурациях и релизах:
- 1С:ERP Управление предприятием 2, релизы 2.5.27.58
Вступайте в нашу телеграмм-группу Инфостарт