Перейти к содержимому
Инвойсбокс
Инвойсбокс
Login iconВойти

Доверенность для машины: как ассистент выставляет счёт и почему не делает это один

Первая часть серии про MCP-сервер Инвойсбокс: как ассистент выставляет счёт, почему деньги не уходят в банк без человека и что продавец получает за одной командой.

59 минКоманда Инвойсбокс
Документ с печатью, над которым механическая рука держит ручку; рядом монета с символом рубля
Август 2026
Мы научили ИИ-ассистента выставлять счета покупателям, оформлять отгрузку и возвращать деньги. Человек пишет в чат обычную фразу («выстави счёт компании по этому ИНН на 122 000 рублей за консультацию») и получает ссылку на оплату с QR-кодом. Кода при этом никто не писал: ассистент работает через MCP-сервер Инвойсбокс, который ставится одной командой.Сто рублей и сто тысяч в диалоге выглядят одинаково. Человек читает «выставляю счёт на 122 000 ₽ за консультацию», отвечает «да, давай» — и не видит, что в сумме на один ноль больше, чем он просил. Модель не соврала: она сложила цену за единицу с ценой позиции и ошиблась там, где путаются и живые бухгалтеры. Счёт уйдёт покупателю, покупатель заплатит. Дальше — неприятный разговор и переделка закрывающих документов.Поэтому сервер обязан поймать такую ошибку раньше банка. Ни одна денежная операция у нас не уходит дальше без человека, который увидел сумму и назвал её сам.Внутри — восемь инструментов, права по умолчанию только на чтение, любая денежная операция в два шага, отказ вместо угадывания. Где-то механика простая до скуки, где-то мы сначала сделали наоборот — и признаёмся, где именно.Коротко, на двадцать секунд.
  • восемь инструментов: от поиска компании по ИНН до возврата денег;
  • права свежей установки — только чтение, запись включает администратор;
  • любая денежная операция идёт в два вызова, и второй проверяет отпечаток параметров;
  • выше 100 000 ₽ сумму называет человек, а не модель;
  • суммы живут целыми копейками, повтор запроса не создаёт второй счёт;
  • когда результат неизвестен, сервер так и отвечает, вместо того чтобы угадать.
Серия из трёх частей. Здесь, в первой, — механика денег и то, что продавец получает от такого сервера. Во второй — границы доверия и способы проверки: чужой текст как данные, что в действительности открывает токен, чем такой сервер проверяют. В третьей — публикация сервера, рынок агентных покупок и то, как ту же задачу решают карточные схемы, банки и российские сервисы.Каждая часть — около часа чтения. Внутри разобран каждый предохранитель, названы места, где мы сначала сделали неправильно, и показано, что с той же задачей делают другие. Если нужен короткий ответ, что это и как попробовать за десять минут, он есть в новости и в документации. А если вы из тех, кто весной 2026 года читал, что протокол MCP умирает, — в третьей части есть отдельный разговор со скептиками.

Что такое MCP, если объяснять с нуля

Ассистентом в этой статье называется любая программа, с которой человек общается словами: Алиса, ChatGPT, Claude, чат-бот на сайте магазина, помощник внутри рабочего мессенджера. Внутри такой программы работает языковая модель, и умеет она ровно одно — писать текст. Написать «счёт выставлен» модель может всегда. Выставить настоящий счёт она сможет только тогда, когда ей дали доступ к системе, которая это делает. — общее правило, по которому такой доступ выдают. Со стороны Инвойсбокс работает отдельная небольшая программа, её называют MCP-сервером. Она сообщает ассистенту список действий, которые готова выполнить: найти организацию по ИНН, выставить счёт, посмотреть оплаты, вернуть деньги. К каждому действию приложены описание обычными словами и перечень нужных данных — вроде подписей к полям формы. Ассистент передаёт список модели, модель выбирает одно действие и заполняет данные.Слово «агент» дальше означает ту же программу, когда она действует по поручению человека и сама выбирает, какие шаги для этого сделать.Как это выглядит на практике. Человек пишет в чат: «выстави счёт компании с таким-то ИНН на 12 000 рублей за консультацию». Модель ничего не отправляет в банк сама — она выбирает из списка действие «выставить счёт» и подставляет плательщика, наименование услуги и сумму. Запрос передаёт ассистент, выполняет его сервер.Чат здесь просто привычный случай. Та же связка работает голосом — человек проговаривает поручение, а счёт уходит покупателю; работает внутри автоматизации, где ассистента вызывает не человек, а сценарий по событию: пришла заявка с сайта, менеджер отметил сделку в CRM, наступила дата продления. Меняется только то, кто и как формулирует поручение; сервер и его предохранители остаются теми же. Набор сценариев, которые мы разобрали отдельно, — в документации.Значит, решение остаётся за сервером. Он выполняет операцию, отказывает или требует участия человека — и он же обязан поймать ошибку модели прежде, чем она превратится в списание денег (спецификация протокола).

О чём первая часть

1
Зачем денежному API сервер для ассистента

2
Мост между ассистентом и платёжной инфраструктурой: что получает продавец

3
Права агента: доверенность с ограничениями

4
Что требует стандарт и что он позволяет не делать

5
Устройство сервера и решения, за которые платили

6
Деньги: подтверждение, идемпотентность и целые копейки

7
Ответственность, цена и данные

Зачем денежному API сервер для ассистента

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

Что берёт на себя протокол и что оставляет вам

Протокол описывает, как ассистент узнаёт, что ему доступно, и как он этим пользуется. Сервер присылает список действий — у каждого есть имя, описание словами, перечень нужных данных и необязательные пометки вроде «это действие только читает» или «его безопасно повторить». Рядом сервер может отдать справочные материалы: списки допустимых значений, короткие инструкции. Программа, в которой человек разговаривает с моделью (её называют клиентом), показывает этот список модели, а модель выбирает, что вызвать. Роли сторон, формат обмена и требования к безопасности собраны в спецификации протокола.Дальше протокол умолкает. Выполнить операцию, отказать или потребовать участия человека решает сервер. Спецификация просит клиентов считать описания и пометки инструментов недоверенными, если сервер не доверенный, и оставлять человеку возможность отклонить вызов (раздел Tools). В нормативной схеме сказано ещё жёстче: пометки — это подсказки, и строить на них решения об использовании инструмента нельзя. Любой запрет, который держится на тексте описания, остаётся просьбой. Для человека, который подписывает платёжки, это переводится так: фраза «модели сказано не платить дважды» — не контроль. Контроль — это код, который откажет.
quoteЛюбой запрет, который держится на тексте описания, остаётся просьбой. Фраза «модели сказано не платить дважды» — не контроль. Контроль — это код, который откажет.

Почему сервер ходит только публичным API

Наш сервер работает клиентом публичного API /v3 Инвойсбокс и доступа к базе не имеет — сознательно. Платим за это тем, что внутренний код переиспользовать нельзя, сервер ходит теми же методами, что любой внешний интегратор, и всё, чего в публичном API нет, для него не существует. Взамен появились два свойства, без которых продукта бы не было.Первое — сервер запускается на машине того, кто им пользуется: клиент поднимает его дочерним процессом и обменивается сообщениями через стандартные потоки ввода-вывода (stdio-транспорт). Ключи остаются у клиента, а наружу отправляется ровно то же, что отправлял бы у любого интегратора. Второе — исходники такого сервера можно открыть целиком, ничего не рассказав об устройстве платёжной платформы. Оба свойства нужны для доверия: решение «пустить ассистента к своим счетам» человек принимает, глядя на код, и заглянуть в него должно быть можно без нашего разрешения.Границу проверяет машина. Тест в сборке сверяет каждый адрес из каталога со списком разрешённых публичных методов и отдельно запрещает пути прежней версии API (test/contract.test.ts). Одной строки достаточно, чтобы сервер пошёл туда, куда внешнему интегратору нельзя, и в код-ревью такая строка теряется среди похожих: человек видит вызов, похожий на десять соседних, и пропускает его.

Кто вызывающая сторона

Обычный SDK вызывает разработчик: он прочитал контракт, знает предметную область и отвечает за последствия. Здесь аргументы собирает модель по тексту описания, а последствия измеряются в рублях и в комплекте закрывающих документов. Инженерный блог Anthropic называет частой ошибкой инструменты, которые просто оборачивают адреса методов существующего API (Writing effective tools for AI agents); у этой ошибки в деньгах есть цена — тонкая обёртка передаёт модели всю свободу API, включая свободу списать не ту сумму не тому получателю.Отсюда расположение проверок.Гейты стоят на сервере, поэтому клиент, который вообще не поддерживает аннотации MCP, всё равно не может выставить счёт одним вызовом модели. К тому же выводу пришла отраслевая классификация рисков. Избыточная агентность поднялась в OWASP GenAI LLM Top 10 с шестого места в 2025 году на третье в 2026 (список 2026), и рекомендация там прямая: права и способность менять состояние держать в коде приложения, модели их не давать, а перед привилегированным, необратимым или видимым снаружи действием требовать явного подтверждения человека.

Восемь инструментов

8

инструментов в первой версии: от поиска по ИНН до возврата

2

сторонние библиотеки в рантайме — официальный SDK и zod

93 из 98

пакетов в транзитивном дереве при закреплённом пороге

15 мин

живёт одноразовый токен подтверждения операции

В первой версии их восемь: поиск компании по ИНН, чтение заказа, поиск заказов, поиск отгрузок, создание заказа, отмена заказа, отгрузка и возврат. Ранние наброски предлагали двенадцать, отдельная постановка — семь; восемь получились как минимальный набор, при котором продукт делает то, что о нём сказано.Ограничение здесь держится не на числе. Инструмент виден модели при двух условиях: включён его набор и серверу хватает настроек, чтобы его выполнить; ненастроенный сервер каталогом ничего и не обещает, а выбрать то, чего в каталоге нет, модель не может. Права по умолчанию — только чтение, запись включает администратор отдельным осознанным действием при запуске.Тот, кто поставил сервер «посмотреть», получает сервер, который умеет смотреть.Все четыре пишущих инструмента двухфазные. Первый вызов не делает ни одной записи: возвращаются сводка операции и токен подтверждения, короткий одноразовый пропуск, привязанный к этой сумме и этому получателю, — и только второй вызов с этим токеном выполняет операцию. Токен живёт пятнадцать минут, срабатывает один раз и подписан вместе с отпечатком параметров, поэтому «подтвердили одну сумму, отправили другую» закрыто проверкой подписи; аккуратность кода самого инструмента тут уже ни при чём. Идею мы не придумывали. В платёжном регулировании ЕС код аутентификации привязан к сумме и получателю, и изменение любого из двух аннулирует подтверждение (RTS к PSD2, ст. 5); в рекомендациях OWASP для агентов то же требование записано как привязка одобрения к конкретным параметрам действия с коротким сроком годности (AI Agent Security).Закрывающие документы — акт или , счёт-фактура и УПД (универсальный передаточный документ, заменяющий связку «акт плюс счёт-фактура») — Инвойсбокс формирует по событию отгрузки. Значит их запускает подтверждённая отгрузка, и отдельный инструмент «сформировать УПД» не нужен. На этом же держится единственное утверждение о первенстве, которое мы себе позволяем: наш сервер закрывает сделку между компаниями целиком — от счёта до закрывающих документов.У банковских MCP-серверов есть платёж и выписка, у зарубежных (Stripe, PayPal, Brex) закрывающих документов нет по устройству их предметной области. Отсюда объём первой версии: без документов утверждение ложно, поэтому отгрузка вошла в неё, а удобства вроде правки счёта, подписок и холдирования отложены.Состав каталога мы сознательно держим узким, и дело не в возможностях платформы: API при необходимости расширяется. Дело в том, что каждый инструмент, попавший в каталог, обязан работать точно — на любых входных данных, включая неудобные. Инструмент, который в девяти случаях из десяти делает верный счёт, хуже отсутствующего: отсутствующий заставит человека открыть кабинет, а неточный однажды выставит покупателю не ту сумму. Поэтому сначала мы убеждаемся в стабильности и точности того, что уже есть, и только потом добавляем следующее.

Граница обещаний

Обещание сформулировано узко. Ошибка модели не превращается в денежную операцию без человека. Гарантии, что модель не ошибётся, здесь нет, и любой, кто такую гарантию даёт, продаёт лотерейный билет. Вся остальная механика служит этому одному обещанию: двухфазная запись, суточные ограничения по числу операций и сумме возвратов, защита от дублей и отдельный исход «неизвестно» вместо угадывания. Позже выяснилось, что решение совпало с позицией регулятора: в методических рекомендациях от 16 июня 2026 года Банк России советует подтверждать операцию сотрудником там, где ИИ применяется в критически важных процессах, и прямо называет среди них платёжные (Банк России).Одно требование ждут от всякого платёжного продукта, поэтому про него отдельно. PCI DSS к серверу не относится по устройству дела: данных карт в его периметре нет вообще — карту покупатель вводит на платёжной странице банка-партнёра, а сервер получает только ссылку на оплату и статус заказа. Это не заслуга сервера, а следствие того, что деньги идут не через него.Третья граница касается демонстрационного доступа, и здесь у нас есть рекомендация из опыта. Пробный магазин с общим токеном — правильная вещь: попробовать сервер без своего договора должно быть можно, иначе до установки дело не дойдёт. Но такой контур один на всех читателей, а значит, пробные счета видит каждый, кто взял те же параметры из документации. Сказать об этом нужно прямо и в трёх местах сразу: в быстром старте, в файле с описанием пакета и в ответах самих инструментов, потому что человек читает что-то одно.Риск здесь не в коде, а в том, что читатель делает ровно написанное — и попадает в общую песочницу с чужими данными. Тесты по функциям и по безопасности кода такого не находят, ищет его только вопрос «а что получится, если пройти документацию буквально».

Чем платит публичность

Сервер поставляется пакетом — это архив с файлами программы, который ставят одной командой из публичного каталога npm. Актуальная версия, 0.2.1 от 11 августа 2026 года, опубликована с провенансом сборки: это запись, по которой любой желающий машинно проверит, из какого репозитория и какого коммита собран архив (provenance в npm). Исходники открыты зеркалом под лицензией MIT.Публичность меняет требования сильнее, чем кажется со стороны.Код читает тот, кто решает, доверить ли ассистенту выпуск счетов у себя, поэтому неудобные места дешевле назвать самим — их всё равно найдут. Установку на чужой машине не откатить: плохая версия живёт там, пока владелец не обновится руками. Телеметрии с машины клиента нет и быть не должно, так что о сбое мы узнаём из жалобы; графика с чужими ошибками у нас не будет никогда. Этими тремя обстоятельствами объясняется почти каждое инженерное решение ниже.

Мост между ассистентом и платёжной инфраструктурой

За короткой командой «выставь счёт» стоит вся машинерия приёма денег: способы оплаты, чеки, отчётные документы, уведомления. Всё это достаётся агенту вместе с одним инструментом выставления счёта, и разбираться в машинерии ему не приходится.

Сколько это занимает у человека

Три часа ночи, в офисе никого. На сайте — снабженец, которому нужно закрыть заявку до утра. Он пишет в чат: «нужен счёт на консультацию по подключению, ИНН 7701234560, 12 000 рублей с НДС». Ассистент находит реквизиты по ИНН, собирает счёт и показывает сводку: кому, за что, на какую сумму, до какого числа. Снабженец подтверждает — и в том же окне появляются ссылка на оплату и QR-код. Он платит по СБП с расчётного счёта.Продавец узнаёт об этом утром, из уведомления о смене статуса. После отгрузки платформа сама сформирует УПД и отправит его по ЭДО.Теперь то же самое руками. Счёт юрлицу в кабинете — это три группы полей на двух-трёх экранах. Плательщик и его данные: ИНН, телефон, электронная почта. Состав счёта: номенклатура, количество, цена, сумма позиции, ставка налога. Реквизиты самого счёта: номер, дата, срок оплаты. Плюс переходы между экранами и проверка, что позиции сошлись в копейку.В диалоге тех же полей нет ни одного.Мы замерили на демонстрационном контуре, на той же живой модели, что стоит на странице демонстрации: от отправленной фразы до сводки — двенадцать секунд, от нажатия «Подтвердить» до ссылки на оплату — пять. Семнадцать секунд, два действия человека, один экран.Оговорку сделаем сразу. Это одно измерение на демо, где работает небольшая открытая модель на нашем узле; сильная модель отвечает быстрее, загруженный узел медленнее, и статистики за этими числами нет. От модели не зависит другое: полей для заполнения ноль, экранов один, а счёт попадает к покупателю в ту секунду, когда он сказал «да», — не письмом, которое ещё нужно найти среди других писем.

Ссылка, за которой стоит касса

Агент делает один вызов и получает в ответ ссылку и QR-код. Дальше он просто передаёт их человеку — в чат, в письмо, в мессенджер. Ссылка ведёт на платёжную страницу Инвойсбокс, и с этого момента способ оплаты выбирает покупатель.Что именно предложат покупателю на этой странице, зависит от способов оплаты, подключённых магазину; полный набор Инвойсбокс выглядит так. Банковские карты «Мир». Оплата по QR-коду через . СБП B2B — перевод между счетами организаций. Оплата по счёту для юридических лиц и индивидуальных предпринимателей, когда бухгалтерии нужен документ для проводки. Отсрочка платежа и гарантийный фонд для случаев, когда деньги и отгрузка расходятся по времени. Как это выглядит для покупателя, описано в разделе про платёжную страницу, а полный перечень — в разделе про платёжные инструменты.Важно, что агент про этот перечень ничего не знает и знать не обязан. Он умеет ровно одно — сформировать счёт и отдать человеку ссылку. Всё остальное — забота платёжной страницы.Для инженера это знакомая экономия. Каждый способ оплаты, добавленный в собственный код, — это своя проверка статуса, свои коды ошибок, свой тестовый контур и свой набор краевых случаев. Когда всё это живёт за одной ссылкой, поверхность интеграции сжимается до одного вызова, и вместе с ней сжимается объём того, что может сломаться после очередного обновления модели или библиотеки.

Цифровой рубль приходит той же дорогой

С 1 сентября 2026 года цифровой рубль обязаны принимать продавцы с выручкой свыше 120 миллионов рублей, которые работают с физлицами и обслуживаются в системно значимом банке. Для выручки до 5 миллионов и для точек без устойчивого интернета сделаны освобождения. Остальные подключаются позже — в 2027 и 2028 годах. Для продавца это звучит как ещё одна интеграция, ещё один срок и ещё один риск не успеть. На практике цифровой рубль принимается через универсальный платёжный код — единый QR-код . Тот же код, та же страница, тот же сценарий для покупателя.Работает он для физических лиц, юридических лиц и индивидуальных предпринимателей, то есть закрывает и розницу, и расчёты между компаниями. Деньги приходят за несколько секунд — без ожидания межбанковского цикла. Физическому лицу формируется кассовый чек по , организации — универсальный передаточный документ, счёт-фактура, акт или накладная, которые уходят по . Подробный разбор — в материале про цифровой рубль.Для агента же в этой истории не меняется вообще ничего: он по-прежнему отдаёт человеку ссылку и код.

Документы формирует платформа, агент подтверждает отгрузку

На отчётных документах обычно тонут самодельные интеграции. Универсальный передаточный документ нужно собрать по правилам, подписать, отправить контрагенту через оператора ЭДО, дождаться квитанции. Ошибка здесь стоит дорого: расхождение в документах всплывает через месяцы, на сверке или на проверке.В Инвойсбокс документы формируются по событию отгрузки. Агенту достаточно подтвердить, что товар передан или услуга оказана, — дальше платформа сама решает, какой документ положен этому покупателю и как его доставить. Учить модель выписывать УПД не требуется, а значит, не требуется и проверять, не сочинила ли она реквизиты. Это тот случай, когда сужение полномочий агента прямо повышает надёжность результата: сам документ модель не заполняет. Ошибиться она может в составе корзины и в реквизитах — против этого стоят проверки, разобранные ниже.

Об оплате сообщают сразу

Второй классический источник боли в том, чтобы вовремя узнать об оплате. Наивный путь: раз в минуту спрашивать платформу «уже?». Такой опрос тратит запросы впустую, отстаёт на минуту в худшем случае и красиво ломается, когда клиентов становится много.Инвойсбокс присылает продавцу уведомление о смене статуса: платёж прошёл — на ваш адрес приходит запрос с данными операции — как это устроено. Агент дожидается события и реагирует на него. Для человека разница видна сразу: подтверждение приходит в тот же момент, когда покупатель закрыл платёжную страницу.

Почему новый способ оплаты не требует правок в агенте

Сложите всё вместе. У агента один инструмент — выставить счёт. За этим инструментом стоят карты «Мир», СБП, СБП B2B, оплата по счёту, отсрочка, гарантийный фонд, цифровой рубль. Плюс чеки и документы по факту отгрузки. Плюс уведомление об оплате.Когда на стороне инфраструктуры появляется очередной способ платить, он появляется на платёжной странице для всех сразу. Агент, написанный полгода назад, начинает его поддерживать в тот же день, и никто не переписывает ни строчки промпта. Для инженера это привычная развязка: точка расширения живёт там, где меняется предметная область, — в платёжной инфраструктуре. Для владельца бизнеса это ответ на вопрос «когда мы это доделаем»: доделывать нечего, набор способов оплаты обновляется без вас.

Что получает продавец

Если свести всё к списку выгод, получится шесть пунктов, и каждый из них — следствие того, что за одним инструментом стоит платёжная платформа целиком.

Ссылка на оплату приходит туда, где человек согласился

Между решением и оплатой не остаётся ни письма, ни ожидания, ни поиска счёта в почте. Каждый такой шаг стоит конверсии.

Покупатель платит привычным способом

Карты «Мир», оплата по QR-коду через СБП, СБП B2B, оплата по счёту, отсрочка платежа, гарантийный фонд, а с 1 сентября 2026 года — цифровой рубль через тот же универсальный QR-код.

Документы формирует платформа

Фискальный чек физлицу, счёт организации, акт, счёт-фактура и УПД после отгрузки. Учить этому модель не нужно.

Об оплате сообщает платформа

Уведомление о смене статуса приходит само, опрашивать API в цикле не требуется.

Новые способы оплаты подключаются без правок агента

Список живёт на платёжной странице, поэтому ни описание инструмента, ни промпт не меняются.

Интеграции хватает одной команды

Сервер ставится одной строкой в конфигурации ассистента, собственного кода писать не нужно вообще.
Теперь о том, чего продавец не получает. Сервер не заменяет учётную систему, не ведёт склад, не решает за человека, кому и на какую сумму выставить счёт. Он превращает поручение, сказанное словами, в корректный счёт — и останавливается там, где начинается решение человека.

Права агента: доверенность с ограничениями

Ассистент, умеющий выставлять счета и отмечать отгрузки, распоряжается деньгами компании. Разумный вопрос — кто и чем ограничивает такого помощника. Границы задаёт владелец, а держит их система, и ниже разобрано, как именно.
Доверенность для агента: перечень разрешённых действий, ограничения по суммам и числу операций за день, подтверждение крупных сумм. Границы держит система, а не формулировка подсказки.

Токен выдаёт сам пользователь

Доступ агента начинается с . Выдаёте его вы сами в личном кабинете Инвойсбокс. При выпуске вы указываете области действия, они же scopes, — перечень того, что этому конкретному ключу разрешено делать.Собственных прав у агента нет. Есть узкий срез ваших, выданный под конкретную задачу. Ключ для помощника, который выставляет счета, не обязан уметь ничего кроме этого — и буквально не умеет.Здесь же лежит ответ на вопрос «а что, если ассистент поведёт себя странно». Странно он себя поведёт внутри того периметра, который вы нарисовали при выпуске ключа. Расширить свои полномочия агент не может: решение о допуске принимает сервер, глядя на области действия предъявленного токена, и уговорить это решение нечем — ни настойчивостью, ни изобретательной формулировкой запроса.

Отказ до попытки

Сервер проверяет права до вызова инструмента. Если у токена нет нужной области действия, приходит отказ «недостаточно прав» — в терминах протокола 403 insufficient_scope.Разница с наивным подходом принципиальна. Наивный вариант — попробовать операцию и посмотреть, что ответит платформа. Тогда сама попытка уже становится событием: где-то заведён черновик, где-то записан лог, где-то сработал счётчик. Ранняя проверка означает, что запрещённое действие не начинает происходить вовсе. Для непрофессионала это как охрана на входе: неподходящий пропуск разворачивают у турникета, до кабинета директора он не доходит.

Области действия — коды прав, которые уже есть в системе

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

Каталог инструментов собирается сознательно

По умолчанию сервер отдаёт агенту только чтение. Инструменты, меняющие данные, включает администратор отдельным решением. То есть свежая установка не может ничего испортить, даже если модель очень захочет.Отдельный приём — каталог зависит от настройки.Сервер настраивается набором параметров: ключ доступа, идентификаторы магазина и контрагента, , включённые наборы инструментов. Пока нужного параметра нет, инструменты, которым без него нечем работать, в каталоге не появляются — и модель физически не может их позвать, потому что не видит. Это сильнее любого запрета. У модели нет соблазна, нет упоминания в подсказке, нет шанса позвать инструмент «на всякий случай» и получить ошибку, которую придётся объяснять человеку. Чем короче список доступного, тем предсказуемее поведение — это верно и для людей, и для моделей.

Суточные ограничения и подтверждение суммы

Поверх областей действия работают суточные ограничения на организацию: по числу счетов, отгрузок и возвратов и по сумме возвращённых денег. Они закрывают сценарий, который права не закрывают, — много мелких разрешённых действий, складывающихся в крупный ущерб; устройство и значения разобраны в главе про деньги ниже.Любая денежная операция идёт в два приёма, сначала подготовка и затем отдельное подтверждение. Крупная сумма требует, чтобы человек назвал её отдельно — подтверждения «да, продолжай» недостаточно. Это защита от самой частой ошибки в диалоге: человек соглашается с формулировкой, не заметив, что в ней три лишних нуля.

Что это значит без технических слов

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

Что требует стандарт и что он позволяет не делать

Спецификация MCP заканчивается раньше, чем начинаются деньги, и различать эти две области приходится постоянно. Часть обещаний клиента проверяется кодом, часть остаётся словами — и защищать деньги своими руками приходится именно там, где остаются слова. — открытый протокол, по которому ассистент подключается к внешним инструментам. Обзор спецификации описывает три роли — , клиент внутри него и сервер, который отдаёт инструменты, — и обмен сообщениями между ними. Актуальная ревизия на сегодня — 2026-07-28.Сервер объявляет клиенту три возможности: инструменты, ресурсы и логирование. Промптов в этом списке нет, запрос их списка отвечает ошибкой метода, и тест требует именно такого ответа. Промпт — заготовка текста, которую сервер подкладывает в разговор человека с моделью. Отдавая её, продукт начинает управлять беседой формулировками, за которые команда отвечает, но проверить их не может: описания инструментов пересчитываются в контрольную сумму и ограничены по длине, поэтому любая их правка видна в тестах, а текст промпта живёт вне любого утверждаемого контракта. Влияние на поведение модели остаётся у описаний инструментов и у данных, которые сервер возвращает.

Справочники вынесены в ресурсы

Ресурс в MCP хранит данные, которые клиент читает и кэширует у себя, ничего при этом не выполняя. Здесь это способ не платить токенами за одно и то же знание в каждом запросе.Три ресурса отдают то, что иначе пришлось бы вписывать в описания инструментов: ставки НДС (включая 22 % в двух формах — налог сверх цены и налог, уже сидящий в цене), девять ходовых кодов единиц измерения из общероссийского классификатора и эталонное тело счёта с суммами в целых копейках. Две формы НДС выглядят педантизмом, пока не посчитать: одна и та же ставка на разной базе даёт разные итоги, и путаница здесь оборачивается расхождением в документах, которое исправляется тоже документами. Тест читает ставки через протокол и проверяет наличие кода RUS_VAT22, чтобы справочник не рассыпался незаметно.Выигрыш посчитан. Из описаний инструментов ушли двенадцать кодов ставок и парные единицы измерения, там осталось только то, что относится к самому вызову. Модель платит за эти строки один раз, при каждом следующем обращении они уже не приходят.

Аннотации: стандарт сам называет их подсказками

Каждый инструмент объявляет четыре аннотации, вычисленные из одного признака «меняет ли данные».

readOnlyHint

Значение: чтение. Смысл: инструмент только смотрит и ничего не меняет

destructiveHint

Значение: запись. Смысл: любая запись в платёжном API помечена разрушительной

idempotentHint

Значение: запись небезопасна на повтор. Смысл: повтор может создать вторую операцию

openWorldHint

Значение: всегда true. Смысл: инструмент обращается к внешней системе
Пометка «разрушительный» у выставления счёта выглядит спорно: счёт ничего не удаляет. Выбор консервативный намеренно — клиент покажет предупреждение перед каждой записью, и цена лишнего подтверждения ниже цены пропущенного. Тесты сверяют пометку чтения у поиска заказов и пометку разрушительности у возврата.Слово «hint» в названии поля описывает силу гарантии точно. Нормативная схема протокола говорит прямо: все свойства аннотаций — подсказки, и клиентам не следует принимать решения о вызове инструмента на основании аннотаций, полученных от недоверенного сервера. То же в разделе Tools: клиенты обязаны считать аннотации недоверенными, пока сервер не признан доверенным, и человек должен иметь возможность запретить вызов. Клиент вправе показать запись без предупреждения, никого не спросить и вызвать инструмент в автоматическом цикле.Подсказку нельзя ставить в контур, который обязан держать деньги. Проверку держит сервер. Первая фаза выставления счёта не отправляет в платёжный API ни одного запроса — возвращается сводка операции и одноразовый токен, и тест проверяет именно отсутствие исходящих вызовов. Клиент, не понимающий аннотаций, всё равно не выставит счёт одним ходом модели: сервер физически не даёт такой возможности.Схемы входа приходят строгими — лишние поля отклоняются. Опечатка в имени параметра останавливается на входе, вместо того чтобы уйти в API молча потерянной. Разница между «сумма не та» и «запрос отклонён» здесь и есть разница между спорным списанием и понятной ошибкой.

Элиситация как штатный диалог

Элиситация — механизм, которым сервер прямо во время вызова просит клиента задать человеку вопрос (раздел спецификации). Он превращает подтверждение из двух отдельных вызовов в один шаг.Если программа-ассистент сообщила, что умеет задавать вопросы человеку, сервер просит у неё ответ ровно на один вопрос — «да» или «нет» — и принимает только явное согласие. Всё остальное считается отказом, включая просто закрытый диалог: молчание не проходит за разрешение. Спецификация запрещает спрашивать через такую форму чувствительные данные — пароли, ключи, платёжные реквизиты, — и вопрос здесь ровно один, из двух вариантов ответа.Если она такого не умеет, сервер не пытается спрашивать и возвращается к двухфазной схеме: сводка операции, а затем отдельный вызов с токеном подтверждения. Отсутствие удобной функции у клиента не превращается в тихое исполнение денежной операции.Сегодня сервер реализует редакцию протокола от 25 ноября 2025 года: сервер сам задаёт вопрос человеку, возможности клиента выясняются при установлении связи, каталог объявляет три возможности вместе с журналированием. Редакция от 28 июля 2026 года переставила эти опоры: протокол стал бессессионным, вопрос человеку оформляется возвратом «нужны данные» с повторным вызовом от клиента, а журналирование и часть смежных возможностей переведены в устаревшие. На поведение с деньгами это не влияет: предохранители живут в коде сервера, и способ задать вопрос на них не влияет.

Два транспорта и авторизация по готовым стандартам

Транспорт отвечает за доставку сообщений, и смысл протокола на любом из них одинаков. Стандартных два: stdio для локального запуска рядом с приложением и для размещённого варианта. Поддержаны оба.Устаревший HTTP+ ревизии 2024-11-05 не поддерживается ни в каком виде: спецификация перевела его в разряд устаревших и к применению не рекомендует. Совместимость с ним означала бы вторую точку входа со своей моделью сессий и своим набором мест, где проверка может не сработать.Вторую такую дверь в денежный сервер мы ставить не готовы. По размещённому варианту спецификация требует немногого, но требует твёрдо: сервер обязан проверять заголовок Origin (защита от подмены имени в браузере), а локально слушать только петлевой адрес и требовать аутентификацию — настоятельные рекомендации, статусом ниже обязательного. Обязательное проверит любой аудитор, рекомендованное придётся обосновывать самому.Авторизация опирается на готовые стандарты. Метаданные защищённого ресурса OAuth 2.0 (RFC 9728) — это документ, по которому клиент сам узнаёт, куда идти за токеном, без договорённостей на словах. Клиентский токен обменивается на токен платёжного API стандартным обменом токенов, поэтому у посредника нет секретов для их самостоятельного выпуска, а правила безопасности MCP как раз запрещают серверу принимать токены, выпущенные не для него: сквозной проброс чужого токена ломает и разграничение доступа, и аудит.Имя недостающего права уходит клиенту в заголовке ответа — без него клиенту нечего просить, когда прав не хватило, и человек видит бессмысленную ошибку вместо понятного запроса на расширение доступа. На локальном запуске авторизация не применяется: учётные данные приходят из окружения, как и предписывает раздел авторизации.

Темп ревизий и то, что стандарт оставляет серверу

Версии MCP — даты, и за девять месяцев протокол прошёл от 2025-11-25 до 2026-07-28. Этот темп стал отдельным аргументом при выборе стека: на официальном SDK смена ревизии сводится к правке конфигурации, на самодельной реализации или сообществом поддерживаемой библиотеке — к переписыванию. Платит за медленную реакцию не команда, а клиент, у которого сервер перестал подключаться после обновления приложения.Две вещи стандарт оставляет на усмотрение сервера, и обе оказались значимыми. Порядок инструментов в ответе на запрос их списка влияет на выбор модели, поэтому он зафиксирован в каталоге (src/tools/catalog.ts) с комментарием и закрыт тестами: перестановка строк меняет поведение продукта, и заметить это без теста нельзя. Сжатие повторяющихся определений корзины и покупателя ссылками внутри схемы мы пока не делаем. Сама возможность в редакции от 28 июля 2026 года появилась: схемы инструментов принимают любые ключевые слова JSON Schema 2020-12, включая ссылки, и для их разрешения записаны отдельные требования (изменения редакции). Останавливает другое. Пока разрешение ссылок поддержано не у всех клиентов, риск потерять поле перевешивает экономию токенов.

Устройство сервера и решения, за которые платили

Почти каждое решение в устройстве такого сервера обменивает удобство на предсказуемость. Ниже показано, из чего он собран, какие ограничения мы наложили на себя сами и чего каждое из них стоило: это как раз то, что хочет знать человек, решающий, ставить ли такую вещь рядом со своими деньгами.

Официальный SDK и запуск одной командой

Начнём со скучного вопроса с неожиданно денежными последствиями. На чём сервер написан и как он попадает на машину пользователя?Сервер написан на TypeScript для Node.js и стоит на официальном SDK протокола MCP — библиотеке, которую выпускают авторы самого протокола. Причину легко проверить — протокол живёт версиями-датами и меняется быстро. За девять месяцев он прошёл от ревизии 2025-11-25 до 2026-07-28, и это не косметика — переделаны транспорты, добавлено согласование расширений, появился отдельный код ошибки на неподдерживаемую версию (версионирование MCP).На официальном SDK смена ревизии для нас сводится к обновлению библиотеки и правке конфигурации. На самодельной реализации та же смена означает, что транспорт и согласование возможностей мы переписываем руками, пока клиенты уже отвечают по новой версии. Для продукта, которому доверяют выпуск счетов, отставание от спецификации — прямой риск: сервер, которого клиент перестал понимать, останавливает работу посреди рабочего дня.Второе свойство важнее первого.Сервер запускается на машине клиента одной командой из конфигурации любого MCP-клиента и общается через стандартный поток ввода-вывода — транспорт stdio, при котором клиент сам поднимает сервер дочерним процессом (stdio). Официальные рекомендации по безопасности советуют локальным серверам именно stdio, чтобы ограничить доступ, и одновременно напоминают: установка локального сервера равносильна запуску произвольного кода на машине пользователя, поэтому клиент обязан показать точную команду без усечения и получить явное согласие (практики). Из этого напоминания растёт всё остальное в разделе.Сервер работает клиентом публичного API /v3 Инвойсбокс и в базу не ходит вовсе: у него нет ни соединения, ни доступа, ни права его получить. Переиспользовать серверный код платёжной платформы здесь нечего: общего у них — только формы данных, а те и так приходят по HTTP в своём конверте. Отсутствие общего кода — осознанная цена за то, что сервер стартует у клиента одной командой и ничего не требует с нашей стороны.

Правило двух зависимостей в рантайме

Самое неочевидное для нетехнического читателя место.Правило касается только того, что исполняется вместе с токеном: в рантайме сервера допустимы ровно две сторонние библиотеки: официальный SDK протокола и zod — валидатор, который сверяет входящие аргументы со схемой до того, как они дойдут до логики. Всё остальное написано нами. Сборочных зависимостей четыре — типы, компилятор и линтер, — но они не попадают в установку у клиента, и в правило не входят: считается то, что исполняется в одном процессе с ключом доступа.Почему длинная цепочка зависимостей опаснее всего именно у платёжного инструмента. Подключая библиотеку, программа подключает вместе с ней всё дерево: библиотека тянет свои зависимости, те — свои, и в типичном проекте на Node.js счёт идёт на сотни пакетов от сотен разных авторов. Код каждого исполняется в том же процессе и с теми же правами, что наш собственный. Для сервера с токеном доступа к платёжному API это означает буквально, что каждый издатель в дереве получает возможность выпускать счета и двигать деньги у всех клиентов, которые обновились. Компромисс одного популярного пакета крадёт токены у всех сразу, и никто из пострадавших этот пакет сознательно не выбирал.Теорией это перестало быть давно:
  • event-stream, 2018: право публикации мейнтейнер передал незнакомцу, предложившему помощь с поддержкой; в версию 3.3.6 приехала зависимость с нагрузкой, целившейся в кошельки Copay с балансом свыше 100 BTC (подтверждённых данных о размере украденного нет) (постмортем npm, issue);
  • colors и faker, январь 2022: автор сам выпустил версии с бесконечным циклом, сборки по всему миру встали (GHSA-5rqg-jm4f-cqx7);
  • color и chalk, сентябрь 2025: аккаунт публикации угнали фишингом, нагрузка подменяла адреса криптотранзакций (advisory мейнтейнера);
  • Shai-Hulud, сентябрь 2025: самораспространяющийся червь подсаживал вредоносные скрипты установки в популярные пакеты через угнанные аккаунты мейнтейнеров (разбор GitHub).
Два случая из четырёх прицельно охотились за деньгами, и ни один не требовал ошибки в нашем коде — достаточно было обновить зависимость.
Инциденты в цепочке поставок npm, 2018–2025: event-stream, colors и faker, color и chalk, Shai-Hulud. Два из четырёх целились в деньги, и ни один не требовал ошибки в чужом коде.
Публичные рекомендации говорят то же. Руководство OpenSSF по npm начинается с минимизации и гигиены зависимостей — чистить лишнее, оценивать репозиторий пакета на приёмке, работать в сборке с файлом блокировки версий только для чтения, держать двухфакторную аутентификацию у мейнтейнера — и прямо называет целью «ограничить последствия захваченной зависимости» (OpenSSF npm, анонс). Для ИИ-приложений это уже отдельный класс риска: в списке OWASP GenAI LLM Top 10 за 2026 год цепочка поставки стоит под номером LLM04 (список).Правило подкреплено числом. Порог транзитивного дерева зафиксирован в файле репозитория и проверяется автоматически: сейчас в рабочем дереве 93 пакета при пороге 98. Дерево растёт само, из чужих релизов, поэтому запас в пять пакетов требует разговора, когда кончится.Чем мы за это платим. Всем, что обычно берут готовым: HTTP-клиент, кэш, ограничитель частоты вызовов, маскирование секретов в логах написаны руками. Своя реализация означает свои баги, и чужие исправления к нам не приезжают. Мы платим временем разработки и риском собственной ошибки, а получаем короткий список людей, способных отправить код в процесс, который выпускает счета. Для инструмента, работающего с деньгами, обмен правильный; для утилиты, которая рисует таблички в терминале, он был бы расточительством.В одном случае правило и техническая нужда совпали.

Свой HTTP-транспорт вместо встроенного fetch

Картина была такая. Сервер не поднимался вообще: на каждом вызове обрыв по таймауту соединения. При этом curl с той же машины, из той же сети, в ту же секунду отвечал 200. Тесты проходили полностью. Код никто не менял.Первая версия ходила в API встроенным в платформу HTTP-клиентом (fetch), и разгадка оказалась в нём. У встроенного клиента жёсткий предел на установку соединения — десять секунд. Рукопожатие TLS из рабочей сети занимало двенадцать. Предел непонижаемый и неповышаемый; поднять его нечем, не добавив зависимость, что запрещено правилом выше.Транспорт переписали на низкоуровневые модули платформы (src/api/transport.ts). Бюджет времени на запрос должен принадлежать приложению. Скрытый таймаут платформы, о котором приложение не знает и которым не управляет, проявляется как «случайные» сбои у части пользователей и диагностируется только живым прогоном из той самой сети. Заодно признаем ошибку метода проверки: наши тесты разбирали логику вызовов и не могли поймать целый класс дефектов, живущих в сетевых настройках по умолчанию.Там же появился keep-alive — переиспользование уже установленных соединений, пул до восьми сокетов с временем жизни 30 секунд. Без пула каждый запрос платил рукопожатие заново, и поиск заказов перестал укладываться в 12-секундный бюджет чтения. Отдельным исключением идёт проверка токена на старте, ей дан свой бюджет 30 секунд: метод проверки прав на демонстрационном контуре отвечает 10–12 секунд, ровно по границе общего бюджета. Под общим бюджетом медленный, но полностью работоспособный контур не давал серверу подняться, и пользователь читал бы это как «сломано».

Наборы инструментов и контекст магазина

Чего модель не видит, того она не вызовет. На этом и построен состав каталога.Состав каталога управляется одной переменной окружения. Набор read включён всегда, даже если администратор задал только write: читать нужно и для того, чтобы записывать осмысленно. write добавляет счёт, отмену и отгрузку. Возврат выделен в отдельный набор refund ровно затем, чтобы он не приезжал заодно с выставлением счёта: это разные полномочия, и магазин, разрешивший ассистенту выставлять счета, не обязан разрешать ему двигать деньги обратно.Разделение прав по инструментам — прямая рекомендация шпаргалки OWASP по безопасности ИИ-агентов, где минимальный набор инструментов и права под каждый инструмент стоят в основе (AI Agent Security). Несуществующий набор даёт ошибку конфигурации с перечислением доступных вместо тихого игнорирования опечатки: администратор, написавший refunds вместо refund, должен узнать об этом при запуске, раньше, чем появится вопрос «почему ассистент не умеет возвраты».Каталог фильтруется ещё и по контексту. Инструменты магазина не показываются в списке, когда магазин не задан: без него в каталоге остаётся один поиск компании по ИНН. Инструмент, который гарантированно откажет при вызове, тратит контекст модели дважды — на описание и на попытку, — и вдобавок приучает модель к тому, что отказ сервера это нормальный ход событий.

Отпечаток каталога ломает сборку при правке описания

К тексту описаний инструментов мы относимся так же серьёзно, как к схеме базы данных. Эта привычка окупилась быстрее прочих.Сервер считает хеш от описания своего каталога — имена, описания, области действия, признак изменяющей операции, вид подтверждения и списки адресов API. Значение зафиксировано тестом, и правка описания инструмента без правки эталона ломает сборку. Причина в том, кто этот текст читает: описания — единственное, по чему модель понимает, что делает вызов. Описание определяет поведение продукта в той же мере, что код, и менять его молча так же опасно, как молча менять тип колонки в базе. Инженерный разбор Anthropic про инструменты для агентов настаивает на том же: описания проходят цикл, принятый для промптов, с оценками до и после правки (Writing effective tools).Тот же отпечаток отдаётся в ответе /health рядом с версией. По одному запросу видно, совпадает ли приехавший набор инструментов с опубликованным справочником — эксплуатационная проверка, для которой не нужен ни доступ внутрь, ни наш ответ на письмо.

Файловое хранилище журнала операций

Про журнал, который не даёт списать одно и то же дважды, и про то, почему он лежит в текстовом файле.Журнал операций пишется строками JSON, каждая новая приписывается в конец файла и ничего в нём не переписывает. Встроенное в платформу хранилище SQLite рассматривалось и было отклонено по бытовой причине с серьёзными последствиями: в текущей версии Node.js оно требует специального флага запуска, а сервер должен подниматься одной командой из конфигурации любого MCP-клиента, без уговоров и без флагов, которые клиент, скорее всего, передать не умеет. Требование к запуску победило требование к хранилищу.Расплатились транзакциями. Их нет, целостность держится на схеме «только дозапись» и на устойчивости читателя к обрыву последней строки. Выключение питания посреди записи портит здесь одну строку и оставляет файл читаемым.Это выбор по умолчанию, и предел здесь не технический, и причина у него простая: мы не знаем заранее, где читатель запустит сервер. Файл работает и на ноутбуке разработчика без единой внешней зависимости, и в контейнере с примонтированным томом. Там, где инфраструктура известна, хранилище должно быть другим: у себя мы исходим из общего хранилища состояния (Redis), которое снимает и вопрос нескольких экземпляров сервера, и вопрос атомарности записи. Внутренний интерфейс хранилища поэтому сделан подменяемым с самого начала, а выбор реализации под конкретную инфраструктуру — настройками, без правки кода — стоит в плане следующих версий.

Отвергнутое: страница подтверждения в кабинете

Дороже всего обходятся ошибки в модели предметной области, и на диаграммах их почти не разглядеть. Вот одна такая, из нашей работы.Идея выглядела правильной. Ассистент готовит операцию, а человек подтверждает её на странице личного кабинета, где видно сумму и получателя. От неё отказались, когда разбор упёрся в предметную область. Ожидающая операция как запись нигде не существует: до подтверждения в Инвойсбокс нет ни заказа, ни возврата, ни отгрузки — есть только намерение внутри диалога с ассистентом. Чтобы страница могла его показать, состояние пришлось бы хранить либо в платёжной платформе, для которой это чужая зона ответственности за сущность, которой у неё нет, либо в самом сервере, с мостом между кабинетом и процессом, живущим на машине клиента. Подтверждение оставили одноразовым токеном в цепочке ассистента.Ошибка сидела в допущении, что «операция в ожидании» существует как объект и её достаточно показать. Прежде чем проектировать экран, мы теперь проверяем, есть ли у показываемого предмета место в модели данных.

Контуры не смешиваются

Короткая проверка на старте против опечатки, которая стоит живых денег.Демонстрационная и боевая конфигурации проверяются на согласованность при загрузке: демонстрационный контур с боевым магазином и боевой контур с демонстрационным магазином дают ошибку конфигурации с текстом, объясняющим оба выхода. Боевой возврат, отправленный из демонстрационной конфигурации, придётся отменять руками; обратный случай ломает доверие к демо-прогонам, и «у нас всё прошло» перестаёт что-либо значить.Ошибка конфигурации, которую видно при запуске, дешевле ошибки конфигурации, которую видно по банковской выписке.

Деньги: подтверждение, идемпотентность и целые копейки

Между решением модели и реальным списанием у нас стоят четыре предохранителя: подтверждение человеком, привязка подтверждения к конкретной сумме, идемпотентность и арифметика, в которой копейка не теряется.

Первая фаза не отправляет ни одного запроса

Любая операция с деньгами делается в два приёма, и первый приём заведомо безвреден.Все четыре пишущих инструмента — счёт, отгрузка, отмена, возврат — на первом вызове собирают сводку и возвращают ответ «требуется подтверждение» с токеном, сроком его жизни, человекочитаемым описанием операции и указанием следующего шага. Запрос в платёжное API при этом не уходит вообще, и это отдельно закреплено тестом, который проверяет именно отсутствие исходящего запроса.Так сделано потому, что стандарт возлагает подтверждение на приложение-хост: спецификация говорит о том, что в контуре SHOULD присутствовать человек с возможностью запретить вызов. SHOULD — пожелание, и клиент, который не показывает аргументы человеку, формально остаётся в рамках стандарта. Гарантия, которая живёт только в клиенте, отсутствует: достаточно одного невоспитанного клиента, чтобы счёт ушёл контрагенту от одного хода модели. Поэтому гарантия перенесена на сервер, где её нельзя выключить настройкой чужого приложения.Порядок вызовов выглядит так:
Две фазы денежной операции: первая не отправляет в платёжное API ни одного запроса, вторая проверяет совпадение отпечатка параметров и суммы, которую назвал человек.
Ключевое здесь — что первая фаза не отправляет в платёжное API ни одного запроса, а вторая проверяет не «человек нажал да», а совпадение отпечатка параметров с тем, что подтверждали.

Токен подтверждения привязан к отпечатку операции

Подтверждение должно относиться к конкретной операции, иначе оно превращается в разрешение «делай что хочешь, я не против».Токен собирается из отпечатка параметров операции, срока жизни и случайной соли, а затем подписывается. Проверка подписи и есть проверка того, что подтверждали именно эту операцию: изменилась сумма, контрагент или состав корзины — токен недействителен, и отказ говорит прямо, что подтверждали другое. Классическая финтех-дыра «подтвердили сто рублей, отправили сто тысяч» закрыта здесь арифметикой подписи, без надежды на аккуратность проверок внутри самого инструмента.Ровно этой логике следует платёжное регулирование. Регламент к PSD2 требует динамической привязки: код подтверждения специфичен для суммы и получателя, согласованных плательщиком, и любое изменение суммы или получателя код аннулирует. Отраслевые своды для агентов пришли к тому же — например, OWASP требует привязывать одобрение к конкретным параметрам действия и давать ему короткий срок годности.Остальные свойства токена служат тому, чтобы украденное подтверждение не сработало дважды и не сработало у другого. Соль (случайная добавка к подписи, разная у каждой операции) одноразовая: повторное предъявление того же токена получает отказ с пояснением, что повтор запрещён намеренно. Секрет подписи создаётся заново на каждое подключение, поэтому утёкший токен бесполезен в другой сессии. Платим за это так. Если сервер перезапустить, все выданные до перезапуска подтверждения перестают действовать, и подтверждать операцию человеку придётся заново. Подписи сверяются сравнением за постоянное время с предварительной проверкой длины — без неё короткий поддельный токен ронял бы вызов исключением вместо аккуратного отказа (src/core/confirmation.ts).Тонкость, на которой ломаются самодельные схемы подтверждения: из отпечатка нужно исключить сами поля подтверждения — токен и продиктованную сумму. Иначе токен входит в отпечаток собственной операции и проверка не сходится никогда.

Порог суммы усиливает подтверждение

Одинаковый жест на пятьсот рублей и на пять миллионов выглядит как удобство, работает как уязвимость.Человек, который двадцать раз в день нажимает «да» на мелких счетах, на двадцать первом нажмёт не читая: привычка вырабатывается быстро и не различает нули в сумме. Цена ошибки при этом растёт быстрее суммы. Лишние пятьсот рублей закрываются возвратом в тот же день и никого больше не касаются.Лишние пять миллионов уходят в кассовый разрыв, попадают в акты сверки и отчётность, застревают в закрытом периоде, требуют переписки с контрагентом и объяснений внутри компании; вернуть деньги — половина работы, вторая половина — привести в порядок документы, которые успели разойтись по нескольким сторонам. Чем крупнее операция, тем больше людей и бумаг придётся возвращать в исходное состояние.Порог по умолчанию — сто тысяч рублей. Выше порога второй вызов обязан передать сумму отдельным полем, строкой, точно равной сумме в копейках; при расхождении приходит отказ с подсказкой, какое значение ожидается. Смысл поля — заставить человека назвать цифру самому, потому что цифра, которую модель просто переписывает из собственного аргумента, ничего не проверяет.А что мешает модели самой подставить нужную сумму в поле подтверждения и обойти порог? Ничего не мешает — если разрешить ей это поле заполнять. Поэтому значение принимается только от человека, и вся конструкция держится на источнике цифры, а не на её правильности.Важно и то, что порог ничего не отменяет, а только добавляет. Если ассистент умеет показывать человеку вопросы, вопрос задаётся при любой сумме — и при пятистах рублях, и при пятистах тысячах. Разница лишь в том, что у крупной суммы к вопросу добавляется отметка: сумма выше порога, отвечайте внимательнее.Первая версия этой проверки была устроена наоборот, и ошибка здесь поучительная. Выше порога сервер отключал вопрос человеку и вместо него требовал, чтобы сумму прислали отдельным полем. Звучит строже, а на деле было слабее: это поле заполняла та же модель, которая только что собрала операцию. Получалось, что чем крупнее платёж, тем меньше в проверке участвует человек.Правило, которое из этого следует, применимо к любой схеме подтверждения: спрашивайте, откуда берётся подтверждающее значение. Если его приносит та же сторона, которая совершает действие, это не второй фактор, а первый под другим именем. В банковской практике независимость факторов аутентификации проверяют отдельным пунктом — ровно по этой причине.Рынок в этом месте сходится с нами. Основной формой агентной коммерции на ближайшие два года исследование Data Insight называет не автономную покупку, а агента с подтверждением: ассистент подбирает товар, собирает корзину, выбирает условия и передаёт финальное решение человеку, а оплату подтверждает человек. Ту же границу люди проводят и деньгами — подробности в третьей части.Причина не полагаться на «модель обычно делает правильно» измерима. В бенчмарке τ-bench, где агент общается с пользователем и вызывает доменные API, сильные модели решают меньше половины задач, а метрика «получилось во всех восьми прогонах» в розничном домене падает ниже 25 %.

«Тот же вызов» как вычислимое свойство

Бытовая версия проблемы знакома каждому: терминал в магазине задумался, кассир проводит платёж ещё раз, и с карты уходит две суммы вместо одной. С ассистентом то же самое случается дешевле — сеть моргнула, ответ не дошёл, вызов повторён, и у контрагента два счёта или два возврата.Платёжные API решают это ключом, который клиент присылает вместе с запросом: у Stripe это заголовок Idempotency-Key с сохранением ответа первой попытки, у Adyen свои границы длины и срока хранения, у Square ключ лежит в теле запроса. Попытка вынести механизм в общий стандарт HTTP до RFC не дожила: черновик Idempotency-Key истёк и помечен в архиве.Такого ключа в нашем платёжном API по умолчанию нет, поэтому «тот же вызов» пришлось сделать вычислимым свойством содержимого: ключ операции — хеш от канонического представления имени инструмента, магазина и аргументов. Канонизация сортирует поля, выбрасывает пустые и сохраняет явные null. Порядок ключей в аргументах модели непредсказуем, и без канонизации повторный вызов с переставленными полями выглядел бы новой операцией.Состояний у операции четыре: выполняется, выполнена, упала, неизвестно. В суточный потолок идут выполненные и неизвестные, упавшая квоту не занимает. Изначально было хуже. Любой отказ записи помечался как выполненная операция без результата, и повтор того же тела навсегда отвечал «дубль не создан», не сделав ничего. Разница между «уже сделано» и «попытка упала» стоит второго счёта или потерянного возврата, поэтому она хранится отдельным полем; выводить её из самого факта наличия записи нельзя.

Незавершённая попытка не даёт создать второй документ

Отдельный случай — когда предыдущая попытка не закончилась ни успехом, ни отказом, и никто не знает, что там произошло.Запись «выполняется» моложе минуты даёт отказ с временем старта и подсказкой дождаться ответа, иначе получится второй документ. Старше минуты — запускается восстановление, чтобы брошенная попытка не блокировала магазин навсегда. До исправления состояние «выполняется» не проверялось вовсе, и параллельный вызов модели спокойно создавал второй документ; оба поведения закреплены тестами.Восстановление различает три исхода. Операция нашлась — ответ помечается как восстановленный. Доказано, что она не прошла — повтор безопасен. Проверить не удалось — наверх идёт отказ «результат неизвестен» с подсказкой сверить в кабинете. «Не знаю» здесь полноправный исход, и сервер не угадывает результат платёжной операции ни в одну из сторон.Способов проверки тоже три, по одному на форму документа: отмена перечитывает статус заказа, отгрузка ищется по номеру документа либо по совпадению суммы и времени создания в окне минуты, остальное — по нашему номеру заказа в выборке. Изначально по нашему номеру искали везде, а в теле отгрузки и отмены его просто нет — восстановление для двух инструментов не находило ничего и не переписывало запись. Неверное допущение о контракте API, «у каждой операции есть наш номер», оказалось ложным для половины каталога, и заметно это стало только на живых прогонах.Сам номер заказа сервер выводит из содержимого запроса, складывая дату и начало ключа операции. Номер детерминированный, поэтому после обрыва по нему же находится созданный документ — идемпотентность получается без поддержки со стороны API.
Четыре состояния операции: выполняется, выполнена, упала, неизвестно. «Неизвестно» — полноправный исход: сервер не угадывает результат платёжной операции ни в одну сторону.

Записи не повторяются

Автоматический повтор кажется удобной привычкой, а в деньгах работает против вас.Пишущие вызовы не повторяются ни разу. Повторяемыми считаются только те коды ответа, которые прямо означают «сейчас занято, приходите позже». Сетевой сбой на записи превращается в отказ «результат неизвестен» с подсказкой сначала проверить выборкой, прошла ли операция; ошибка сервера на записи даёт тот же код, на чтении — обычную ошибку API. Различие между «не выполнено» и «неизвестно» протянуто от транспорта до ответа инструмента, и на нём держится вся логика восстановления выше.В инженерной библиотеке Amazon сказано, что вызов с побочными эффектами небезопасно повторять, если он не даёт идемпотентности, а сами повторы усиливают нагрузку на уже деградировавший сервис.

Суммы живут целыми копейками

Дробные деньги в двоичной плавающей точке дают расхождения, которые первым замечает бухгалтер.Десятичные дроби вроде 0,1 и 0,01 в двоичной плавающей точке непредставимы точно; это классика, разобранная Голдбергом ещё в 1991 году. Платёжная компания Modern Treasury показывает на числах, как 2,78 хранится в виде 2,7799999713897705078125 и как один и тот же расчёт даёт 16,77 или 16,78 в зависимости от порядка округления. В документе это выглядит как копейка расхождения между суммой позиций и итогом по счёту: состав не сходится, платёжное API его отклоняет, а у продавца, покупателя и банка на руках оказываются разные цифры. Один счёт с двумя разными итогами разбирают уже как спор, и на уровне опечатки его закрыть не получится.Суммы на входе принимаются строкой целых копеек, и схема инструмента разрешает только цифры. Строка вместо числа отнимает у модели саму возможность родить 122.00000000000001. Внутри живёт отдельный тип для целых копеек — тот самый паттерн Money, который Фаулер описывает как обёртку над суммой, валютой и правилами округления. Обратное преобразование строгое: сумма, приехавшая из API числом с плавающей точкой, проверяется на целость в копейках, и 122,005 отклоняется вместо тихого округления — молчаливое округление означает потерянную копейку в закрывающих документах (src/core/money.ts).Множитель сто в этом коде — допущение про рубль. Показатель минимальной единицы задаёт ISO 4217, и он не всегда равен двум: у иены нулевой, у кувейтского динара третий, а Stripe в своей документации перечисляет ещё и валюты, где правило меняли задним числом. Мультивалютному серверу пересчёт пришлось бы выносить в таблицу показателей.

Состав корзины сверяется до отправки

Корзина — самое хрупкое место в денежном запросе: цена за единицу, количество, НДС позиции и итог должны сходиться между собой, иначе платёжная платформа отклонит заказ целиком.Поэтому сервер сверяет состав у себя, прежде чем что-то отправлять. Произведение цены на количество считается с округлением до копейки, потому что количество бывает дробным (проверено на 2,5 и 1,33). Дальше сходятся НДС позиции против суммы позиции, сумма позиций против шапки и НДС позиций против НДС шапки. Инвойсбокс отклоняет несходящийся состав ответом 422 без указания места ошибки — локальная сверка превращает эту удалённую ошибку в понятный отказ с номером позиции и обеими цифрами, ещё до отправки.Общее правило для любого, кто собирает корзину машиной: проверять инвариант «цена единицы × количество + НДС = сумма позиции» на своей стороне и в своих тестовых данных держать количество, отличное от единицы. При количестве один цена единицы и сумма позиции совпадают, поэтому целый класс ошибок в арифметике на таких данных невидим.

Возврат без состава корзины отклоняется до отправки

При возврате ошибка сразу видна клиенту и сразу считается деньгами.Частичный возврат без состава корзины отклоняется отдельным сообщением до отправки запроса. Без состава API вернуло бы весь доступный остаток: попросили 122 рубля — ушло бы 244. Тест следит именно за тем, что запрос не отправлен. Полный возврат собирается по одной единице на позицию с суммой, равной доступному остатку: остаток после частичного возврата не обязан делиться на исходное количество, и попытка сохранить количество даёт арифметику, которую API не примет.НДС шапки в возврате всегда считается из позиций, а переданное значение служит сверкой ожидания: расхождение даёт ошибку входных данных и ни одного запроса. Молчаливая подмена НДС в возврате — прямой налоговый риск, поэтому отказ здесь предпочтительнее исправления за пользователя.

Суточные ограничения по количеству операций и сумме возвратов

10

возвратов в сутки на организацию по умолчанию

50 000 ₽

суточный потолок суммы возвратов

100 + 100

счетов и отгрузок в сутки

100 000 ₽

порог, выше которого сумму называет человек

Последним рубежом, когда всё предыдущее обошли, стоит ограничение ущерба за сутки.Считаются они на организацию от начала суток: по умолчанию десять возвратов, пятьдесят тысяч рублей возвратов, сто счетов, сто отгрузок. Сутки при этом отсчитываются по единому времени сервера — в какой часовой зоне живёт пользователь, серверу неизвестно. Ключ — именно организация, иначе ограничение обходится открытием второй сессии. Проверка вызывается уже после подтверждения, в момент реальной попытки: просмотр сводок квоту не жжёт, а отказ приходит с цифрами. Такие лимиты OWASP советует агентам отдельным пунктом, под именем Denial of Wallet, а Google в главе о перегрузке описывает квоты на клиента как штатный механизм эксплуатации. Значения из двух разных шкал, и это стоит проговорить: порог усиленного подтверждения по умолчанию выше суточного ограничения на возвраты, поэтому возврат до этого порога подтверждается обычным «да», а суточная сумма кончится раньше, чем потребуется называть цифру. Обе величины настраиваются, и владельцу счёта стоит выставить их согласованно: порог — ниже суточного ограничения по возвратам.У любого предохранителя должен быть тест на срабатывание; тест на счастливый путь этого не заменяет.

Ответственность, цена и данные

Три вопроса, которые задают раньше технических, а в статьях про ИИ-интеграции почему-то отвечают последними. Сколько это стоит. Кто отвечает, если счёт ушёл не на ту сумму. И что из данных уходит за пределы вашего контура.

Сколько это стоит

Сам сервер бесплатен. Это открытый пакет под лицензией MIT: его ставят одной командой, читают и правят как любой другой открытый код. Отдельной платы за MCP как за канал нет — сервер только превращает поручение в вызовы того же публичного API, к которому у вас и так есть доступ.Платите вы за то же, за что и без ассистента: за приём платежей по договору с Инвойсбокс. Тарифы зависят от способов оплаты, оборота и типа бизнеса, и обсуждаются они там же, где обсуждались бы без всякого ИИ. Появление агента в этой схеме не добавляет комиссий и не меняет расчёты.Отдельная статья расходов существует, но платите вы её не нам: это токены модели. Каждое описание инструмента, каждый ответ сервера и каждый отказ занимают место в окне разговора, и тарифицирует их поставщик модели. Как считать этот расход и на чём он экономится, разобрано во второй части серии.

Кто отвечает, если счёт ушёл не на ту сумму

Начнём с механики, потому что она определяет ответ. Сервер работает на вашей стороне и вашим ключом доступа: с точки зрения платформы операцию совершаете вы, а не «ИИ». Подтверждение крупной суммы делает человек, и именно его действие превращает подготовленную операцию в реальную. Поэтому неверный счёт — это неверный счёт, выставленный вашей организацией, с обычными последствиями: исправление, отмена, возврат.Что из этого следует практически. Счёт до оплаты отменяется, оплаченный заказ возвращается целиком или частично, и оба сценария у сервера есть. Двойное списание защищено ключом идемпотентности: повторный вызов возвращает результат первой попытки, ничего не создавая заново. Лишний возврат — единственный случай, где деньги уходят покупателю и вернуть их автоматически нельзя: суточное ограничение по сумме возвратов существует ровно затем, чтобы этот случай остался мелким.Дальше начинается зона, где мы не будем притворяться, что у нас есть ответ.Юридической доктрины по агентным платежам в России пока нет — ни судебной практики, ни отраслевого соглашения о том, кто отвечает за операцию, которую подготовила модель. Границы ответственности между вами, нами и поставщиком модели определяет договор, а не эта статья. Единственное, что регулятор уже сказал по существу: в методических рекомендациях от 16 июня 2026 года Банк России советует подтверждать платёжную операцию сотрудником там, где ИИ применяется в критически важных процессах (Банк России). Наша двухфазная схема — это ровно та рекомендация, выраженная кодом.Были ли у пользователей денежные инциденты, мы не знаем. Телеметрии с машины клиента нет и по нашему решению не будет, поэтому о сбое мы узнаём из обращения — и то, что мы не видели инцидентов, не означает, что их не было. Обратная сторона того же решения: у вас не остаётся сомнений, кто смотрит в ваши счета.

Что уходит в чужую модель

Этот риск в агентных сценариях недооценивают чаще прочих, и он не про наш сервер.Ассистент отправляет модели весь контекст разговора. Значит, наименования контрагентов, ИНН, состав корзины, суммы и ваши формулировки уходят туда, где эта модель работает: в облако поставщика или на ваш собственный сервер, если модель локальная. Наш сервер к этой передаче отношения не имеет — он вообще не знает, какой моделью пользуется клиент, — но проектировать интеграцию приходится с учётом того, что данные операции видит третья сторона.Отсюда пять практических правил.Выбирайте, где живёт модель. Если в диалоге появляются данные физических лиц — фамилия, адрес, телефон покупателя, — их передача в облако за пределами России требует отдельного разбора с юристами по 152-ФЗ. Локальная модель или модель российского поставщика снимает этот вопрос целиком. Для счетов организациям всё проще: ИНН и наименование юридического лица — публичные сведения из государственного реестра.Не кладите в диалог больше, чем нужно для счёта. Тип плательщика private не требует ИНН, а сумма, наименование услуги и способ связи — это всё, что нужно для выставления счёта физлицу. Паспортные данные, адреса и истории покупок в диалоге про счёт не нужны никогда.Помните, что журналы и контекст живут по разным правилам. Наш сервер маскирует чувствительные поля в своём журнале и не отправляет наружу ничего, но контекст модели — не наш журнал. Маскирование на нашей стороне не отменяет того, что в промпте у поставщика модели лежит полное наименование контрагента.Спросите поставщика модели про обучение и хранение. Условия «данные не используются для обучения» и сроки хранения промптов отличаются у разных поставщиков и у разных тарифов одного поставщика. Для платёжного сценария это такой же параметр выбора, как точность модели.Опубликуйте политику обработки данных и дайте на неё ссылку из пакета. Каталоги — в том числе каталог коннекторов Anthropic — требуют ссылку на публичную политику обработки персональных данных и отклоняют подачу без неё. Политика обработки персональных данных Инвойсбокс опубликована: www.invoicebox.ru/ru/docs/privacypolicy. В пакете сервера ссылка стоит в трёх местах, где её ищут каталоги и люди: раздел «Privacy Policy» в README.md, поле privacy_policies в manifest.json и _meta в server.json.Дальше по тексту есть отдельная глава про границы доверия — она о другой стороне того же вопроса: что делать с чужим текстом, который приходит из внешних источников и попадает в контекст модели как будто бы указание.

Ассистент не двигает деньги в одиночку

Дело тут не в недоверии к моделям.Модель формулирует вызов вероятностно. Она может собрать безупречный счёт сто раз и на сто первый сложить цену за единицу с ценой позиции. Для неё это нормальное состояние. Поэтому предохранители живут не в подсказке, которую можно уговорить, а в коде, который откажет: два шага у любой денежной операции, подтверждение, привязанное к отпечатку параметров, права по умолчанию только на чтение, суточные ограничения, отдельный исход «неизвестно» вместо угадывания.Вторая половина ответа практическая. Всё, что за одним инструментом выставления счёта — способы оплаты, фискальный чек, закрывающие документы, уведомление об оплате, — агенту знать не нужно. Он формирует счёт и отдаёт человеку ссылку; остальное делает платёжная платформа. Именно поэтому подключение занимает одну команду, а не квартал разработки.Попробовать это можно, ничего не подписывая. В быстром старте лежат готовые строки конфигурации для Claude Desktop, Cursor и VS Code и демонстрационный доступ: сервер поднимется, счёт выставится, деньги не двинутся. Полный перечень инструментов и того, что каждый требует, — в каталоге, а посмотреть живой диалог с настоящей моделью, не устанавливая ничего, можно на странице демонстрации.Вторая часть — границы доверия и проверка: чужой текст как данные, что в действительности открывает токен, чем проверяется денежный сервер и какие поломки просыпаются только на втором пользователе.
#MCP#ИИ-агенты#B2B#ЭДО#интеграции

Читайте также