v8unpack-mcp
# v8unpack-mcp
MCP-сервер (stdio) для полного цикла работы с бинарными файлами 1С
(`.cf` / `.cfe` / `.epf` / `.erf`) **без импорта в проект EDT**:
```
unpack → чтение/правка → repack → cleanup
```
Единственная точка распаковки — `unpack`. Все остальные инструменты принимают
`dir_path` — каталог, созданный `unpack`, и не выполняют неявной распаковки.
---
## Возможности
| Инструмент | Сигнатура | Что делает |
|---|---|---|
| `unpack` | `(file_path)` | полная распаковка в отдельный временный каталог (без лимита размера), возвращает путь |
| `unpack_async` | `(file_path)` | асинхронная распаковка: сразу `{job_id, status:"running"}`, результат — через `job_status` (не срывается таймаутом клиента на крупных файлах) |
| `repack_async` | `(dir_path, output_path)` | асинхронная сборка: сразу `{job_id, status:"running"}`, результат — через `job_status` |
| `job_status` | `(job_id)` | статус фонового задания `unpack_async`/`repack_async`: `status`/`progress`/`result`/`error` |
| `list_objects` | `(dir_path)` | список объектов внутри контейнера `{вид_объекта: [имена]}` (только имена) |
| `get_metadata` | `(dir_path, object_path="", detail=false)` | метаданные: вид, счётчики по типам, объект (uuid, синоним, формы, макеты, модули) |
| `read_module` | `(dir_path, object_path="", module_name="")` | исходник BSL-модуля объекта (защищённый помечает `encrypted`) |
| `read_bytecode` | `(dir_path, object_path="")` | разбор байт-кода закрытого модуля (методы, константы, опкоды) |
| `search_code` | `(dir_path, pattern, ...)` | поиск подстроки/regex по коду, формам, макетам (слои `layers`) |
| `set_help` | `(dir_path, object_path="", help_html="", overwrite=false)` | записать справку объекта в raw-слой (сборку делает `repack`) |
| `diff` | `(dir_a, dir_b, full=true)` | сравнение двух распакованных каталогов пообъектно + дифф |
| `repack` | `(dir_path, output_path)` | сборка файла из распакованного каталога |
| `cleanup` | `(dir_path=null, all=false)` | удалить каталог unpack (или все по префиксу) |
Поиск бинарников `.cf/.cfe/.epf/.erf` на диске — стандартными файловыми
инструментами клиента (glob/list).
### Рабочий цикл
1. `unpack(file_path)` → `{status, dir, file, kind}`. Каталог `dir` содержит:
- **организованное дерево** (`Тип/Имя` + `.json` / `.obj.bsl` / формы / макеты) —
чтение и правка кода, форм, макетов, реквизитов;
- **raw-слой** `.v8unpack_raw/` (brace-файлы: `text`/`image`/help) — для
`read_bytecode`/`set_help`.
2. Чтение — `list_objects` / `get_metadata` / `read_module` / `read_bytecode` / `search_code`;
правка — файлами в `dir` (или `set_help`).
3. `repack(dir_path, output_path)` → `{status, output, bytes}`.
4. `cleanup(dir_path)` (или `cleanup(all=true)`).
Ошибки (нет файла/каталога, неверный тип) — исключениями. Каталог после `repack`
не удаляется автоматически — можно переиспользовать для нескольких сборок.
### Асинхронный режим для крупных файлов
`unpack`/`repack` ждут завершения работы синхронно — на больших `.cf` (сотни МБ–ГБ)
это может превысить таймаут MCP-клиента, и агент «сорвёт» запрос, хотя сервер продолжит
работать. Для таких случаев используйте асинхронные варианты — каждый вызов возвращается
мгновенно и не зависит от таймаута клиента:
1. `unpack_async(file_path)` → `{job_id, status:"running"}`;
2. повторять `job_status(job_id)`, пока `status != "done"` (промежуточные вызовы
возвращают `progress` — число обработанных объектов);
3. при `status="done"` взять результат из поля `result` (тот же dict, что у `unpack`);
при `status="error"` — сообщение в поле `error`.
Аналогично `repack_async(dir_path, output_path)` + `job_status`. Синхронные
`unpack`/`repack` сохранены для малых файлов и обратной совместимости.
### Схема `repack`
`repack` собирает через `v8unpack.build(use_raw=True)`:
- организованное дерево **не правилось** → raw-слой восстанавливается побайтно
(сохраняются help, байт-код, шифрованные модули);
- организованное дерево **правилось** → пересборка из организованного дерева.
Ограничение (all-or-nothing): в одной сессии либо правки организованного слоя
(код/формы), либо raw-слоя (help/байт-код) — не оба сразу. Пообъектное слияние —
отдельная задача.
### Что ищется в `search_code`
- `.bsl` — исходники модулей;
- `.json` — заголовки объектов, реквизиты и дерево элементов форм;
- `.txt` / `.html` — текстовые и HTML-макеты;
- `.bin` (СКД) — схема компоновки данных: бинарный префикс + XML с текстом запроса.
Параметр `layers` ограничивает области поиска: `modules` (`.bsl`), `forms` (`.json`),
`templates_text` (`.txt`), `templates_html` (`.html`), `dcc` (`.bin`-СКД). Пусто = все.
Каждое совпадение содержит поле `layer`.
Не ищется (бинарное): `.mxl` (табличный документ), картинки, роли (`.c1brace`),
зашифрованные модули. Парсер MXL — отдельная research-задача (см. `.ai/`).
### Сравнение (`diff`)
`diff(dir_a, dir_b, full=true)` сравнивает два распакованных каталога пообъектно:
- перечисляет каталоги объектов (`Тип/Имя` для cf/cfe, корень для epf/erf);
- собирает файлы каждого объекта (без служебного `.id.json`);
- статусы: `changed` / `added` / `removed` / `unchanged`;
- для изменённых строится `unified diff`, обрезанный лимитами
(MAX_DIFF_LINES=400, MAX_DIFF_FILES=20);
- `full=false` — только факт изменения, без построения диффа.
---
## Архитектура
- **Ядро распаковки** — [saby v8unpack](https://github.com/saby-integration/v8unpack)
(Python, MIT). **Вендорено** в `src/v8unpack/` с локальными патчами
(`keep_raw`/`use_raw`, `detect_format` для 8.3.24+, толерантность к неизвестным
группам метаданных).
- **Своя обёртка** — `src/v8unpack_mcp`: `core.py` (логика), `textlayers.py`
(извлечение текстовых слоёв), `server.py` (MCP-сервер).
- Распаковка — в отдельный временный каталог `%TEMP%\v8unpack_unpack_*` на каждый
вызов `unpack`; общий кэш отсутствует (агент сам управляет жизненным циклом через
`cleanup`).
- Для MCP отключаем multiprocessing v8unpack (серийный пул) и глушим stdout/stderr,
чтобы не ломать stdio-протокол; `OrganizerFile.pack/unpack` пропускают `.v8unpack_raw`.
```
v8unpack-mcp/
├── src/
│ ├── v8unpack/ # вендоренное ядро saby v8unpack (MIT) + патчи
│ └── v8unpack_mcp/
│ ├── __init__.py
│ ├── __main__.py # python -m v8unpack_mcp
│ ├── core.py # инструменты: unpack/чтение/правка/repack/cleanup
│ ├── textlayers.py # извлечение текстовых слоёв (поиск)
│ ├── bytecode.py # чтение байт-кода закрытых модулей (из raw-слоя)
│ ├── decompiler.py # декомпилятор байт-кода → BSL
│ ├── diffing.py # сравнение распакованных каталогов
│ └── server.py # MCP-сервер (stdio)
├── tests/
│ ├── test_core.py
│ └── test_server_e2e.py
└── pyproject.toml
```
---
## Установка и запуск
```bash
# MCP-сервер (вендоренное ядро v8unpack входит в пакет)
pip install -e .
# запуск (stdio)
python -m v8unpack_mcp
# или консольная команда
v8unpack-mcp
```
### Подключение к клиенту (MCP)
Сервер работает по **stdio**: каждый клиент сам запускает его отдельным процессом
по одной команде. Все инструменты принимают **абсолютные пути** к файлам, поэтому
рабочий каталог процесса не важен. Временные каталоги распаковки создаются в
системном `%TEMP%` с префиксом `v8unpack_unpack_`.
Рекомендуемая команда запуска — консольный скрипт `v8unpack-mcp` (создаётся при
`pip install`) либо `python -m v8unpack_mcp`. Для GUI-клиентов, которые не наследуют
ваш `PATH`, надёжнее указывать **абсолютный путь** к интерпретатору.
#### Стандартный формат MCP (`command` + `args`)
Claude Desktop, Claude Code, Cline, Continue, Roo, VS Code (`.mcp.json`) и другие
используют общий формат с полями `command` и `args`:
```json
{
"mcpServers": {
"v8unpack": {
"command": "v8unpack-mcp",
"args": []
}
}
}
```
Или с явным интерпретатором:
```json
{
"mcpServers": {
"v8unpack": {
"command": "~/путь/к/python.exe",
"args": ["-m", "v8unpack_mcp"]
}
}
}
```
Где разместить:
- **Claude Desktop** — `claude_desktop_config.json` (Настройки → Разработчик → Edit Config);
- **Claude Code** — `~/.claude.json` или проектный `.mcp.json`;
- **Cline / Continue / Roo** — проектный `.mcp.json` (шарится между участниками) или настройки пользователя;
- **VS Code** — `.vscode/mcp.json` (для сервера проекта) или пользовательские настройки.
#### Kilo Code / Kilo CLI (`kilo.json`, команда — массив)
Формат Kilo отличается: серверы задаются в `kilo.json` под ключом `"mcp"`, а команда
передаётся **одним массивом** (без разделения на `command`+`args`). Файл — проектный
`./kilo.json` / `.kilo/kilo.json` либо глобальный `~/.config/kilo/kilo.json`.
```jsonc
// kilo.json (проект)
{
"mcp": {
"v8unpack": {
"type": "local",
"command": ["v8unpack-mcp"],
"enabled": true,
"timeout": 15000
}
}
}
```
Или через `python -m`:
```jsonc
{
"mcp": {
"v8unpack": {
"type": "local",
"command": ["python", "-m", "v8unpack_mcp"],
"enabled": true
}
}
}
```
Сервер включается/выключается в TUI командой `/mcps`. Унаследованный сервер можно
отключить: `{ "v8unpack": { "enabled": false } }`.
Права на инструменты сервера — по ключам `v8unpack_*` (glob, срабатывает последнее
совпадение сверху вниз):
```jsonc
{
"permission": {
"v8unpack_*": "allow"
}
}
```
#### Рекомендации для нескольких клиентов
- **Установка**: один раз `pip install -e .` (для разработки) или
`pip install dist/v8unpack_mcp-0.2.0-py3-none-any.whl` (из собранного колеса);
зависимость `v8unpack` подтянется автоматически из `pyproject.toml`.
- **Единый интерпретатор**: используйте консольную команду `v8unpack-mcp` (попадает
в `PATH` установки) либо один и тот же абсолютный путь к `python.exe` во всех
конфигах — тогда любой клиент подхватит ту же установку.
- **Клиенты независимы**: каждый клиент держит свой stdio-процесс; общее состояние
— только временные каталоги unpack на диске. Можно смело подключать один и тот же
сервер к нескольким клиентам одновременно.
- **Пути с пробелами/кириллицей**: в JSON-конфигах пути заключайте в кавычки;
в массиве `command` (Kilo) элементы экранируются автоматически.
- **Тихий запуск**: сервер глушит прогресс распаковки и работает только по stdio —
интерактивный вывод в конфиги добавлять не нужно.
## Сборка
```bash
pip install build wheel # инструменты сборки
python -m build # создаст dist/v8unpack_mcp-<ver>-py3-none-any.whl и .tar.gz
pip install dist/v8unpack_mcp-0.2.0-py3-none-any.whl # установка из колеса
```
---
## Тесты
```bash
python tests/test_core.py # юнит-смоук ядра
python tests/test_server_e2e.py # end-to-end через stdio
```
Тесты используют файлы из `../testdata` (личные файлы, в git не входят — положите свои).
---
## Ограничения
- Большие `.cf` (сотни МБ — ГБ): `unpack` делает полный extract в отдельный каталог.
Пообъектный индекс (чтение одного объекта без полного extract) — следующий шаг.
- Табличные макеты (`.mxl`) пока не ищутся — бинарный формат, парсер в TODO.
- Защищённые (зашифрованные) модули: исходник без пароля не восстановить, но
`read_bytecode` разбирает компилированный байт-код, а `decompiler.py` умеет
декомпилировать его в BSL (инструмент `decompile` — в планах).
- Правки организованного слоя и raw-слоя (help/байт-код) в одной сессии не сливаются
(all-or-nothing `use_raw`).
## Заимствованные компоненты
Проект переиспользует открытые разработки сообщества:
| Компонент | Лицензия | Назначение | Ссылка |
|---|---|---|---|
| saby v8unpack | MIT (Copyright 2015 infactum) | ядро распаковки/сборки контейнеров 1С — **вендорено** в `src/v8unpack/` с патчами | https://github.com/saby-integration/v8unpack |
| EvilBeaver/v8asm | MIT | формат стека и таблица опкодов байт-кода 1С | https://github.com/EvilBeaver/v8asm |
| 1C-inversion | без явной лицензии (учебная, форк v8asm) | алгоритм декомпиляции байт-кода → BSL | https://github.com/ProhorP/1C-inversion |
`saby v8unpack` включён в состав пакета как `src/v8unpack/` (лицензия MIT сохранена в
`src/v8unpack/LICENSE`). `decompiler.py` — порт алгоритма 1C-inversion; `bytecode.py`
использует формат из v8asm.
⚠️ **Правовое замечание.** См. `DISCLAIMER.md` и `LICENSE`:
- Проект распространяется по лицензии MIT «как есть», без гарантий — использование
**на свой риск**.
- Лицензия «1С:Предприятия 8» запрещает изменять код/данные продукта нештатными
средствами, а также декомпилировать программную часть системы. Это ограничение
защищает платформу и **типовые** конфигурации 1С; на **собственные** конфигурации,
расширения и внешние обработки/отчёты оно не распространяется — работайте только
со своими объектами.
- Декомпиляция закрытых (запароленных) модулей реализована в исследовательских
целях и не должна применяться для взлома или снятия защиты чужих конфигураций
(ст. 146 УК РФ). Используйте только для восстановления собственных модулей.
## Полезные ссылки
- saby v8unpack: https://github.com/saby-integration/v8unpack
- EvilBeaver/v8asm: https://github.com/EvilBeaver/v8asm
- 1C-inversion: https://github.com/ProhorP/1C-inversion
- Формат MXL8 (спека): https://github.com/azubar/SpreadSheet/blob/main/docs/format-mxl.md
TDQS
Scored across 20 tools
Each tool targets a clearly distinct operation: unpack/repack lifecycle with async wrappers and job_status, object enumeration vs metadata, module source vs structure vs bytecode, help/template/DCS read/write variants, search, diff, and cleanup. Even read_dcs and export_dcs are differentiated by granularity (structured exploration vs full platform XML export). No two tools appear to perform the same job.
Most tools follow a verb_noun snake_case pattern (list_objects, get_metadata, read_module, import_template, export_dcs), and async variants consistently use the <verb>_async suffix. Minor deviations include bare verbs like unpack, repack, diff, cleanup, the noun-style job_status, and an arbitrary read/get split among extraction tools. Overall the naming is still predictable and readable.
At 20 tools, the server is on the upper boundary for tool count, but the 1C unpack/repack domain is complex enough to justify dedicated tools for async operations, module analysis, bytecode, help, templates, DCS, diff, and cleanup. A few closely related tools (read_dcs/export_dcs) could potentially be merged, but none feel like filler.
The toolset covers the full lifecycle: unpack (sync/async), inspection (list, metadata, modules, bytecode, search), modification (help, template, DCS imports into raw layer), repack (sync/async), diff, and cleanup. Minor gaps exist—no direct tool for writing arbitrary module code or forms—but editing the unpacked directory externally and repacking is a viable workaround.