Ozon Consumer MCP
# Ozon Consumer MCP
Локальный MCP для покупателя Ozon с постоянным видимым Chrome и внутренним
`gpt-6-luna / medium`. Внешний агент оценивает товары; Luna выполняет короткие
задачи адаптации интерфейса по компактному YAML-дереву и снимкам. HTML, cookies
и ключ модели ей не передаются. CSS-классы Ozon не используются.
## Инструменты
Все названия имеют префикс `ozon_`.
| Инструмент | Назначение |
|---|---|
| `auth_open`, `auth_status`, `auth_save` | Вход, проверка и приватная резервная копия сессии |
| `search`, `search_filters` | Поиск, цена Ozon Карты, рейтинг/отзывы/вопросы, доставка, сортировки и фильтры категории |
| `product_variants`, `product_variant_select` | Группы вариантов, точные ID и переключение с проверкой SKU |
| `product` | Описание, комплектация, характеристики, продавец, доставка и цена |
| `product_media`, `review_media` | Галереи товара и конкретного ранее прочитанного отзыва |
| `media_get` | Настоящий MCP image; `analyze=true` добавляет анализ Luna. Для видео — наблюдаемый URL |
| `media_import` | Регистрация выбранного изображения из приватной папки uploads |
| `reviews`, `questions` | Отдельные сущности, полные тексты/ответы/комментарии и пагинация |
| `shopping_list` | Личный лист покупок без обложки: создать/дополнить, проверить SKU, повторно прочитать |
| `collections`, `collection_create`, `favorite` | Подборки/вишлисты, создание с полями/обложкой/товарами, добавление товара |
| `cart`, `cart_add` | Чтение корзины и добавление точного SKU/количества |
| `orders`, `order_detail` | Активные/завершённые заказы и детали конкретного заказа |
| `checkout_prepare` | «Купить сейчас», точный состав/итог/оплата/ПВЗ/дата без отправки |
| `discount_prepare`, `return_prepare`, `review_prepare` | Подготовка скидки, возврата или отзыва без отправки |
| `action_confirm`, `action_status` | Единственный конечный исполнитель и его сохранённый результат |
### Поиск и исследование
```json
{"query":"мобильные кондиционеры до 30000 руб","limit":5}
```
Сначала можно узнать `search_filters`, затем передать точные параметры:
```json
{"query":"мобильный кондиционер","limit":3,"sort":"cheaper","maxPriceRub":30000,"brands":["CENTEK"],"deliveryWithinDays":7}
```
`sort`: `relevance`, `popular`, `cheaper`, `expensive`, `rating`, `new`.
Категорийные параметры: `filters:[{"name":"имя из search_filters","values":["реальный вариант"]}]`.
Финансовые промо не предлагаются как полезные фильтры. Неподтверждённое ограничение
возвращает `filter_unavailable`, а не молча игнорируется.
`priceOzonCard.amount=null` означает неизвестную цену Ozon Карты. Первая/самая
низкая цена и ежемесячный платёж её не заменяют. Если в выдаче нет подписи банка,
цена проверяется в отдельной вкладке товара. Рейтинг магазина хранится отдельно.
Не показанное число вопросов остаётся `null`; его можно прочитать в карточке.
Для отзывов: `useful`, `new`, `rating_asc`, `rating_desc`. Если сайт предлагает
только «новые и полезные», строгая сортировка по дате не выдумывается.
`nextCursor` продолжает тот же товар/сортировку; `coverage.pageComplete` относится
к странице, `coverage.complete` — к концу всего списка. Повторяющийся курсор
при отсутствии прогресса не выдаётся.
Медиа возвращается с `mediaId`, URL и размерами; `media_get` действительно отдаёт
изображение внешнему агенту. `originalResolutionVerified=false` означает, что
скрытый оригинал не доказан: выдаётся крупнейший **наблюдаемый** источник, без
выдумывания CDN-ссылок. Полное видео не подменяется анализом его обложки.
### Действия и подтверждение
Обратимые изменения по умолчанию возвращают preview. `apply=true` допустим,
когда добавление/создание следует из поручения владельца. У новой подборки явно
задаются `isPublic`, название, описание, `coverMediaId` и начальные `productUrls`.
Код проверяет поля и выбранные товары до финального сохранения.
Для конкретной подборки/вишлиста передайте `favorite.listName`.
Карточку заказа без ссылки можно открыть через `order_detail.orderId` из `orders`.
```text
*_prepare → точный preview + confirmation → поручение владельца
→ action_confirm → повторная проверка → одна попытка → результат
```
`action_confirm` получает `confirmation:{token,digest,confirmed:true}`.
`confirmed=true` — утверждение вызывающего агента о поручении владельца,
а не доказательство человеческого согласия. Наличие токена покупку не разрешает.
Токен действует 15 минут и привязан к аккаунту, товару, количеству, сумме,
наблюдаемым условиям оплаты/доставки и заполненным полям.
У Luna нет инструмента конечной отправки. Код заново читает форму и сравнивает
план, затем сохраняет попытку **до** клика. `unknown`, сбой или перезапуск запрещают
слепой повтор. Новый токен для того же товара не обходит неизвестную попытку.
Сначала нужна сверка реального заказа/заявки; автоматического снятия блокировки нет.
Приватные предпочтения: цена Ozon Карты, Ozon Рассрочка, обычный ПВЗ.
`checkout_prepare` позволяет явно изменить ПВЗ или выбрать `ozon_bank`.
`maxTotalRub` задаёт предел итоговой суммы; превышение блокирует подготовку.
Оплата после получения и другой банк не подставляются. Срок хранения ПВЗ не
считается датой прибытия. Рассрочка возвращается с реально показанными условиями,
включая приблизительный график, если так отображает Ozon.
Для возврата `reason` — точная причина из формы; пояснение — `comment`.
Если форма не готова, `availableChoices` показывает наблюдаемые варианты.
**При разработке запрещены реальные покупки, отправка скидок, возвраты и
публикация отзывов. Покупать дешёвые товары для теста владелец запретил.**
## Запуск и подключение
Нужны Linux с рабочим столом, Node.js 22+, Google Chrome по пути
`/usr/bin/google-chrome` (другой путь задаётся в настройках) и собственный API-ключ.
Windows/macOS не проверены: брокер использует Unix-сокет.
```sh
git clone https://github.com/errogaht/ozon-mcp.git
cd ozon-mcp
npm ci
mkdir -p ~/.config/ozon-mcp
chmod 700 ~/.config/ozon-mcp
node src/cli.js config-init
```
Перед первым поиском отредактируйте `~/.config/ozon-mcp/settings.json`.
Нужен совместимый с OpenAI Responses API провайдер со streaming, tool calling,
vision и поддержкой выбранного reasoning. Доступ к модели не включён в проект.
`gpt-6-luna` — проверенная конфигурация авторской установки через собственный
шлюз; у вашего провайдера это имя может отсутствовать. Укажите доступные вам
модели отдельно по назначениям. Совместимость с другими провайдерами не проверена.
Пример структуры (замените значения):
```json
{
"provider": {"baseURL":"https://api.openai.com/v1", "keyFile":"~/.config/ozon-mcp/provider.key"},
"models": {
"browserAgent":{"model":"YOUR_TOOL_CALLING_VISION_MODEL", "reasoningEffort":"medium"},
"mediaAnalysis":{"model":"YOUR_VISION_MODEL", "reasoningEffort":"medium"}
}
}
```
Сохраните сам ключ в указанный файл локальным редактором и выполните
`chmod 600 ~/.config/ozon-mcp/provider.key`. Не передавайте ключ аргументом CLI.
Вызовы модели оплачиваются у вашего провайдера. Авторизация Ozon выполняется
в вашем собственном отдельном профиле Chrome; чужие сессии в проект не входят.
```sh
npm ci
node src/cli.js login
# Войти в отдельном Chrome обычным способом.
node src/cli.js auth-save
node src/cli.js status
node src/cli.js search 'мобильные кондиционеры до 30000 руб'
node src/cli.js rpc search_filters '{"query":"мобильный кондиционер"}'
```
Chrome остаётся открытым после завершения клиента. Перезапуск брокера:
```sh
node src/cli.js service-stop
```
Окно и авторизация сохраняются; следующий запрос запускает брокер снова.
После изменения кода перезапускайте брокер, когда операция завершилась.
```sh
codex mcp add ozon -- "$(command -v node)" "$PWD/src/cli.js" serve
```
В `[mcp_servers.ozon]` задайте `tool_timeout_sec = 900` для длинных чтений/очереди.
Новые инструменты могут потребовать обновления MCP или новой сессии Codex.
```sh
node scripts/smoke-mcp.mjs
node scripts/acceptance-readonly.mjs product media reviews_low questions
```
Acceptance содержит фиксированный список безопасных read/preview вызовов.
Он не вызывает `action_confirm`, не добавляет товары и не отправляет формы.
## Приватное состояние и устройство
Ключ: `~/.config/ozon-mcp/provider.key` (`0600`). Профиль:
`~/.local/share/ozon-mcp/chrome-profile-tcp`. Резервная копия:
`~/.local/share/ozon-mcp/auth/storage-state.json` (`0600`). Папки имеют `0700`.
Эти файлы не попадают в Git или модель. После явного выхода/отзыва сессии cookies
автоматически не подставляются.
Настройки по умолчанию: `config/defaults.json`; приватные переопределения:
`~/.config/ozon-mcp/settings.json`:
```json
{"models":{"browserAgent":{"model":"gpt-6-luna","reasoningEffort":"medium"},"mediaAnalysis":{"model":"gpt-6-luna","reasoningEffort":"medium"}}}
```
Предпочтения: `~/.config/ozon-mcp/preferences.json`. Настройки меняют следующие
запуски, активная операция сохраняет свой снимок. Ограничения ключа редактированием
JSON не расширяются. Для изоляции есть `OZON_MCP_STATE_DIR`, `OZON_MCP_CONFIG`.
Для загрузки обложки/отзыва положите выбранный файл в `~/.local/share/ozon-mcp/uploads`
и зарегистрируйте через `media_import`. Произвольные локальные файлы не читаются.
```text
MCP stdio → приватный Unix-сокет → очередь целых операций
├─ постоянный Chrome
├─ YAML/refs → короткие задачи Luna
├─ проверка фактов, полей и лимитов
└─ одноразовые планы → конечный исполнитель
```
Refs вида `s2:e123` привязаны к неизменяемому снимку. Отзывы/вопросы разбираются
по одному в изолированных областях; временные метки удаляются после чтения.
Длинный текст копируется из браузера, а не пересказывается. Звёзды дополнительно
проверяются по отображаемым значкам. Модель находит галерею, код обходит миниатюры
внутри проверенной области. У модели нет произвольных JS/HTTP/сетевых инструментов.
Общий лимит операции по умолчанию — 240 секунд. Большие списки читаются страницами.
Смена интерфейса и недостающие сведения отражаются в `coverage.missing`.
Фактическая матрица live-проверки записана в `PROJECT_STATUS.md`: наличие
инструмента не означает проверку его необратимой отправки на Ozon.
При challenge/VPN-ошибке возвращается `site_blocked`, окно остаётся для ручной
проверки. Chrome использует ненулевой loopback CDP-порт: порт 0 вызывал здесь
блокировку. Stealth, подмена navigator/UA и вмешательство в VPN не применяются.
## Проверки
```sh
npm run check
npm test
```
Тесты используют синтетические страницы; реальный заказ в них создать нельзя.
Приватные live-результаты: `~/.local/share/ozon-mcp/dev/acceptance`.
Они содержат данные аккаунта и не предназначены для публикации.
## Публичная версия
Экспериментальный независимый проект; не является официальным продуктом Ozon.
Лицензия MIT. Публикуется исходный код в GitHub; пакет в npm не опубликован.
Файлы сессии, ключи, предпочтения доставки и live-результаты не входят в релиз.
Передавайте API-провайдеру только те данные аккаунта, которые разрешены для
вашего сценария: на обработку могут уходить тексты отзывов, заказов и адресов.
Подробности границ и сообщения об уязвимостях — в SECURITY.md.
### Диагностика провайдера
При сбое модели MCP возвращает `diagnosticId`. Приватная последняя запись:
`~/.local/share/ozon-mcp/diagnostics/provider-last.json` (0600; при
`OZON_MCP_STATE_DIR` — внутри выбранной папки). Запись содержит этап request/stream,
длительность, количество SSE-событий, HTTP-статус, допустимый request ID и
разрешённые типы/сетевые коды цепочки причин. Сырые сообщения исключений, стеки,
заголовки, ключи и тексты страниц не сохраняются. Это ограниченный журнал последнего
сбоя, а не архив. Ошибка записи диагностики не заменяет исходную ошибку.
Таймаут SDK возвращается как `timeout`; автоматические повторы остаются отключены.
### Варианты и область отзывов
Вызовите `ozon_product_variants({"url":"..."})`, выберите `variantId` из
`groups[].options[]`, затем `ozon_product_variant_select({"url":"...","variantId":"..."})`.
Инструмент заново читает варианты и сверяет конечный SKU. Далее передавайте
возвращённый `url` в карточку, медиа, корзину или оформление. Варианты без
подтверждённого адреса/SKU пока только перечисляются; переключение возвращает
`variant_identity_unverified`. Нераскрытые группы отмечаются в coverage.
`ozon_reviews({"url":"...","scope":"current","limit":3})` по умолчанию выбирает
«Этот вариант товара». `scope="all"` явно запрашивает всю группу. Для текущего
варианта читается встроенная лента основной карточки: отдельная `/reviews/`
может не иметь фильтра. Проверяется native checked/ARIA либо реальное визуальное
состояние чипа. Если выбор не подтверждён, возвращается
`review_scope_unavailable` с пустыми items. Курсоры привязаны к URL, сортировке
и scope; менять область посреди пагинации нельзя. Медиа отзыва восстанавливает
ту же область. Названия/цены/оценки других вариантов не переносятся автоматически.
### Обычный лист покупок
`ozon_shopping_list({"name":"Сравниваю инструменты","productUrls":["https://www.ozon.ru/product/123/"],"apply":true})`
создаёт **личную подборку** без обложки, а не подарочный вишлист. Description
необязателен и заполняется при создании. Личные `collection_create` без обложки
используют этот же сценарий. Ozon выбирает товары из избранного: только назначенные
SKU при необходимости добавляются туда, затем отмечаются в подборке. После
сохранения индекс и содержимое читаются заново, проверяются имя, приватность,
полный набор SKU и отсутствие лишних позиций. Возвращается наблюдаемая ссылка.
Повтор готового запроса возвращает `already_present`, без повторных нажатий.
Для независимой сверки: `ozon_collections({"kind":"collection","name":"Сравниваю инструменты"})`.
Без name читается индекс; kind: all/collection/wishlist. Неполный индекс явно
помечается, одноимённые списки не склеиваются. При unverified поле missing
содержит причину, reconcile — инструмент и параметры чтения. Не повторяйте
неопределённое сохранение вслепую; приватные чеки попыток сохраняются вне Git.
Выдача поиска ожидает актуальный запрос в поисковом поле и стабильные карточки.
Только выбранные адаптером совпадения могут попасть в результат; первые карточки
страницы больше не подставляются для заполнения limit. Точные латинские модели
и бренды дополнительно сверяются с буквальным названием.
### Купоны — обязательная проверка перед покупкой
Карточка и результаты проверки цены возвращают `coupon`: наличие, фактическое
применение, буквальные условия/срок из ценового блока и `priceRub` — подтверждённую
цену Ozon Карты с купоном. `alreadyIncludedInDisplayedPrice=true` означает, что
скидка **уже** учтена: повторно вычитать процент нельзя. В поиске непрочитанная
отметка купона требует открытия карточки. Купон не заменяет условия Ozon Карты.
`checkout_prepare` активирует обнаруженный купон через его настоящий контрол
и проверяет состояние на исходном SKU. Если купон не применён, цена неизвестна
или итог выше подтверждённой цены с купоном × количество, подготовка блокируется.
Более дорогая оплата/доставка также требует разбирательства, а не молчаливого
перехода к заказу. Оплата и ПВЗ автоматически не заменяются. Купон и цена входят
в подтверждаемый план; `action_confirm` повторяет проверку перед конечным кликом.
Оформление реального заказа в тестах запрещено.
TDQS
Scored across 28 tools
Tools are grouped by clear purpose (auth, search/product, reviews/media, orders, lists, cart/checkout, prepare/confirm actions), and the prepare→confirm→status pattern is explicit. The main fuzziness is in the media quartet (ozon_media_get, ozon_review_media, ozon_product_media, ozon_media_import) and the list trio (ozon_favorite, ozon_collections, ozon_shopping_list), where boundaries are clarified only by long descriptions.
All 28 tools share the ozon_ prefix and snake_case, mostly following a noun_verb or verb_noun scheme (ozon_search, ozon_order_detail, ozon_cart_add, ozon_return_prepare). Minor deviations: ozon_media_get puts the verb last while ozon_product_media has no verb, and ozon_media_get/ozon_media_import use 'media' as the noun prefix instead of the resource-first style seen elsewhere.
28 tools is heavy (above the ~25 threshold) and will strain an agent's tool selection context. However, the surface covers a genuinely broad marketplace domain — auth, search, product, variants, reviews, media, questions, orders, cart, checkout, discounts, returns, collections — so most tools map to a distinct need rather than being redundant.
Read coverage is excellent (search+filters, product, variants, media, reviews+review media, questions, orders, order detail, cart, collections, auth) and the write side has a coherent preview→confirm gate covering purchase, discount, return, review publish, favorites, and collection creation. Notable gaps remain around mutation/cleanup: no cart quantity update or item removal, no collection/list update or delete, no unfavorite, and no order cancellation.