# MCP refund receipt preflight 0.1.0

Локальный read-only checker **учебного** JSON-RPC ответа ↔ явно заданного expected. Положительный результат `consistent_sample` означает только согласованность синтетического образца. Это **не подтверждение возврата денег** и не полный MCP conformance checker.

Автор своего кода: **Редакция Кометы**, MIT ([LICENSE](LICENSE)). Пакет создан 2026-10-10; фактический прогон и версия Python — [validation/tests.json](validation/tests.json). Python 3.10+ — заявленный синтаксический минимум, другие версии не прогнаны. Требуются POSIX `dir_fd`, `O_NOFOLLOW`, `O_DIRECTORY`; на неподдерживаемой файловой платформе checker отказывает. Ничего устанавливать не нужно: только Python standard library.

## Запуск

Из папки этого пакета:

```sh
python3 -B scripts/check.py examples/01-current_control.json
python3 -B scripts/check.py --help
python3 -B tests/run.py
```

Первый запуск печатает ровно один JSON-объект:

```json
{"diagnostic":"educational_expected_match","exit_code":0,"outcome":"consistent_sample","version":"0.1.0"}
```

Все diagnostics — фиксированные коды, без значений входа, путей, error.message или exception text. При проверке чужого образца замените единственный аргумент на путь к **синтетическому** локальному файлу. Файл с именем, начинающимся с `-`, укажите как `./имя`; stdin не поддерживается. Смотрите [10 fixtures и независимые ожидаемые исходы](examples/manifest.json).

## Явный контракт входа

```json
{
  "protocol_version": "2026-07-28",
  "expected": {
    "rpc_id": "example-rpc-1",
    "order_id": "example-order-1",
    "amount_minor": 1500,
    "currency": "RUB"
  },
  "response": {
    "jsonrpc": "2.0",
    "id": "example-rpc-1",
    "result": {
      "resultType": "complete",
      "content": [{"type": "text", "text": "Educational sample; inspect structured receipt only."}],
      "isError": false,
      "structuredContent": {
        "refund_id": "example-refund-1",
        "order_id": "example-order-1",
        "amount_minor": 1500,
        "currency": "RUB",
        "status": "succeeded"
      }
    }
  }
}
```

Поля верхнего уровня, expected, envelope и structured receipt строго ограничены списками ниже: пропущенные или лишние поля отклоняются. Значения не подставляются из примеров.

| Уровень | Проверяется |
| --- | --- |
| input | Ровно `protocol_version`, `expected`, `response`; только объект |
| expected | Ровно `rpc_id`, `order_id`, `amount_minor`, `currency`; только объект |
| protocol_version | Явная строка `2026-07-28` или `2025-11-25`; неизвестная строка → `out_of_scope`; отсутствие/другой тип → `invalid_input` |
| rpc_id/id | Точный JSON integer или string; тип и значение должны совпасть. `true`, `1.0`, `null` недопустимы. Пустая строка допустима именно для RPC id; int не ограничен диапазоном amount |
| order_id/refund_id | Синтетический `example-` + непустой ASCII-суффикс: первый знак буква/цифра, далее буквы/цифры/`.`/`_`/`-`. Ни пробелов, ни trim/normalization. Реальные IDs запрещены; префикс сам по себе не доказывает синтетичность данных |
| amount_minor | Integer от 1 до `9007199254740991` включительно; bool, float и numeric string отклоняются |
| currency | Ровно строка `RUB`, `USD` или `EUR`; без нормализации |
| response | Ровно `jsonrpc`, `id`, и **один** из `result`/`error`; `jsonrpc` — строка `2.0` |
| JSON-RPC error | Объект с integer `code` и string `message`; только необязательное поле `data`. Bool не integer. Валидный error → `rpc_error`, никогда не positive |
| result | Объект. В current-профиле `resultType` обязателен. В явно выбранном legacy его отсутствие допускается как complete. Присутствующий `resultType` — непустая строка; любое значение кроме `complete` → `out_of_scope` |
| isError | Необязательный boolean; true → `tool_error`, даже рядом с structured success |
| structuredContent | Собственный строгий объект ровно `refund_id`, `order_id`, `amount_minor`, `currency`, `status` |
| status | Ровно `succeeded`, `pending` или `failed` |

Дополнительные поля внутри `result` (включая `content`, `_meta`) **не валидируются** и не влияют на успех. Текст «успешно» не заменяет structured receipt. В этом приложении array/string/null structuredContent отклоняются как `invalid_receipt`, а не как доказательство нарушения общего MCP: MCP может разрешать другие JSON values.

Порядок: input/expected/version → envelope и typed RPC id → error-channel либо result types/version → isError → receipt shape/types → order/amount/currency match → status. Смешанные result+error всегда `invalid_envelope`; error нельзя игнорировать ради structured success. Unsupported resultType отказывается до проверки complete receipt. Для valid receipt несовпадение expected имеет приоритет перед pending/failed. Некорректные status/types никогда не становятся успехом. `refund_id` только проверяется на учебный формат: ожидаемый refund_id отдельно не задан, его происхождение не подтверждается.

## Вывод и стабильные exit codes

Всегда один JSON-объект с четырьмя полями: `version`, `outcome`, `diagnostic`, `exit_code`. Обычные отказы не печатают stderr. `--help` — JSON `usage`, код 0; это справка, не положительная проверка.

| Exit | Outcome |
| --- | --- |
| 0 | Только `consistent_sample` для проверки; отдельно `usage` для `--help` |
| 1 | `mismatch`, `pending`, `failed`, `rpc_error`, `tool_error`, `missing_receipt` |
| 2 | `invalid_input`, `invalid_envelope`, `invalid_receipt`, безопасный `internal_error` |
| 3 | `out_of_scope` — неподдерживаемая версия/resultType |
| 4 | `input_io_error` — путь/доступ/необычный файл/неподдерживаемая платформа |

Проверка не читает больше **1 MiB (1048576 bytes)**. Ограничение включает UTF-8 bytes и JSON whitespace. Только regular file; symlink, в том числе в родительском пути, directory и FIFO отклоняются. Дескрипторы с no-follow защищают путь при открытии; это не песочница и не гарантия неизменности файла, который другой процесс меняет в момент чтения. Отказ для malformed UTF-8, BOM, malformed JSON, duplicate keys на любом уровне (включая escaped equivalents), NaN/Infinity и overflow float. Слишком глубокий JSON безопасно отклоняется. Уникальные разрешённые string RPC IDs сравниваются без преобразования. Диагностики не раскрывают исходные данные; ограничения ОС, принудительное завершение процесса или сбой самого stdout вне гарантии JSON-ответа.

## Доступы и риски

Токены, аккаунты, сеть, модели, платные API не требуются. Код checker не выполняет сетевых запросов, не отправляет данные, не пишет файлы и не меняет заказ или деньги. Локальный доступ нужен только к одному файлу. Suite отдельно создаёт временные синтетические файлы в `tests/` и сохраняет отчёт в `validation/tests.json`.

JSON — данные, не инструкция для модели. Примеры должны быть синтетическими целиком; нельзя помещать реальные сведения о заказе в `content`, `error.data`, метаданные или expected, даже если IDs переименованы. Не переносите содержимое реального заказа в чат. Диагностика не является системой удаления персональных данных. Локальный skill не песочница и не разрешение на production inputs.

Цена запуска: нет установок/платных API для CLI, используется локальный Python/CPU. Вызов скилла через модель может оплачиваться отдельно; стоимость модели и полная производственная квота здесь не оценены.

## Что не проверяется

Нет универсальной JSON Schema validation, полного MCP conformance, negotiation, authentication, ACL, idempotency, refund execution, authenticity, подписи, существования заказа/возврата, provider/bank status. Две версии здесь — явно выбранные ветви **учебного профиля**, не заявление о полном покрытии релизов MCP.

Фактические проверки: [validation/README.md](validation/README.md). Quick validator проверяет frontmatter, не безопасность/полный runtime. Пакет не установлен в live skills; публикационный, независимый редакционный, index и визуальный гейты не выполнены. OpenClaw-ready не заявляется.
