mcp-wb
by Albertharbez
README.md
# HARBEZ WB Feedbacks — MCP-коннектор
Отзывы и вопросы Wildberries приходят прямо в чат Claude — тебе и сотрудникам,
с любого устройства организации. Токены Seller API лежат только в окружении сервера:
их нет ни в переписке, ни на ноутбуках, ни в файлах проекта.
```
Claude (проект «Коммерческий отдел»)
│ HTTPS, streamable HTTP, персональный Bearer-ключ
▼
Caddy (TLS, Let's Encrypt)
│
MCP-сервер (Docker, слушает только внутри сети compose)
│ токены WB из .env
▼
feedbacks-api.wildberries.ru
```
## Что умеет
| Инструмент | Назначение |
|---|---|
| `wb_accounts_list` | Список селлер-аккаунтов (ключи и названия, без токенов) |
| `wb_feedbacks_unanswered_count` | Сколько отзывов без ответа, из них за сегодня, средняя оценка |
| `wb_feedbacks_list` | Отзывы с фильтрами: аккаунт, артикул, оценка, период, статус ответа |
| `wb_feedbacks_stats` | Сводка по SKU: средняя, распределение 1–5, доля негатива, без ответа |
| `wb_product_rating` | Рейтинг конкретного артикула |
| `wb_questions_list` | Вопросы покупателей |
| `wb_feedback_answer` | Публикация ответа. Выключена по умолчанию, требует `confirm=true` |
Запросы к WB троттлятся, тексты отзывов обрезаются до 600 символов,
`wb_feedbacks_stats` возвращает только агрегаты — чтобы не выжигать контекст.
## Развёртывание на ВПС
Нужны: ВПС с Ubuntu/Debian, поддомен с A-записью на него, root на старте.
```bash
# 1. На ВПС под root — пользователи, фаервол, Docker, клон репозитория
git clone git@github.com:Albertharbez/mcp-wb.git /srv/wb-mcp
bash /srv/wb-mcp/deploy/setup-vps.sh albert <логин-сотрудника>
# 2. Заполнить /srv/wb-mcp/.env (шаблон — .env.example)
# MCP_DOMAIN, WB_TOKEN_*, MCP_CLIENT_KEYS
# 3. Поднять
cd /srv/wb-mcp && docker compose up -d --build
curl https://<MCP_DOMAIN>/healthz # -> {"ok":true}
```
Обновление кода потом — `deploy/update.sh`: делает `git pull`, пересобирает,
поднимает и проверяет `/healthz`, а при неудаче показывает логи.
**Токены WB** создаются в каждом селлер-аккаунте: Настройки → Доступ к API →
категория **«Вопросы и отзывы»**. Один аккаунт — один токен.
## Работа двоих на одном ВПС
- Отдельный SSH-пользователь на каждого, вход только по ключу, пароли и root-логин
отключены. Общего аккаунта нет — иначе не видно, кто что сделал.
- Оба в группах `wb` (каталог проекта `/srv/wb-mcp`, setgid — новые файлы наследуют
группу) и `docker` (поднять и посмотреть сервис).
- `.env` — `640 root:wb`: читают оба, посторонние на сервере нет.
- Код правится через git, а не вживую на сервере: `git pull` в `deploy/update.sh`.
- **Персональный `MCP_CLIENT_KEYS` на человека**, не общий. В логах видно, кто
обращался; увольнение — удалить строку и перезапустить, без ротации токенов WB.
## Подключение к Claude
Claude → Settings → Connectors → Add custom connector:
URL `https://<MCP_DOMAIN>/mcp`, заголовок `Authorization: Bearer <персональный ключ>`.
Подключает владелец организации — тогда коннектор доступен отделу с любого устройства.
На корпоративном тарифе кастомные коннекторы может потребоваться сначала разрешить
в настройках организации.
## Безопасность
- Токены только в env сервера. В git не попадают (`.gitignore`), в ответах
инструментов не фигурируют, в `toString` аккаунта заменены заглушкой.
- Аутентификация **fail-closed**: пустой `MCP_CLIENT_KEYS` — сервер не стартует.
Открытый режим только явным `MCP_ALLOW_NO_AUTH=true` и только для localhost.
- Наружу открыты три пути: `POST /mcp`, `GET /healthz` (без деталей),
`GET /status` (детали, за ключом). Остальное — 404 на уровне Caddy.
- Сам сервер порт наружу не публикует: единственный вход — через Caddy.
- Публикация ответов — единственная необратимая операция: `destructiveHint`,
`confirm=true` и рубильник `WB_ALLOW_WRITES` на сервере.
- **Токен, который уже был отправлен в переписку, считается скомпрометированным. Отозвать.**
## Что было исправлено при развёртывании
| Что | Почему |
|---|---|
| Аутентификация fail-open → fail-closed | Пустой `MCP_CLIENT_KEYS` открывал сервер с токенами WB всему интернету |
| `/healthz` больше не отдаёт состав аккаунтов и режим записи | Публичный эндпоинт раскрывал внутреннюю структуру; детали ушли в `/status` за ключом |
| Фильтр по оценке ушёл внутрь листания | Запрос «жалобы, limit=100» возвращал не 100 жалоб, а те несколько, что попались среди первых 100 отзывов |
| Троттлер выстроен в цепочку | Параллельные вызовы читали одно значение `lastCall` и уходили к WB разом, лимит не соблюдался |
| `GET`/`DELETE /mcp` → 405 с объяснением | Stateless-сервер отдавал 404 и невнятную ошибку |
| `@types/express` приведён к 4.x | В зависимостях express 4, типы стояли от 5 |
## Проверено и не проверено
- Синтаксис и логика правок вычитаны; `tsc --noEmit` и запуск **не выполнялись** —
на машине, где велась работа, нет Node. Первый прогон сборки — на ВПС
(`docker compose up -d --build`), там же tsc отработает внутри образа.
- Обращения к WB на живых токенах не тестировались. После заполнения `.env`
проверять на `wb_feedbacks_unanswered_count` — самый дешёвый вызов.
- Состав полей и лимиты сверить перед продом: https://dev.wildberries.ru
(раздел «Отзывы и вопросы»).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues