ru-marketplace-mcp

CI
Python 3.12+
License: MIT
MCP

MCP-серверы для российских и китайских маркетплейсов. Цены, наличие,
рейтинги, отзывы и реквизиты продавцов с Wildberries, Ozon, Яндекс Маркета,
Детского мира, Авито, Taobao, Мегамаркета, Lamoda, DNS и Ситилинка. Плюс
сравнение цен по всем источникам одним вызовом.

Только чтение. Ключи API, токены и регистрация не нужны — площадки с жёстким
анти-ботом читаются через ваш собственный Chrome. Одно исключение по желанию:
опциональный MPStats берёт платный токен (MPSTATS_MP_AUTH) — без него всё
остальное работает как прежде.

English version below · Архитектура ·
Как добавить источник · Про анти-бот


Что внутри

Сервер Инструментов Что нужно, чтобы читалось Что умеет
Wildberries 9 анонимный HTTP Поиск, карточки, отзывы, вопросы о товаре, реквизиты продавца, каталог и товары категории
Яндекс Маркет 3 анонимный HTTP Цены разных продавцов, разбивка оценок по звёздам, отзывы
Детский мир 4 анонимный HTTP Детские товары, наличие в офлайн-магазинах, категории
Ozon 4 ваш Chrome; с домашнего IP часто и без него Поиск, карточки, отзывы
Авито 4 ваш Chrome + российский домашний IP и запросы вразрядку — иначе блок по IP Поиск объявлений, карточки, репутация продавца
Taobao 3 ваш Chrome с активным входом в Taobao Поиск и карточки, цены в юанях
Мегамаркет 3 ваш Chrome с активным входом — анонимной сессии API отдаёт пусто Поиск и карточки через мобильный API
Lamoda 3 карточки анонимно (GraphQL), поиск — ваш Chrome Поиск, карточки с размерами
DNS 3 ваш Chrome (Qrator) Поиск и карточки электроники
Ситилинк 3 ваш Chrome (Qrator) Поиск и карточки электроники
Сравнение 2 опрашивает всё перечисленное «Где дешевле?» одним вызовом
MPStats 3 платный аккаунт MPStats, cookie mp_auth (опционально) Продажи/остатки/графики за 30 дней по SKU Ozon/WB, остатки по складам (FBS/FBO)

Читается анонимно, без браузера: Wildberries, Яндекс Маркет, Детский мир и
карточки Lamoda. Остальным нужен ваш залогиненный Chrome (CDP). Taobao и
Мегамаркет вдобавок требуют активного входа в саму площадку — без него Taobao
упирается в стену логина, а Мегамаркет отдаёт пустой ответ. Авито ещё и блокирует
по IP: с датацентрового адреса это глухой отказ, с российского домашнего — работает,
если не частить запросами. Запросы к CDP-источникам идут вразрядку: очередь
подряд без пауз роняет их (DNS и Taobao в проверке так и деградировали), поэтому
коннекторы держат паузу между вызовами сами. Точное состояние из вашей сессии
покажет marketplace-mcp doctor.

MPStats стоит особняком: это единственный платный источник. Без
MPSTATS_MP_AUTH сервер запускается, но инструменты отвечают auth_missing
поэтому он опционален и подключается по желанию, на остальные двенадцать
серверов он не влияет никак.

Всего 33 инструмента в 12 серверах на общем рантайме mcp-core. Плюс объединённый
marketplace-mcp, который монтирует всё разом — одна запись в конфиге клиента
вместо двенадцати. Он добавляет свой инструмент marketplace_sources (какие коннекторы
поднялись, а какие отвалились и почему), так что в нём 34 инструмента: 33
смонтированных плюс этот.

Быстрый старт

Нужны Python 3.12+ и uv.

git clone https://github.com/Vladimir-Human/ru-marketplace-mcp.git
cd ru-marketplace-mcp
uv sync --all-packages
uv run pytest -q -m "not live and not cdp"   # 1182 офлайн-тестов, сеть не нужна

Проверка живого эндпоинта:

uv run python -c "
import asyncio
from wb_connector.server import wb_selfcheck
print(asyncio.run(wb_selfcheck()).status)   # ждём success
"

Подключение к MCP-клиенту

Каждый сервер — консольная команда, поэтому пути в конфиге не зашиваются.

Claude Desktopclaude_desktop_config.json

Windows: %APPDATA%\Claude\claude_desktop_config.json
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

Проще всего подключить одну запись — объединённый сервер монтирует все
источники разом, а имена инструментов (wb_search, avito_seller, …) не
меняются:

{
  "mcpServers": {
    "marketplace": {
      "command": "uv",
      "args": ["run", "--directory", "C:/путь/к/ru-marketplace-mcp", "marketplace-mcp"],
    },
  },
}

Если нужны отдельные серверы, marketplace-mcp install claude напечатает
готовый блок для вставки. Путь к вашему checkout там уже подставлен: заглушку
/path/to/ru-marketplace-mcp править руками не придётся. При установке из wheel
вместо путей печатаются консольные команды на PATH. Неизвестное имя клиента
(допустимы claude, claude-code, cursor, dsh) команда отклоняет с пояснением и
кодом возврата 2 — молча подставить блок для Claude она не может. Минимальный
вариант вручную:

{
  "mcpServers": {
    "wildberries": {
      "command": "uv",
      "args": ["run", "--directory", "C:/путь/к/ru-marketplace-mcp", "wb-mcp"],
    },
    "ozon": {
      "command": "uv",
      "args": ["run", "--directory", "C:/путь/к/ru-marketplace-mcp", "ozon-mcp"],
    },
    "compare-prices": {
      "command": "uv",
      "args": ["run", "--directory", "C:/путь/к/ru-marketplace-mcp", "compare-mcp"],
    },
  },
}

Путь пишите с прямыми слешами / или двойными обратными \\. Полный список
команд — wb-mcp, ozon-mcp, yandex-mcp, detmir-mcp, avito-mcp,
taobao-mcp, megamarket-mcp, lamoda-mcp, dns-mcp, citilink-mcp,
compare-mcp, marketplace-mcp.

Claude Code
claude mcp add wildberries -- uv run --directory /путь/к/ru-marketplace-mcp wb-mcp
claude mcp add yandex-market -- uv run --directory /путь/к/ru-marketplace-mcp yandex-mcp
claude mcp add detsky-mir -- uv run --directory /путь/к/ru-marketplace-mcp detmir-mcp
claude mcp add ozon -- uv run --directory /путь/к/ru-marketplace-mcp ozon-mcp
claude mcp add compare-prices -- uv run --directory /путь/к/ru-marketplace-mcp compare-mcp
Cursor.cursor/mcp.json
{
  "mcpServers": {
    "compare-prices": {
      "command": "uv",
      "args": ["run", "--directory", "/путь/к/ru-marketplace-mcp", "compare-mcp"],
    },
  },
}
Другой stdio-клиент

Запустите uv run --directory /путь/к/репозиторию <команда>, где команда — одна из
wb-mcp, ozon-mcp, yandex-mcp, detmir-mcp, compare-mcp. Серверы говорят по
JSON-RPC через stdin и stdout, диагностику пишут в stderr. Опциональный
mpstats-mcp запускается так же, с MPSTATS_MP_AUTH в окружении.

DeepSeek Harness (dsh) — плагин-бандл

В dsh это не запись mcpServers, а слой профиля. Бандл лежит в подкаталоге
dsh/ и ставится штатным менеджером плагинов (pnpm нужен на PATH):

dsh plugin --profile web add github:Vladimir-Human/ru-marketplace-mcp#path:/dsh

Сразу после установки появляются 13 навыков и ни одного MCP-инструмента: обе
строки MCP выключены, пока не задана переменная RU_MARKETPLACE_MCP_DIR с путём к
клону. Так сделано потому, что смонтированный сервер платится в каждом запросе:
рекомендуемый режим сравнения цен стоит ~0,9 тыс. токенов, полный набор — ~13 тыс.
Включение и полный режим описаны в dsh/README.md.

После подключения перезапустите клиент и прогоните marketplace-mcp doctor. Он
запускает канарейку каждого коннектора и отвечает success, drift_detected или
inconclusive.

Инструменты

Канарейки *_selfcheck в этом перечне не значатся намеренно: они не публикуются
по MCP, потому что диагностика оператора стоила бы модели ~7,5 тыс. токенов в
каждом запросе. Запускает их marketplace-mcp doctor — все разом, из командной
строки.

Wildberries — wb_*

Инструмент Что делает
wb_search(query, page) Поиск по тексту, до 100 товаров на страницу с ценами и остатками
wb_card(nm_ids) Пакетный запрос до 100 известных SKU
wb_root_info(nm_id) Находит imt_id (нужен для отзывов) и цветовые варианты
wb_reviews(imt_id, limit, sort) Пул отзывов. Ключ — imt_id, а не nm_id
wb_questions(imt_id, limit, skip, answered_only) Вопросы покупателей и ответы продавца. Тоже по imt_id
wb_seller(supplier_id) Юрлицо, ИНН, КПП, ОГРН, юридический адрес
wb_categories(root, max_depth) Дерево каталога с шардами и запросами самого WB
wb_category_products(shard, query, page, sort, dest) Товары категории по shard и query из wb_categories

wb_seller отвечает на вопрос, который карточка товара скрывает: кто на самом деле
продаёт? Возвращает зарегистрированное юрлицо и налоговые номера. Так отличают
официальный магазин бренда от перекупщика с похожим названием.

wb_questions закрывает другой пробел. Отзывы говорят, каково владеть товаром;
вопросы уточняют, что это вообще за товар — «10 или 16 ампер», «кабель в комплекте?».
Ответ продавца часто единственное публичное утверждение об этом. Пул общий для всех
вариантов товара, ключ — imt_id из wb_root_info.

wb_category_products замыкает связку с wb_categories: та отдаёт shard и query,
это — товары по ним. Формат элементов совпадает с wb_search, поэтому обход категорий
и текстовый поиск сравнимы напрямую. Часть крупных разделов WB помечает шардом
blackhole — у них нет своей выдачи, и инструмент честно об этом говорит вместо
пустого списка.

Яндекс Маркет — yandex_*

Инструмент Что делает
yandex_search(query, page, limit) Поиск с обеими ценами, рейтингами, продавцами
yandex_card(product_id, include_reviews) Карточка целиком: разбивка по звёздам и отзывы

Две цены, всегда. price_rub платит любой покупатель. price_with_plus
требует подписку Яндекс Плюс и обычно на 25–30% ниже. Интерфейс Яндекса показывает
вторую крупным шрифтом, поэтому назвать её без оговорки — значит пообещать цену,
которую человек без подписки не получит.

rating_stars даёт распределение вида {1: 10, 2: 3, 3: 10, 4: 19, 5: 502}. Из
него видно, честная ли средняя 4.8 или за ней прячется кучка единиц.

Детский мир — detmir_*

Инструмент Что делает
detmir_categories(parent, limit, region) Дерево каталога. Начинать отсюда
detmir_category(alias, limit, offset, region) Товары категории с настоящим счётчиком
detmir_card(product_id, region) Цена, рейтинг, наличие онлайн и в магазинах

Регион задаётся на каждый вызов. Цены и особенно наличие в офлайн-магазинах
сильно зависят от города: один и тот же товар лежал в 152 магазинах Москвы, 37
Петербурга и 2 Хабаровска. Параметр region перекрывает DETMIR_REGION, так что
города можно сравнивать в одной сессии.

Текстового поиска здесь нет, и это намеренно. API Детского мира молча игнорирует
любые текстовые фильтры и возвращает весь каталог на 300 тысяч позиций, а сайтовый
роут поиска отдаёт 404 с промо-карусселью. Инструмент поиска возвращал бы уверенно
неверные товары, поэтому навигация идёт через категории. Подробности в
docs/ANTI_BOT.md.

Ozon — ozon_*

Инструмент Что делает
ozon_search(query) Поиск по тексту
ozon_card(sku_or_path) Карточка товара
ozon_reviews(sku_or_path, limit, sort) Отзывы

Ozon отклоняет датацентровый трафик, поэтому коннектор двухуровневый. Сначала
TLS-имперсонация. Если Cloudflare выдаёт челлендж, запрос выполняется внутри вашего
залогиненного Chrome через DevTools Protocol. Ничего не хранится: вход выполняете вы
сами, в браузере, который контролируете. Настройка описана в
docs/CDP_SETUP.md.

С российского домашнего IP первый уровень обычно работает, и браузер не нужен.

Авито — avito_*

Инструмент Что делает
avito_search(query, page, location_id, category_id) Поиск объявлений через внутренний js/items API
avito_card(item_id_or_url) Одно объявление: цена, описание, просмотры, продавец
avito_seller(seller_id_or_url) Рейтинг продавца, число отзывов, активные объявления

Авито — это объявления, а не каталог: пула отзывов на товар нет, репутация
продавца и есть сигнал доверия. Бесплатное/обменное объявление приходит с
price_rub: null — никогда не 0, чтобы не оказаться «самым дешёвым» в
сравнении. С датацентрового IP Авито отвечает 403-файрволом, поэтому коннектор
двухуровневый: TLS-имперсонация, дальше ваш Chrome (как у Ozon).

Taobao — taobao_*

Инструмент Что делает
taobao_search(query, page) Поиск по каталогу Taobao
taobao_card(item_id_or_url) Карточка товара

Поиск Taobao — клиентское React-приложение с подписанным mtop API: каждый запрос
требует sign, вычисленный из cookie-токена, поэтому анонимного пути нет.
Все чтения идут внутри вашего Chrome, где сайт сам подписывает запросы. Цены в
юанях (CNY)
и не конвертируются: зашитый курс молча устарел бы, так что
сравнение с рублёвыми источниками делайте явно.

Мегамаркет, Lamoda, DNS, Ситилинк

Эти четыре читаются через ваш Chrome (CDP). Мегамаркет (megamarket_*) — мобильный
JSON API из-за ServicePipe, и одного пройденного челленджа мало: анонимной сессии
API отдаёт пустой список, нужен активный вход в Мегамаркет. DNS (dns_*) и Ситилинк
(citilink_*) — отрисованный DOM из-за Qrator; у всех трёх анонимного пути нет вообще.
Lamoda (lamoda_*) наполовину: карточки берутся анонимно через GraphQL, а поиск —
через Chrome. Chrome с CDP (scripts/start_chrome_cdp.sh) нужен всем, кроме карточек
Lamoda.

Всего через CDP ходят семь источников — эти плюс Ozon и Авито, где Chrome лишь
запасной уровень: их tier 1 обычно отвечает, а браузер включается, когда анонимный
уровень упёрся в челлендж. marketplace-mcp doctor из вашего браузера скажет, какие
эндпоинты подтверждены.

Сравнение цен — compare_*

Инструмент Что делает
compare_prices(query, per_source_limit, sources) Все маркетплейсы сразу, с ранжированием
compare_sources() Какие маркетплейсы доступны в этой установке
compare_prices("кроссовки мужские")

  wildberries      712 ₽   Кроссовки изи дышащие спортивные
  wildberries      814 ₽   Зимние кроссовки теплые с мехом
  yandex_market   2499 ₽   Кеды A-LOW
  yandex_market   3480 ₽   Кеды

  дешевле всего: wildberries 712 ₽, разброс 5858 ₽, complete: true

Маркетплейсы опрашиваются параллельно, и каждый отчитывается сам за себя. Если один
заблокирован, сравнение не рушится: complete: false вместе с source_outcomes
покажет, что именно вы видите. Подписочные цены в ранжировании не участвуют.
Совпадающие предложения по паре (источник, id товара) схлопываются, так что один
и тот же товар не занимает два места в ранжировании.

У каждого предложения есть currency (строчный ISO-код, по умолчанию rub) и
price_native — цена в этой валюте, как её показывает маркетплейс. Для российских
источников она совпадает с price_rub; у Taobao в ней лежит цена в юанях, которую
price_rub намеренно оставляет пустой. Раньше юаневую цену забирали и молча
выбрасывали, и строка Taobao приходила с пустой ценой без намёка, что цена вообще
есть. Теперь юань виден, но в рублёвом ранжировании по-прежнему не участвует: в
warnings появляется foreign_currency: … с числом исключённых предложений и
причиной. Конвертировать здесь значило бы зашить курс, который молча устареет, —
пересчёт за вами.

MPStats — mpstats_*

Аналитика продаж и остатков по SKU Ozon и Wildberries через плагин MPStats.
В отличие от всех остальных коннекторов, этот опционален и требует платный
аккаунт MPStats
: авторизация — одна cookie mp_auth (JWT из залогиненной
сессии плагина на mpstats.io), задаётся переменной MPSTATS_MP_AUTH. Без неё
инструменты возвращают auth_missing, а сервер запускается как обычно — ни на
что другое это не влияет.

Инструмент Что делает
mpstats_item(skus, place, oz_fbs=True) Аналитика за 30 дней по до 100 SKU: заказы, цена, остатки, графики по дням, продавец/бренд
mpstats_warehouses(skus, place) Остатки по складам: FBS (склад продавца) и FBO (склад маркетплейса), last_update

placeozon или wildberries. Графики длиной 30, от старых к новым:
последняя ненулевая ячейка — текущая цена или остаток. Цена и остаток при
сплошь нулевом графике ведут себя намеренно по-разному: цена становится None
(ложный 0 выиграл бы любое сравнение «где дешевле»), а остаток — 0, потому
что «нулевой остаток» это осмысленное показание, а не отсутствие данных. Пустой
график даёт None в обоих случаях. Ноль в отдельной ячейке — «нет данных за тот
день», а не «значение было нулевым», поэтому сумму за окно считайте по графику. Отсутствие
токена и транспортные сбои selfcheck отчитывает как inconclusive, не drift:
гоняться за дрейфом схемы, которого не было, не нужно. Токен — секрет платного
аккаунта с квотой: не логируйте и не коммитьте его.

Навыки для агента

У каждого коннектора — свой навык в skills/, тринадцать штук на тринадцать
серверов. Навык это не пересказ README: он объясняет агенту, когда за этот
источник вообще браться, чего у источника нет, и каким его ответам нельзя верить
без второго взгляда.

Навык Сервер
skills/wb-connector wb-mcp
skills/ozon-connector ozon-mcp
skills/yandex-connector yandex-mcp
skills/detmir-connector detmir-mcp
skills/avito-connector avito-mcp
skills/taobao-connector taobao-mcp
skills/megamarket-connector megamarket-mcp
skills/lamoda-connector lamoda-mcp
skills/dns-connector dns-mcp
skills/citilink-connector citilink-mcp
skills/compare-prices compare-mcp
skills/mpstats-connector mpstats-mcp
skills/marketplace marketplace-mcp

mcp-core — общий рантайм под остальными серверами. Своего навыка у него нет.

Соответствие проверяется тестом
(packages/marketplace-connector/tests/test_skills_parity.py): новый коннектор
без навыка роняет прогон, как и навык, который называет несуществующий
инструмент или забыл существующий. До этого теста навык DNS почти год советовал
формат ссылки /product/<24-hex>/ — тот самый шаблон, который чинили как баг.

Скиллы едут в Docker-образ (/app/skills/), но в колёсах их нет: skills/
лежит в корне репозитория. Ставите с PyPI — возьмите навыки
из репозитория отдельно.

Настройка

Все параметры задаются переменными окружения с префиксом коннектора. Все
необязательные.

Префикс Основные параметры
WB_ TIMEOUT, MIN_GAP, DEFAULT_DEST, NET_RETRIES, MAX_BODY_BYTES, CACHE_TTL, PROXY
YANDEX_ TIMEOUT, MIN_GAP, CACHE_TTL, PROXY
DETMIR_ REGION (RU-MOW, RU-SPE и другие), CACHE_TTL, PROXY
OZON_ TIMEOUT, MIN_GAP, IMPERSONATE, CACHE_TTL, PROXY
AVITO_ TIMEOUT, MIN_GAP, IMPERSONATE, CACHE_TTL, PROXY, LOCATION_ID
TAOBAO_ TIMEOUT, MIN_GAP, CACHE_TTL, PROXY