Skills Plugins MCP Prompt Model 导航 博客 资讯 我的中心
工具与能力 #marketplace#mcp#ozon#price-comparison#russia#wildberries

ru-marketplace-mcp (dsh)

面向十一家电商平台的技能与可选 MCP 行:跨 Wildberries、Detsky Mir、Yandex Market 比价,以及各平台(含 Ozon、Avito、AliExpress)的搜索、商品卡与评论。安装后 14 个技能立即可用;两行 MCP 默认关闭,需将 RU_MARKETPLACE_MCP_DIR 指向本地克隆,该克隆需要 Python 3.12+ 与 uv。

vladimir-human @vladimir-human ⬇ 3 ★ 106 main

安装

dsh plugin --profile web add github:vladimir-human/ru-marketplace-mcp
下载安装清单

需要可复现安装时,可在仓库后追加 #commit 固定提交。

面向十一家电商平台的技能与可选 MCP 行:跨 Wildberries、Detsky Mir、Yandex Market 比价,以及各平台(含 Ozon、Avito、AliExpress)的搜索、商品卡与评论。安装后 14 个技能立即可用;两行 MCP 默认关闭,需将 RU_MARKETPLACE_MCP_DIR 指向本地克隆,该克隆需要 Python 3.12+ 与 uv。

该插件未提供要点说明,请参考仓库 README。

marketplacemcpozonprice-comparisonrussiawildberries
  1. 安装并启动 DeepSeek Harness:npx @deepseek-ai/dsh web
  2. 在终端执行上面的安装命令(CLI 会解析插件并核验来源)
  3. 用 dsh plugins list 确认已安装,必要时重启 Harness 生效

插件以当前 dsh 进程的权限运行,安装时可能执行代码。请先通读仓库源码与许可证,确认无破坏性命令与越权访问;本站只做索引,不对第三方插件安全性作担保。

代码仓库github.com/vladimir-human/ru-marketplace-mcp/tree/main/dsh
许可证MIT
主要语言main
下载量3
GitHub 星标106
最近推送2026-09-18
收录日期2026-09-19
分类工具与能力

事实信息来自公开插件目录快照(2026-10-03),介绍文案由本站再加工。

以下为插件仓库 README 全文(原始内容,由公开目录抓取整理)。

# ru-marketplace-mcp

[![CI](https://github.com/Vladimir-Human/ru-marketplace-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/Vladimir-Human/ru-marketplace-mcp/actions/workflows/ci.yml)
[![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![MCP](https://img.shields.io/badge/MCP-stdio%20%7C%20http-orange.svg)](https://modelcontextprotocol.io)

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

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

[English version below](#english-version) · [Архитектура](docs/ARCHITECTURE.md) ·
[Как добавить источник](docs/ADDING_A_SOURCE.md) · [Про анти-бот](docs/ANTI_BOT.md)

Для проверок в браузере добавлен опциональный режим сохранения вкладки:
`CHROME_CHALLENGE_HANDOFF_S=120`. После завершения проверки повтор того же
запроса в той же MCP-сессии продолжает чтение этой вкладки. Поддержка и ограничения
описаны в [настройке Chrome](docs/CDP_SETUP.md#optional-challenge-handoff).

---

## Что внутри

| Сервер            | Инструментов | Что нужно, чтобы читалось                                                  | Что умеет                                                                                 |
| ----------------- | ------------ | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **Wildberries**   | 8            | анонимный HTTP                                                             | Поиск, карточки, отзывы, вопросы о товаре, реквизиты продавца, каталог и товары категории |
| **Яндекс Маркет** | 2            | анонимный HTTP                                                             | Цены разных продавцов, разбивка оценок по звёздам, отзывы                                 |
| **Детский мир**   | 3            | анонимный HTTP                                                             | Детские товары, наличие в офлайн-магазинах, категории                                     |
| **Ozon**          | 3            | ваш Chrome; с домашнего IP часто и без него                                | Поиск, карточки, отзывы                                                                   |
| **Авито**         | 3            | ваш Chrome + российский домашний IP и запросы вразрядку — иначе блок по IP | Поиск объявлений, карточки, репутация продавца                                            |
| **Taobao**        | 2            | ваш Chrome с активным входом в Taobao                                      | Поиск и карточки, цены в юанях                                                            |
| **Мегамаркет**    | 2            | ваш Chrome с активным входом — анонимной сессии API отдаёт пусто           | Поиск и карточки через мобильный API                                                      |
| **Lamoda**        | 2            | карточки анонимно (GraphQL), поиск — ваш Chrome                            | Поиск, карточки с размерами                                                               |
| **DNS**           | 2            | ваш Chrome (Qrator)                                                        | Поиск и карточки электроники                                                              |
| **Ситилинк**      | 2            | ваш Chrome (Qrator)                                                        | Поиск и карточки электроники                                                              |
| **AliExpress**    | 2            | ваш Chrome (x5sec)                                                       | Поиск и карточки, цены в рублях                            |
| **Циан**          | 2            | ваш Chrome (WAF по IP)                                                     | Недвижимость: поиск по фильтрам (продажа, аренда, посуточно) и карточка объявления         |
| **Сравнение**     | 4            | опрашивает всё перечисленное                                               | «Где дешевле?» одним вызовом                                                              |
| **MPStats**       | 2            | платный аккаунт 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` —
поэтому он опционален и подключается по желанию, на остальные тринадцать
серверов он не влияет никак.

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

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

Нужны **Python 3.12+** и [uv](https://docs.astral.sh/uv/).

```bash
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"   # 1735 офлайн-тестов, сеть не нужна
```

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

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

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

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

Claude Desktop — claude_desktop_config.json

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

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

```jsonc
{
  "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 она не может. Минимальный
вариант вручную:

```jsonc
{
  "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`.

### Только нужные площадки: `MARKETPLACE_SOURCES`

Объединённый сервер монтирует все источники, а описания их инструментов уходят
в контекст **в каждом запросе**. Переменная `MARKETPLACE_SOURCES` оставляет
только перечисленные:

```jsonc
{
  "mcpServers": {
    "marketplace": {
      "command": "uv",
      "args": ["run", "--directory", "C:/путь/к/ru-marketplace-mcp", "marketplace-mcp"],
      "env": {
        "MARKETPLACE_SOURCES": "wildberries,ozon,yandex_market,avito,aliexpress,dns,compare",
      },
    },
  },
}
```

Имена — канонические (`wildberries`, `ozon`, `yandex_market`, `detsky_mir`,
`avito`, `taobao`, `megamarket`, `lamoda`, `dns`, `citilink`, `aliexpress`,
`cian`, `compare`, `mpstats`); короткие псевдонимы `wb`, `ym`/`yandex`, `detmir`, `ali`
тоже принимаются. Неизвестное имя отклоняется при запуске с перечнем
поддерживаемых источников, чтобы опечатка не превратилась в частичный сервер.
Переменная не задана или пуста — монтируется всё, как раньше.
Отключённые источники видно в `marketplace_sources`: они попадают в `skipped`
с пометкой, что их сняли, а не что они не импортировались. `compare_prices`
опрашивает ровно тот же набор.

Claude Code

```bash
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

```jsonc
{
  "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`, `aliexpress-mcp`, `cian-mcp`, `compare-mcp`. Серверы говорят по
JSON-RPC через stdin и stdout, диагностику пишут в stderr. Опциональный
`mpstats-mcp` запускается так же, с `MPSTATS_MP_AUTH` в окружении.

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

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

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

Сразу после установки появляются 15 навыков и **ни одного** MCP-инструмента: обе
строки MCP выключены, пока не задана переменная `RU_MARKETPLACE_MCP_DIR` с путём к
клону. Так сделано потому, что смонтированный сервер платится в каждом запросе:
рекомендуемый режим сравнения цен стоит ~0,9 тыс. токенов, полный набор — ~13,6 тыс.
Включение и полный режим описаны в [dsh/README.md](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% ниже. Интерфейс Яндекса показывает
вторую крупным шрифтом, поэтому назвать её без оговорки — значит пообещать цену,
которую человек без подписки не получит.

**Строка поиска — это оффер из выдачи, а не дефолтный оффер карточки.** Один
`product_id` покрывает семейство товаров, и в выдаче может показываться один его
представитель, а в карточке по тому же id — другой; сверяйте строки поиска с
карточками по `sku_id`, а не по URL. `price_old_rub` в строках поиска — зачёркнутая
базовая цена, контекст скидки; называть её ценой нельзя.

`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](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](docs/CDP_SETUP.md).

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

**Отзывы на Ozon общие для всей карточки-семейства, и соседи по пулу — часто другой
товар другого бренда.** У карточки масляного радиатора Huter 1500 Вт (SKU 5264146973,
рейтинг 4.8 из 356 отзывов) среди 100 вытянутых отзывов не оказалось ни одного о
самом Huter: 38 про Ресанту 2000 Вт, 34 про Ресанту 1500 Вт, 5 про Eurolux и так
далее — всего 12 товаров в пуле. Поэтому каждый отзыв несёт `item_id` — SKU того
товара, о котором он написан, а ответ дополнительно отдаёт `requested_item_id`,
`own_reviews` (сколько отзывов действительно об этом SKU) и `pool_variants`
(`SKU → название` всех товаров пула). `rating_score` и `distribution` считаются по
пулу, а не по товару: прежде чем делать вывод, отзывы нужно отфильтровать по
`item_id`, а при `own_reviews: 0` — честно сказать, что своих отзывов у товара нет.

### Авито — `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 ходят восемь источников — эти плюс Taobao, AliExpress, Ozon и
Авито, где Chrome лишь
запасной уровень: их tier 1 обычно отвечает, а браузер включается, когда анонимный
уровень упёрся в челлендж. `marketplace-mcp doctor` из вашего браузера скажет, какие
эндпоинты подтверждены.

### AliExpress — `aliexpress_*`

| Инструмент                         | Что делает                              |
| ---------------------------------- | --------------------------------------- |
| `aliexpress_search(query)`         | Поиск: до 48 карточек с ценами в рублях |
| `aliexpress_card(item_id_or_url)`  | Карточка: цена, рейтинг, число заказов  |

Читается через ваш Chrome (CDP): x5sec ставит капчу анонимным клиентам, поэтому
коннектор садится на страницу поиска (её не челленджат) и открывает карточку
новой вкладкой из неё. Цены в рублях и участвуют в `compare_prices`. Карточка с
названием, но без цены — известное состояние: под нагрузкой x5sec перестаёт
отдавать ценовой модуль, коннектор пишет `price_missing`, а не выдумывает число.
Цена «N ₽ с купоном» в `price_rub` не публикуется: там обычная цена, про купон
коннектор честно предупреждает отдельно. Тексты отзывов не отдаются: только
рейтинг и число заказов. Как и у остальных CDP-источников, зелёный
`aliexpress_selfcheck` доказывает, что транспорт ответил, — не то, что цена
верна.

### Циан — `cian_*`

| Инструмент                                                                                  | Что делает                                              |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| `cian_search(deal, offer_type, region, rooms, price_min, price_max, area_min, area_max, page)` | Поиск по фильтрам: продажа, аренда, посуточно — 28 объявлений на страницу |
| `cian_card(offer_id_or_url)`                                                                | Карточка: цена и её история, планировка, дом, адрес, метро, публикатор |

Недвижимость, а не товары: квартиры, комнаты, дома и коммерция на продажу, в
долгосрочную аренду и посуточно (`deal="daily"`; коммерции посуточно у Циана
нет, такой запрос отклоняется). Длительная и суточная аренда — два разных рынка,
в одной выдаче не смешиваются: у суточных цена за ночь, `price_period` и
`lease_term` пустые, поэтому у каждой строки есть `price_unit` — `total`,
`month` или `day`. Сравнивать цены между единицами нельзя.
Поиск только по фильтрам — текстового поиска у Циана нет.
Регион задаётся id Циана: 1 Москва, 2 Санкт-Петербург, 4593 Московская область,
4588 Ленинградская область (все четыре проверены живьём); остальным регионам
нужен их id. Читается через ваш Chrome (CDP): WAF Циана режет голый HTTP по IP,
а из сессии браузера отвечает собственный JSON-API сайта, так что HTML не
парсится. Цена «не указана» приходит как `null`, не `0`. Агентской страницы как
инструмента нет: она не отдаёт структурированных данных, агент приходит внутри
карточки. В `compare_prices` источник не участвует.

### Сравнение цен — `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`         |

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

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

У каждого коннектора — свой навык в `skills/`: пятнадцать штук, по одному
на источник плюс общий `marketplace`. Навык это не пересказ 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/aliexpress-connector` | `aliexpress-mcp`  |
| `skills/cian-connector`       | `cian-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`,                                                                                               |
| `ALI_`               | `TIMEOUT`, `MIN_GAP`, `CACHE_TTL`                                                                                                |
| `MEGAMARKET_`        | `TIMEOUT`, `MIN_GAP`, `CACHE_TTL`, `USE_PROFILE_ADDRESS` (0/1, по умолчанию 0 — приватный список адресов профиля не читается; 1 = адрес по умолчанию из залогиненного профиля, цены «как видит оператор») |
| `LAMODA_`            | `TIMEOUT`, `MIN_GAP`, `CACHE_TTL`, `PROXY`                                                                                       |
| `DNS_` / `CITILINK_` | `TIMEOUT`, `MIN_GAP`, `CACHE_TTL`                                                                                                |
| `CHROME_`            | `CDP_HOST`, `CDP_PORT`, `SCRAPING_PROFILE`, `BINARY`, `HEADLESS`, `STEALTH`                                                      |
| `COMPARE_`           | `SOURCE_TIMEOUT`                                                                                                                 |
| `MPSTATS_`           | `MP_AUTH` (единственный обязательный — без него инструменты отвечают `auth_missing`), `TIMEOUT`, `MIN_GAP`, `CACHE_TTL`, `PROXY` |
| `MCP_`               | `TRANSPORT` (`stdio` по умолчанию, либо `http`), `HTTP_HOST`, `HTTP_PORT`                                                        |

`CHROME_CDP_HOST` указывает, куда дозвониться CDP-клиенту (по умолчанию
`127.0.0.1`). Из контейнера ставьте `chrome` (сайдкар) или `host.docker.internal`
— это открывает tier-2 источники (Ozon, Авито, Taobao, Мегамаркет, Lamoda,
DNS, Ситилинк) в Docker без host networking. Подробности в
[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md).

`*_CACHE_TTL=0` выключает кэш. `*_PROXY` перекрывает стандартные `HTTPS_PROXY` и
`ALL_PROXY` — свой префикс есть у семи коннекторов: `WB_`, `YANDEX_`, `DETMIR_`,
`OZON_`, `AVITO_`, `LAMODA_` и `MPSTATS_`. У Taobao своего нет намеренно: поиск там
подписан и ходит через собственный клиент. У Мегамаркета, DNS и Ситилинка тоже нет:
их трафик идёт через ваш Chrome, а его egress — дело настроек браузера. Кэшируются
только удачные ответы: запомнить сбой значило бы растянуть секундную помеху на весь
TTL.

У Ozon прокси применяется к первому уровню. Второй идёт через ваш собственный Chrome,
и его трафик — дело настроек этого браузера.

**Секрет один, и тот опциональный.** Всем серверам, кроме MPStats, ничего не нужно:
нечего настраивать, нечему утечь. У MPStats есть `MPSTATS_MP_AUTH` — JWT платного
аккаунта, и потому его место только в env клиентской записи: в коде и коммитах его
нет и быть не должно.

## Разработка

```bash
uv sync --all-packages
uv run pytest -q -m "not live and not cdp"    # 1735 офлайн-тестов
uv run pytest -q -m "not live"                # то, что гоняет CI
uv run pytest -q -m "not live" --cov          # покрытие, порог 70% в CI
uv run ruff check . && uv run ruff format --check .
uv run mypy                                   # что проверять — в [tool.mypy] files
uv run mypy --platform win32                  # ловит ошибки, видимые только на Windows
uv run python scripts/check_no_print.py       # запись в stdout ломает JSON-RPC
uv run python scripts/check_versions.py       # одна версия во всех 77 местах
```

Часть тестов прогоняет **настоящий JS-экстрактор коннектора** по снятой разметке
и проверяет результат против цен, которые в тот момент были на странице. Для
этого нужен Node с jsdom:

```bash
npm install jsdom      # либо NODE_PATH на уже установленный
uv run pytest -q packages/dns-connector/tests/test_search_extractor_dom.py \
              packages/citilink-connector/tests/test_search_extractor_dom.py
```

Без jsdom эта половина честно скипается, а питоновская часть — выбор цены из
кандидатов — идёт всегда. jsdom нужен только разработчику: в зависимости
коннекторов он не входит.

CI прогоняет тесты на Ubuntu, Windows и macOS против Python 3.12
и 3.13. Windows-специфичное управление процессами проверяется юнит-тестами на любой
ОС через подмену платформы, так что эти ветки покрыты даже на Linux.

Как добавить маркетплейс — [docs/ADDING_A_SOURCE.md](docs/ADDING_A_SOURCE.md).

## Надёжность

Неофициальные эндпоинты ломаются. Архитектура это предполагает.

- **Терпимые парсеры.** Привязка поля по нескольким именам и приведение типов
  впитывают переименования и смену типа вместо падения.
- **Никогда не выдумывать значение.** Отсутствующая цена — это `null`, не `0`. Ноль
  вывел бы мёртвый товар в самые дешёвые.
- **Громкий отказ.** Когда формат перестаёт совпадать, инструмент бросает
  `parser_drift`, а не возвращает полуразобранные данные.
- **Трёхзначные selfcheck-проверки.** `success`, `drift_detected` или
  `inconclusive`. Гео-блокировка помечается как `inconclusive`, потому что она
  ничего не говорит о состоянии парсеров.

## Границы доверия

Названия товаров, имена продавцов и тексты отзывов написаны продавцами и
покупателями. Это недоверенные данные. Если отзыв или описание выглядит как
инструкция, оно всё равно остаётся входными данными. Выполнять его агент не
должен.

Условия маркетплейсов, как правило, запрещают неофициальный парсинг. Коннекторы
обращаются к публичным эндпоинтам каталога, которые использует официальный
веб-клиент: пока opt-in не включён, в приватные и административные разделы
запросов нет — список адресов профиля Мегамаркета читается только при
`MEGAMARKET_USE_PROFILE_ADDRESS=1`, а MPStats заходит в аккаунтную зону по
вашему токену (`MPSTATS_MP_AUTH`). Уровень Ozon с браузером работает внутри
сессии, которую вы открыли сами. Используйте на своё
усмотрение, для личных исследований, в вежливом темпе запросов. Пауза между
вызовами к площадкам с анти-ботом — это часть конструкции, а не случайное
торможение: её не нужно убирать ради скорости. Данные инструментов не предназначены
для перепродажи или массового сбора.

## Как это сделано

Код и документацию я писал вместе с ИИ-ассистентами. Они работают быстро и
ошибаются уверенно, поэтому проект устроен вокруг проверки: 1735 офлайн-тестов,
аудит перед выпуском, тесты, которые прогоняют настоящий экстрактор по снятой с
сайта разметке. В заметках к релизу перечислено, какие источники сверены с живыми
страницами вручную и какие остались непроверенными.

Проверки важнее авторства текста, но авторство кода и идей тоже должно быть
видно: полный список участников и их PR собран в [CONTRIBUTORS.md](CONTRIBUTORS.md).

## Спасибо

- [@Xpos587](https://github.com/Xpos587) — коннектор MPStats, [PR #5](https://github.com/Vladimir-Human/ru-marketplace-mcp/pull/5).
- [@avxone](https://github.com/avxone) — исправление Avito selfcheck, [PR #37](https://github.com/Vladimir-Human/ru-marketplace-mcp/pull/37).
- [@Khalmatov](https://github.com/Khalmatov) — provenance отзывов Ozon, [PR #38](https://github.com/Vladimir-Human/ru-marketplace-mcp/pull/38).
- [@fosteev](https://github.com/fosteev) — macOS CDP stealth и коннектор Циана, [PR #42](https://github.com/Vladimir-Human/ru-marketplace-mcp/pull/42), [PR #47](https://github.com/Vladimir-Human/ru-marketplace-mcp/pull/47).
- [@ilodezis](https://github.com/ilodezis) — выбор источников unified-сервера через `MARKETPLACE_SOURCES`, [PR #48](https://github.com/Vladimir-Human/ru-marketplace-mcp/pull/48).

## Лицензия

MIT, файл [LICENSE](LICENSE).

---

# English version

**MCP servers for Russian and Chinese marketplaces.** Read prices, stock, ratings,
reviews and seller identity from Wildberries, Ozon, Yandex Market, Detsky Mir, Avito,
AliExpress, Taobao, Megamarket, Lamoda, DNS and Citilink, then compare prices across
all of them in one call. Taobao and AliExpress are the Chinese ones; the other nine
are Russian.

Read-only. No credentials, no API keys, no account required — the marketplaces with
hard anti-bot are read through your own Chrome. One optional exception: MPStats
takes a paid account token (`MPSTATS_MP_AUTH`) if you want its analytics; without
it every other server is unaffected.

## What you get

| Server            | Tools | What it takes to read                                                         | Notes                                                                                              |
| ----------------- | ----- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| **Wildberries**   | 8     | anonymous HTTP                                                                | Search, cards, reviews, buyer questions, seller legal identity, catalog tree and category listings |
| **Yandex Market** | 2     | anonymous HTTP                                                                | Multi-seller prices, star distribution, reviews                                                    |
| **Detsky Mir**    | 3     | anonymous HTTP                                                                | Kids' goods, offline store stock, category listings                                                |
| **Ozon**          | 3     | your Chrome; often no browser from a residential IP                           | Search, cards, reviews                                                                             |
| **Avito**         | 3     | your Chrome + a Russian residential IP and spaced requests — else an IP block | Classified search, cards, seller reputation                                                        |
| **Taobao**        | 2     | your Chrome with an active Taobao login                                       | Search and cards, prices in yuan                                                                   |
| **Megamarket**    | 2     | your Chrome with an active login — an anonymous session reads empty           | Search and cards via the mobile API                                                                |
| **Lamoda**        | 2     | cards anonymous (GraphQL), search via your Chrome                             | Search, cards with sizes                                                                           |
| **DNS**           | 2     | your Chrome (Qrator)                                                          | Electronics search and cards                                                                       |
| **Citilink**      | 2     | your Chrome (Qrator)                                                          | Electronics search and cards                                                                       |
| **AliExpress**    | 2     | your Chrome (x5sec)                                                           | Search and cards, ruble prices                            |
| **Cian**          | 2     | your Chrome (WAF by IP)                                                      | Real estate: filter search (sale, long-term rent, daily) and one offer's card                      |
| **Compare**       | 4     | aggregates the above                                                          | "Where is this cheapest?" in one call                                                              |
| **MPStats**       | 2     | paid MPStats account, `mp_auth` cookie (optional)                             | 30-day sales/stock graphs per Ozon/WB SKU, warehouse split (FBS/FBO)                               |

Anonymous, no browser: Wildberries, Yandex Market, Detsky Mir and Lamoda cards.
The rest need your logged-in Chrome (CDP). Taobao and Megamarket additionally need
you signed into the marketplace itself — without it Taobao hits a login wall and
Megamarket returns an empty result. Avito also blocks by IP: from a datacenter
address it is a flat refusal, from a Russian residential one it works as long as
you do not burst requests. Requests to the CDP sources are paced apart — a run of
back-to-back calls degrades them (DNS and Taobao both dropped that way in testing),
so the connectors hold a gap between calls themselves. Run `marketplace-mcp doctor`
from your own session for the current state.

MPStats stands apart as the only **paid** source: without `MPSTATS_MP_AUTH` the
server boots but its tools answer `auth_missing`. It is therefore optional —
plug it in if you have an account; the other thirteen servers never notice.

39 tools across 14 stdio MCP servers, sharing one runtime (`mcp-core`), plus the
unified `marketplace-mcp` that mounts them all under one client entry. It adds its
own `marketplace_sources` tool — which connectors mounted, and which dropped out and
why — so it exposes 40 tools: the 39 mounted plus that one. stdio is the default;
HTTP transport is opt-in for remote deployment — see
[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md).

## Quickstart

Requires **Python 3.12+** and [uv](https://docs.astral.sh/uv/).

```bash
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"    # 1735 offline tests, no network needed
```

Client configuration mirrors the Russian section above. Each server is a console
script (`wb-mcp`, `ozon-mcp`, `yandex-mcp`, `detmir-mcp`, `aliexpress-mcp`, `cian-mcp`, `compare-mcp`) launched
through `uv run --directory /path/to/repo

数据来源:公开的 DeepSeek Harness 插件目录与各插件 GitHub 仓库。本站为独立第三方目录,与 DeepSeek、幻方(High-Flyer)及插件作者均无隶属或背书关系。

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。