# ru-marketplace-mcp (oci · ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0)

Read-only MCP servers for Russian marketplaces: prices, stock, ratings, reviews, seller identity

- Trust score: 38/100 (low)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-08-22

## Components

- oci · `ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0`: 38/100 (this document), [markdown](https://verifymcp.io/servers/vladimir-human-ru-marketplace-mcp/ghcr-io-vladimir-human-ru-marketplace-mcp-1-6-0.md), [page](https://verifymcp.io/servers/vladimir-human-ru-marketplace-mcp/ghcr-io-vladimir-human-ru-marketplace-mcp-1-6-0)

## Channel facts

- Registry: `oci`
- Package: `ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-08-22.

- **Supply Chain Security**: 0/100
  - Malware scan not yet available for this package.
  - Known CVEs could not be checked: this artifact ships no SBOM, so there is no dependency list to read. Publishing one would let us assess it.
  - Install-script risk not yet assessed.
  - Dependency health could not be checked: this artifact ships no SBOM, so there is no dependency list to read. Publishing one would let us assess it.
- **Provenance & Transparency**: 48/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 1 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 67/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 8330 tokens (~231/item across 36 items; 36 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

**Unverified: 2 categories.** Categories scored 0 because we could not verify them: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the ru-marketplace-mcp server?

ru-marketplace-mcp runs locally as a container image, launched with docker run --rm -i ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0. Ready-made configuration for Claude, Cursor, VS Code, Codex and 3 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add vladimir-human-ru-marketplace-mcp -- docker run --rm -i ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0
```

### Cursor

```json
{
  "mcpServers": {
    "vladimir-human-ru-marketplace-mcp": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "vladimir-human-ru-marketplace-mcp": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add vladimir-human-ru-marketplace-mcp -- docker run --rm -i ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "vladimir-human-ru-marketplace-mcp": {
      "type": "local",
      "command": [
        "docker",
        "run",
        "--rm",
        "-i",
        "ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0"
      ],
      "enabled": true
    }
  }
}
```

### Hermes

```yaml
mcp_servers:
  vladimir-human-ru-marketplace-mcp:
    command: "docker"
    args: ["run", "--rm", "-i", "ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0"]
```

### Netclaw

```json
{
  "McpServers": {
    "vladimir-human-ru-marketplace-mcp": {
      "Transport": "stdio",
      "Command": "docker",
      "Arguments": [
        "run",
        "--rm",
        "-i",
        "ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0"
      ]
    }
  }
}
```

### Other

```json
{
  "mcpServers": {
    "vladimir-human-ru-marketplace-mcp": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-08-20 (score 38)

First indexed and scored.

## MCP tools (36)

### `marketplace_sources` (~179 tokens)

Which Marketplaces Are Loaded

List which connectors this unified server actually mounted.

\## Why this exists

Connectors are imported defensively, so a missing dependency removes a
marketplace instead of killing the server. That is the right failure mode,
but it is invisible from the client: absent tools look the same as a source
that simply found nothing. Call this before concluding a marketplace has no
results — if it is in ``skipped``, it was never queried at all.

\## Return Format

MarketplaceSourcesResponse: {mounted, skipped, mounted_count, skipped_count,
server_version}. ``skipped`` maps source name to the import error that
removed it, which is usually a missing optional dependency.

\## Error Format

Never raises ToolError: pure introspection of the mounted connectors — a
failed import is recorded in ``skipped`` instead of being raised.

Output parameters:

- `mounted`
- `mounted_count`
- `server_version`
- `skipped`
- `skipped_count`

### `wb_card` (~267 tokens)

WB Product Cards

Fetch product card data from WB v4 API.

Returns prices in rubles, brand, supplier, supplier_rating, review_rating,
feedbacks count, total_quantity for up to 100 SKUs.

\## Return Format

WbCardResponse: {dest, count, items, meta}. Items carry nm_id, name, brand,
supplier, supplier_id, supplier_rating, review_rating, feedbacks,
total_quantity, in_stock, price_rub, price_original_rub. price_rub is None
when the SKU has no sellable price — never 0. Unknown SKUs are NOT an
error — they simply do not appear in items.

\## Error Format

ToolError: BadRequestError on malformed nm_ids; RateLimitedError on HTTP
429; TransportDownError on network failures, non-200 responses, Cloudflare
HTML pages and unexpected internal errors; ParserDriftError on a non-JSON
or mis-shaped body.

Input parameters:

- `dest` (string): Region ID. Default -1257786 (Moscow). Required for valid prices/stocks. Other examples: -1257786 Moscow, -1029256 Saint Petersburg.
- `nm_ids` (array, required): 1..100 nmId integers (positive). Batch product cards from WB v4 API.

Output parameters:

- `count`
- `dest`
- `items`
- `meta`

### `wb_root_info` (~169 tokens)

WB Root Info (imt_id)

Fetch full card metadata from basket CDN. Returns imt_id (root_id) for review pool.

All variants of one product share imt_id. Reviews indexed by imt_id, NOT by nmId.

\## Return Format

WbRootInfoResponse: {imt_id, subj_name, subj_root_name, colors,
compositions, options, host_used, meta}. imt_id is the root product id
shared by every variant — wb_reviews is indexed by it, not by nmId.

\## Error Format

On validation or transport/parse failure, raises ToolError with a JSON
message describing the error code and whether it is retryable.

Input parameters:

- `nm_id` (integer, required): nmId (positive integer). The SKU whose basket CDN card.json is fetched for imt_id resolution.

Output parameters:

- `colors`
- `compositions`
- `host_used`
- `imt_id`
- `meta`
- `options`
- `subj_name`
- `subj_root_name`

### `wb_reviews` (~352 tokens)

WB Reviews by imt_id

Fetch reviews by imt_id (root_id from wb_root_info).

All product variants share one review pool, indexed by imt_id NOT nmId.

The WB feedbacks endpoint returns a fixed pool (~1000 most-recent reviews,
newest-first) and IGNORES server-side order params (verified Nov 2026), so
\`sort` is applied CLIENT-SIDE over that pool. To surface complaints, "worst"
reorders the returned pool by lowest rating first.

\## Return Format

WbReviewsResponse: {imt_id, sort, pool_size, feedback_count, valuation,
valuation_distribution, feedbacks, host_used, meta}. Review items carry
rating, text, pros, cons, user, date. An empty pool is NOT an error — it
returns a healthy response with zero feedbacks.

\## Error Format

ToolError: BadRequestError on a bad limit or sort; ParserDriftError when
a 200 body has no feedbacks list; TransportDownError when every review
host fails and on unexpected internal errors.

Input parameters:

- `imt_id` (integer, required): Root ID (imt_id) from wb_root_info. All product variants share one review pool indexed by imt_id, NOT by nmId.
- `limit` (integer): Max review texts to return (1..100). Counts always full.
- `sort` (string): "recent"/"newest"/"default" (as returned, newest-first), "best"/"highest" (highest rating first), "worst"/"lowest"/"complaints" (LOWEST rating first — finds downsides). Reorders the ~1000-review pool…

Output parameters:

- `feedback_count`
- `feedbacks`
- `host_used`
- `imt_id`
- `meta`
- `pool_size`
- `sort`
- `valuation`
- `valuation_distribution`

### `wb_questions` (~391 tokens)

WB Buyer Questions by imt_id

Fetch buyer questions and seller answers by imt_id (root_id from wb_root_info).

Answers this tool exists for: buyers ask what a listing omits — "does it fit
a 60cm opening", "is the cable included", "is this the 10A or the 16A model" —
and the seller's reply is often the only public statement of that fact.
Reviews describe the experience of owning the product; questions clarify what
it actually is.

Keyed by imt_id, exactly like ``wb_reviews``: every colour and size variant
shares one question pool. Passing an nmId returns an empty pool with no error,
so resolve the root id via ``wb_root_info`` first.

\## Return Format

WbQuestionsResponse: {imt_id, total_available, returned, skip,
answered_count, has_more, questions, meta}. Question items carry
question_id, text, date, user, answered, answer_text, answer_date, nm_id.
An empty pool is NOT an error — it means nobody has asked yet.

\## Error Format

ToolError: BadRequestError on a bad limit or skip; RateLimitedError on
HTTP 429; TransportDownError on network failures, non-200 responses and
unexpected internal errors; ParserDriftError when a 200 body loses the
count key or the questions shape.

Input parameters:

- `answered_only` (boolean): Return only questions the seller has answered. Unanswered questions carry no product information.
- `imt_id` (integer, required): Root ID (imt_id) from wb_root_info. Questions are pooled per imt_id across every variant, NOT by nmId.
- `limit` (integer): Max questions to return (1..100). Fetched in pages of 30, which is the upstream cap.
- `skip` (integer): Offset into the question pool, for walking past the first page.

Output parameters:

- `answered_count`
- `has_more`
- `imt_id`
- `meta`
- `questions`
- `returned`
- `skip`
- `total_available`

### `wb_search` (~261 tokens)

WB Catalog Search

Search WB catalog by text query.

Uses the lightweight `search-goods.wildberries.ru/search` endpoint which
returns just product IDs (no PoW protection, very high rate limit). Then
enriches via `wb_card` for full details.

\## Return Format

WbSearchResponse: {query, page, page_size, total_ids, count, items, meta},
with items in the same shape as wb_card. Zero matches — or a page past the
end — returns WbNoResultsResponse {status: "no_results", query, page,
total_ids} instead. That is NOT an error.

\## Error Format

ToolError: BadRequestError on a page outside 1..20; RateLimitedError on
HTTP 429 from the v9 search endpoint; TransportDownError on unexpected
internal errors. Plain transport failures of both search paths degrade to
a no_results response rather than an error.

Input parameters:

- `dest` (string): Region ID. Default -1257786 (Moscow). Required for valid prices/stocks.
- `page` (integer): Page number 1..20 (each page returns ~30 IDs from the long list).
- `query` (string, required): Russian text search query for the WB catalog.

Output parameters:

- `result`

### `wb_seller` (~231 tokens)

WB Seller Legal Info

Look up the registered legal entity behind a WB seller.

Answers "who actually ships this?" — the question a marketplace listing is
worst at. Returns the registered name, tax ids (INN/KPP/OGRN) and legal
address, which is how you tell an official brand store from a reseller
trading under a lookalike name, and how you spot several storefronts sharing
one entity.

Chain from `wb_card`: its `supplier_id` field feeds straight into this tool.

\## Return Format

WbSellerResponse: {supplier_id, name, full_name, trademark, inn, kpp,
ogrn, legal_address, taxpayer_code, foreign_codes, host_used, meta}.
foreign_codes carries non-RU registration codes (unp/bin/unn) for EAEU
sellers.

\## Error Format

On validation or transport/parse failure, raises ToolError with a JSON
message describing the error code and whether it is retryable.

Input parameters:

- `supplier_id` (integer, required): WB supplier id (positive integer). Get it from wb_card's supplier_id field.

Output parameters:

- `foreign_codes`
- `full_name`
- `host_used`
- `inn`
- `kpp`
- `legal_address`
- `meta`
- `name`
- `ogrn`
- `supplier_id`
- `taxpayer_code`
- `trademark`

### `wb_categories` (~275 tokens)

WB Catalog Categories

Browse the Wildberries catalog tree.

Use this to discover what exists before searching: `wb_search` needs a query
string, but a shopper's question is often "what categories of humidifiers are
there?". Each node carries WB's own `shard` and `query` selectors, which are
the addressing needed to pull a category feed.

The live menu is ~800 KB, so responses are always a bounded slice — start at
'top', then expand the branch you care about.

\## Return Format

WbCategoriesResponse: {root, max_depth, total_returned, truncated, items,
host_used, meta}. Nodes carry id, name, url, shard, query, depth,
children_count, children; shard+query are the selectors
wb_category_products needs to list a category feed.

\## Error Format

On validation or transport/parse failure, raises ToolError with a JSON
message describing the error code and whether it is retryable.

Input parameters:

- `max_depth` (integer): How many levels below the root to include. 1 = direct children only.
- `root` (string): 'top' for the top-level sections, or a category name / URL path / id to expand (e.g. 'Электроника', '/catalog/elektronika', '8126').

Output parameters:

- `host_used`
- `items`
- `max_depth`
- `meta`
- `root`
- `total_returned`
- `truncated`

### `wb_category_products` (~496 tokens)

WB Category Product Listing

List the products in a catalog category, using the shard and query from wb_categories.

This closes the loop `wb_categories` opens. That tool hands back WB's own
\`shard` and `query` selectors — the address of a category feed — and this is
the tool that fetches it. Browsing "what humidifiers exist" no longer requires
inventing a search phrase and hoping WB's relevance ranking agrees with you.

Items come back in the same shape `wb_search` and `wb_card` return, so a
category walk and a text search are directly comparable.

\**Not every category has a feed.** WB marks those with the shard
\`blackhole`, and several of its largest sections (smartphones, laptops, TV and
audio) are among them: they exist as navigation, not as a listable endpoint.
Asking for one raises a clear error naming the alternative rather than
returning an empty list, because an empty list here would read as "this
category has no products", which is false.

\## Return Format

WbCategoryProductsResponse: {shard, query, page, sort, dest, count,
has_more, items, meta}, with items in the same shape as wb_card. has_more
is inferred from a full page — WB reports no total here.

\## Error Format

ToolError: BadRequestError on malformed selectors or the unlistable
'blackhole' shard; NotFoundError on a 404 (stale shard/query pair);
RateLimitedError on HTTP 429; TransportDownError on network failures,
non-200 responses, Cloudflare HTML pages and unexpected internal errors;
ParserDriftError on a non-JSON or mis-shaped body.

Input parameters:

- `dest` (string): WB region id. Defaults to WB_DEFAULT_DEST (Moscow). Prices and stock are region-specific.
- `page` (integer): Page number. Each page carries up to 100 products.
- `query` (string, required): WB catalog selector from wb_categories, e.g. 'cat=9845' or 'subject=1234'.
- `shard` (string, required): WB catalog shard from wb_categories, e.g. 'electronic58'. The literal 'blackhole' means the category has no listable feed.
- `sort` (string): Upstream ordering: popular, priceup, pricedown, newly, rate, or benefit.

Output parameters:

- `count`
- `dest`
- `has_more`
- `items`
- `meta`
- `page`
- `query`
- `shard`
- `sort`

### `ozon_card` (~246 tokens)

Ozon Product Card

Fetch Ozon product card data via composer-api.bx.

Tier-1 (curl_cffi) tried first. Falls back to Tier-2 (Chrome CDP at port
9222\) when Tier-1 hits Cloudflare 403. Tier-2 requires the operator running Chrome
via scripts/start_chrome_cdp.ps1 (Windows) or scripts/start_chrome_cdp.sh (Linux/macOS) first.

\## Return Format

OzonCardResponse: {status, price, card_price, price_original, is_available,
rating_score, rating_count, title, seller, characteristics, url, tier_used,
meta} on success. Fields are None when the page does not carry them.

\## Error Format

Raises ToolError on validation (BadRequestError), transport/block
(TransportDownError — including the catch-all for unexpected internal
errors), or parser drift (ParserDriftError). No-results is NOT an error —
an empty widgetStates payload returns a healthy response with null fields.

Input parameters:

- `sku_or_path` (string, required): SKU integer-as-string, full Ozon URL, or /product/<digits>/ path. Other paths are rejected (SSRF prevention).

Output parameters:

- `_meta`
- `card_price`
- `characteristics`
- `is_available`
- `price`
- `price_original`
- `rating_count`
- `rating_score`
- `seller`
- `status`
- `tier_used`
- `title`
- `url`

### `ozon_reviews` (~385 tokens)

Ozon Product Reviews

Fetch Ozon product review texts + star distribution via composer-api.bx.

Tier-1 (curl_cffi) tried first, Tier-2 (Chrome CDP) fallback — same path
as ozon_card. Returns review texts (comment/positive/negative), per-review
score, helpfulness votes, author first name, date, plus the overall star
distribution and total count.

Pages are walked automatically (30/page) until `limit` texts are collected
or pages run out, deduplicating by review uuid. Hard cap of 10 pages.

\## Return Format

OzonReviewsResponse: {status, sort, rating_score, reviews_count, distribution,
returned, partial, stop_reason, last_error, requested_limit, reviews, meta}
on success. A later-page failure with reviews already collected is a
PARTIAL SUCCESS (partial=True, stop_reason set), NOT an error.

\## Error Format

Raises ToolError on validation (BadRequestError), transport/block
(TransportDownError), or parser drift (ParserDriftError) — but ONLY when no
reviews have been collected yet. Once at least one page yielded reviews, a
later-page failure degrades to a partial-success return.

Input parameters:

- `limit` (integer): Max review texts to return (1..100). Distribution+total always full.
- `sku_or_path` (string, required): SKU integer-as-string, full Ozon URL, or /product/<digits>/ path. Normalized to /product/<digits>/reviews/ (SSRF-allowlisted).
- `sort` (string): Review ordering. Aliases: "recent"/"default" -> newest, "best"/"highest" -> highest rated first, "worst"/"lowest"/"complaints" -> LOWEST rated first. Raw API values published_at_desc/score_desc/score…

Output parameters:

- `_meta`
- `distribution`
- `last_error`
- `pages_fetched`
- `partial`
- `rating_score`
- `requested_limit`
- `returned`
- `reviews`
- `reviews_count`
- `sort`
- `status`
- `stop_reason`
- `tier_used`
- `url`

### `ozon_search` (~165 tokens)

Ozon Catalog Search

Search Ozon catalog. Tier-1 curl_cffi → Tier-2 CDP fallback.

Returns sku/title/price/rating per item. Schema parses Nov 2026
\`tileGridDesktop-*` widgets with `mainState` atom structure.

\## Return Format

OzonSearchResponse: {status, query, page, tier_used, count, items, meta} on
success. Zero matching items is NOT an error — it returns a healthy response
with count=0 and empty items.

\## Error Format

Raises ToolError on validation (BadRequestError), transport/block
(TransportDownError), or parser drift (ParserDriftError).

Input parameters:

- `page` (integer): Result page (1..10).
- `query` (string, required): Russian search text.

Output parameters:

- `_meta`
- `count`
- `items`
- `page`
- `query`
- `status`
- `tier_used`

### `yandex_search` (~334 tokens)

Yandex Market Search

Search Yandex Market and return products with both prices, ratings and sellers.

Yandex Market aggregates many sellers per product, which makes it the best
single source for "what does this cost right now" across the Russian market —
including goods Wildberries and Ozon do not carry.

Each result reports `price_rub` (what anyone pays) and `price_with_plus`
(requires a Yandex Plus subscription, typically 25-30% lower). Prefer
\`price_rub` when quoting a price to a person.

Note `rating_count` counts star ratings, not written reviews; the written
count is available per product via `yandex_card`.

\## Return Format

YandexSearchResponse: {query, page, page_count, total_available,
has_next_page, returned, items, meta}. Items carry product_id, sku_id,
title, brand, seller, price_rub (everyday — None when absent, never 0),
price_with_plus, price_old_rub, currency, rating, rating_count, in_stock,
is_express, url, image. Zero results is NOT an error — it is reported via
meta.warnings.

\## Error Format

On validation or transport/parse failure, raises ToolError with a JSON
message describing the error code and whether it is retryable.

Input parameters:

- `limit` (integer): Maximum products to return from the page.
- `page` (integer): Page number, 1-based.
- `query` (string, required): Free-text search query in Russian, e.g. 'iphone 15' or 'стиральная машина узкая'.

Output parameters:

- `has_next_page`
- `items`
- `meta`
- `page`
- `page_count`
- `query`
- `returned`
- `total_available`

### `yandex_card` (~292 tokens)

Yandex Market Product Card

Fetch full detail for a Yandex Market product: prices, rating breakdown, reviews.

Two things here are hard to get anywhere else. The **star distribution**
(`rating_stars`) shows whether a 4.8 average hides a cluster of one-star
complaints. And **reviews arrive with the card** in one request, complete with
pros, cons and helpfulness votes.

Reviews are capped at the ~13 Yandex renders server-side; the remainder load
through an API this connector deliberately does not touch.

\## Return Format

YandexCardResponse: {product_id, sku_id, title, brand, seller,
description, image, price_rub, price_with_plus, price_before_discount_rub,
discount_percent, currency, offers_count, rating, rating_count,
review_count, rating_stars, reviews, url, meta}. price_rub is None when
the page has no usable price — never 0. Review items carry author,
rating, date, pros, cons, comment, votes_up, votes_down, photos.

\## Error Format

On validation or transport/parse failure, raises ToolError with a JSON
message describing the error code and whether it is retryable.

Input parameters:

- `include_reviews` (boolean): Include the server-rendered reviews (first ~13).
- `product_id` (string, required): Numeric Yandex Market product id — take it from yandex_search results.

Output parameters:

- `brand`
- `currency`
- `description`
- `discount_percent`
- `image`
- `meta`
- `offers_count`
- `price_before_discount_rub`
- `price_rub`
- `price_with_plus`
- `product_id`
- `rating`
- `rating_count`
- `rating_stars`
- `review_count`
- `reviews`
- `seller`
- `sku_id`
- `title`
- `url`

### `detmir_card` (~317 tokens)

Detsky Mir Product Card

Fetch price, rating, stock and seller for one Detsky Mir product.

Covers the kids-and-baby category that the general marketplaces cover
unevenly, and distinguishes Detsky Mir's own stock from third-party
marketplace sellers.

\**Region matters most here.** ``store_count`` is the number of physical shops
holding the item, and it swings hard by city — one item verified live sat in
152 Moscow stores, 37 in St Petersburg, 2 in Khabarovsk. Pass ``region`` to
ask about a specific city; it overrides ``DETMIR_REGION`` for this call only,
so one session can compare cities.

\## Return Format

DetmirCardResponse: {product, region, meta}. product carries product_id,
title, article, brand, price_rub (None when absent — never 0),
old_price_rub, discount_percent, rating, review_count, questions_count,
availability, available_online, available_offline, store_count,
is_marketplace, vendor, url, picture.

\## Error Format

On validation or transport/parse failure, raises ToolError with a JSON
message describing the error code and whether it is retryable.

Input parameters:

- `product_id` (integer, required): Numeric Detsky Mir product id — the digits in /product/index/id/<id>/.
- `region` (string): ISO region code for prices and offline stock, e.g. 'RU-MOW' or 'RU-SPE'. Defaults to DETMIR_REGION.

Output parameters:

- `meta`
- `product`
- `region`

### `detmir_category` (~249 tokens)

Detsky Mir Category Listing

List products in a Detsky Mir category, with the total match count.

This is the reliable way to enumerate the catalog: unlike text search, it is a
real JSON endpoint with proper pagination and an upstream total, so it
supports "what's available and how much does it cost" without scraping.

\## Return Format

DetmirListResponse: {query, mode, total_available, category_title,
returned, offset, items, region, meta}. Items carry the same product
shape as detmir_card. An empty page is NOT an error — it is reported via
meta.warnings.

\## Error Format

On validation or transport/parse failure, raises ToolError with a JSON
message describing the error code and whether it is retryable.

Input parameters:

- `alias` (string, required): Category slug from a catalog URL, e.g. 'pups' in /catalog/index/name/pups/.
- `limit` (integer): Items per page.
- `offset` (integer): Items to skip, for pagination.
- `region` (string): ISO region code for prices and offline stock, e.g. 'RU-MOW' or 'RU-SPE'. Defaults to DETMIR_REGION.

Output parameters:

- `category_title`
- `items`
- `meta`
- `mode`
- `offset`
- `query`
- `region`
- `returned`
- `total_available`

### `detmir_categories` (~262 tokens)

Detsky Mir Catalog Categories

Browse the Detsky Mir catalog tree and get the aliases `detmir_category` needs.

This is the discovery step: Detsky Mir has **no working text search** (see the
module docstring), so the way to find products is to walk the tree and then
list a category. Each node carries its `alias` and a `products_count`, so you
can see where the inventory actually is before fetching a listing.

\## Return Format

DetmirCategoriesResponse: {parent, returned, total_available, items,
region, meta}. Items carry category_id, alias, title, full_name, level,
products_count, parent_id, url; alias is what detmir_category needs to
list a category.

\## Error Format

On validation or transport/parse failure, raises ToolError with a JSON
message describing the error code and whether it is retryable.

Input parameters:

- `limit` (integer): Maximum categories to return.
- `parent` (string): 'top' for the 27 top-level sections, or a parent category id to list its children.
- `region` (string): ISO region code for prices and offline stock, e.g. 'RU-MOW' or 'RU-SPE'. Defaults to DETMIR_REGION.

Output parameters:

- `items`
- `meta`
- `parent`
- `region`
- `returned`
- `total_available`

### `avito_search` (~199 tokens)

Avito Search

Search Avito listings via the internal js/items API.

\## Return Format

AvitoSearchResponse: {status, query, page, location_id, tier_used, count,
total_count, items[], meta}. Items carry item_id, title, price_rub (None
when the ad has no price — never 0), url, location, seller fields.

\## Error Format

ToolError: BadRequestError on malformed arguments; TransportDownError on
firewall blocks (with the captcha/proxy guidance inline); ParserDriftError
when a reached-200 body no longer parses as the expected envelope.

Input parameters:

- `category_id`: Optional Avito category id to narrow the search
- `location_id`: Avito location id; default AVITO_LOCATION_ID (637640 = Moscow)
- `page` (integer): Result page (1-based)
- `query` (string, required): Search text, e.g. 'thinkpad x1 carbon'

Output parameters:

- `_meta`
- `count`
- `items`
- `location_id`
- `page`
- `query`
- `status`
- `tier_used`
- `total_count`

### `avito_card` (~144 tokens)

Avito Item Card

Fetch one Avito listing by id or URL.

\## Return Format

AvitoCardResponse: {status, item_id, title, price_rub, description, location,
posted_at, views, images, seller, url, tier_used, meta}. price_rub is None
when the ad has no price — never 0.

\## Error Format

ToolError: BadRequestError when no id can be extracted; NotFoundError on a
404 (deleted or never existed); TransportDownError on blocks; ParserDriftError
when the envelope changed.

Input parameters:

- `item_id_or_url` (string, required): Item id, slug path or full avito.ru URL

Output parameters:

- `_meta`
- `description`
- `images`
- `item_id`
- `location`
- `posted_at`
- `price_rub`
- `seller`
- `status`
- `tier_used`
- `title`
- `url`
- `views`

### `avito_seller` (~131 tokens)

Avito Seller Profile

Fetch an Avito seller profile — reputation is the review signal here.

Classifieds have no per-item review pool; the seller's rating, review count
and active-listing count are what a buyer checks.

\## Return Format

AvitoSellerResponse: {status, seller, active_items, tier_used, meta}.

\## Error Format

ToolError: BadRequestError on empty input; NotFoundError on 404;
TransportDownError on blocks; ParserDriftError on envelope drift.

Input parameters:

- `seller_id_or_url` (string, required): Seller id or profile URL from a card/search hit

Output parameters:

- `_meta`
- `active_items`
- `seller`
- `status`
- `tier_used`

### `taobao_search` (~171 tokens)

Taobao Search

Search Taobao listings, rendered in the operator's Chrome.

\## Return Format

TaobaoSearchResponse: {status, query, page, tier_used, count, items[], meta}.
Items carry item_id (string), title, price_cny (None when hidden — never 0),
shop_name, sales label, url.

\## Error Format

ToolError: TransportDownError when Chrome/CDP is unreachable or the page
lands on a login wall (log into taobao.com in the scraping profile, then
retry); ParserDriftError when a rendered page yields zero items, which means
the DOM shape moved.

Input parameters:

- `page` (integer): Result page (1-based)
- `query` (string, required): Search text, Chinese or English, e.g. '手机' or 'headphones'

Output parameters:

- `_meta`
- `count`
- `items`
- `page`
- `query`
- `status`
- `tier_used`

### `taobao_card` (~182 tokens)

Taobao Item Card

Fetch one Taobao item card.

\## Return Format

TaobaoCardResponse: {status, item_id, title, price_cny, shop_name, sales,
description_images, url, tier_used, meta}. price_cny is None when the page
hides it or prices by variant — never 0. When the description-image count
drifts to a non-number, description_images degrades to 0 and meta.warnings
names the drift — the card itself still answers.

\## Error Format

ToolError: BadRequestError when no id can be extracted; NotFoundError when
the item page reports itself gone; TransportDownError on login walls and CDP
failures; ParserDriftError when a rendered card has neither title nor price.

Input parameters:

- `item_id_or_url` (string, required): Item id or item.taobao.com URL

Output parameters:

- `_meta`
- `description_images`
- `item_id`
- `price_cny`
- `sales`
- `shop_name`
- `status`
- `tier_used`
- `title`
- `url`

### `megamarket_search` (~120 tokens)

Megamarket Search

Search the Megamarket catalog via the mobile API, inside the operator's Chrome.

\## Return Format

MegamarketSearchResponse: {status, query, tier_used, count, total_count,
items[], meta}. price_rub is None when absent — never 0.

\## Error Format

ToolError: TransportDownError on ServicePipe refusals (with the fix inline);
ParserDriftError when a reached-200 body no longer parses.

Input parameters:

- `query` (string, required): Search text, e.g. 'стиральная машина'

Output parameters:

- `_meta`
- `count`
- `items`
- `query`
- `status`
- `tier_used`
- `total_count`

### `megamarket_card` (~125 tokens)

Megamarket Product Card

Fetch one Megamarket product card.

\## Return Format

MegamarketCardResponse: {status, item_id, title, price_rub, old_price_rub,
is_available, rating, rating_count, url, tier_used, meta}.

\## Error Format

ToolError: BadRequestError on unparseable input; NotFoundError on a missing
goods id; TransportDownError on ServicePipe refusals; ParserDriftError on
envelope drift.

Input parameters:

- `item_id_or_url` (string, required): Goods id or megamarket.ru product URL

Output parameters:

- `_meta`
- `is_available`
- `item_id`
- `old_price_rub`
- `price_rub`
- `rating`
- `rating_count`
- `status`
- `tier_used`
- `title`
- `url`

### `lamoda_search` (~130 tokens)

Lamoda Search

Search Lamoda, rendered in the operator's Chrome (discovery is blocked tier 1).

\## Return Format

LamodaSearchResponse: {status, query, tier_used, count, items[], meta}.
Items carry sku, title, brand, price_rub (None when absent — never 0), url.

\## Error Format

ToolError: TransportDownError on CDP/nav failures; ParserDriftError when a
rendered page yields zero SKUs, which means the tile shape moved.

Input parameters:

- `query` (string, required): Search text, e.g. 'кроссовки nike'

Output parameters:

- `_meta`
- `count`
- `items`
- `query`
- `status`
- `tier_used`

### `lamoda_card` (~143 tokens)

Lamoda Product Card

Fetch one Lamoda product card via the anonymous GraphQL endpoint (tier 1).

\## Return Format

LamodaCardResponse: {status, sku, title, brand, price_rub, old_price_rub,
is_available, sizes[], url, tier_used, meta}. Lamoda exposes no ratings.

\## Error Format

ToolError: BadRequestError when no SKU can be extracted; NotFoundError when
the SKU has no product; TransportDownError on HTTP failures; ParserDriftError
when the GraphQL envelope changed.

Input parameters:

- `sku_or_url` (string, required): SKU (MP002XM1RMM3) or lamoda.ru product URL

Output parameters:

- `_meta`
- `brand`
- `is_available`
- `old_price_rub`
- `price_rub`
- `sizes`
- `sku`
- `status`
- `tier_used`
- `title`
- `url`

### `dns_search` (~104 tokens)

DNS-Shop Search

Search DNS-Shop, rendered in the operator's Chrome.

\## Return Format

DnsSearchResponse: {status, query, tier_used, count, items[], meta}.
price_rub is None when absent — never 0.

\## Error Format

ToolError: TransportDownError on CDP/Qrator failures; ParserDriftError when
a rendered page yields zero product tiles.

Input parameters:

- `query` (string, required): Search text, e.g. 'ноутбук lenovo'

Output parameters:

- `_meta`
- `count`
- `items`
- `query`
- `status`
- `tier_used`

### `dns_card` (~131 tokens)

DNS-Shop Product Card

Fetch one DNS-Shop product card.

\## Return Format

DnsCardResponse: {status, product_id, title, price_rub, old_price_rub,
is_available, url, tier_used, meta}.

\## Error Format

ToolError: BadRequestError when the URL carries no product id;
TransportDownError on CDP/Qrator failures; ParserDriftError when a rendered
card has neither title nor price.

Input parameters:

- `product_url` (string, required): dns-shop.ru product URL containing /product/<id>/, e.g. /product/b7a1667f9b19ed20/

Output parameters:

- `_meta`
- `is_available`
- `old_price_rub`
- `price_rub`
- `product_id`
- `status`
- `tier_used`
- `title`
- `url`

### `citilink_search` (~109 tokens)

Citilink Search

Search Citilink, rendered in the operator's Chrome.

\## Return Format

CitilinkSearchResponse: {status, query, tier_used, count, items[], meta}.
price_rub is None when absent — never 0.

\## Error Format

ToolError: TransportDownError on CDP/Qrator failures; ParserDriftError when
a rendered page yields zero product tiles.

Input parameters:

- `query` (string, required): Search text, e.g. 'ноутбук lenovo'

Output parameters:

- `_meta`
- `count`
- `items`
- `query`
- `status`
- `tier_used`

### `citilink_card` (~136 tokens)

Citilink Product Card

Fetch one Citilink product card.

\## Return Format

CitilinkCardResponse: {status, product_id, title, price_rub, old_price_rub,
is_available, url, tier_used, meta}.

\## Error Format

ToolError: BadRequestError when the URL carries no product id;
TransportDownError on CDP/Qrator failures; ParserDriftError when a rendered
card has neither title nor price.

Input parameters:

- `product_url` (string, required): citilink.ru product URL containing /product/<slug>/, e.g. /product/noutbuk-lenovo-2169270/

Output parameters:

- `_meta`
- `is_available`
- `old_price_rub`
- `price_rub`
- `product_id`
- `status`
- `tier_used`
- `title`
- `url`

### `aliexpress_search` (~113 tokens)

AliExpress Search

Search AliExpress, rendered in the operator's Chrome.

\## Return Format

AliSearchResponse: {status, query, tier_used, count, items[], meta}.
price_rub is None when absent — never 0.

\## Error Format

ToolError: TransportDownError on x5sec challenges or CDP failures (with the
manual-check hint inline); ParserDriftError when a rendered page yields zero
product tiles.

Input parameters:

- `query` (string, required): Search text, e.g. 'умные часы'

Output parameters:

- `_meta`
- `count`
- `items`
- `query`
- `status`
- `tier_used`

### `aliexpress_card` (~181 tokens)

AliExpress Product Card

Fetch one AliExpress product card, rendered in the operator's Chrome.

Review TEXTS are deliberately not exposed: they require navigating the
review tab, which x5sec challenges; this tool returns the rating and the
order count instead.

\## Return Format

AliCardResponse: {status, item_id, title, price_rub, old_price_rub, rating,
orders_count, url, tier_used, meta}.

\## Error Format

ToolError: BadRequestError when no item id can be extracted;
TransportDownError on x5sec challenges or CDP failures; ParserDriftError
when a rendered card has neither title nor price.

Input parameters:

- `item_id_or_url` (string, required): AliExpress item id (9-16 digits) or aliexpress.ru item URL, e.g. /item/1005010003103368.html

Output parameters:

- `_meta`
- `item_id`
- `old_price_rub`
- `orders_count`
- `price_rub`
- `rating`
- `status`
- `tier_used`
- `title`
- `url`

### `compare_prices` (~405 tokens)

Compare Prices Across Russian Marketplaces

Price one product across every configured Russian marketplace at once.

Queries each marketplace concurrently and returns a single list ranked by
price, plus a per-source report of what answered and what did not. This is
the tool for "where is X cheapest" — running the per-marketplace search tools
one at a time gives the same data far more slowly and without the ranking.

Two things to read carefully in the output:

\- `cheapest` is chosen on everyday prices. Yandex Market's subscriber price
  appears as `price_with_subscription_rub` and is deliberately excluded from
  ranking, since it requires a paid Yandex Plus subscription.
\- `source_outcomes` shows which marketplaces answered. A blocked or timed-out
  source means the comparison is partial, not that the product is absent
  there — `complete` tells you which case you are in.

Titles are matched loosely: marketplaces name things differently, so scan the
results rather than assuming every row is the identical model.

\## Return Format

CompareResponse: {query, sources_queried, sources_ok, complete,
total_offers, cheapest, price_spread_rub, offers, source_outcomes,
warnings, server_version}. offers is ranked by everyday price_rub —
cheapest first, offers without a rouble price after the ranked ones.
warnings carries validation/completeness warnings.

\## Error Format

On validation failure, raises ToolError with a JSON message describing the
error code and whether it is retryable. Individual source failures do NOT
raise — they are reported in `source_outcomes`.

Input parameters:

- `per_source_limit` (integer): How many offers to take from each marketplace.
- `query` (string, required): What to price, in Russian — e.g. 'стиральная машина узкая' or 'iphone 15 128'.
- `sources`: Restrict to specific marketplaces (wildberries, yandex_market, ozon). Omit to query all.

Output parameters:

- `cheapest`
- `complete`
- `offers`
- `price_spread_rub`
- `query`
- `server_version`
- `source_outcomes`
- `sources_ok`
- `sources_queried`
- `total_offers`
- `warnings`

### `compare_sources` (~131 tokens)

List Available Marketplaces

Report which marketplaces this installation can actually query.

Call this first when a comparison comes back partial: it distinguishes "the
connector isn't installed" from "the marketplace refused us", which need
completely different fixes.

\## Return Format

Plain object: {installed, searchable, not_installed, notes,
source_timeout_s, server_version, server_started_at, process_id}. notes
explains per-source access quirks (CDP-only sources, currencies, missing
text search).

\## Error Format

Never raises ToolError: pure introspection of which connector packages
are installed — nothing here touches the network.

### `mpstats_item` (~417 tokens)

MPStats Item Analytics

Fetch per-SKU 30-day sales analytics from MPStats (Ozon or Wildberries).

Returns, per SKU: seller/brand identity, current stock and price, a rolling
orders-per-day average, aggregated totals over the window, and four
per-day graphs (orders, prices, stock count, rubric positions). Graphs are
length ``days`` (default 30), oldest-first; a zero cell means "no data for
that day", not "the value was zero".

Requires the ``MPSTATS_MP_AUTH`` env var (a paid MPStats account JWT cookie).
Without it the tool returns an ``auth_missing`` error.

\## Return Format

MpStatsItemResponse: {place, days, count, items, meta}. Each item carries
sku, place, seller, seller_id, brand, stock_now, price_avg_rub,
orders_per_day, days_on_stocks, totals {orders, sum, sum_prev} and four
per-day graphs (orders, prices, count, rubrics), oldest-first. Missing
values are None, never 0; a zero graph cell means "no data for that day".

\## Error Format

ToolError: BadRequestError on malformed skus or place; AuthMissingError
when MPSTATS_MP_AUTH is missing or rejected; RateLimitedError on HTTP
429; TransportDownError on network failures, non-200 responses and HTML
blocks; ParserDriftError on a non-JSON or mis-shaped body; NotFoundError
when no requested SKU has analytics.

Input parameters:

- `oz_fbs` (boolean): Ozon FBS (Fulfilled-by-Seller) mode. Ozon-specific; harmless for wildberries. Default true.
- `place` (string, required): Marketplace: 'ozon' or 'wildberries'. Determines which MPStats dataset the SKUs resolve against.
- `skus` (array, required): 1..100 SKU integers (positive). Per-SKU 30-day sales/price/stock analytics from MPStats.

Output parameters:

- `count`
- `days`
- `items`
- `meta`
- `place`

### `mpstats_warehouses` (~303 tokens)

MPStats Warehouse Stock

Fetch per-SKU warehouse stock split from MPStats (Ozon or Wildberries).

Returns, per SKU: FBS (seller warehouse) stock count, total FBO (marketplace
warehouse) stock count, the raw per-warehouse FBO entries when MPStats
populates them, and the upstream ``last_update`` timestamp.

Requires the ``MPSTATS_MP_AUTH`` env var (a paid MPStats account JWT cookie).
Without it the tool returns an ``auth_missing`` error.

\## Return Format

MpStatsWarehousesResponse: {place, days, count, items, meta}. Each item
carries sku and stocks {fbs, fbo, fbo_warehouses, last_update}. Missing
stock counts are None, never 0.

\## Error Format

ToolError: BadRequestError on malformed skus or place; AuthMissingError
when MPSTATS_MP_AUTH is missing or rejected; RateLimitedError on HTTP
429; TransportDownError on network failures, non-200 responses and HTML
blocks; ParserDriftError on a non-JSON or mis-shaped body; NotFoundError
when no requested SKU has stock data.

Input parameters:

- `place` (string, required): Marketplace: 'ozon' or 'wildberries'. Determines which MPStats dataset the SKUs resolve against.
- `skus` (array, required): 1..100 SKU integers (positive). Per-SKU warehouse stock split from MPStats.

Output parameters:

- `count`
- `days`
- `items`
- `meta`
- `place`

## Diagnostics

Captured diagnostic sections: Provenance. The full working is on the page: https://verifymcp.io/servers/vladimir-human-ru-marketplace-mcp/ghcr-io-vladimir-human-ru-marketplace-mcp-1-6-0#diagnostics

## Score history

- 2026-08-22: 38
- 2026-08-21: 38
- 2026-08-20: 38

## Common questions

### What is the ru-marketplace-mcp server?

ru-marketplace-mcp is listed in the public MCP registry as io.github.Vladimir-Human/ru-marketplace-mcp. Read-only MCP servers for Russian marketplaces: prices, stock, ratings, reviews, seller identity. This page covers its container image (ghcr.io/vladimir-human/ru-marketplace-mcp:1.6.0).

### Is the ru-marketplace-mcp server safe to use?

ru-marketplace-mcp scores 38 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the ru-marketplace-mcp server expose?

ru-marketplace-mcp exposes 36 tools: marketplace_sources, wb_card, wb_root_info, wb_reviews, wb_questions, and 31 more. Their descriptions and schemas cost roughly 8,246 tokens of context every time the server is loaded.

### Is the ru-marketplace-mcp server still maintained?

ru-marketplace-mcp is still listed as active in the MCP registry. We last reached this channel on 22 August 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the ru-marketplace-mcp server under?

ru-marketplace-mcp declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- Repository: https://github.com/Vladimir-Human/ru-marketplace-mcp
- Changelog RSS feed: https://verifymcp.io/servers/vladimir-human-ru-marketplace-mcp/ghcr-io-vladimir-human-ru-marketplace-mcp-1-6-0.xml
- Changelog JSON feed: https://verifymcp.io/servers/vladimir-human-ru-marketplace-mcp/ghcr-io-vladimir-human-ru-marketplace-mcp-1-6-0.json
- HTML version of this page: https://verifymcp.io/servers/vladimir-human-ru-marketplace-mcp/ghcr-io-vladimir-human-ru-marketplace-mcp-1-6-0
