Reads Citilink inside the operator's Chrome over CDP. The whole domain answers HTTP 429 (stricter than DNS), and its data transport is gRPC-web — a binary protobuf protocol that is not worth reversing. A real Chrome passes Qrator and renders the pages, so the connector reads the DOM instead.
When to use
- Search Citilink by text
- One product's price, old price, availability
Tools available
citilink_search(query)— tiles from the rendered search pagecitilink_card(product_url)— one card. The URL carries/product/<slug>/, where the slug ends in a numeric id:/product/noutbuk-lenovo-2169270/.
Not an MCP tool: citilink_selfcheck() is a tri-state canary. It is
CLI-only — marketplace-mcp doctor runs every connector's canary at once.
Why the price used to come back null
Citilink renders the currency glyph in a separate element from the digits:
<span data-meta-price="59990"><span>59 990</span><span>₽</span></span>
The old extractor filtered text lines for руб|₽ and so never saw a line
carrying both. Title parsed, price did not — on every product.
Read the data-meta attributes, not the class names
Citilink's class names are build-hashed (app-catalog-51bw0j-…) and change with
every deploy, so they are useless as selectors. The site publishes a stable
data-meta-* contract for its own analytics, and that is what the extractor
keys on:
| attribute | holds |
|---|---|
data-meta-name="Snippet__title" |
the product name anchor |
data-meta-name="Snippet__price" |
the current price (glyph in a child span) |
data-meta-name="Snippet__old-price" |
the strikethrough price, no glyph |
data-meta-price="59990" |
the exact amount, machine-readable |
data-meta-price needs no parsing and cannot be confused with an instalment or a
badge, so it is preferred; the display strings are the fallback.
If prices start coming back null, check whether those attributes still exist
before touching anything else — a class-name change is expected and harmless, a
data-meta-* change is the real drift.
Numbers in a tile that are not the price
A tile also carries - 10% (discount badge), в 1356 пунктов (delivery points),
(от 8 дней) (delivery estimate), a rating and an opinion count. Only a number
attached to the currency glyph is treated as the price. Also note the first
product anchor in a tile is an empty overlay link — it has no text, so the
title comes from the Snippet__title anchor.
Gotchas
- gRPC-web is deliberately not reversed: DOM extraction is the maintainable route.
- A green
citilink_selfcheckproves the page rendered and the extractor returned a shape. It does not prove the numbers are right — for that, open the product page in the same Chrome and compare. - From a datacenter address the domain answers 429, so nothing can be verified there. This connector only means anything from the operator's own machine.
- A missing price is
null, never0:price_rub: nullmeans Citilink had no usable price for the tile — treat it as no data, never as a free item.
DSH activation
In DeepSeek Harness, the default profile exposes only compare_prices and
compare_sources through the cheap compare mount. Per-marketplace tools and
marketplace_sources require RU_MARKETPLACE_MCP_FULL=1 and a profile restart;
do not call them in the default mode.