← 返回列表
需源码安装
Python 3.12+
暂不能直接安装(需源码编译或环境不满足):缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装。 · 最近上游提交 2026/9/18 · 已提供中文文档
十一个市场平台和Циан房地产作为MCP服务器:Wildberries、Ozon、Яндекс Маркет、Детский мир、Авито、AliExpress、Taobao、Мегамаркет、Lamoda、DNS、Ситилинк、Циан。外加通过一次调用对所有商品来源进行价格比较。仅限读取,无需密钥。
综合分
59.6
GitHub 分
59.6
用户评分
—
★ Stars
107
周下载量
—
兼容 / 相关生态插件(非 dsh 原生,请按其对应运行时安装)
git clone https://github.com/Vladimir-Human/ru-marketplace-mcp.git🟢实装验证通过· 2026/9/18
由 dsh-plugin-verify(GitHub Actions)在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/19(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查需源码安装
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包ru-marketplace-mcp(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明
缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装
验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/17 04:56:26
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
ru-marketplace-mcp
CI
Python 3.12+
License: MIT
MCP
面向俄罗斯和中国市场的 MCP 服务器。 来自 Wildberries、Ozon、Yandex Market、
Detsky Mir、Avito、AliExpress、Taobao、Megamarket、Lamoda、DNS 和 Citilink 的
价格、库存、评分、评价和卖家信息。
外加来自 Циан 的房地产信息,以及通过一次调用对所有商品来源进行价格比较。
仅限读取。无需 API 密钥、令牌和注册——具有严格反机器人机制的网站通过您自己的
Chrome 读取。一个可选的例外:可选的 MPStats 需要付费令牌(MPSTATS_MP_AUTH)——
没有它,其他一切照常工作。
下方英文版 · 架构 ·
如何添加来源 · 关于反机器人
为浏览器检查添加了可选的标签页保存模式:
CHROME_CHALLENGE_HANDOFF_S=120。检查完成后,在同一 MCP 会话中重复同一
请求会继续读取该标签页。支持和限制
在 Chrome 配置 中描述。
包含内容
| 服务器 | 工具数 | 读取所需条件 | 功能 |
| ----------------- | ------------ | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Wildberries | 8 | 匿名 HTTP | 搜索、商品卡片、评价、商品问题、卖家信息、目录和分类商品 |
| Yandex Market | 2 | 匿名 HTTP | 不同卖家的价格、按星级的评分分布、评价 |
| Detsky Mir | 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 —
поэтому он опционален и подключается по желанию, на остальные тринадцать
серверов он не влияет никак.
在共享运行时 mcp-core 上,14 个服务器中共有 39 个工具。此外还有合并的
marketplace-mcp,它一次性挂载所有内容——在客户端配置中只需一条记录,
而不是十四条。它会添加自己的工具 marketplace_sources(哪些连接器
已启动,哪些失败了以及原因),因此它共有 40 个工具:39 个
已挂载的加上这一个。
快速开始
需要 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" # 1735 个离线测试,无需网络
检查实时端点:
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",json
"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。服务器通过
stdin 和 stdout 使用 JSON-RPC 通信,诊断信息写入 stderr。可选的
mpstats-mcp 以相同方式启动,并在环境中带有 MPSTATS_MP_AUTH。
DeepSeek Harness (dsh) — 插件包
在 dsh 中,这不是 mcpServers 条目,而是配置文件层。该包位于子目录
dsh/ 中,并通过标准插件管理器安装(需要 pnpm 在 PATH 中):
console
dsh plugin --profile web add github:Vladimir-Human/ru-marketplace-mcp#path:/dsh
安装后立即会出现 15 项技能和零个 MCP 工具:在未设置指向克隆的变量 RU_MARKETPLACE_MCP_DIR 之前,两行 MCP 都是关闭的。这样做是因为挂载的服务器会在每次请求中产生开销:
推荐的比价模式约花费 0.9 千个 token,完整集合约 13.6 千个。
启用方式和完整模式在 dsh/README.md 中描述。
连接后,重启客户端并运行 marketplace-mcp doctor。它会为每个连接器运行金丝雀测试,并返回 success、drift_detected 或
inconclusive。
工具
_selfcheck 金丝雀在这个清单中是故意不列出的:它们不通过 MCP 发布,因为操作员诊断会在每次请求中消耗模型约 7.5 千个 token。它们由 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) | 法人、INN、KPP、OGRN、法定地址 |
| wb_categories(root, max_depth) | 带有 WB 自身分片和查询的目录树 |
| wb_category_products(shard, query, page, sort, dest) | 按来自 wb_categories 的 shard 和 query 获取分类商品 |
wb_seller 回答了商品卡片所隐藏的问题:实际是谁在卖?它返回注册法人和税号。这样可以区分品牌官方店铺和名称相似的中转商。
wb_questions 填补了另一个空白。评论说明拥有该商品是什么体验;问题则澄清这到底是什么商品——“10 还是 16 安培”,“附带线缆吗?”。卖家的回答往往是关于这一点唯一的公开声明。评论池对商品的所有变体是共用的,键是来自 wb_root_info 的 imt_id。
wb_category_products 补全了与 wb_categories 的关联:后者返回 shard 和 query,前者按它们返回商品。元素格式与 wb_search 一致,因此遍历分类和文本搜索可以直接比较。WB 的部分大型分区被标记为 blackhole 分片——它们没有自己的搜索结果,工具会如实说明这一点,而不是返回空列表。
Yandex Market — yandex_
| 工具 | 功能 |
| ------------------------------------------ | ---------------------------------------------- |
| 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 是否诚实,还是背后藏着一堆一星。
儿童世界 — detmir_
| 工具 | 作用 |
| ----------------------------------------------- | ------------------------------------------- |
| detmir_categories(parent, limit, region) | 目录树。从这里开始 |
| detmir_category(alias, limit, offset, region) | 带真实计数的分类商品 |
| detmir_card(product_id, region) | 价格、评分、线上和门店库存 |
区域按每次调用设置。 价格,尤其是线下门店的库存,
很大程度上取决于城市:同一件商品在莫斯科的 152 家门店、圣彼得堡的 37 家
和哈巴罗夫斯克的 2 家门店有货。参数 region 会覆盖 DETMIR_REGION,因此
可以在同一会话中比较城市。
这里没有文本搜索,这是有意为之。 儿童世界的 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 发出质询,请求会在你已登录的 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) | Одно объявление: цена, описание, просмотры, продавец |
| 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,而不是编造数字。
price_rub 中不会发布“N ₽ 使用优惠券”的价格:那里是普通价格,连接器会单独
如实提示优惠券。评论文本不会返回:只有评分和订单数。与其他 CDP 来源一样,
绿色的 aliexpress_selfcheck 只能证明传输层有响应——而不是价格正确。
Цian — 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";Цian 没有商业地产短租,此类请求会被拒绝)。长期租赁和短租
是两个不同的市场,不会混在同一个结果中:短租的价格是每晚价格,price_period 和
lease_term 为空,因此每一行都有 price_unit——total、
month 或 day。不能跨单位比较价格。
只能按筛选条件搜索——Цian 没有文本搜索。
区域由 Цian 的 id 指定:1 莫斯科,2 圣彼得堡,4593 莫斯科州,
4588 列宁格勒州(这四个都已实际验证);其他区域需要其 id。通过您的 Chrome(CDP)读取:
Цian 的 WAF 会按 IP 拦截裸 HTTP,而从浏览器会话中会响应网站自己的 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 一致;对于淘宝,其中是人民币价格,而
price_rub 有意留空。以前人民币价格会被取走并悄悄丢弃,淘宝的条目会带着空价格返回,完全没有暗示价格其实存在。现在人民币可见了,但仍然不参与卢布排序:在
warnings 中会出现 foreign_currency: …,包含被排除的报价数量以及
原因。在这里进行换算就等于硬编码一个会悄悄过期的汇率——
换算由你来做。
MPStats — mpstats_
通过 MPStats 插件获取 Ozon 和 Wildberries SKU 的销售与库存分析。
与所有其他连接器不同,这个是可选的,并且需要付费的
MPStats 账户:授权使用一个 cookie mp_auth(来自 mpstats.io 上已登录插件会话的 JWT),通过变量 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。单独单元格中的零表示“当天没有数据”,而不是“值曾为零”,因此窗口内的总和请按图表计算。缺少令牌和传输故障时,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//——正是那个作为 bug 修复过的模板。
技能会进入 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 |
CHROME_CDP_HOST 指定 CDP 客户端要连接到哪里(默认
127.0.0.1)。在容器中请设置为 chrome(边车)或 host.docker.internal
——这样无需 host networking 即可在 Docker 中启用 tier-2 来源(Ozon、Avito、Taobao、Megamarket、Lamoda、
DNS、Citilink)。详情见
docs/DEPLOYMENT.md。
_CACHE_TTL=0 会关闭缓存。_PROXY 会覆盖标准的 HTTPS_PROXY 和
ALL_PROXY——有七个连接器拥有自己的前缀:WB_、YANDEX_、DETMIR_、
OZON_、AVITO_、LAMODA_ 和 MPSTATS_。Taobao 故意没有自己的前缀:那里的搜索
经过签名并通过自有客户端发起。Megamarket、DNS 和 Citilink 也没有:
它们的流量经过你的 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 # 覆盖率,CI 中阈值为 70%
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 提取器在抓取到的标记上运行,
并将结果与当时页面上的价格进行核对。为此需要带 jsdom 的 Node:
bash
npm install jsdom # 或者将 NODE_PATH 指向已安装的 jsdom
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 运行测试。Windows 特有的进程管理会在任何
操作系统上通过替换平台用单元测试进行检查,因此即使在 Linux 上这些分支也有覆盖。
如何添加市场——docs/ADDING_A_SOURCE.md。
可靠性
非官方端点会失效。架构已预见到这一点。
- 宽容的解析器。 按多个名称绑定字段并进行类型转换,
可以吸收重命名和类型变化,而不是直接崩溃。
- 绝不臆造值。 缺失的价格是 null,不是 0。零
会把已下架商品排到最便宜的位置。
- 响亮的失败。 当格式不再匹配时,工具会抛出
parser_drift,而不是返回半解析的数据。
- 三值 selfcheck 检查。 success、drift_detected 或
inconclusive。地理封锁会被标记为 inconclusive,因为它
不能说明解析器的状态。
信任边界
商品名称、卖家名称和评论文本由卖家和
买家撰写。这是不可信数据。如果评论或描述看起来像
指令,它仍然只是输入数据。代理不应执行它。
市场条款通常禁止非官方解析。连接器
访问官方 Web 客户端所使用的公开目录端点:在 opt-in 未启用时,不会向私有和管理区域
发起请求——Megamarket 个人资料地址列表仅在
MEGAMARKET_USE_PROFILE_ADDRESS=1,而 MPStats 通过
您的令牌(MPSTATS_MP_AUTH)进入账户区域。Ozon 的浏览器级别在您自己打开的
会话内工作。请按自己的判断使用,用于个人研究,保持礼貌的请求节奏。对带有
反机器人机制的平台的调用之间的暂停是设计的一部分,而不是随机的减速:不要为了
速度而移除它。工具数据不打算用于转售或大规模采集。
这是如何做到的
代码和文档是我与 AI 助手一起编写的。它们工作得快,犯错也自信,因此项目围绕
验证构建:1735 个离线测试、发布前审计、用从网站抓取的标记运行真实提取器的
测试。发布说明中列出了哪些来源已与实时页面手动核对,哪些仍未验证。
验证比文本的作者身份更重要,但代码和想法的作者身份也应该可见:完整的参与者
名单及其 PR 收集在 CONTRIBUTORS.md 中。
感谢
- @Xpos587 — MPStats 连接器,PR #5。
- @avxone — 修复 Avito selfcheck,PR #37。
- @Khalmatov — Ozon 评论的来源追踪,PR #38。
- @fosteev — macOS CDP stealth 和 Циан 连接器,PR #42、PR #47。
- @ilodezis — 通过 MARKETPLACE_SOURCES 选择 unified 服务器的来源,PR #48。
许可证
MIT,文件 LICENSE。
英文版
面向俄罗斯和中国市场的 MCP 服务器。 从 Wildberries、Ozon、Yandex Market、Detsky Mir、Avito、
AliExpress、Taobao、Megamarket、Lamoda、DNS 和 Citilink 读取价格、库存、评分、
评论和卖家身份,然后在一次调用中比较所有这些平台的价格。Taobao 和 AliExpress 是中国的;其余九个
是俄罗斯的。
只读。无需凭据、无需 API 密钥、无需账户——具有
严格反机器人机制的市场通过您自己的 Chrome 读取。一个可选的例外:MPStats
如果您想要其分析功能,需要一个付费账户令牌(MPSTATS_MP_AUTH);没有
它,其他所有服务器都不受影响。
您将获得
| 服务器 | 工具 | 读取所需条件 | 备注 |
| ----------------- | ----- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Wildberries | 8 | 匿名 HTTP | 搜索、商品卡、评论、买家提问、卖家法定身份、目录树和分类列表 |
| Yandex Market | 2 | 匿名 HTTP | 多卖家价格、星级分布、评论 |
| Detsky Mir | 3 | 匿名 HTTP | 儿童用品、线下门店库存、分类列表 |
| Ozon | 3 | 你的 Chrome;住宅 IP 通常无法使用浏览器 | 搜索、商品卡、评论 |
| Avito | 3 | 你的 Chrome + 俄罗斯住宅 IP 和间隔请求——否则会被封 IP | 分类信息搜索、商品卡、卖家信誉 |
| Taobao | 2 | 你的 Chrome 并保持淘宝登录状态 | 搜索和商品卡,价格以人民币显示 |
| Megamarket | 2 | 你的 Chrome 并保持登录状态——匿名会话读取为空 | 通过移动 API 进行搜索和商品卡 |
| Lamoda | 2 | 商品卡匿名访问(GraphQL),搜索通过你的 Chrome | 搜索、带尺码的商品卡 |
| DNS | 2 | 你的 Chrome(Qrator) | 电子产品搜索和商品卡 |
| Citilink | 2 | 你的 Chrome(Qrator) | 电子产品搜索和商品卡 |
| AliExpress | 2 | 你的 Chrome(x5sec) | 搜索和商品卡,卢布价格 |
| Cian | 2 | 你的 Chrome(基于 IP 的 WAF) | 房地产:筛选搜索(出售、长期租赁、日租)和单个房源的商品卡 |
| Compare | 4 | 聚合上述来源 | 一次调用即可回答“哪里最便宜?” |
| MPStats | 2 | 付费 MPStats 账户、mp_auth cookie(可选) | 每个 Ozon/WB SKU 的 30 天销售/库存图表,按仓库拆分(FBS/FBO) |
匿名、无需浏览器:Wildberries、Yandex Market、Detsky Mir 和 Lamoda 卡片。
其余需要你已登录的 Chrome(CDP)。淘宝和 Megamarket 额外需要
你已登录到该市场本身——否则淘宝会撞上登录墙,
Megamarket 会返回空结果。Avito 还会按 IP 封锁:从数据中心
地址访问会被直接拒绝,从俄罗斯住宅地址访问则可用,只要
你不突发请求。对 CDP 来源的请求会错开节奏——连续
背靠背调用会使其降级(DNS 和淘宝在测试中都因此掉线),
因此连接器自身会在调用之间保持间隔。在你自己的会话中运行 marketplace-mcp doctor
以查看当前状态。
MPStats 是唯一付费来源,与众不同:没有 MPSTATS_MP_AUTH 时,
服务器会启动,但其工具会返回 auth_missing。因此它是可选的——
如果你有账户就接入;其他十三个服务器不会察觉。
14 个 stdio MCP 服务器共 39 个工具,共享一个运行时(mcp-core),
外加统一的 marketplace-mcp,将它们全部挂载在一个客户端入口下。它添加了自己的
marketplace_sources 工具——哪些连接器已挂载,哪些掉线以及
原因——因此它暴露 40 个工具:已挂载的 39 个加上那一个。stdio 是默认方式;
HTTP 传输是远程部署的可选项——参见
docs/DEPLOYMENT.md。
快速开始
需要 Python 3.12+ 和 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
客户端配置与上面的俄语部分相同。每个服务器都是一个控制台
脚本(wb-mcp、ozon-mcp、yandex-mcp、detmir-mcp、aliexpress-mcp、cian-mcp、compare-mcp),通过
uv run --directory /path/to/repo 启动。可选的 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 上):
console
dsh plugin --profile web add github:Vladimir-Human/ru-marketplace-mcp#path:/dsh
这会立即给你 15 个技能,并且没有 MCP 工具:两行 MCP 都保持
在 RU_MARKETPLACE_MCP_DIR 指向一个克隆之前处于禁用状态。挂载的服务器会在每次请求时计费——推荐的价格比较模式约 0.9k tokens,完整集合约 13.6k tokens——因此是否选择启用由你决定。dsh/README.md 涵盖了如何启用它以及完整模式。
连接后,运行 marketplace-mcp doctor。它会运行每个连接器的金丝雀测试,并为每个连接器报告 success、drift_detected 或 inconclusive。
工具
_selfcheck 金丝雀测试被有意从这些表中省略:它们不会通过 MCP 发布,因为操作员诊断会在每次请求时消耗模型约 7.5k tokens。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) | 注册实体、INN、KPP、OGRN、法定地址 |
| wb_categories(root, max_depth) | 目录树,包含 WB 自己的分片/查询选择器 |
| wb_category_products(shard, query, page, sort, dest) | 类别中的产品,使用这些选择器 |
wb_seller 回答了商品列表所隐藏的问题:实际发货的是谁?它返回注册的法律实体和税务 ID,这正是你区分官方品牌店和以相似名称交易的经销商的方式。
wb_questions 弥补了另一个缺口。评论描述拥有该产品是什么感觉;问题则澄清它实际上是什么——“10A 还是 16A?”、“包含线缆吗?”——而卖家的回复往往是该事实的唯一公开声明。每个 imt_id 一个池,在所有变体之间共享。
wb_category_products 闭合了 wb_categories 打开的循环:那个工具返回 WB 的 shard 和 query,而这个工具获取它们背后的产品。条目使用与 wb_search 相同的结构,因此类别遍历和文本搜索可以直接比较。WB 的几个最大板块带有分片 blackhole,完全没有 feed;该工具会说明这一点,而不是返回空列表。
Yandex Market — yandex_
| 工具 | 功能 |
| ------------------------------------------ | ------------------------------------------- |
| yandex_search(query, page, limit) | 搜索,同时返回两种价格、评分和卖家 |
| yandex_card(product_id, include_reviews) | 完整详情,外加星级分布和评论 |
始终有两种价格。 price_rub 是任何人都要支付的价格。price_with_plus 需要付费的
Yandex Plus 订阅,且低 25–30%。Yandex 优先展示订阅者价格,因此不加辨别地引用它会
错误地表述真实成本。
搜索行是 SERP 的报价,而不是卡片的默认报价。 一个 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 协议在你自己的已登录 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 → 名称)。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) | 单条房源:价格、描述、浏览量、卖家 |
| avito_seller(seller_id_or_url) | 卖家评分、评论数、在售房源 |
Avito 是分类信息平台,不是商品目录:没有按商品划分的评论池,卖家的信誉就是信任信号。免费/置换房源会以 price_rub: null 返回——绝不会是 0,因此无法赢得“最便宜”。从数据中心 IP 访问时,Avito 会返回 403 防火墙响应,因此采用双层传输:先进行 TLS 模拟,然后使用你的 Chrome,与 Ozon 完全一样。
Taobao — taobao_
| 工具 | 功能 |
| ------------------------------- | ------------------------ |
| taobao_search(query, page) | 商品目录搜索 |
| taobao_card(item_id_or_url) | 商品卡片 |
Taobao 搜索是一个带签名的 mtop React 应用:每个请求都需要一个由 cookie token 派生出的 sign,因此没有匿名访问路径。所有读取都在你的 Chrome 内运行,网站会自行对请求签名。价格保持为人民币(CNY),绝不进行换算——内置汇率会悄然过时,因此请显式比较卢布和人民币房源。
Megamarket、Lamoda、DNS、Citilink
这四个来源通过你的 Chrome(CDP)读取。Megamarket(megamarket_)通过 ServicePipe 后面的移动端 JSON API 访问,需要有效登录——匿名会话读取结果为空。DNS(dns_)和 Citilink(citilink_)在 Qrator 后面渲染 DOM,完全没有匿名路径。Lamoda(lamoda_)是分开的:卡片通过匿名 GraphQL,搜索通过 Chrome。除 Lamoda 卡片外,它们都需要带 CDP 的 Chrome(scripts/start_chrome_cdp.sh)。共有八个来源通过 CDP 运行:这些加上 Taobao、AliExpress、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)匹配的报价会被合并,因此一个商品列表不再能占据两个排名位置。
每个报价都带有 currency(小写 ISO 代码,默认 rub)和 price_native,即市场报价所用货币的价格。对于俄罗斯来源,它与 price_rub 一致;对于淘宝,它保存的是 price_rub 有意留空的元价格。那个元价格过去被获取后又被静默丢弃,因此淘宝行会显示空白价格,没有任何迹象表明存在真实价格。现在元会被报告出来,但仍然绝不会与卢布一起排名:foreign_currency: … 警告会列出有多少报价被排除以及原因。在这里转换会固化一个会静默过期的汇率,所以如果调用方想要转换,就由调用方自己转换。
MPStats — mpstats_
通过 MPStats 浏览器插件,按 Ozon 或 Wildberries SKU 提供销售和库存分析。与其他所有连接器不同,这个是可选的,并且需要付费的 MPStats 账户:认证是单个 mp_auth cookie(来自 mpstats.io 已登录插件会话的 JWT),通过 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——不会去追查从未发生过的 schema 漂移。
令牌是付费、按配额计费账户上的机密:切勿记录或提交它。
Agent 技能
每个连接器都在 skills/ 下附带自己的技能——共十五个——每个来源一个,外加一个共享的
marketplace 概览。技能不是对本 README 的复述:它告诉 agent 何时该动用该来源、该来源
不具备什么,以及它的哪些答案在没有二次核实的情况下不应被信任。
| 技能 | 服务器 |
| ----------------------------- | ----------------- |
| 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//,而这正是某个修复已经移除的确切模式。
技能会被复制到 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。
参见 docs/DEPLOYMENT.md。
一个密钥,而且是可选的。 除 MPStats 外,每台服务器都不需要任何东西:
无需配置,无泄露风险。仅 MPStats 需要 MPSTATS_MP_AUTH,即付费账户的 JWT——它只应存在于客户端入口的环境变量中,绝不出现在代码或提交中。
只使用你需要的源:MARKETPLACE_SOURCES。 统一服务器会挂载所有源,且它们的工具 schema 会在每次请求时发送给客户端。在 marketplace-mcp 入口的环境变量中将 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 查询的是同一子集。
开发
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 # 覆盖率,CI 强制 70% 下限
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 # 一个 print() 就会破坏 JSON-RPC
uv run python scripts/check_versions.py # 所有 79 处使用同一个版本
部分测试会针对捕获的标记执行连接器的真实提取器 JavaScript,并将输出与页面被捕获时显示的价格进行比对。这需要带有 jsdom 的 Node:
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 时,那一半会如实跳过,而 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 的个人资料地址列表仅在 MEGAMARKET_USE_PROFILE_ADDRESS=1 时才会读取,而 MPStats 通过你自己的令牌(MPSTATS_MP_AUTH)进入你的账户区域。Ozon CDP 层级运行在你自行建立的浏览器会话中。请自行斟酌使用,仅用于个人研究,并保持礼貌的请求频率;对反机器人来源的调用之间的退避是有意为之,不应为了速度而移除。工具输出不用于再分发或批量抓取。
这是如何构建的
我和 AI 助手一起编写了代码和文档。它们很快,但也自信地出错,因此项目围绕验证来组织:1735 个离线测试、发布前的审计、用真实提取器针对从实时网站捕获的标记运行的测试。发布说明会指出哪些来源是手动与实时页面比对过的,哪些未经验证。
检查比谁写了这些文字更重要,但代码和想法也应获得可见的认可:完整的贡献者和 PR 索引在
CONTRIBUTORS.md 中。
致谢
- @Xpos587 — MPStats 连接器,PR #5。
- @avxone — Avito 自检修复,PR #37。
- @Khalmatov — Ozon 评论来源,PR #38。
- @fosteev — macOS CDP 隐蔽性和 Cian 连接器,PR #42,PR #47。
- @ilodezis — 统一服务器来源选择,PR #48。
许可证
MIT,见 LICENSE。扫码进群