Metadata-Version: 2.4
Name: wb-mcp
Version: 0.1.0
Summary: MCP-сервер для API продавца Wildberries
Author: S-typy
License-Expression: AGPL-3.0-only AND LicenseRef-WB-MCP-Commercial
Project-URL: Repository, https://github.com/S-typy/WB-MCP
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: wbmcp/cloud/LICENSE
Requires-Dist: mcp<3,>=2.2
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.7
Requires-Dist: jmespath>=1.0
Provides-Extra: crypto
Requires-Dist: cryptography>=42; extra == "crypto"
Provides-Extra: postgres
Requires-Dist: psycopg[binary,pool]>=3.2; extra == "postgres"
Provides-Extra: redis
Requires-Dist: redis>=5; extra == "redis"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-cov>=5; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: cryptography>=42; extra == "dev"
Requires-Dist: psycopg[binary,pool]>=3.2; extra == "dev"
Requires-Dist: pgserver>=0.1; extra == "dev"
Requires-Dist: redis>=5; extra == "dev"
Dynamic: license-file

# wb-mcp

MCP-сервер для API Wildberries.
Один процесс, любой MCP-клиент, сколько угодно кабинетов.
Список методов лежит в пакете (`wbmcp/specs`): 305 методов в 13 разделах, снимок 28.09.2026.

Проблематика: агенту, как правило, нужен доступ ко всему API, при этом
нужно учитывать лимиты запросов WB. Сервер служит middleware — проверяет, что токену доступен
метод, смотрит лимит, ведёт журнал записей и отдаёт ответ WB как есть.
Повторные чтения справочников и карточек берутся из кэша.

## Состав

- `wbmcp/tokens.py`, `wbmcp/sellers` -- разбор токена (sid, категории, только чтение,
  песочница, срок) и реестр кабинетов. Токены лежат в SQLite или Postgres, при наличии ключа
  `WBMCP_TOKEN_KEY` шифруются.
- `wbmcp/catalog` -- каталог операций: допуск по категориям и типу
  токена, таблицы лимитов, схемы аргументов, поиск.
- `wbmcp/ratelimit` -- token bucket на кабинет и класс лимита.
- `wbmcp/client` -- сам запрос: допуск, лимит, повторы, журнал записей, файлы из ответов.
- `wbmcp/tools` -- инструменты MCP, базовые, курируемые и сгенерированные.
- `wbmcp/cloud` -- каркас многоарендного режима и Redis, у них своя лицензия, см. в конце.

## Установка

Python 3.12 или выше. Установка из репозитория:

```
pip install git+https://github.com/S-typy/WB-MCP.git
```

Дополнительные зависимости, если нужны:

```
pip install "wb-mcp[crypto] @ git+https://github.com/S-typy/WB-MCP.git"
pip install "wb-mcp[postgres] @ git+https://github.com/S-typy/WB-MCP.git"
pip install "wb-mcp[redis] @ git+https://github.com/S-typy/WB-MCP.git"
```

`crypto` нужен для шифрования токенов в базе, `postgres` -- для общего реестра кабинетов и
журнала, `redis` -- для общего лимитера и кэша. Можно объединить: `wb-mcp[crypto,postgres]`.
У Redis-модулей коммерческая лицензия, установка зависимостей её не заменяет.

## Токен

Токен выпускается в личном кабинете продавца, раздел «Настройки, Доступ к API». Для локального
режима подходит любой тип: персональный, сервисный, базовый, тестовый. Сервер сам читает из
токена, какие категории открыты, только чтение или признак песочницы. С тестовым токеном
запросы уходят на `*-sandbox.wildberries.ru`, ничего переключать не надо.

Самый короткий запуск с одним кабинетом:

```
export WB_TOKEN=eyJ...
wb-mcp --check      # собрать сервер и напечатать сводку, без запросов к WB
wb-mcp --smoke      # пять шагов проверки с запросами к WB
wb-mcp              # stdio для MCP-клиента
```

`--smoke` проверяет кабинеты, ping, сведения о продавце, новости и квоту. Для тестового
токена вместо сведений о продавце и новостей запрашиваются склады и новые заказы.
При нескольких кабинетах укажите `--seller main`; этот флаг относится только к `--smoke`.

Для клиента, поддерживающего stdio-серверы, достаточно такого описания:

```json
{"mcpServers": {"wb": {"command": "wb-mcp", "args": ["--config", "/home/me/wb-mcp.toml"]}}}
```

## Настройка

Несколько кабинетов, профили инструментов и ключи клиентов для HTTP описываются в TOML.
Путь к файлу передаётся через `--config` или переменную `WBMCP_CONFIG`.

```toml
[server]
profiles = ["orders", "content"]
data_dir = "~/.wb-mcp"

[[sellers]]
alias = "main"
token_env = "WB_TOKEN_MAIN"

[[sellers]]
alias = "second"
token_file = "~/.wb-mcp/second.token"

[[clients]]                      # нужны только для --transport http
name = "agent"
key_env = "WBMCP_KEY_AGENT"
sellers = ["main"]
scopes = ["read", "write"]
```

Профили: `core` (только базовые инструменты), `orders`, `content`, `promo`, `analytics`,
`communication`, `finance`, `common`, `full`. Профиль решает, какие из 305 сгенерированных
инструментов `wb_op_*` попадут в список. Все 305 сразу отдавать не стоит, агенту столько
не переварить, а `wb_request` доступен всегда.

Без настройки включён `core`; курируемые инструменты тоже доступны. Профили можно задать
при запуске: `wb-mcp --profiles orders,content`. Они меняют список инструментов, права
клиента задаются отдельно через `scopes` и `sellers`. `--read-only` запрещает записи в WB
для всего сервера, в том числе с `force=true`.

Токены, добавленные через `wb_seller_add`, сохраняются в базе. Для шифрования установите
`crypto` и задайте `WBMCP_TOKEN_KEY` -- ключ Fernet. Например, один раз создайте ключ:

```sh
export WBMCP_TOKEN_KEY="$(python -c '
from wbmcp.sellers.crypto import FernetCipher
print(FernetCipher.generate_key())
')"
```

Сохраните его для следующих запусков: новый ключ старые записи не откроет. Без ключа токены
в базе хранятся открыто. Уже записанные токены при включении шифрования автоматически не
перешифровываются; токены из TOML, переменных и файлов остаются в своих источниках.

## Каталог API

В пакет входит собранный `catalog.json`: пути, хосты, схемы, допуски и лимиты. Исходных
OpenAPI-файлов и полных текстов документации WB в поставке нет, названия методов сохранены.
Поэтому поиск и `wb_api_describe` работают по каталогу, но пояснения WB к полям будут пустыми.

Свою копию OpenAPI-спецификаций можно подключить через `--specs /path/to/snapshot`,
`WBMCP_SPECS` или `specs_dir` в `[server]`. В каталоге должны лежать JSON-файлы разделов;
если они есть, сервер читает их вместо собранного каталога. Сам сервер снимок не обновляет.

## Инструменты

`wb_sellers` показывает кабинеты и что умеют их токены. В остальных инструментах кабинет
указывается аргументом `seller`, при одном кабинете его можно не писать.

`wb_api_search` и `wb_api_describe` ищут метод и отдают карточку с допуском, лимитом и схемой
аргументов. `wb_request` зовёт любой метод по пути, например `GET /api/v3/orders/new`:
путь сверяется с каталогом, аргументы со спецификацией. `wb_op_<operationId>` делает то же,
но с готовой схемой, по одному инструменту на метод из включённых профилей.

Для частых задач есть `wb_orders_new`, `wb_orders`, `wb_cards`, `wb_feedbacks`,
`wb_warehouses`, `wb_prices_set`, `wb_stocks_set` и `wb_report`. `wb_orders` и `wb_cards`
собирают страницы до `max_pages`; `capped=true` в ответе значит, что достигнут этот предел.
Остальные постраничные методы отдают одну страницу. `wb_report` умеет ждать готовности
остатков, платного хранения, приёмки и отчёта по воронке.
Служебные: `wb_ping`, `wb_seller_info`, `wb_news`,
`wb_changes` (новости WB, сопоставленные с каталогом), `wb_quota`, `wb_journal`, `wb_audit`.
`wb_seller_add` и `wb_seller_remove` меняют реестр, им нужен `admin`; кабинеты из TOML
удаляются из самого файла настроек. Для кэша и сохранённых выгрузок есть `wb_cache` и `wb_result`.

## Большие ответы и файлы

Небольшой JSON приходит целиком. Если данные больше 200 000 байт, они сохраняются в
`data_dir/results`, а в ответе остаются образец, `truncated=true` и `stored`: id, путь,
размер и sha256. Полные данные из-за этого не теряются.

`wb_result` без аргументов показывает сохранённые выгрузки. С `result_id` читает данные,
для списков есть `offset` и `limit` (по умолчанию 100). Полный JSON можно получить через
ресурс `wb://results/<id>`, в том числе по HTTP, без доступа к диску сервера.

`select` -- выражение JMESPath. Например, у `wb_cards` можно передать
`"[].{nmID: nmID, title: title}"`, чтобы оставить только номера и названия. Проекция
выполняется до сохранения: в файле будет уже выбранная часть. У `wb_result` она применяется
к сохранённым данным, затем к списку применяются `offset` и `limit`.

Порог и срок хранения меняются в TOML, ниже значения по умолчанию:

```toml
[server.results]
inline_max_bytes = 200000
preview_items = 5
keep_hours = 168
```

Старые выгрузки удаляются при старте и при сохранении новых. Бинарные файлы из ответов
(отчёты, ярлыки, документы) пишутся отдельно в `data_dir/files`, в ответе путь и sha256.
Файлы до 512 КиБ включительно возвращаются ещё и в base64. Лимит файла по умолчанию
20 МиБ, меняется через `max_file_bytes` в `[server]`; файлы здесь автоматически не чистятся.

## Кэш

По умолчанию кэш в памяти: карточки -- 5 минут, склады и тарифы -- час, справочники -- сутки.
Новые заказы и остатки по умолчанию не кэшируются. Настройки, например для SQLite:

```toml
[server.cache]
enabled = true
backend = "sqlite"
max_entries = 5000
invalidate_on_write = true

[server.cache.ttl]
"path:/content/v2/get/cards" = 60
```

Варианты `backend`: `memory`, `sqlite`, `redis`. SQLite переживает перезапуск, Redis требует
`redis_url`. Правила TTL задаются для `op:<operationId>`, `path:<METHOD /путь>`, префикса
`path:/путь`, `section:<раздел>` или `*`, в таком порядке приоритета. Нулевой TTL отключает
кэш для правила, `enabled=false` -- весь кэш. Пишущие методы не кэшируются.

`fresh=true` у `wb_request`, `wb_cards`, `wb_orders` и сгенерированных читающих инструментов
заставляет сходить в WB заново. `wb_cache` показывает статистику и правила, `action="clear"`
сбрасывает кэш; можно указать `seller` и `section`. У клиента без `admin` сбрасываются только
его кабинеты. Успешная запись по умолчанию очищает кэш своего раздела для этого кабинета.

## Лимиты

У каждого метода в описании есть таблица лимитов: период, число запросов, интервал, всплеск,
отдельно по типам токена. Сервер держит token bucket на пару «кабинет плюс класс лимита» и при
нехватке квоты откладывает запрос. Учитывает, что 4XX в Маркетплейсе стоит десять запросов,
читает заголовки `X-Ratelimit-*` и при ответе 429 выдерживает указанную WB паузу.
Бюджет класса один на кабинет, его делят все агенты и инструменты этого процесса.
Для нескольких процессов нужен общий Redis. `wb_quota` показывает остаток.

## Записи

У пишущих инструментов есть `dry_run` (собрать запрос и показать, не отправляя) и `force`.
Каждая запись идёт через журнал: таймаут или 5xx считаются неоднозначным исходом, и повтор
с теми же аргументами блокируется. После проверки результата в кабинете повтор можно
разрешить через `force=true`.
Повтор успешной записи с теми же аргументами тоже блокируется в течение десяти минут.

`force=true` обходит блокировку повторной записи в журнале, проверку аргументов по схеме
и запрос подтверждения опасного действия. Проверки прав доступа и лимиты сохраняются.
Отмена, удаление, бюджеты и доступы пользователей по умолчанию запрашивают подтверждение
у клиента. Флаг запуска `--yes` автоматически подтверждает такие действия.

## HTTP-режим

```
wb-mcp --transport http --host 0.0.0.0 --port 8000 --config wb-mcp.toml
```

Клиенты ходят на `/mcp` с bearer-ключом из `[[clients]]`, у каждого ключа свои кабинеты и
scope: `read`, `write`, `finance`, `admin`. `/health` и `/metrics` (формат Prometheus) открыты
без ключа. Без `[[clients]]` HTTP не поднимется, разве что с `--insecure` для отладки.

Для финансовых методов и документов нужен `finance` вместе с `read` или `write`.
`admin` сам по себе не добавляет остальные scope. В stdio клиент имеет все права на
настроенные кабинеты; ограничения `[[clients]]` работают в HTTP.

Для multipart-загрузок по HTTP путь к файлу должен лежать внутри `data_dir/uploads` на
сервере. Каталог создайте и наполните заранее; он общий для HTTP-клиентов этого процесса.
Вместо пути можно передать `base64` и `filename`. В stdio допускается любой доступный
процессу файл. Пути в ответах относятся к серверу, а не к машине HTTP-клиента.

## Postgres

По умолчанию кабинеты, журнал записей и аудит лежат в SQLite в `data_dir`. Для нескольких
экземпляров сервера укажите `database_url` в `[server]` или переменную `WBMCP_DATABASE_URL`.
Схема и таблицы создаются при старте.

Для общего лимитера установите extra `redis` и задайте `redis_url` в `[server]` или
переменную `WBMCP_REDIS_URL`, например `redis://localhost:6379/0`. Все экземпляры должны
использовать одну базу Redis, часы на хостах должны быть синхронизированы (например, NTP).
Без этой настройки лимитер хранит состояние в памяти каждого процесса. Redis-кэш включается
отдельно через `backend = "redis"` в `[server.cache]`.

Файлы и индекс выгрузок остаются в `data_dir`, даже с Postgres и Redis. Эти две настройки
сами по себе не делают сохранённый результат доступным на другом экземпляре сервера.

## Облачный режим

В `wbmcp/cloud` есть проверки типа токена, реестры арендаторов и Redis-хранилища.
Подключение кабинетов через OAuth пока не реализовано, готового облачного сервиса в пакете
нет. HTTP-режим выше -- способ подключить клиентов к своему серверу.

## Лицензия

Ядро распространяется под GNU AGPL-3.0-only, текст в файле `LICENSE`. Каталог `wbmcp/cloud`
(включая Redis-лимитер и Redis-кэш) под коммерческой лицензией, `wbmcp/cloud/LICENSE`: исходники
открыты, использовать в production можно только по договору. Коммерческая лицензия на ядро
для тех, кому AGPL не подходит, обсуждается отдельно, контакт в профиле
https://github.com/S-typy.
