mcpField
R
ru-marketplace-mcp

ru-marketplace-mcp

本地服务
github.com商业与营销1652 天前更新

关于 ru-marketplace-mcp

ru-marketplace-mcp 以只读方式提供俄罗斯和中国电商平台及 Cian 房源信息。它适用于查询价格、库存、评价、卖家资料或跨平台比价;部分来源通过用户自己的 Chrome 会话读取。

主要功能

  1. 01搜索商品并查看商品信息
  2. 02读取价格、库存、评分和评论
  3. 03获取卖家身份及信誉信息
  4. 04搜索 Cian 房源
  5. 05比较可用来源的商品价格
  6. 06挂载选定的电商平台连接器

适用场景

  1. 01比较多个平台上的商品价格
  2. 02购买前查看评论和库存
  3. 03识别商品页面背后的卖家
  4. 04按筛选条件搜索 Cian 房源

MCP 相关

各平台连接器以 MCP 服务形式运行,提供读取平台数据的工具。整合版 `marketplace-mcp` 可统一挂载来源,并可用 `MARKETPLACE_SOURCES` 限定启用的连接器。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"vladimir-human-ru-marketplace": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/vladimir-human/ru-marketplace-mcp:2.5.3"
]
}
}
}
启动命令docker run -i --rm ghcr.io/vladimir-human/ru-marketplace-mcp:2.5.3

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
ocighcr.io/vladimir-human/ru-marketplace-mcp:2.5.3

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

ru-marketplace-mcp

CI Python 3.12+ License: MIT MCP

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

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

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

Для проверок в браузере добавлен опциональный режим сохранения вкладки: CHROME_CHALLENGE_HANDOFF_S=120. После завершения проверки повтор того же запроса в той же MCP-сессии продолжает чтение этой вкладки. Поддержка и ограничения описаны в настройке Chrome.


Что внутри

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

Читается анонимно, без браузера: Wildberries, Яндекс Маркет и Детский мир. Карточки Lamoda сначала читаются по HTTP GraphQL; при отказе нужен ваш Chrome (CDP) для запасного уровня. Остальным нужен ваш залогиненный 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 смонтированных плюс этот.

Проверка 2.5.3: 13 октября с одного адреса selfcheck прошли Ozon, Яндекс Маркет, Детский мир, Авито (поиск, карточка, продавец), Taobao, Ситилинк, AliExpress и Циан; Wildberries отвечает через WB_TRANSPORT=dom. Lamoda, DNS и Мегамаркет честно отвечают «заблокировано», пока человек не пройдёт их проверку в Chrome: куки живут минуты (12 октября после проверки они работали). Дрейфа не найдено, визуальная приёмка не выполнена. Статус источников.

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

Нужны Python 3.12+ и uv.

Для установки клиента, выбора источников и проверки первого предложения — пошаговый старт. Node нужен только для тестов разработчика.

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

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

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, …) не меняются:

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

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

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

{
  "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
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, aliexpress-mcp, cian-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

Сразу после установки появляются 15 навыков и ни одного MCP-инструмента: обе строки MCP выключены, пока не задана переменная RU_MARKETPLACE_MCP_DIR с путём к клону. Так сделано потому, что смонтированный сервер платится в каждом запросе: рекомендуемый режим сравнения цен стоит ~0,9 тыс. токенов, полный набор — ~13,6 тыс. Включение и полный режим описаны в 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 — у них нет своей выдачи, и инструмент честно об этом говорит вместо пустого списка.

Если с вашего адреса API WB отвечает 403 (так бывает: отказ зависит и от адреса, и от клиента), включите WB_TRANSPORT=dom. Тогда wb_search и wb_card читают отрисованные страницы WB в вашем Chrome через CDP, как DNS и Ситилинк, без обращений к API. В выдаче поиска price_rub — цена по WB Кошельку (price_kind: wallet); карточка отдаёт обычную цену, цену по Кошельку и зачёркнутую раздельно, а также магазин, склад, срок доставки, возврат, гарантию, значок «Оригинал» и строку «Все N предложений от X ₽». Подробности — ANTI_BOT.md.

Яндекс Маркет — 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.

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 первый уровень обычно работает, и браузер не нужен.

Отзывы на 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)Страница объявления через CDP: название, цена, описание, число фото, продавец (рейтинг, ссылка на профиль)
avito_seller(seller_id_or_url)Рейтинг продавца, число отзывов; число активных объявлений недоступно (null)

Авито — это объявления, а не каталог: пула отзывов на товар нет, репутация продавца и есть сигнал доверия. Бесплатное/обменное объявление приходит с price_rub: null — никогда не 0, чтобы не оказаться «самым дешёвым» в сравнении. Поиск использует TLS-имперсонацию и ваш Chrome как запасной путь. Карточки читают schema.org Product и блок продавца со страницы объявления через CDP: seller содержит репутацию и ссылку на профиль, если блок доступен. На 2026-10-12 /web/1/js/items игнорирует itemId и userId; разные продавцы получают один общий каталог с totalCount: 1500, а не свои объявления. Поэтому продавцы читаются с отрисованного профиля через CDP с теми же паузами и разрешёнными хостами, что и карточки. Принимаются /brands/<hash>, /user/<hash>/profile и голый ID: 32 шестнадцатеричных символа ведут в brands, остальные ID — в user. Недоступный счётчик active_items остаётся null. Полный URL карточки используется без query/fragment; для ID применяется /{id}: Авито перенаправляет его на страницу объявления. location, posted_at, views остаются null: их нет в Product JSON-LD. Без Product или одновременно без названия и цены возвращается parser_drift, а не пустой успех. Canary проходит цепочку поиск → карточка → профиль продавца. С датацентрового IP возможен 403-файрвол.

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_*) наполовину: поиск идёт через Chrome, а карточки сначала читаются через HTTP GraphQL (tier 1). При ответе не 200 или httpx.TransportError выполняется ровно один GraphQL POST внутри страницы вашего Chrome через CDP (tier 2); успешный fallback отмечен tier_used: graphql_cdp. HTTP 200 не вызывает CDP. Причина: 2026-10-12 GraphQL по HTTPS отвечал циклом 307. Chrome с CDP (scripts/start_chrome_cdp.sh) нужен остальным всегда, а карточкам Lamoda — только для запасного уровня.

Всего через CDP ходят девять источников — эти плюс Taobao, AliExpress, Циан, Ozon и Авито. У Ozon, а также у поиска и продавцов Авито Chrome — запасной уровень, когда анонимный tier 1 упёрся в челлендж; карточки Авито всегда требуют CDP. 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 товара, id варианта) схлопываются. Разные SKU Яндекса сохраняются даже внутри одной товарной семьи. Наличие в сравнении трёхзначное: true — подтверждено, false — подтверждено отсутствие, null — неизвестно. При in_stock_only=true победитель выбирается только среди true; неизвестные остатки WB и неоднозначные подписи Ozon остаются видимыми в конце списка.

У каждого предложения есть 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-connectorwb-mcp
skills/ozon-connectorozon-mcp
skills/yandex-connectoryandex-mcp
skills/detmir-connectordetmir-mcp
skills/avito-connectoravito-mcp
skills/taobao-connectortaobao-mcp
skills/megamarket-connectormegamarket-mcp
skills/lamoda-connectorlamoda-mcp
skills/dns-connectordns-mcp
skills/citilink-connectorcitilink-mcp
skills/aliexpress-connectoraliexpress-mcp
skills/cian-connectorcian-mcp
skills/compare-pricescompare-mcp
skills/mpstats-connectormpstats-mcp
skills/marketplacemarketplace-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, Ситилинк, AliExpress, Циан) в Docker без host networking. Подробности в 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 клиентской записи: в коде и коммитах его нет и быть не должно.

Разработка

uv sync --all-packages
uv run pytest -q -m "not live and not cdp"    # 2270 офлайн-тестов
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:

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.

Надёжность

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

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

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

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

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

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

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

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

Спасибо

Лицензия

MIT, файл LICENSE.


英文版

面向俄罗斯和中国电商平台的 MCP 服务。 从 Wildberries、Ozon、Yandex Market、Detsky Mir、Avito、AliExpress、Taobao、Megamarket、Lamoda、DNS 和 Citilink 读取价格、库存、评分、评论和卖家身份信息,然后通过一次调用比较商品价格。Cian 另行提供房地产搜索和房源卡片。

首次成功查询将带你完成安装、选择服务、针对性健康检查,以及在推荐商品前验证报价。

只读。无需凭据、API 密钥或账号——对于反机器人措施严格的平台,通过你自己的 Chrome 读取数据。唯一的可选例外是 MPStats:如果你需要其分析功能,则需提供付费账号令牌(MPSTATS_MP_AUTH);没有令牌也不会影响其他任何服务。

2.5.3 验证: 10 月 13 日,从同一地址对 Ozon、Yandex Market、Detsky Mir、Avito(搜索、商品卡片、卖家)、Taobao、Citilink、AliExpress 和 Cian 的自检均通过;Wildberries 通过 WB_TRANSPORT=dom 响应。Lamoda、DNS 和 Megamarket 报告被拦截,直到有人通过浏览器验证;验证产生的 Cookie 仅持续数分钟(10 月 12 日通过验证后,它们立即可用)。没有偏差;没有视觉验收。请参阅各来源的发布证据。

你将获得什么

服务工具数读取所需条件备注
Wildberries8匿名 HTTP搜索、商品卡片、评价、买家问题、卖家法律身份、目录树和分类列表
Yandex Market2匿名 HTTP多卖家价格、星级分布、评价
Detsky Mir3匿名 HTTP儿童用品、线下门店库存、分类列表
Ozon3你的 Chrome;住宅 IP 通常无法访问搜索、商品卡片、评价
Avito3你的 Chrome + 俄罗斯住宅 IP,并间隔发送请求——否则 IP 会被封锁分类信息搜索、商品卡片、卖家信誉
Taobao2使用已登录 Taobao 账号的你的 Chrome搜索和商品卡片,价格以元计
Megamarket2使用已登录账号的你的 Chrome——匿名会话读取结果为空通过移动端 API 搜索和商品卡片
Lamoda2商品卡片:HTTP GraphQL → 遭拒时改用 Chrome;搜索使用 Chrome搜索、含尺码的商品卡片
DNS2你的 Chrome(Qrator)电子产品搜索和商品卡片
Citilink2你的 Chrome(Qrator)电子产品搜索和商品卡片
AliExpress2你的 Chrome(x5sec)搜索和商品卡片,价格以卢布计
Cian2你的 Chrome(基于 IP 的 WAF)房地产:筛选搜索(出售、长期租赁、日租)和单个房源的商品卡片
比价4汇总上述平台一次调用即可回答“哪里最便宜?”
MPStats2付费 MPStats 账号,mp_auth cookie(可选)按 Ozon/WB SKU 提供 30 天销量/库存图表,并按仓库拆分(FBS/FBO)
匿名、无需浏览器:Wildberries、Yandex Market 和 Detsky Mir。Lamoda 卡片
会先尝试 HTTP GraphQL,只有在备用层级才需要你的 Chrome(CDP)。
其余来源都需要你已登录的 Chrome(CDP)。Taobao 和 Megamarket 还需要你
登录商城本身——否则 Taobao 会遇到登录墙,而 Megamarket 会返回空结果。
Avito 也会封锁 IP:从数据中心地址访问时会直接拒绝;从俄罗斯住宅 IP 访问则
可用,但前提是不要突发发送请求。对 CDP 来源的请求会间隔发送——连续
调用会导致它们性能下降(测试中 DNS 和 Taobao 都因此掉线),因此连接器会自行
在调用之间留出间隔。运行 marketplace-mcp doctor,查看你当前会话中的状态。

MPStats 与众不同,是唯一的付费来源:如果没有 MPSTATS_MP_AUTH,服务 仍会启动,但它的工具会返回 auth_missing。因此它是可选的——如果你有账号, 可以接入;其他 13 个服务完全不会受影响。

39 个工具分布在 14 个 stdio MCP 服务中,共享一个运行时(mcp-core),另有 统一的 marketplace-mcp,可在一个客户端入口下挂载所有服务。它还添加了自己的 marketplace_sources 工具,用于查看已挂载哪些连接器、哪些连接器未能启动以及 原因——因此总共提供 40 个工具:39 个已挂载的工具,加上这一个。默认使用 stdio; 远程部署时可选择启用 HTTP 传输——参见 docs/DEPLOYMENT.md。

快速入门

需要 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"    # 2270 offline tests, no network needed

客户端配置与上文俄语部分一致。每个服务都是一个控制台脚本(wb-mcp、ozon-mcp、yandex-mcp、detmir-mcp、aliexpress-mcp、cian-mcp、compare-mcp),通过 uv run --directory /path/to/repo <script> 启动。可选的 mpstats-mcp 以相同方式运行,并在条目的 env 中设置 MPSTATS_MP_AUTH(需要付费的 MPStats 账号;没有该变量时,工具会返回 auth_missing)。运行 marketplace-mcp install [claude|claude-code|cursor|dsh] 会打印配置块,并自动填入你检出仓库的实际路径 ——无需手动编辑占位符——若已作为 wheel 安装,则会使用 PATH 中的控制台脚本路径; 未知客户端名称会被拒绝。dsh 目标会打印 cordis.patch.yml 条目,而不是 mcpServers JSON——参见 dsh/README.md。

**DeepSeek Harness(dsh)**作为插件包安装,而不是 mcpServers 条目;安装路径为 dsh/ 子目录(pnpm 必须位于 PATH 中):

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

这样即可立即获得 15 个技能,但没有 MCP 工具:只有当 RU_MARKETPLACE_MCP_DIR 指向一个克隆仓库时,两个 MCP 条目才会启用。每次请求 都会产生已挂载服务的调用成本——推荐的价格比较模式约 0.9k 个 token,全量模式约 13.6k 个 token——是否启用由你决定。dsh/README.md 介绍了如何 启用它以及全量模式。

连接后,运行 marketplace-mcp doctor。它会运行每个连接器的 canary,并针对每个 连接器报告 success、drift_detected 或 inconclusive。

工具

这些表格有意不列出 *_selfcheck canary:它们不会通过 MCP 发布,因为操作员诊断 会让模型在每次请求中消耗约 7.5k 个 token。marketplace-mcp doctor 会在命令行中 运行所有这些检查。

Wildberries — wb_*
Tool功能说明
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)注册实体、INN、KPP、OGRN、法定地址
wb_categories(root, max_depth)目录树,包含 WB 自有的分片/查询选择器
wb_category_products(shard, query, page, sort, dest)使用这些选择器获取分类中的商品

wb_seller 可以回答商品列表中隐藏的问题:究竟是谁在发货?它会返回注册的法人实体和税号,帮助你区分品牌官方店铺与冒用相似名称经营的转售商。

wb_questions 填补了另一个信息空白。评论描述的是购买和使用该商品的体验;问题则能澄清商品的实际规格——“是 10A 还是 16A?”“是否附带线缆?”——而卖家的回复往往是关于这些事实唯一公开的说明。每个 imt_id 对应一个问题集合,所有变体共用。

wb_category_products 补上了 wb_categories 开启的流程:前者会返回 WB 的 shard 和 query,而此工具会据此获取商品。商品条目的结构与 wb_search 相同,因此可以直接比较分类浏览结果和文本搜索结果。WB 一些最大的分类使用 blackhole 分片,完全没有商品源;此时工具会明确说明,而不是返回空列表。

如果 WB 的 API 从你的地址返回 403(是否拒绝取决于地址和客户端),请设置 WB_TRANSPORT=dom:此时 wb_search 和 wb_card 会通过 CDP 在你的 Chrome 中读取 WB 渲染后的页面,就像 DNS 和 Citilink 一样,不会调用 API。在搜索结果卡片中,price_rub 是 WB Wallet 价格(price_kind: wallet);商品卡片会分别报告常规价格、Wallet 价格和划线价,还会提供店铺、仓库、送达日期、退货政策、保修、“Оригинал”徽标以及“Все N предложений от X ₽”。详情见 ANTI_BOT.md。

Yandex Market — yandex_*
Tool功能说明
yandex_search(query, page, limit)搜索并提供两种价格、评分和卖家信息
yandex_card(product_id, include_reviews)完整详情,以及星级分布和评论

始终提供两种价格。 price_rub 是所有人实际支付的价格。price_with_plus 需要付费订阅 Yandex Plus,价格低 25–30%。Yandex 会优先展示订阅用户价格,因此不加说明地引用该价格会低估实际成本。

搜索结果条目对应的是搜索结果页上的商品报价,不是商品卡片的默认报价。 一个 product_id 对应一个商品系列,搜索结果可能展示其中一个款式,而相同 ID 对应的商品卡片默认展示另一个款式——请使用 sku_id 而非 URL,将搜索结果条目与商品卡片对应起来。搜索结果条目中的 price_old_rub 是划线参考价,不能当作实际报价。

rating_stars 提供评分分布,例如 {1: 10, 2: 3, 3: 10, 4: 19, 5: 502}。这能揭示 4.8 的平均分究竟实至名归,还是掩盖了一批投诉。

Detsky Mir — detmir_*
工具功能说明
detmir_categories(parent, limit, region)商品目录树,从这里开始
detmir_category(alias, limit, offset, region)某个分类中的商品,包含实际总数
detmir_card(product_id, region)价格、评分、线上和线下门店库存

每次调用都可指定地区。 价格,尤其是线下库存,会因城市而异——某件商品在莫斯科有 152 家门店有货,在圣彼得堡有 37 家,在哈巴罗夫斯克则只有 2 家。region 参数会覆盖 DETMIR_REGION,因此同一会话可以比较不同城市。

这里有意不提供文本搜索。 Detsky Mir 的 API 会悄悄忽略所有文本筛选条件,并返回完整的 30 万件商品目录;网站的搜索路由返回 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 验证时,再通过 DevTools Protocol 使用你自己已登录的 Chrome 进行请求。不会存储任何内容;你需要在自己控制的浏览器中自行登录。设置方法:docs/CDP_SETUP.md。

使用俄罗斯住宅 IP 时,通常第一层就能正常工作,无需浏览器。

Ozon 会在同系列商品之间共享评论,而相邻商品往往来自不同品牌。 一张实时商品卡片对应一款 1500 W 的 Huter 油汀(SKU 5264146973,356 条评论,评分为 4.8),但返回的 100 条评论中,没有一条是关于 Huter 的:38 条关于一款 2000 W 的 Resanta,34 条关于一款 1500 W 的 Resanta,5 条关于一款 Eurolux——该评论池共涉及 12 款商品。因此,每条评论都带有 item_id,标明其实际对应的 SKU;响应还会添加 requested_item_id、own_reviews(返回的评论中真正属于该 SKU 的数量)和 pool_variants(整个评论池的 SKU → name 映射)。rating_score 和 distribution 是整个评论池的数据,并非单个商品的数据:得出结论前,请按 item_id 筛选;当 own_reviews 为 0 时,请明确说明该商品没有自己的评论。

AliExpress — aliexpress_*
工具功能说明
aliexpress_search(query)搜索:最多返回 48 个商品卡片,价格以卢布计
aliexpress_card(item_id_or_url)商品卡片:标题、价格、评分、订单数

通过你的 Chrome(CDP)读取:x5sec 会验证匿名客户端,因此连接器会先打开搜索页面(不会触发验证),再从该页面在新标签页中打开商品卡片。价格以卢布计,并会计入 compare_prices。商品卡片可能有标题但没有价格,这是已知情况——负载较高时,x5sec 会停止提供价格模块,连接器会报告 price_missing,而不会编造价格。带优惠券的价格不会写入 price_rub:其中填写的是常规价格,优惠券则作为警告报告。不会公开评论文本,但会提供评分和订单数。与所有 CDP 数据源一样,绿色的 aliexpress_selfcheck 只能证明传输有响应,不能证明具体价格正确。

Cian — 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";Cian 没有商业地产日租市场,因此会拒绝该组合)。长期租赁和日租是彼此独立的市场,不会共用结果页:日租价格按晚计算,Cian 会将其中的 price_period 和 lease_term 留空,因此每条记录都带有 price_unit——total、month 或 day。切勿跨单位比较价格。 只能按筛选条件搜索——Cian 不支持文本搜索。地区使用 Cian id:1 为莫斯科,2 为圣彼得堡,4593 为莫斯科州,4588 为列宁格勒州(这四个均已通过实时验证);其他地区需要使用各自的 id。通过你的 Chrome(CDP)读取:Cian 的 WAF 会按 IP 封锁普通 HTTP 请求,而在浏览器会话中,网站自有的 JSON API 会正常响应,因此不会解析 HTML。Cian 未注明的价格会返回 null,而非 0。没有 Agent 工具:Agent 页面不提供结构化数据,发布者信息包含在详情卡中。此数据源不参与 compare_prices。

Avito — avito_*
工具功能说明
avito_search(query, page, location_id, category_id)通过内部 js/items API 搜索分类信息
avito_card(item_id_or_url)CDP 商品页面:标题、价格、描述、图片数量、卖家(评分、个人资料链接)
avito_seller(seller_id_or_url)卖家评分、评价数量;无法获取在售商品数量(null)

Avito 是分类信息平台,而非商品目录:每件商品都没有独立的评价集合,卖家的信誉才是信任依据。免费或交换类商品会返回 price_rub: null——而非 0,因此不会被判为“最便宜”。搜索使用 TLS 模拟,失败时回退到 Chrome。通过 CDP 读取商品页面的 schema.org Product 和卖家信息块;若该信息块可用,seller 会包含信誉信息和个人资料链接。截至 2026-10-12,/web/1/js/items 会忽略 itemId 和 userId:不同卖家会收到相同的通用目录,totalCount: 1500,而非他们发布的商品。因此,卖家信息通过 CDP 读取渲染后的个人资料,使用与商品详情卡相同的请求节奏和允许访问的主机。支持的路径为 /brands/<hash> 和 /user/<hash>/profile,也可使用纯 id:32 位十六进制字符会映射到 brands,其他 id 会映射到 user。无法获取的 active_items 保持为 null。完整的商品详情卡 URL 会去掉查询参数和片段;id 使用 /{id},Avito 会将其重定向到商品页面。location、posted_at 和 views 保持为 null,因为 Product JSON-LD 中没有这些字段。缺少 Product,或 name 和 price 均缺失时,会引发 parser_drift,而非返回空结果。金丝雀测试按搜索 → 商品详情卡 → 卖家个人资料的流程进行。数据中心 IP 仍可能收到 403 防火墙错误。

淘宝 — taobao_*
工具功能说明
taobao_search(query, page)商品目录搜索
taobao_card(item_id_or_url)商品卡片

淘宝搜索使用签名式 mtop React 应用:每个请求都需要一个由 Cookie 令牌生成的 sign,因此无法匿名访问。所有读取操作都在你的 Chrome 中执行,由网站为自己的请求签名。价格保留为人民币(CNY),绝不进行换算——内置汇率会悄然过时,因此请明确比较卢布和人民币标价。

Megamarket、Lamoda、DNS、Citilink

这四个平台都通过你的 Chrome(CDP)读取数据。Megamarket(megamarket_*)通过 ServicePipe 后面的移动端 JSON API 访问,并且需要登录;匿名会话读取到的内容为空。DNS(dns_*)和 Citilink(citilink_*)会在 Qrator 后面渲染 DOM,完全无法匿名访问。Lamoda(lamoda_*)采用分流方式:搜索通过 Chrome 进行,商品卡片则优先通过 HTTP GraphQL 获取(第 1 层)。如果响应不是 200,或出现 httpx.TransportError,就会通过 CDP 使用你的 Chrome 在页面内执行一次 GraphQL POST(第 2 层);成功回退读取会报告 tier_used: graphql_cdp。HTTP 200 响应不会触发 CDP。这样设计是因为 2026-10-12 出现了 GraphQL over HTTPS 的 307 循环。所有这些平台都需要启用 CDP 的 Chrome(scripts/start_chrome_cdp.sh),但 Lamoda 商品卡片只在回退时需要它。总共有九个数据源通过 CDP 运行:以上这些平台,以及 Taobao、AliExpress、Cian、Ozon 和 Avito;对于 Ozon 和 Avito,Chrome 也只会在匿名访问受阻时作为回退层使用。

跨电商平台比较 — compare_*
工具功能说明
compare_prices(query, per_source_limit, sources)一次查询所有电商平台并排序
compare_sources()查询此安装可访问的电商平台
compare_prices("кроссовки мужские")

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

  cheapest: wildberries 712 RUB, spread 5858 RUB, complete: true

各数据源会并发查询,并分别报告结果。某个平台被拦截不会导致整个比较失败:complete: false 和 source_outcomes 会准确说明当前结果的情况。订阅价格不会排在前列。匹配(来源、商品 ID、变体 ID)相同的重复结果会合并。同一商品系列中不同的 Yandex SKU 仍会分别显示。比较结果中的库存状态有三种:true 表示已确认有货,false 表示无货,null 表示未知。设置 in_stock_only=true 后,只有已确认有货的商品才能排在前列;未报告的 WB 数量和含义不明确的 Ozon 标签仍会显示在列表末尾。

每个报价都带有 currency(小写 ISO 代码,默认值为 rub)和 price_native,即电商平台报价所用货币的价格。对于俄罗斯数据源,它与 price_rub 相同;对于 Taobao,它保存人民币价格,而 price_rub 则有意保留为 null。此前,系统会获取该人民币价格后将其丢弃,因此 Taobao 的结果会显示空白价格,也没有任何提示表明实际存在价格。现在会报告人民币价格,但仍不会将其与卢布价格进行排名:foreign_currency: … 警告会列出被排除报价的数量及原因。若在此处换算,就会使用一个悄然过时的汇率,因此需要时由调用方自行换算。

MPStats — mpstats_*

通过 MPStats 浏览器插件按 Ozon 或 Wildberries SKU 分析销售和库存。与其他所有连接器不同,此连接器是可选的,且需要付费的 MPStats 账号:认证只需一个 mp_auth cookie(来自已登录插件会话的 JWT,位于 mpstats.io),通过 MPSTATS_MP_AUTH 环境变量设置。未设置时,工具会返回 auth_missing,而服务仍会正常启动——其他功能不受影响。

工具功能
mpstats_item(skus, place, oz_fbs=True)最多 100 个 SKU 的 30 天分析:订单、价格、库存、每日图表、卖家/品牌
mpstats_warehouses(skus, place)仓库拆分:FBS(卖家仓库)与 FBO(市场平台仓库),以及 last_update

place 为 ozon 或 wildberries。图表长度为 30,按时间从旧到新排列:最后一个非零单元格表示当前价格或库存。当整个图表均为零时,两者会有意采用不同处理方式:价格设为 None(错误的 0 会在任何“最低价”比较中胜出),而库存设为 0,因为“无库存”是实际读数,而非缺少数据。空图表的两者结果均为 None。零值单元格表示“当天无数据”,而非“数值为零”,因此应对图表求和来计算某时间段的总量。缺少令牌或传输失败时会报告为 inconclusive,而非 drift——不会追查从未发生过的架构漂移。该令牌是付费且按配额计费账号的机密:切勿记录或提交。

Agent 技能

每个连接器都在 skills/ 下附带自己的技能——共十五个——每个来源各一个,另有一个共享的 marketplace 概览。技能并非对本 README 的复述:它会告诉 Agent 何时应使用该来源、该来源缺少什么,以及哪些回答需要再次核查后才能信任。

技能服务
skills/wb-connectorwb-mcp
skills/ozon-connectorozon-mcp
skills/yandex-connectoryandex-mcp
skills/detmir-connectordetmir-mcp
skills/avito-connectoravito-mcp
skills/taobao-connectortaobao-mcp
skills/megamarket-connectormegamarket-mcp
skills/lamoda-connectorlamoda-mcp
skills/dns-connectordns-mcp
skills/citilink-connectorcitilink-mcp
skills/aliexpress-connectoraliexpress-mcp
skills/cian-connectorcian-mcp
skills/compare-pricescompare-mcp
skills/mpstats-connectormpstats-mcp
skills/marketplacemarketplace-mcp

mcp-core 是共享运行时,而非服务,因此没有对应的技能。

映射关系由测试强制执行 (packages/marketplace-connector/tests/test_skills_parity.py):新增连接器却没有技能会导致测试失败;技能所列工具不存在,或遗漏了实际存在的工具,也会导致测试失败。在这项测试出现之前,DNS 技能曾数月要求操作人员传入 /product/<24-hex>/,而修复早已移除了这一确切模式。

技能会复制到 Docker 镜像中(/app/skills/),但不会打包进 wheel: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

*_CACHE_TTL=0 会禁用缓存。*_PROXY 会覆盖标准的 HTTPS_PROXY/ALL_PROXY——以下七个连接器支持此项:WB_、YANDEX_、DETMIR_、 OZON_、AVITO_、LAMODA_ 和 MPSTATS_。Taobao 按设计不支持此项, Megamarket、DNS 和 Citilink 也不支持: 它们的流量会经过你自己的 Chrome,出口取决于该浏览器的 配置。只有成功读取的结果才会缓存:如果记住失败结果,一次持续一秒的短暂故障 就会被延长到整个 TTL 时段。

Ozon 的代理仅应用于第 1 层。第 2 层在你自己的 Chrome 中运行,其出口取决于该浏览器的 配置,而不是我们的配置。

容器。 CHROME_CDP_HOST 用于将 CDP 客户端指向 Chrome(默认值为 127.0.0.1;在 Docker 中使用 chrome 或 host.docker.internal)。只需设置这一个 变量,就能从容器访问第 2 层数据源——Ozon、Avito、Taobao、Megamarket、 Lamoda、DNS、Citilink、AliExpress 和 Cian,无需使用主机网络。 请参阅 docs/DEPLOYMENT.md。

一个密钥,而且可选。 除 MPStats 外,所有服务都无需任何配置,也没有任何内容需要泄露。 只有 MPStats 需要 MPSTATS_MP_AUTH,即付费账户的 JWT——只能放在客户端条目的 env 中,绝不能放入代码或 提交记录中。 仅使用指定来源:MARKETPLACE_SOURCES。 统一服务会挂载所有来源,并在每次请求时将它们的工具架构发送给客户端。在 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 也会查询同一子集。

开发

uv sync --all-packages
uv run pytest -q -m "not live and not cdp"    # 2270 offline tests
uv run pytest -q -m "not live"                # what CI runs
uv run pytest -q -m "not live" --cov          # coverage, CI enforces a 70% floor
uv run ruff check . && uv run ruff format --check .
uv run mypy                                   # the tree lives in [tool.mypy] files
uv run mypy --platform win32                  # catches Windows-only type errors
uv run python scripts/check_no_print.py       # a print() breaks JSON-RPC
uv run python scripts/check_versions.py       # one version across all 79 places

有些测试会针对捕获的标记执行连接器的真实提取器 JavaScript,并将输出与捕获时页面显示的价格进行核对。这需要安装带 jsdom 的 Node:

npm install jsdom      # or point NODE_PATH at an existing copy
uv run pytest -q packages/dns-connector/tests/test_search_extractor_dom.py \
              packages/citilink-connector/tests/test_search_extractor_dom.py

如果没有 jsdom,这部分测试会如实跳过,而 Python 部分——从候选价格中选择价格——仍会运行。jsdom 仅供开发使用;没有连接器依赖它。

CI 会在 Ubuntu、Windows 和 macOS 上,使用 Python 3.12 和 3.13 运行 lint、mypy 和完整测试套件。Windows 专属的进程处理会在每个平台上通过平台覆盖进行单元测试,因此即使在 Linux 上也能覆盖这些分支。

添加电商平台:docs/ADDING_A_SOURCE.md。

可靠性

非官方端点会失效。设计时已将此纳入考量。

  • 宽容的读取器。 多别名字段绑定和类型强制转换可以吸收字段重命名和类型变化,而不会导致崩溃。
  • 绝不捏造值。 缺失价格为 null,绝不会是 0。否则,已失效的商品会被排成最便宜的选项。
  • 明确报告失败。 如果载荷不再符合预期,工具会引发 parser_drift,而不是返回只解析了一部分的数据。
  • 三态自检。 success、drift_detected 或 inconclusive。遇到地理区域屏蔽时会报告为 inconclusive,因为这与解析器无关。

信任边界

工具输出,也就是商品标题、卖家名称和评论文本,均由卖家和买家编写。请将其视为不可信数据。如果评论或描述中似乎包含指令,应将其视为输入,而非策略。

电商平台的服务条款通常不允许非官方解析。这些连接器会读取官方网页客户端使用的公开目录端点:只要选择加入项保持关闭,就不会触及经过身份验证的区域或管理区域——只有设置 MEGAMARKET_USE_PROFILE_ADDRESS=1 时,才会读取 Megamarket 的个人资料地址列表;MPStats 则通过你自己的令牌(MPSTATS_MP_AUTH)进入你的账户区域。Ozon CDP 层运行在你自行建立的浏览器会话中。请自行斟酌,仅用于个人研究,并保持适当的请求频率;对反爬虫来源的调用间隔是有意设置的,不应为了提速而移除。工具输出不用于再分发或批量采集。

构建过程

我使用 AI 助手编写了代码和文档。它们速度很快,也会信心十足地出错,因此项目围绕验证而构建:2270 项离线测试、发布前审计,以及使用从实时网站捕获的标记运行真实提取器的测试。发布说明会指出哪些来源经过了人工与实时页面比对,哪些尚未验证。

检查比谁写了文字更重要,但代码和想法也值得获得明确认可:完整的贡献者和 PR 索引见 CONTRIBUTORS.md。

致谢

许可证

MIT,详见 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。