odata1c-gate
Локальный MCP-шлюз между Claude Code и стандартным OData-интерфейсом 1С:Предприятие 8.3.
Модель получает доступ к данным базы — справочникам, документам, регистрам, — но между ней и 1С
стоит гейт псевдонимизации: реквизиты (ИНН, счета, паспорта, телефоны) и, на выбранном уровне,
названия организаций и ФИО заменяются токенами вида [[type:tail]] до того, как данные увидит
модель. Обратная подмена происходит только внутри шлюза, перед отправкой запроса в 1С.
Один пользователь, одна машина, несколько сессий агентов одновременно, несколько баз 1С. Клиент — Claude Code (другие клиенты MCP работают, но подтверждение записи у них устроено иначе, см. docs/install.md).
Статус: чтение и запись закрыты (M1, M2), идёт этап поставки M3. Работают демон, лаунчер,
индекс метаданных, гейт всех уровней, девять тулов чтения и семь тулов записи; приёмка на живой
базе 1С пройдена и для чтения, и для записи (docs/probes/M1d-live-check.md,
docs/probes/M2-live-check.md). Этап M3 собирает всё это в пакет
PyPI и плагин Claude Code; первый выпуск — 0.1.0 (CHANGELOG.md).
Зачем это
Дать модели читать рабочую базу 1С — значит отдать ей персональные данные и коммерческую тайну: ИНН контрагентов, расчётные счета, телефоны и адреса физических лиц, названия клиентов. Обезличивать выгрузку заранее неудобно (модель должна видеть свежие данные и уметь дозапрашивать), а инструктировать модель «не показывай ИНН» бессмысленно — инструкция не механизм.
Шлюз решает это подменой на границе: модель работает с живой базой, но защищаемые значения
заменяются токенами до того, как попадут в её контекст. Токен детерминирован (одно значение — один
токен), поэтому по нему можно отбирать, связывать записи и вести разговор, не зная исходного
значения. Разработчик, который читает ответы модели, видит [[inn:M4T2Q9XZ7K]], а не ИНН — и при
необходимости раскрывает его сам, командой в терминале, мимо модели.
Что видит модель, а что нет
Что уходит модели. Структура базы (сущности, поля, ключи, навигация) и данные, прошедшие
гейт. Номера и даты документов, суммы, количества, коды, GUID, значения перечислений не
защищаются ни на одном уровне — без них работа с базой теряет смысл. Названия организаций и ФИО
(классы org и person) и защищаемые реквизиты — ИНН, КПП, ОГРН, счета, БИК, карты, СНИЛС,
документы, телефоны, почта, даты рождения, адреса — приходят токенами [[type:tail]] по правилам
политики базы. Уровень задаётся на базу: off — гейт выключен, identifiers — реквизиты,
identifiers+names — реквизиты плюс названия и ФИО.
Что не уходит никогда. Реальные значения защищаемых классов не выходят через MCP ни в одном
ответе: ни в данных, ни в текстах ошибок 1С, ни в превью записи, ни в журнале, ни в
odata1c_raw_get, ни в вопросах подтверждения. Это инвариант, а не тест: последний проход по
готовому ответу делает страж утечек — он ищет в сериализованном ответе известные словарю значения
и заменяет их токенами, если что-то прошло мимо гейта. Учётные данные 1С модель не получает:
пароль не покидает домашнего каталога вовсе, имя пользователя 1С не попадает ни в один ответ, а
адрес публикации базы не возвращает ни один тул. О самой базе модель узнаёт ровно то, что
перечисляет odata1c_bases: имя, подпись, роль, уровень гейта, разрешена ли запись, состояние
индекса (собран ли, когда, сколько сущностей) и конфигурацию 1С, к которой база отнесена.
Единственное исключение по адресу — текст сетевого сбоя, в который его может вписать HTTP-клиент.
Хук плагина вдобавок запрещает модели читать файлы домашнего каталога шлюза. Раскрыть токен может
только владелец машины, командой odata1c reveal в своём терминале.
Кто подтверждает запись. Тулы записи ничего не пишут в 1С: они готовят операцию, показывают
превью в токенах и возвращают pending_id. Выполняет её отдельный вызов odata1c_commit, и
только после подтверждения человека механизмом клиента — в Claude Code это диалог разрешения,
у клиентов с elicitation — вопрос шлюза. Реплика «да» в чате подтверждением не считается: модель
не может подтвердить запись сама себе. Каждая выполненная запись попадает в локальный журнал и
откатывается тулом odata1c_undo. Записи в базах с ролью prod по умолчанию нет вовсе, а состав
разрешённого сужается флагами разрешений в настройках базы.
Установка
Нужен uv — он сам поставит подходящий Python, отдельно ставить
интерпретатор не нужно. Дальше две команды в терминале ставят плагин Claude Code вместе со шлюзом:
claude plugin marketplace add Romandredan/odata1c-gate
claude plugin install odata1c@odata1c-gate
Плагин приносит MCP-сервер шлюза, три навыка, хук подтверждения записи и агента-следователя.
Версия пакета закреплена в plugin/.mcp.json, поэтому claude plugin update odata1c обновляет и
плагин, и шлюз.
Две оговорки. Первая: обе команды заработают начиная с выпуска 0.1.0 — пока тег не
опубликован, пакета odata1c-gate на PyPI нет, и uvx при первом запуске шлюза ответит отказом
(.mcp.json плагина закреплён на версии из репозитория). Вторая: репозиторий пока приватный,
claude plugin marketplace add клонирует его через git, поэтому на машине нужны учётные данные
git с доступом к нему. Открытие репозитория — отдельное решение владельца.
Отдельно ставится командная строка — она нужна владельцу базы, а не модели (описать базу, собрать индекс, раскрыть токен, править политику гейта):
uv tool install odata1c-gate
После этого команда odata1c доступна в терминале. Без установки то же самое запускается как
uvx --from odata1c-gate odata1c <команда>. Подробности, Linux, обновление и разбор типовых
сбоев — docs/install.md.
Первые пять минут
odata1c init # ~/.claude/odata1c/ с шаблонами настроек, права владельца
odata1c base add ut_test --role test --recipes ut # спросит адрес, подпись, пользователя, пароль
odata1c base test ut_test # проверить соединение с 1С
odata1c reindex ut_test # разобрать $metadata и построить индекс метаданных
odata1c doctor # проверить окружение: uv, дом, базы, демон, Claude Code
Начинать с init обязательно: плагин домашнего каталога не создаёт — его делают либо эта команда,
либо лаунчер при первой сессии Claude Code. doctor на пустом месте честно ответит FAIL в
строке домашнего каталога, поэтому он и стоит последним; запускать его можно в любой момент.
Адрес базы — это адрес публикации OData 1С, он оканчивается на /odata/standard.odata/
(например https://1c.example.local/ut/odata/standard.odata/). Пользователь 1С заводится
отдельный, с правами только на то, что нужно читать, — например odata_claude. Роль базы задаёт
умолчания: prod — уровень гейта identifiers+names и только чтение, test — уровень
identifiers, dev — гейт выключен и запись разрешена; любое поле переопределяется явно.
Пароль ложится в bases.yaml домашнего каталога открытым текстом, файл закрывается правами
владельца (ADR-0014).
Первый реиндекс долгий: у типовой УТ $metadata — это около 17 МБ описания и больше семи тысяч
сущностей. Дальше индекс пересобирается, только если у публикации изменилась контрольная сумма
$metadata.
Теперь можно спрашивать в Claude Code:
- «какие базы 1С мне доступны?» — модель вызовет
odata1c_bases; - «найди справочник контрагентов и покажи состав его полей» —
odata1c_find_entity, затемodata1c_describe_entityс классами гейта у каждого поля; - «возьми любого контрагента и покажи его пять последних заказов клиента» —
odata1c_query; название контрагента придёт токеном, номера и суммы документов — как есть.
Что модель видит и в каком порядке ходит — навык odata1c из плагина; справочные темы об
устройстве OData 1С, токенах, политике и протоколе записи — тул odata1c_info.
Что внутри
Два процесса из одного пакета: демон (odata1c daemon) — единственный на машину, держит
соединения с базами, индекс, словарь и журнал, отвечает по MCP Streamable HTTP на
127.0.0.1:7171; лаунчер (odata1c mcp) — тонкий stdio-процесс на сессию, который поднимает
демон при необходимости и проксирует ему вызовы. Собственной логики у лаунчера нет.
Тулы чтения: odata1c_bases, odata1c_find_entity, odata1c_describe_entity, odata1c_query,
odata1c_get, odata1c_info, odata1c_reindex, odata1c_raw_get, odata1c_recipe.
Тулы записи: odata1c_create, odata1c_update, odata1c_mark_for_deletion, odata1c_action
(проведение и отмена), odata1c_undo, odata1c_commit, odata1c_journal.
Рецепт — именованный параметризованный запрос к одной сущности («остатки на складе на дату», «задолженность контрагента»): модель подставляет параметры, шлюз строит запрос сам. Рецепты копятся по конфигурации 1С, а не по отдельной базе.
Ограничения
- Физического удаления объектов нет. «Удаление» для объектов и подчинённых регистров — только
пометка удаления (
DeletionMark = true).PUTне используется. - Сокращённое название в свободном тексте проходит открытым. Словарь знает полные написания названия; если в комментарии документа человек написал узнаваемое сокращение, которого нет в справочнике, гейт его не заменит. Ловить «ядро» названия отвергнуто: ядра — обычные слова, их замена портила бы данные ложными срабатываниями.
- Аутентификации у демона нет. Он слушает только
127.0.0.1; модель угроз — диск и машина владельца. Публикация по сети с TLS и токенами — этап M4. - Записи в регистры, подчинённые регистратору, нет ни при каком флаге разрешений: их пишет проведение документа, а не прямая запись.
- Шаблоны рецептов заполнены только для УТ. Для БП и ЗУП шаблоны пока пустые: базы для сверки имён нет, а невыверенный рецепт хуже пустого.
- Клиент — Claude Code. Claude Desktop и Cowork не поддерживаются (ADR-0012).
Документы
| Файл | Что внутри |
|---|---|
| docs/install.md | установка на Windows и Linux, обновление, типовые сбои, раздел сопровождающего |
| CHANGELOG.md | что вошло в выпуск |
| SPEC.md | спецификация v0.2, 16 разделов — главный артефакт проекта |
| CONTEXT.md | глоссарий: термины и запрещённые синонимы |
| docs/adr/ | 15 архитектурных решений (0001–0015), статус — во frontmatter |
| AGENTS.md | вводная для AI-агентов: архитектура, стек, инварианты, процесс |
| CLAUDE.md | указатель для Claude Code поверх AGENTS.md |
Структура репозитория
src/odata1c/ пакет PyPI odata1c-gate: демон, лаунчер, CLI (SPEC §2.2, §11.1)
config/ bases.yaml и daemon.yaml, роли, валидация, права файлов
registry/ реестр баз, статус индекса, видимость по сессии
client1c/ httpx-пул на базу, IBSession, семафор, маппинг ошибок 1С
index/ парсер EDMX → metadata.sqlite, реиндекс, нечёткий поиск
gate/ детекторы реквизитов, словарь, подмена в обе стороны, страж
write/ разрешения, pending-операции, commit, журнал, undo
recipes/ загрузка рецептов и рендеринг параметров в OData-литералы
tools/ регистрация тулов, ресурсов, промптов, server instructions
templates/ файлы-шаблоны в поставке: конфигурация и рецепты УТ/БП/ЗУП
cli.py команды odata1c: init, base, reindex, policy, recipe, doctor, reveal, daemon, mcp
daemon.py демон: тулы, ресурсы, промпт, MCP Streamable HTTP на 127.0.0.1
launcher.py лаунчер: stdio-прокси демону, подъём демона, проброс elicitation
plugin/ плагин Claude Code: манифест, .mcp.json, навыки, хук, агент, evals (SPEC §11.2)
.claude-plugin/ маркетплейс плагина для claude plugin marketplace add
tests/ unit / property / integration + образцы EDMX (SPEC §12)
tools/ bump_version.py, plugin_dev_copy.py, probes/ — скрипты проверок и приёмки
docs/adr/ архитектурные решения
docs/plans/ планы реализации по этапам
docs/probes/ отчёты технических проверок P1–P8 и приёмок на живой базе
Что не попадает в репозиторий
bases.yaml хранит пароли 1С открытым текстом (SPEC §3.4), поэтому в git не коммитятся
конфигурация рабочей машины, env-файлы, базы SQLite (словарь, индекс, журнал), выгрузки
$metadata и логи — см. .gitignore. В поставке живут только файлы-шаблоны
в src/odata1c/templates/, в тестах — урезанные образцы $metadata, не полные дампы.
Лицензия
MIT.
Release files for odata1c-gate 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| odata1c_gate-0.1.0.tar.gz | 502.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| odata1c_gate-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.1 MB
Release files / odata1c_gate-0.1.0.tar.gz
| Download URL | odata1c_gate-0.1.0.tar.gz |
|---|---|
| Size | 502.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
647180fc70488d7f2eafc2fa8f9c3c958a90ecd0efe79403e3ba20b734009c70
|
|
BLAKE2b-256 checksum How to use checksums |
e0349fd51c7004de77e5227789f7107286b25fd2a8e65db4b6fb97e431415130
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / odata1c_gate-0.1.0-py3-none-any.whl
| Download URL | odata1c_gate-0.1.0-py3-none-any.whl |
|---|---|
| Size | 555.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d92ca3034339f13e7384af3e2dac9a6407eb3be5aa3821293b1b425bf4fbfdfe
|
|
BLAKE2b-256 checksum How to use checksums |
ceeedfef719390f3cb42fcf605a47feff1c3386c8ec2ff771bb2377195204cee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log