Все статьи

3 октября 2026 г.

Как заставить Cursor понимать структуру регистров накопления 1С: MCP-сервер Графон на практике

Почему Cursor «не видит» регистр накопления

Cursor — текстовый агент. Он индексирует файлы и умеет искать по строкам, но не имеет семантической модели метаданных 1С. Для него РегистрНакопления.ТоварыНаСкладах — это просто набор XML-файлов:

  • Registers/ТоварыНаСкладах.xml — свойства регистра (вид: Остатки, периодичность, режим записи);
  • Registers/ТоварыНаСкладах/Ext/ManagerModule.bsl — модуль менеджера;
  • Ext/RecordSetModule.bsl, Ext/Form/... — модуль набора записей и формы;
  • десятки файлов движений в модулях документов и подписок.

Агент не понимает, что Количество — это Ресурс, а Склад — Измерение; что регистр имеет вид «Остатки», а значит доступна виртуальная таблица Остатки(); что запись движений возможна только внутри ОбработкаПроведения() при Движения.<Регистр>.Записывать = Истина. В итоге он:

  1. Придумывает несуществующие поля (ОстатокКоличество вместо КоличествоОстаток).
  2. Пытается «записать движение» через РегистрыНакопления.ТоварыНаСкладах.СоздатьНаборЗаписей() в обход движений документа.
  3. Теряет измерения в группировке запроса и генерирует невалидный BSL, который платформа отвергает на этапе компиляции.
  4. Скармливает вам «простыню» на 2000 строк из XML, сжигая 80–120k токенов на одну задачу.

Корень проблемы — не модель, а отсутствие контекста о структуре метаданных.

Что именно должен знать агент о регистре накопления

| Элемент структуры | Где лежит в конфигурации | Зачем агенту |
|---|---|---|
| Вид регистра (Остатки/Обороты) | ТоварыНаСкладах.xml | Определяет доступные виртуальные таблицы |
| Измерения | XML + RecordSetModule.bsl | Ключи группировки, обязательность заполнения |
| Ресурсы | XML | Поля для РегистраторОтразилДвижение и расчёта итогов |
| Реквизиты | XML | Контроль остатков, служебные признаки |
| Регистраторы | Граф движений по конфигурации | Кто и когда пишет движения |
| Виртуальные таблицы | Вычисляются платформой | .Остатки(), .Обороты(), .ОстаткиИОбороты() |
| Подписки и вызовы | Граф связей модулей | Регресс при изменении структуры |

Без графа зависимостей эту таблицу агенту взять неоткуда — он видит только текст.

Архитектура: Графон как семантический слой

Графон ([grafonbase.ru](https://grafonbase.ru)) — локальный инструмент и MCP-сервер. Он один раз индексирует выгрузку конфигурации в формате XML, строит граф:

  • Узлы метаданных: справочники, документы, регистры, планы видов характеристик.
  • Рёбра: движения регистраторов, подписки на события, вызовы процедур и функций, обращения к виртуальным таблицам в текстах запросов.
  • Срезы: сигнатуры процедур/функций с параметрами и экспортностью.

Через MCP (Model Context Protocol) Cursor обращается к графу и получает точный срез — 10–30 файлов вместо 300 000 строк XML. Токенов тратится в 10 раз меньше, галлюцинации по полям практически исчезают.

Подключение Графона к Cursor за 4 шага

1. Индексация выгрузки

grafon index --src ./src --out .grafon/index.db

2. Конфиг MCP в проекте — .cursor/mcp.json:

{
  "mcpServers": {
    "grafon": {
      "command": "grafon",
      "args": ["mcp", "--index", ".grafon/index.db"],
      "env": { "GRAFON_SRC": "./src" }
    }
  }
}

3. Рестарт Cursor и проверка: в панели MCP должны появиться инструменты metadata_search, register_structure, find_movements, call_graph, impact_analysis, query_fields.

4. Файл правил .cursor/rules/1c.mdc — он заставляет агента сначала спрашивать граф, а потом писать код:

Всегда перед генерацией BSL, работающего с регистром накопления,
вызывай grafon.register_structure и grafon.find_movements.
Никогда не угадывай имена измерений, ресурсов и полей виртуальных таблиц.
Для условий в виртуальных таблицах параметры передавай через &Параметр,
список полей выводи явно.

Практические MCP-запросы

Структура регистра:

{ "tool": "register_structure", "args": { "name": "РегистрНакопления.ТоварыНаСкладах" } }

Ответ: вид регистра, измерения с типами, ресурсы, реквизиты, доступные виртуальные таблицы и точные имена полей (КоличествоОстаток, КоличествоОборот, КоличествоРасход).

Кто пишет движения:

{ "tool": "find_movements", "args": { "register": "ТоварыНаСкладах", "includeSubscriptions": true } }

Ответ — список процедур ОбработкаПроведения документов и подписок с номерами строк. Агент больше не ищет регистраторов grep’ом по гигабайтам кода.

Оценка влияния изменения:

{ "tool": "impact_analysis", "args": { "object": "РегистрНакопления.ТоварыНаСкладах", "change": "add_dimension", "dimension": "Склад" } }

Ответ: какие формы, отчёты, запросы и движения затронуты.

Сценарий: добавить измерение «Склад»

Без Графона. Агент читает XML регистра, не находит измерение Склад, правит XML, затем «на глаз» добавляет поле в 14 документов и ломает три отчёта с группировкой по складу.

С Графоном. Последовательность запросов:

  1. register_structure → измерения: Номенклатура, Характеристика; ресурсы: Количество.
  2. find_movements → 14 процедур ОбработкаПроведения, три из них уже используют Склад из шапки документа.
  3. impact_analysis → отчёты и формы, где потребуется новое измерение.
  4. Агент выдаёт точечные патчи — только по 17 затронутым файлам.

Итоговый код движения, который генерирует агент:

Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    Движения.ТоварыНаСкладах.Записывать = Истина;
    Для Каждого СтрокаТЧ Из Товары Цикл
        Движение = Движения.ТоварыНаСкладах.Добавить();
        Движение.ВидДвижения       = ВидДвиженияНакопления.Приход;
        Движение.Период            = Дата;
        Движение.Регистратор       = Ссылка;
        Движение.Номенклатура      = СтрокаТЧ.Номенклатура;
        Движение.Характеристика    = СтрокаТЧ.Характеристика;
        Движение.Склад             = Склад;  // новое измерение
        Движение.Количество        = СтрокаТЧ.Количество;
    КонецЦикла;
КонецПроцедуры

И запрос к виртуальной таблице с корректными именами полей:

Запрос.Текст =
"ВЫБРАТЬ
|    Остатки.Номенклатура,
|    Остатки.Склад,
|    Остатки.КоличествоОстаток КАК Количество
|ИЗ
|    РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад) КАК Остатки
|ГДЕ
|    Остатки.КоличествоОстаток > 0";

Ключевая разница: имена КоличествоОстаток и параметры виртуальной таблицы взяты из графа, а не выдуманы.

Сравнение: без Графона vs с Графоном

| Метрика (задача «добавить измерение в регистр») | Без Графона | С Графоном |
|---|---|---|
| Прочитано контекста, токенов | 90 000 – 140 000 | 8 000 – 12 000 |
| Время ответа агента | 4–8 мин | 40–70 сек |
| Галлюцинации имён полей | 3–7 на задачу | 0–1 |
| Затронутые файлы найдены | частично (grep) | 100 % (граф движений) |
| Стоимость запроса (условно) | 1.0x | 0.1x |
| Правки «наугад» в чужих модулях | да | нет |

Типичные ошибки внедрения

  • Индексировать только src/, но не Ext/ — тогда в графе нет движений и модулей наборов записей.
  • Давать агенту весь XML регистра «на всякий случай» — контекст забивается, и MCP-вызовы теряют смысл.
  • Не описывать правила в .cursor/rules — модель по привычке пойдёт читать файлы напрямую.
  • Игнорировать impact_analysis перед правкой структуры — получите тихий регресс в отчётах.
  • Забыть про подписки на события при поиске регистраторов — часть движений уйдёт мимо агента.

Чек-лист

  1. Выгрузка конфигурации в XML лежит в репозитории рядом с кодом.
  2. grafon index обновляется в CI или по pre-commit-хуку.
  3. .cursor/mcp.json подключён, MCP-инструменты видны в Cursor.
  4. .cursor/rules/1c.mdc запрещает угадывать поля регистров.
  5. Для задач по регистрам накопления обязательны вызовы register_structure и find_movements.
  6. Перед изменением метаданных — impact_analysis и обновление графа.

Регистр накопления — это не файл, а узел графа: измерения, ресурсы, регистраторы и виртуальные таблицы связаны рёбрами. Пока Cursor видит только текст, он будет гадать. Как только вы отдаёте ему граф через MCP, агент начинает работать как разработчик 1С, который читает конфигурацию, а не как автодополнение.