> For the complete documentation index, see [llms.txt](https://docs.onerpa.ru/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.onerpa.ru/mcp-servery-1c/servery/syntax-check-server.md).

# SyntaxCheckServer

Проверка синтаксиса кода 1С через bsl-analyzer.

## Назначение

SyntaxCheckServer проверяет код 1С на синтаксические ошибки, используя bsl-analyzer. Это самый простой в установке MCP-сервер — он не требует данных конфигурации или embedding модели.

Сервер поддерживает два режима проверки:

* `syntaxcheck` — проверка текста BSL, переданного прямо в MCP-вызове.
* `syntaxcheck_file` — проверка файла из подключённого каталога. Инструмент регистрируется только если задана переменная `FILES_DIR` и каталог существует внутри контейнера.

## Доступные инструменты MCP

ИИ получает следующие инструменты:

| Инструмент         | Описание                                                                      |
| ------------------ | ----------------------------------------------------------------------------- |
| `syntaxcheck`      | Проверка синтаксиса переданного текста BSL через bsl-analyzer                 |
| `syntaxcheck_file` | Проверка BSL-файла из подключённого каталога; доступен только при `FILES_DIR` |
| `plugin_state`     | Состояние загруженных плагинов, hooks, таблиц и ошибок                        |
| `plugin_reload`    | Атомарно перечитать каталог плагинов без перезапуска сервера                  |

### syntaxcheck

Анализирует BSL-код на синтаксические ошибки с помощью bsl-analyzer. Код сохраняется во временный файл, передаётся на анализ, результат возвращается структурированно и, для совместимости, текстом.

| Параметр    | Тип    | По умолчанию | Описание                                                                                                                                                                               |
| ----------- | ------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `code`      | string | —            | Код на языке 1С (BSL) для анализа                                                                                                                                                      |
| `file_name` | string | `""`         | Логическое имя модуля для переданного кода, например `ObjectModule.bsl`. Только имя файла с расширением `.bsl` — никогда не путь. Пустое значение анализирует код под служебным именем |

`file_name` говорит анализатору, чем является код: с `ObjectModule.bsl` он видит модуль объекта под этим именем, и каждая позиция в ответе названа этим именем, а не временным путём сервера. Имя, которое не является именем — разделитель пути, переход к родительскому каталогу, абсолютная или дисковая форма, управляющий символ, пустая основа или неизвестное расширение — приводит к ошибке вызова с указанием причины, а не молча заменяется.

**Возврат**: две части.

* **Структурированная часть** — то, что следует читать: `diagnostics` (типизированные записи), `diagnostic_asides` (значения, вынесенные из диагностики, см. ниже), `summary` с `total` (сколько диагностик выдал анализатор), `returned` (сколько попало в ответ) и `truncated`; `filters` — объявляет, что сузило список (фильтр строк, фильтр важности, таблица подавлений или плагин); блок `provenance`; и `request_rewrite`.
* **Текстовая часть beta** — один документ [TOON](https://github.com/toon-format/toon) под ключом `events`. Он несёт те же события, значения и порядок, что прежний JSONL, но повторяющиеся поля диагностик записываются одной табличной шапкой. Stable `latest` пока возвращает JSONL. Клиентам следует читать неизменившийся structured content; парсер старой текстовой части с beta несовместим.

{% hint style="warning" %}
В beta-поставке в `bsl-analyzer.toml` отключены `UnresolvedMethodCall`, `UnresolvedField` и `QueryToMissingMetadata`. При проверке отдельного временного файла анализатор не видит контекст всей конфигурации, поэтому эти межмодульные диагностики давали ложные ошибки на типичном коде 1С. Они не проходят через таблицу подавлений сервера и потому не меняют `filters.suppression_applied`. Сервер проверяет синтаксис и включённые локальные правила, но не доказывает корректность разрешения методов, полей и метаданных запроса во всём проекте.

Эти три проверки можно получить обратно — не включением их в анализаторе, а из индекса всей конфигурации: см. [Режим полного индекса](#режим-полного-индекса-beta).
{% endhint %}

#### `diagnostic_asides`

Табличная форма TOON возможна только тогда, когда каждая диагностика — строка из примитивных значений одних и тех же полей. Анализатор пишет `tags` только у части диагностик и списком, и одного этого достаточно, чтобы таблица не состоялась: первая beta с TOON (`1.0.4-beta.20260824` / `1.1.2-beta.20260824`) не давала экономии вовсе.

Поэтому непримитивное значение выносится **рядом** с диагностиками — в `diagnostic_asides`: по одной записи на значение, каждая называет свою диагностику индексом в `diagnostics`:

| Поле записи  | Что означает                                                       |
| ------------ | ------------------------------------------------------------------ |
| `diagnostic` | Индекс диагностики в `diagnostics`, считая с `0`                   |
| `field`      | Имя поля, которым это значение было записано                       |
| `value`      | Одно значение этого поля (список из двух значений даёт две записи) |

Поля, объявленные схемой ответа — `code`, `message`, `severity` и четыре поля диапазона — не выносятся никогда. Структурированная часть объявляет `diagnostic_asides` всегда и несёт пустой список, когда выносить было нечего; текстовая часть в этом случае ключ опускает. Хуки плагинов работают как раньше: вынос происходит после последнего хука, поэтому плагину диагностика приходит такой, какой её написал анализатор.

Замер на одном общем модуле конфигурации ЗУП (101 диагностика, токенизатор `o200k_base`): **3601 токен против 6685 у прежнего JSONL — на 46 % меньше**, 16,4 КБ против 25,7 КБ.

Все диапазоны в обеих частях 1-based, и отчёт это объявляет: событие `start` несёт `"line_base": 1`, структурированная часть — `"provenance": {"line_base": 1}`. Метрики события `file` описывают весь проанализированный файл, а не соседние диагностики: `"provenance": {"file_metrics_scope": "whole_file"}`.

Блок `provenance` полностью описывает, чем и как получен результат:

| Поле                    | Что означает                                                                                                                                                                                                                                                              |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tool`                  | Имя инструмента, ответившего на вызов                                                                                                                                                                                                                                     |
| `analyzer_version`      | Версия `bsl-analyzer`, произведшая отчёт. В stable `latest` это версия, закреплённая в образе; в beta она читается из `/app/bsl_analyzer_nix/installed-release.json` и меняется после самообновления (см. [Самообновление анализатора](#самообновление-анализатора-beta)) |
| `bundled_configuration` | Работал ли анализатор со встроенным `bsl-analyzer.toml`                                                                                                                                                                                                                   |
| `source_encoding`       | Кодировка, в которой прочитан анализируемый текст                                                                                                                                                                                                                         |
| `line_base`             | База нумерации строк (всегда `1`)                                                                                                                                                                                                                                         |
| `file_metrics_scope`    | Область метрик события `file` (`whole_file`)                                                                                                                                                                                                                              |
| `index`                 | Состояние полнотекстового индекса конфигурации: `absent`, `building`, `ready` или `failed` (см. [Режим полного индекса](#режим-полного-индекса-beta))                                                                                                                     |

Блок `request_rewrite` сообщает, менял ли плагин аргументы вызова: `applied`, что было запрошено (`requested`), что использовано (`used`) и какие файлы плагинов это сделали (`changed_by`).

### syntaxcheck\_file

Анализирует файл из каталога, указанного в `FILES_DIR`. Путь передаётся относительно этого каталога. Можно ограничить выдачу диагностик конкретными строками.

| Параметр    | Тип    | По умолчанию | Описание                                                                                               |
| ----------- | ------ | ------------ | ------------------------------------------------------------------------------------------------------ |
| `file_path` | string | —            | Относительный путь к `.bsl`-файлу внутри `FILES_DIR`                                                   |
| `lines`     | string | `""`         | Необязательный список строк и диапазонов, например `5, 10-20, 35`. Пустое значение проверяет весь файл |

**Возврат**: те же две части, что у `syntaxcheck`, по той же схеме. Отличие — в том, что они говорят о суженном вызове: при заданном `lines` в `summary` поле `total` — это диагностики всего файла, а `returned` — сколько из них пересекается с запрошенными строками, и `filters` несёт `"line_filter_applied": true`. Файл с двенадцатью диагностиками, суженный до одной строки, читается как «двенадцать выдано, одна возвращена», а не как файл с одной диагностикой. Метрики события `file` по-прежнему описывают весь файл.

Путь в ответе выражен в терминах вызывающей стороны: событие `file` называет файл относительно корня проекта в POSIX-форме, и ни в одном поле — включая полезную нагрузку отказа — не появляется собственный путь сервера.

### Ошибки инструментов

Если анализ не удалось выполнить, оба инструмента возвращают ошибку инструмента (а не успешный результат) с полезной нагрузкой `{"error": {"code", "message", "retryable"}}`. Коды:

`files_dir_unavailable`, `invalid_lines`, `invalid_file_name`, `path_outside_files_dir`, `file_not_found`, `file_unreadable`, `analyzer_not_found`, `analyzer_timeout`, `analyzer_failed`, `analyzer_no_output`, `malformed_output`, `internal_error`.

Код `invalid_file_name` принадлежит только `syntaxcheck`: `syntaxcheck_file` берёт имя из самого файла.

## Режим полного индекса (beta)

По умолчанию контейнер анализирует один модуль за раз и ничего не знает об остальной конфигурации. Именно поэтому в `bsl-analyzer.toml` выключены `UnresolvedMethodCall`, `UnresolvedField` и `QueryToMissingMetadata`: вызов, который не может разрешить межмодульную ссылку, помечает неразрешённым всё подряд. На одном общем модуле конфигурации ЗУП это было 167 ложных находок из 268.

`FULLINDEX=true` вместе со смонтированным `FILES_DIR` индексирует этот каталог при старте и отвечает на эти три проверки из индекса:

```powershell
docker run -d --name 1c_syntaxcheck_mcp `
  -e LICENSE_KEY=YOUR_LICENSE_KEY `
  -e FILES_DIR=/files -e FULLINDEX=true `
  -v "C:/path/to/sources:/files:ro" `
  -v 1c_syntaxcheck_index:/index `
  -p 8002:8002 `
  comol/1c_syntaxcheck_mcp:latest-beta
```

### Чего это стоит

Замер на 10156 модулях:

| Что                                                          | Сколько          |
| ------------------------------------------------------------ | ---------------- |
| Индексация при старте                                        | около 8 минут    |
| Проверка файла без индекса                                   | около 200 мс     |
| Первая проверка файла после того, как индекс сообщил `ready` | около 190 секунд |
| Каждая следующая проверка                                    | около 11 секунд  |

Первая цена — это индекс, отвечающий на вопрос впервые; она платится один раз на контейнер, а не на файл.

### Состояния индекса

Контейнер отвечает на вызовы всё время, пока идёт индексация. Пока индекс не готов, три проверки остаются выключенными — их ответы до этого были бы теми самыми ложными. Каждый ответ сообщает, в каком состоянии он получен, в `provenance.index`:

| Значение   | Что означает                                               |
| ---------- | ---------------------------------------------------------- |
| `absent`   | Режим не включён — обычное поведение сервера               |
| `building` | Индекс строится; три межмодульные проверки ещё не отвечают |
| `ready`    | Индекс готов; три проверки отвечают из него                |
| `failed`   | Индекс не удалось построить; ответ такой же, как без него  |

Чистый отчёт при `absent` и при `ready` означает разное — поэтому состояние объявляется в каждом ответе.

{% hint style="info" %}
Три проверки **никогда** не включаются в собственной конфигурации анализатора — ни в каком состоянии. Это измерено: даже когда индекс в том же контейнере готов, `analyze -s <файл>` с этими проверками выдаёт те же 167 ложных находок. Процессы делят только каталог кэша, поэтому включение проверки в анализаторе не даёт ему контекста — оно лишь заставляет его отвечать неверно. Ответы приходят из индекса и сливаются с отчётом до хуков, поэтому плагин получает один список, а фильтр строк сужает их как всё остальное.
{% endhint %}

### Хранение индекса

`INDEX_DIR` задаёт, где лежит индекс; по умолчанию `/index` — этот путь образ объявляет томом, поэтому индекс переживает перезапуск даже без явного `-v`. Каталог, в который нельзя писать, не фатален: индекс тогда строится внутри контейнера и пересобирается при перезапуске, а запись о старте это сообщает.

Файл, изменившийся в смонтированном каталоге, переиндексируется без перезапуска.

### Как часто индекс перечитывается

Пока индекс строится, сервер спрашивает его состояние каждые пять секунд — три межмодульные проверки включаются сразу, как только он готов. **Готовый индекс переспрашивается раз в час**; это `FULLINDEX_REINDEX_INTERVAL_SEC`, и именно эта пауза определяет, как быстро подхватывается пересборка исходников.

Отключить переиндексацию переменной нельзя: `0`, отрицательное значение и не-число читаются как значение по умолчанию, а положительное ограничивается диапазоном 60…86400 секунд. Раньше опрос шёл каждые пять секунд всегда, и простаивающий контейнер переиндексировал себя десятки раз в час впустую.

Медленнее при этом не стало ничего важного: остановившийся индексный процесс по-прежнему замечается за секунды, а часовой опрос, заставший пересборку, сразу возвращается к пятисекундному темпу до её завершения. Плата — `provenance.index` может сообщать `ready` в первые минуты пересборки, о которой сервер ещё не спрашивал.

{% hint style="warning" %}
Без `FULLINDEX` в контейнере не меняется ничего: индексный процесс не запускается, индексация не идёт, ответы те же, что и раньше.
{% endhint %}

## Самообновление анализатора (beta)

В stable `latest` версия `bsl-analyzer` закреплена в образе и меняется только с новым образом. В beta-каналах этого закрепления больше нет: в образ попадает последний релиз upstream на момент сборки, а дальше контейнер сам спрашивает у upstream новую версию — при старте и далее раз в сутки — и заменяет бинарник на месте.

Обновление не останавливает сервер: проверка идёт в отдельном потоке, замена — это переименование на тот же путь, поэтому уже идущий анализ работает с тем файлом, который открыл. Контейнер без доступа к GitHub отвечает тем анализатором, который у него есть, и повторяет попытку позже.

Каждая проверка пишет в журнал одну запись `server.analyzer` с итогом из закрытого списка: `installed`, `up_to_date`, `unreachable`, `unpublished_platform`, `failed`, `disabled`. Какой релиз установлен сейчас:

```bash
docker exec 1c_syntaxcheck_mcp cat /app/bsl_analyzer_nix/installed-release.json
```

Та же версия попадает в `provenance.analyzer_version` каждого ответа, так что отчёт называет анализатор, который его действительно построил, а не тот, с которым собирался образ.

Лицензионные тексты upstream в `/app/licenses/bsl-analyzer/` обновляются раньше бинарника, и обновление, которое не смогло их забрать, не выполняется вовсе.

{% hint style="warning" %}
Новый релиз анализатора может поменять идентификаторы правил, уровни важности, тексты сообщений и диапазоны — сервер не удерживает новый релиз в рамках находок старого. Где это важно, ставьте `BSL_ANALYZER_AUTO_UPDATE=0` и обновляйтесь осознанно, вытягивая новый образ.
{% endhint %}

Переменные окружения, управляющие этим, — в [установке](https://docs.onerpa.ru/mcp-servery-1c/servery/pages/U84jx5SE4PNdpFQvwXfS#переменные-окружения).

## Возможности

ИИ получает инструменты для:

* Проверки синтаксиса кода 1С
* Проверки файлов конфигурации из подключённого каталога
* Обнаружения ошибок до выполнения
* Анализа конструкций языка

## Примеры использования

ИИ может проверить код перед предложением:

* "Проверь синтаксис этой процедуры"
* "Есть ли ошибки в этом коде?"
* "Проанализируй этот модуль на ошибки"

## Требования

* Docker Engine или Docker Desktop с поддержкой Linux-контейнеров
* Лицензионный ключ

{% hint style="success" %}
Этот сервер можно запустить сразу — он не требует подготовки данных или настройки embedding.
{% endhint %}

## Порт

**8002**

## Образ Docker

```
comol/1c_syntaxcheck_mcp:latest
```

### Архитектуры и теги

| Тег           | Платформа   | Анализатор                                                                                                                        | Статус   |
| ------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `latest`      | linux/amd64 | Официальный релизный бинарник `bsl-analyzer`, закреплённый по digest и проверяемый при сборке                                     | Stable   |
| `latest-beta` | linux/amd64 | Анализатор больше не закреплён: в образ попадает последний релиз upstream на момент сборки, дальше контейнер обновляет его сам    | **Beta** |
| `arm64-beta`  | linux/arm64 | Собирается из исходников релиза, который разрешает сборка: upstream не публикует arm64-бинарник, поэтому самообновления здесь нет | **Beta** |

У SyntaxCheckServer нет тегов `light`, `arm64` и `light-beta`. Полная таблица: [Каналы образов](/mcp-servery-1c/kanaly-obrazov.md).

{% hint style="warning" %}
На arm64-хосте (Apple Silicon, ARM-серверы) стабильный образ запускается только через эмуляцию:

```bash
docker run -d -p 8002:8002 --platform linux/amd64 `
  -e LICENSE_KEY=YOUR_LICENSE_KEY `
  comol/1c_syntaxcheck_mcp:latest
```

Нативная альтернатива — `arm64-beta`. Её анализатор собран из исходников во время сборки самого образа, но не воспроизводим побайтно, не имеет attestation upstream и не проверялся на реальном arm64-железе. Кроме того, upstream не публикует Linux arm64-артефакт, поэтому обновляться такому контейнеру не до чего: ежедневная проверка записывает `unpublished_platform` и ничего не скачивает. Поэтому канал — бета, и стабильного arm64-тега нет.
{% endhint %}

## Быстрый старт

```powershell
docker run -d -p 8002:8002 `
  --name 1c_syntaxcheck_mcp `
  -e LICENSE_KEY=YOUR_LICENSE_KEY `
  comol/1c_syntaxcheck_mcp:latest
```

### Проверка файлов

```powershell
docker run -d -p 8002:8002 `
  --name 1c_syntaxcheck_mcp `
  -e LICENSE_KEY=YOUR_LICENSE_KEY `
  -e FILES_DIR=/app/files `
  -v "E:/1C_Export/Files:/app/files" `
  comol/1c_syntaxcheck_mcp:latest
```

По умолчанию используется `streamable-http` на `/mcp`. Для SSE включите `-e USESSE=true`: поток событий будет доступен на `/sse`, а сообщения отправляются на `/messages/`. Старое размещение SSE-потока на `/mcp` можно временно сохранить через `MCP_SSE_PATH=/mcp`.

### Границы Streamable HTTP-сессий

| Переменная                          | Назначение                                                        | По умолчанию |
| ----------------------------------- | ----------------------------------------------------------------- | ------------ |
| `MCP_SESSION_IDLE_TTL_SECONDS`      | Освободить неактивную сессию через N секунд                       | `1800`       |
| `MCP_SESSION_MAX_LIFETIME_SECONDS`  | Абсолютный срок жизни даже активной сессии                        | `28800`      |
| `MCP_SESSION_MAX_CONCURRENT`        | Максимум одновременных сессий; сверх лимита — временный отказ 503 | `64`         |
| `MCP_SESSION_REAP_INTERVAL_SECONDS` | Период уборки просроченных сессий                                 | `30`         |

Эти границы действуют только для Streamable HTTP. SSE живёт столько же, сколько открывшее его соединение, и отдельным реестром не управляется.

## Конфигурация Cursor

```json
{
  "mcpServers": {
    "1c-syntax-checker-mcp": {
      "url": "http://localhost:8002/mcp",
      "connection_id": "1c_lsp_service_001"
    }
  }
}
```

## Структура раздела

* [Установка](/mcp-servery-1c/servery/syntax-check-server/ustanovka.md) — команды запуска

## Доработка

Сервер расширяется плагинами: четыре hooks (`on_startup`, `on_request`, `on_diagnostics`, `on_result`) и таблица `SUPPRESSED_DIAGNOSTICS`. Производного состояния нет — все хуки действуют в рамках вызова. См. [Систему плагинов](/mcp-servery-1c/sistema-pluginov.md) и [Установку](/mcp-servery-1c/servery/syntax-check-server/ustanovka.md).
