26 сентября я прогнал проверку по тринадцати подключениям MCP (Model Context Protocol, протокол, по которому нейросеть в чате вызывает внешние системы) к базам 1С, и три из них не работали вовсе. Одна база отвечала кодом 409, вторая 502, третья молчала десять секунд, а через несколько минут отвечала быстрее секунды. Ещё у двух работала только половина: список инструментов модель видела, а вызовы падали. Индикатор подключения в приложении при таких поломках остаётся зелёным. И во всех пяти случаях сам MCP был исправен, ломалось ниже.
Это и есть тезис, с которым я готов спорить: когда MCP к базе 1С "не подключается", чинить почти всегда надо веб-публикацию, а не MCP. Переставлять Node, пересобирать образ Docker с сервером и менять версию клиента в этот момент бесполезно. HTTP-сервис 1С отвечает кодом, код однозначно говорит, на каком этаже сломано, а этажей там шесть, и у каждого своя дверь.
Ниже лестница кодов, которую я собрал за полтора месяца на своих подключениях, и маленький скрипт, который проходит её сам и останавливается на сломанной ступени. Данных в базе он не меняет и произвольный код не выполняет: только GET-запросы и рукопожатие MCP.
Что спрашивают
Повод написать это отдельно дали комментарии под чужими публикациями. Серверы там другие, это поиск по справке и по конфигурации в контейнере, но вопросы звучат так же, как у всех, кто подключает модель к 1С.
Под MCP-сервером с векторным поиском по конфигурации один читатель пишет коротко: к серверу подключиться не может, и всё. У другого один редактор подключается к тому же адресу без вопросов, а второй получает 400 на адресе старого транспорта SSE, и заработало у него только после перехода на новый транспорт, streamable HTTP. Третий видит сервер в списке подключённых, но проверочный инструмент говорит, что сервер не запущен, а вызовы падают на разборе параметров.
Под вторым сервером с поиском по синтаксис-помощнику люди застревают ещё раньше, на сборке контейнера и версии анализатора кода.
Общее у всех одно: сообщение "не подключается" ничего не говорит о том, где искать. Если MCP ходит в саму базу 1С через HTTP-сервис, у этой беды есть хорошая сторона: база отвечает кодами, и коды честные.
Из чего состоит подключение
Схема у большинства решений "MCP в 1С" одинаковая, отличаются имена. Модель в чате зовёт инструмент. Клиент MCP передаёт вызов серверу MCP: это может быть расширение в самой базе или промежуточный мост, который держит список баз и учётки. Дальше обычный HTTP-запрос к публикации базы, адрес вида http://сервер/база/hs/mcp/rpc.
А дальше этажи, которые 1С-ник знает по веб-клиенту, только теперь они видны кодами:
- Сеть и веб-сервер. Имя резолвится, порт слушает, IIS или Apache отвечает.
- Публикация. Каталог с
default.vrdсуществует и называется так, как написано в адресе. - Модуль расширения веб-сервера 1С. Загружен, разрешён, его версия совпадает с версией кластера серверов 1С.
- Кластер и рабочий процесс. Модуль достучался до сервера 1С и получил свободный рабочий процесс.
- Пользователь базы. Логин и пароль подходят именно к этой базе.
- HTTP-сервис. Он есть в конфигурации или расширении, опубликован, и его модуль компилируется.
Порядок важен, и из него следует первое правило чтения кодов: ответ всегда приходит от самого нижнего сломанного этажа. Всё, что выше, в этот момент просто не проверялось.
Лестница кодов
Таблица собрана по ответам на адрес сервиса с Basic-авторизацией. Коды живые, большая часть снята 26.09.2026 на IIS 10 и 7.5 и платформе 8.3.27, остальные из записей подключений августа и сентября.
| Ответ | Этаж | Что это значит | Где чинить |
|---|---|---|---|
| нет соединения | 1 | имя не резолвится, порт закрыт, брандмауэр | адрес, DNS, VPN, служба веб-сервера |
| 404 на корне публикации | 2 | веб-сервер жив, публикации с таким именем нет | имя базы в адресе (на Apache и регистр букв) |
| 404.2 | 3 | модуль 1С запрещён в "Ограничениях ISAPI и CGI" | настройки IIS |
| 500 с кодом 0x8007007f | 3 | модуль не загрузился после смены версии платформы | пул приложений под нужную версию модуля |
| 409 с текстом 1С | 3 | версии модуля веб-сервера и кластера разошлись | переопубликовать базу нужной версией |
| 502 с текстом 1С | 4 | не нашёлся свободный рабочий процесс, кластер недоступен | кластер, адрес в vrd, лицензии |
| 401 (в IIS 401.5) | 5 | логин или пароль не подходят к этой базе | пользователь базы, кодировка логина |
| 404 на адресе сервиса | 6 | сервиса нет в конфигурации или он не опубликован | расширение и корневой URL; строка сервиса в vrd |
| 500 на адресе сервиса | 6 | модуль сервиса не компилируется | конфигуратор, проверка модулей |
| 405 | 6 | сервис есть, метод запроса не тот | это хороший ответ на GET |
| 403 на методе исполнения кода | 6 | у меня появлялся, когда мост звал метод GET-ом; кто его отдаёт, не разобрал | переключить вызов на POST |
Дальше про те строки, на которых я терял время.
401 отвечает раньше 404
Первое, что сбивает. С неверным паролем я получил 401 на существующий сервис, на несуществующий сервис и на адрес, которого не бывает вовсе. Без пароля, кстати, тот же 401.5. Код один и тот же для всех трёх случаев, потому что пользователь проверяется раньше, чем модуль начинает искать сервис.
Отсюда практическое следствие: пока вы видите 401, про существование сервиса вы не знаете ничего. Часто это читают как "сервис есть, но закрыт правами" и идут раздавать роли. Сначала пароль.
Второе следствие касается кириллических логинов. Basic-авторизация кодирует строку "логин:пароль", и клиенты делают это по-разному: кто в UTF-8, кто в однобайтовой кодировке. Логин латиницей работает везде, кириллический работает не у всех. Этот случай я разбирал отдельно в статье про HTTP-сервисы под нагрузкой: подкод 401.5 там отправлял чинить не ту сторону.
409 и 502 отвечают на любой адрес
Два этажа ниже пользователя ломаются так, что ответ не зависит от адреса вообще. Вот что отдавала база с кодом 409, дословно:
HTTP: Conflict by reason: Различаются версии клиента и сервера (8.3.27.1606 - 8.3.27.2325), клиентское приложение: Модуль расширения веб-сервера
Кластер обновили, публикацию оставили от прежней версии. Клиентом для кластера здесь выступает сам модуль веб-сервера, и сервер его не пускает. Тот же ответ приходит на корень /hs/, на выдуманный сервис, с паролем и без. Лечится переопубликованием базы из конфигуратора нужной версии или правкой пути к модулю в настройках веб-сервера.
У 502 текст другой:
Ошибка установки соединения by reason: Свободный рабочий процесс сервера 1С:Предприятия не найден за 5 попыток с интервалом 1000 миллисекунд
Ответ приходил за 4,07-4,19 секунды на каждый запрос. Это сходится с текстом: пять попыток с интервалом в секунду дают четыре секундных паузы между ними. Параметры попыток живут в элементе pool файла публикации, и про то, как они влияют на 502 под нагрузкой, я писал в той же статье про HTTP-сервисы. Здесь 502 означал одно: кластер, на который смотрит публикация, рабочий процесс не выдал. Смотреть надо на сервер 1С.
405 - хорошая новость
Если на адрес сервиса с правильным паролем приходит 405, значит пройдены все этажи: модуль загружен, кластер ответил, пользователь узнан, сервис найден. Не понравился только метод: HTTP-сервис MCP ждёт POST, а проверка шла GET-ом. Для диагностики это самый дешёвый способ сказать "до сервиса доходим", и ничего в базе при этом не выполняется.
У 404 два разных смысла, и их легко спутать. 404 на корне публикации значит, что веб-сервер не знает такого приложения: опечатка в имени базы, а на Apache ещё и другой регистр букв (IIS к регистру в пути равнодушен). 404 на адресе сервиса при живом корне значит, что приложение есть, а сервиса в нём нет. Проверяется одним запросом к корню.
Публикация без сервиса и сервис без публикации
Сервис может быть в конфигурации или расширении и при этом не опубликован. 26 сентября я посмотрел, что тогда приходит. В публикации, где сервисы конфигурации выключены в файле публикации (enable="false"), каждый из них отвечает 404 с короткой страницей 1С "HTTP: Not found". Выдуманное имя сервиса на том же сервере даёт 404.0 от самого IIS. По коду неопубликованный сервис от несуществующего не отличить, отличается только тело, и то не везде: на другом сервере одной и той же страницей IIS отвечали и выдуманное имя, и живой сервис на неизвестный путь.
В августе у меня было иначе. В публикации, где сервисы перечислены поимённо, строки сервиса MCP не было, и мост получил на нём 500. Я дописал строку, и ответ сменился на 401. Повторить этот 500 26 сентября не вышло: на том же сервере неопубликованный адрес с неверным паролем отвечает 401, с верным 404. Отчего тогда был 500, я не знаю. Поэтому 404 на адресе сервиса проверяю в двух местах: есть ли сервис в конфигурации и есть ли его строка в файле публикации. За второе отвечает блок httpServices: либо всё разом, либо каждый сервис своей строкой.
<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system" base="/base" ib="..."> <!-- опубликовать все сервисы, включая сервисы расширений --> <httpServices publishByDefault="true" publishExtensionsByDefault="true"/> </point>
Или точечно, если публиковать всё подряд не хочется:
<httpServices publishByDefault="false"> <service name="mcp" rootUrl="mcp" enable="true"/> <service name="api" rootUrl="api" enable="true"/> </httpServices>
Имена здесь условные, у вашего решения свои. Затык в том, что сервисов у MCP-решения бывает больше одного. В моей сборке их три: через первый клиент узнаёт список методов, через второй идут сами вызовы, третий для этой статьи неважен. Если опубликован первый, а второй забыт, получается самая неприятная картина: список инструментов модель видит, а вызов упадёт. У двух баз из моих тринадцати второй сервис на своём адресе отвечает 404 веб-сервера, а проверка "до сервиса MCP доходим" у них зелёная. Файлы публикации я открыл: в одной строка второго сервиса есть, другая публикует всё разом. Значит, в самой конфигурации этих баз второго сервиса нет.
500 без текста
Отдельная история с 500 на методе исполнения кода. 6 сентября я правил модуль расширения, и сервис стал отвечать 500 с пустым телом. Попытка в модуле ничего не ловила. Это логично, как выяснилось: модуль не компилируется, до выполнения дело не доходит, и ловить исключение просто некому.
Строку ошибки даёт конфигуратор в пакетном режиме:
1cv8.exe DESIGNER /S сервер/база /N Пользователь /P Пароль
/CheckModules -Extension ИмяРасширения -Server
/Out C:/temp/check.log /DisableStartupDialogs
Через минуту в логе лежала точная строка: модуль, номер строки и колонки, и "Переменная не определена" с именем переменной. Запускать это лучше скрытым процессом с ограничением по времени: зависший конфигуратор держит базу.
Оговорка: проверка модулей пропускает часть ошибок, которые ловит только выполнение. У меня так проскакивали переменные с именами, совпадающими со словами языка, и ошибки в модуле формы при открытии. Для HTTP-сервиса этого обычно хватает, для обработки нет.
Похожий случай, где текст всё-таки был. 22 сентября я подключал к мосту типовую базу. Сервис MCP отвечал 405, список методов приходил, проверка связи проходила, а любой вызов метода падал с 500 "Ошибка инициализации модуля" второго сервиса. Модуль того сервиса был собран в другой конфигурации и разбирал тело запроса через общий модуль библиотеки КоннекторHTTP, которой в типовой нет. Замена на платформенный разбор:
// Было: общий модуль библиотеки, которой в этой конфигурации нет // Аргументы = КоннекторHTTP.JsonВОбъект(Запрос.ПолучитьТелоКакСтроку()); ЧтениеJSON = Новый ЧтениеJSON; ЧтениеJSON.УстановитьСтроку(Запрос.ПолучитьТелоКакСтроку()); // Истина - вернуть Соответствие: обработчики методов читают его через Получить() Аргументы = ПрочитатьJSON(ЧтениеJSON, Истина); ЧтениеJSON.Закрыть();
Второй параметр тут не для красоты. Обработчики методов звали Аргументы.Получить(...), а у структуры такого метода нет, и без Истина починка просто перенесла бы падение на строку ниже.
Мост отвечает вежливо
Если между клиентом и базой стоит мост, появляется ещё один способ не увидеть ошибку. 26 сентября я проверил это на своём мосте:
- Неверный пароль. Рукопожатие MCP прошло с кодом 200, список инструментов пришёл. Базу мост на этом шаге не трогал вообще.
- Вызов инструмента с тем же паролем. Код ответа снова 200, в результате флаг ошибки
isError: false, а внутри текста: "Ошибка выполнения инструмента: Client error '401 Unauthorized'". - База с 409. Всё то же, только внутри текста "409 Conflict".
То есть транспорт докладывает, что всё прошло, а настоящий код лежит строкой внутри ответа. По спецификации MCP такая ошибка должна приходить с isError: true, так что это особенность моста. Протокол тут ни при чём, но легче от этого не становится. Дальше всё зависит от того, как модель перескажет этот текст, а индикатор в приложении останется зелёным. Он показывает только, что процесс клиента запустился, про базу он не знает ничего.
Четвёртый случай оказался хуже: для базы с 502 мост не ответил даже на рукопожатие за 30 секунд, и клиент отвалился по таймауту. Почему мост ждёт базу уже на рукопожатии, если при неверном пароле не ждёт, я не разобрал. Но выглядит это ровно как "сервер MCP не запускается", и человек в такой момент начинает переустанавливать клиент.
Про коды самого моста я писал в статье про один MCP на десять баз: там 502, 404 и 405 выдаёт сам прокси по своим правилам, до публикации запрос может и не дойти. Это другой слой, и путать их не стоит: 404 моста значит "такой базы нет в реестре", 404 публикации значит "нет сервиса".
Скрипт, который проходит лестницу
Эту лестницу я в итоге записал в скрипт. Он на Node без зависимостей, делает только GET-запросы, а мосту отправляет рукопожатие и, по желанию, один инструмент без параметров. Пароль берёт из переменных среды и в вывод не пишет. Файл proverka-mcp-1c.mjs приложен к статье.
set MCP_1C_USER=mcp_check set MCP_1C_PASSWORD=... node proverka-mcp-1c.mjs --pub http://server/base --exec hs/api node proverka-mcp-1c.mjs --pub http://server/base --bridge http://bridge/mcp/ --tool list_methods
Самая хитрая в скрипте ступень 2, она закрывает сразу три этажа. Скрипт идёт на адрес сервиса, которого заведомо нет, с паролем. У живой связки ответ 404: модуль загрузился, кластер ответил, пользователь узнан, и только сервиса не нашлось. Всё, что сломалось ниже, ответит своим кодом, и скрипт на этом остановится.
На ступени 4 тот же 404 читается по-другому, и скрипт различает их по телу ответа. Если 404 пришёл с JSON и текстом отказа, это ответил сам модуль сервиса, то есть он жив и компилируется. Голый 404 веб-сервера значит, что сервиса нет в конфигурации или в публикации.
Живой прогон по базе, где всё в порядке (адреса и имена сервисов заменены):
[ OK ] 1. сеть до веб-сервера 200 публикация отвечает (50 мс). О базе это ещё ничего не говорит [ OK ] 2. модуль 1С и кластер 404 (404.0) модуль загружен, кластер отвечает, учётка принята (38 мс) [ OK ] 3. сервис hs/mcp/rpc 405 (405.0) сервис есть, на GET отвечает "не тот метод" - так и должно быть [ OK ] 4. сервис hs/api 404 модуль сервиса компилируется и сам отвечает на неизвестный метод [ OK ] 5. мост MCP 200 сквозной вызов list_methods прошёл (47 мс) Все ступени живы.
И по базе с обновлённым кластером:
[ OK ] 1. сеть до веб-сервера 200 публикация отвечает (66 мс). О базе это ещё ничего не говорит
[СТОП] 2. модуль 1С и кластер 409 версия модуля веб-сервера разошлась с версией кластера:
переопубликовать нужной версией (8.3.27.1606 - 8.3.27.2325)
Сломано на ступени 2
Код возврата равен номеру сломанной ступени, так что скрипт встаёт в планировщик и шлёт уведомление только тогда, когда есть что чинить. Я гонял его на живых базах по девяти сценариям. Четыре поломки я устроил сам: неверный пароль, пароль не передан, опечатка в имени базы, опечатка в пути сервиса. Три базы были сломаны по-настоящему: 409, 502 и отсутствующий второй сервис. Плюс контрольный прогон по живой базе и прогон через мост. Во всех семи поломках скрипт остановился на той ступени, на которой было сломано.
Когда связь есть и хочется убедиться, что попали в ту базу, дальше помогает одна строка через метод исполнения кода. В моей сборке результат возвращается переменной ЛокальныйРезультат, и на сервере эта строка вернёт имя машины, где работает рабочий процесс:
ЛокальныйРезультат = ИмяКомпьютера();
Скрипт этого не делает намеренно: выполнение кода в базе уже не диагностика подключения, и такой вызов должен делать человек, который знает, в какую базу стучится.
Что я проверял и отбросил
"Публикация открывается в браузере, значит с ней всё в порядке"
Первое, что делают все, и я тоже. 26 сентября корень публикации отдал 200 и стартовую страницу веб-клиента на всех трёх проблемных базах, включая ту, где кластер не выдавал рабочий процесс, и ту, где версии разошлись. Стартовую страницу веб-сервер отдаёт сам, до кластера запрос не доходит. Проверять надо адрес внутри /hs/.
"Индикатор зелёный, значит подключение живо"
Индикатор показывает только запущенный процесс клиента, про это выше в разделе о мосте.
"500 без текста - это BOM или зарезервированное имя"
6 сентября до конфигуратора я успел заподозрить метку порядка байтов, зарезервированное имя переменной и порядок объявления функций. Все мимо, точную строку за минуту дала проверка модулей.
"403 - значит учётке не хватает прав"
Мост получал 403 на методе исполнения кода, и первая мысль была про роли. Переключение вызова на POST сняло 403 сразу. Причину я до конца не разобрал: прямой GET на тот же адрес без параметров отвечает 200 с текстом "Не указан параметр code". Похоже, 403 появляется, когда код едет внутри адреса GET-запроса, но кто именно его отдаёт, я не выяснил.
"405 на сервисе MCP, значит всё работает"
Случай с типовой базой 22 сентября: 405 на сервисе MCP при мёртвом втором сервисе. Из него в скрипте появилась ступень 4.
"Сервер не отвечает, значит он лежит"
Третья проблемная база 26 сентября не ответила за 10 секунд, а через несколько минут отвечала за 29-989 миллисекунд. Что было в эти десять секунд, ни один журнал у меня не объясняет. Одиночный таймаут я сначала повторяю, а потом уже диагностирую.
"Сервер MCP не видит конфиг, значит опечатка в JSON"
Один раз подключения просто не было в конфиге. Я вписал сервер руками, а настольное приложение клиента при следующем событии сессии переписало файл своей копией из памяти. Ручная запись прожила четыре секунды, JSON был правильный. С тех пор серверы в конфиг вписываются скриптом, который ждёт закрытия приложения.
Границы
Всё снято на IIS. На Apache я ожидаю тех же 409, 502 и 405, потому что их отдаёт модуль платформы, но не проверял. Подкоды вида 401.5 и 404.2 это особенность IIS.
Платформа 8.3.27, одна публикация на 8.3.20. 404.2 и 0x8007007f взяты из записей подключений августа, сегодня я их не воспроизводил: для этого пришлось бы ломать живую публикацию. Август дал и 500 на неопубликованном сервисе, 26 сентября на таких адресах был 404: что из этого зависит от версии платформы, а что от способа запроса, я не проверял.
Мост у меня один, и его поведение с рукопожатием может быть особенностью именно его. Если у вас другой мост или сервер MCP живёт прямо в расширении, ступени 1-4 скрипта от этого не меняются, а пятую стоит проверить на вашем.
Другие наши инструменты
- Чек-ап веб-публикации 1С на IIS - если лестница остановилась на ступенях 2-3 и надо разобрать саму публикацию: версии модуля, пулы, настройки vrd.
- Матрица прав доступа для нейросети - когда подключение заработало и пора решить, что учётка MCP видит в базе.
- Выгрузка метаданных для LLM - структура конфигурации для модели, чтобы она не придумывала реквизиты.
- Выгрузка данных 1С для нейросети - если MCP ради разовой задачи поднимать не хочется, а данные в чат нужны сейчас.
Вопрос
На каком коде застревали вы, когда подключали модель к базе? Особенно интересно про 403 на исполнении кода: если вы докопались, где именно он рождается на длинном GET, напишите, я так и не нашёл.
Вступайте в нашу телеграмм-группу Инфостарт