Ozon MCP
Ozon MCP is a server exposing one Ozon buyer account to an MCP client: read-only access to orders, purchases, returns, catalog, cart, favorites, wishlists, подборки and finances, plus gated writes for cart/lists and placing/cancelling orders.
Orders & returns: list active/completed/all orders with statuses and items, view order products, search purchase history, match purchases to orders, and list returns.
Catalog & search: storefront search with text/category/filters/sorts, get facets, product details with variants, prices, characteristics and photos, reviews, descriptions, delivery estimates, and find cheaper lots.
Cart: read the full cart with ticks and group headings, set quantities (0 removes), and select which items compose the order.
Favorites & wishlists: list favorites, add/remove favorites, manage wishlists (create, delete, membership), and track favorite price drops.
Подборки: list, view and create selections; add, remove or replace their products; edit name/description; publish/unpublish; and delete.
Checkout & payment: read checkout state, configure payment method, pickup point, points and pay-on-delivery, place orders (gated, spends real money), pay unpaid orders and see remaining steps, list cancel reasons, and cancel whole or partial orders.
Session & money: check session status and write/order gates, restore a dead session with a one-time login code, and read Ozon Card balance, points and seller bonuses.
Safety gating: writes and order placement are disabled by default and controlled by environment variables; session_status reports what is allowed.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Ozon MCP What's the status of my latest order?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
English · Русский
Ozon MCP
An MCP server for one ozon.ru buyer account. It reads orders, purchases, returns, the cart, favorites, wishlists, «Подборки», product cards, catalog search, the Ozon Card balance and points — and, when explicitly allowed, changes the cart and the lists and runs an order: forming a checkout, placing it, paying, cancelling it item by item.
None of this has a public API. The server talks to the same internal
composer-api / entrypoint-api endpoints the site's own frontend uses, under
your authenticated session.
Requirements
A Russian IP. Ozon blocks datacenter and VPN egress.
A real Chromium. Headless is detected; the image runs it under Xvfb.
One interactive login, performed once against the profile directory.
--shm-size=1g. Chromium crashes on Docker's default 64 MB.
Related MCP server: marketplace-mcp
How it works
The Variti antibot blocks non-browser clients, but a browser only has to pass
the challenge once. A real Chromium starts, passes it, and hands over its
cookies and the exact client-hint header set; from then on every read and write
is a direct HTTP call through curl_cffi
with Chrome's TLS fingerprint. No tool renders a page, which is why a call takes
about 0.2 s instead of 15. The browser stays only to own the profile, refresh
the session, and serve as the fallback for one reading — the delivery estimate.
The session lives in a persistent Chromium profile, not a cookie snapshot.
OzonID, the auth realm guarding checkout, keeps its state outside cookies and
localStorage, so a Playwright storage_state snapshot silently loses it and
checkout falls back to a login prompt. A profile directory keeps everything, so
one login stays valid and the server runs unattended.
Playwright's sync API cannot run inside a live event loop, and its objects belong to the thread that created them, so tool calls are dispatched to a single worker thread. They are therefore serialised, and a slow one — a bootstrap, a login — makes the others wait.
Tools
Orders and returns
Tool | |
| Orders (active / archive / all), each with its |
| Items of one order: sku, title, price paid, variant, seller, and each item's parcel status |
| Everything ever ordered, as product tiles; with a query, Ozon's own search over the whole history |
| What was actually received: the purchase list matched against the orders, with the coverage of the scan |
| Returns: number, date, status, amount, the products going back |
Catalog
Tool | |
| Storefront search by text and/or category slug, with sort, facet filters and a depth |
| Facets available for a query, and the values |
| Card: price, variants (each with its own sku), characteristics, photos; description and reviews on request |
| Rating, review count, the breakdown per star, and reviews to any depth ( |
| Description text plus the images embedded in it |
| When a product would arrive, to which address, from which warehouse |
| The cheapest lots of the same thing: Ozon's own other-seller offers plus a price-sorted search |
Cart
Tool | |
| The whole cart: items, quantities, ticks, group headings, and the size Ozon declares |
| Set the quantity; 0 removes |
| Tick what makes up the order — |
Favorites and wishlists
Tool | |
| Favorites as product tiles |
| Add or remove |
| Wishlists with their ids and sizes; with a sku, whether each holds it |
| Make a wishlist, or delete one |
| Put a product in a wishlist or take it out |
| Price changes since the previous call |
«Подборки»
Tool | |
| Selections with size, status, ids and the share link |
| One selection in full, including description and visibility |
| What it holds: sku, title, price, card link |
| Create one around a product; private by default |
| Add products, keeping the ones already in it |
| Take products out, keeping the rest |
| Replace the whole list of products |
| Rename it, replace its description |
| Publish to the public profile, or unpublish |
| Delete it; the products stay |
Checkout
Tool | |
| The order being formed: payment methods, pay-on-delivery, destinations, pickup points, shipments, points, totals |
| Set payment, points, pay-on-delivery and pickup point in one call |
| Spends money. Submit, waiting until the order exists |
| Charge an order left unpaid, and report what is left to do |
| Reasons Ozon accepts for cancelling |
| Cancel an order, or named items of it, returning them to the cart |
Session and money
Tool | |
| Whether the session acts as the account, and which gates are open |
| Restore a dead session with a one-time code |
| Ozon Card balance and total points |
| Points by type, burning points, per-store seller bonuses |
Changing the account is off by default. OZON_ENABLE_WRITES=1 allows the cart,
favorites, lists and cancellations; OZON_ENABLE_ORDERS=1 allows place_order
and is separate because it spends money. session_status reports both, so a
caller can plan around them instead of finding out halfway through.
What a caller needs to know
Some of Ozon's behaviour does not follow from a tool's name.
The cart ticks are the order. get_checkout reports nothing orderable until
something is ticked, and it orders exactly what is ticked — so "buy these two"
is select_cart_items(skus, mode="only"), whatever else the cart holds. Ozon's
checkout is a snapshot of those ticks taken when checkout is entered, and only an
entry replaces it: unticking an item afterwards changes the cart and not the
order. get_checkout and place_order therefore enter checkout before reading,
which keeps the options already chosen.
Two money figures, never interchangeable. totals.total is what Ozon charges
today; totals.order_total is what the order costs. On a pay-on-delivery order
today's charge is 0 ₽, so order_total is the figure to quote to a person.
An order row has no total. Ozon prints none on the order list, so a row
carries amount_due_at_pickup («К оплате при получении») and, per item, that
item's price and paid. The sum owed on collection is not any item's price, and
paid is null when Ozon says nothing — which is not unpaid.
«Текущие заказы» is not a list of current orders. Ozon keeps the recently
received and cancelled ones on that page. scope="active" leaves them out;
state says which of the three a row is.
Pay-on-delivery is not all-or-nothing. Ozon defers payment per shipment, so
an order can be part deferred and part prepaid — an imported item usually has to
be paid up front. pay_after_receipt.scope is full, partial or none; on a
partial order pay_now_items and pay_on_receipt_items name the lines on each
side, as Ozon splits them.
«Купленные товары» is a list of products, not of outcomes. It holds
everything ever ordered — including what was refused at the pickup point — and
states no order, no date and no status; its price is today's catalogue price, not
what was paid. On the account this was built against, 17 of 29 items matching one
query had been ordered and never taken home. The outcome lives in the orders: an
order is delivered in parcels with separate fates, so order_products reports
shipment_status and received per item, and bought_items matches the
list against the orders to fill them in, and its answer states how far it looked:
each order costs a request, so a bounded scan is partial and says so —
unresolved is "never seen in the orders scanned", provisional is "seen only
as cancelled, an older order may have been received".
An order's dates are status dates. The archive prints only «Получен 11 августа» / «Отменён 12 февраля 2021», so a date range filters on when an order was received or cancelled — an order placed on 30 June and received on 2 July lands in July. The archive is a newest-first cursor, so a window is walked down to: the cost is the depth, not the width. A recent fortnight comes back in half a second; a month two years back takes tens of seconds.
A rating is a number Ozon states. product_details carries rating,
reviews_count and questions_count; get_reviews adds the per-star breakdown
and walks pages to the depth asked for, reporting count (the product's total)
beside fetched (what came back) — they are different numbers. Ozon offers no
filter by star, so the one-star reviews are reached with sort="worst" and
depth. A card's reviews cover its variants, so each review names the size or
colour it is about, and «Достоинства» / «Недостатки» stay apart from the comment.
Three prices, and only one of them is charged. A card states price («С
банками» — what the account pays), price_regular («С другими банками») and
price_old (struck through). Comparing lots on anything but the first calls a
lot cheaper than it is, so search(sort="cheap") and find_cheaper rank on it.
Ozon's text search is literal. A lot whose own title omits the brand is not
returned for a query that includes it — and when nothing matches exactly Ozon
fills the page with "you might also like", which is dropped rather than passed
off as results. So search the model, not brand-plus-model, and confirm what a lot
is with product_details: a tile's title is the seller's wording and may name no
model at all. limit is depth — pages are walked until it is met, because the
cheapest lot is regularly not on the first one.
Wishlists and «Подборки» are different things. A wishlist has a numeric id
and holds anything. A selection has a uuid, a cover, a description and a
visibility; its products come from favorites only, and publishing puts it on the
account owner's public profile after review. Its products are a separate read —
the list states a count — and Ozon has no "add" of its own: the form submits the
whole list, which is what add_to_selection / remove_from_selection do around
the current contents.
Cancelling works line by line. cancel_order(order, skus=[...]) drops those
items and leaves the rest of the order standing, in as many passes as it has
items.
A card charge finishes on Ozon's bank domain, which signs the account in to
the bank. pay_order drives the payment to that point and reports what remains:
the amount, how much to top the card up by, and the page to finish at.
Pay-on-delivery avoids the whole thing.
Errors carry a code as well as a sentence — [rate_limited],
[session_expired], [writes_disabled], [orders_disabled],
[total_mismatch], [upstream_unavailable]. Branch on the code; the sentence is
written to be relayed to a person. A failure never arrives as an empty result: a
502 or a timeout raises, because "no orders" and "Ozon did not answer" are
different answers.
Configuration
Variable | Default | |
|
| Persistent Chromium profile holding the session |
|
| Copy of a known-good profile, restored on a sign-out |
|
| Legacy cookie snapshot, imported once to seed a new profile |
|
| curl_cffi TLS-impersonation profile |
|
| Allow cart / favorites / list / cancellation changes |
|
| Allow |
|
| Favorites price-history file |
|
| Seconds for one HTTP call |
|
| Attempts before a call is reported as failed |
|
| Base wait between attempts, doubled and jittered |
|
| Longest wait, including a |
|
| Seconds for a browser navigation or a login step |
|
| Close the idle browser; the HTTP session stays |
|
|
|
|
| Bind for the HTTP transports; the same port serves |
|
|
|
See env.example.
Running
docker build -t ozon-mcp .
# stdio: the client attaches to the container's stdin/stdout
docker run -i --rm --shm-size=1g -v /opt/ozon-mcp:/data ozon-mcp
# http: long-lived service on :8084 (/mcp + /metrics)
docker run -d --name ozon-mcp --shm-size=1g -v /opt/ozon-mcp:/data \
-e OZON_TRANSPORT=http -p 8084:8084 ozon-mcp/data has to be a bind mount. It holds the profile, and Ozon rotates the
session constantly: cookies rotated over HTTP are pushed back into the profile so
the refresh chain survives a restart. On an ephemeral directory the login is lost
on every container recreation.
The profile is a credential. It grants full access to the account — orders, addresses, card balance, checkout. Never commit it; back it up encrypted.
Locally, with uv:
uv sync
uv run playwright install --with-deps chromium
uv run python -m ozon_mcp # needs a display or XvfbUnder http and sse the same port serves Prometheus metrics at /metrics: upstream
request outcomes and latency, antibot re-challenges, session bootstraps, browser
state.
Session lifetime
A signed-out session looks exactly like an empty account — no orders, no
balances, no explanation — so every tool raises instead of answering,
session_status reports the state, and start_login / submit_login_code
restore it with a one-time code that only the account owner receives.
A profile known to be signed in is copied, and a sign-out is recovered from that copy automatically, so in most cases nobody is asked for a code.
Several agents at once
Multiple clients can connect over the HTTP transports, and each gets its own MCP session. They share one Ozon account and one worker thread, which has two consequences.
Calls are serialised: a second agent waits out the first, including a 13-second browser bootstrap.
More importantly, the account state is shared. Cart ticks live on Ozon's
side, not in the MCP session, so two agents composing orders at once overwrite
each other's selection — and place_order buys whatever is ticked at that
moment, at a total the caller read a second earlier. Reads are safe for any
number of agents; writes are not. Keep one writer per account.
The endpoint has no authentication: whoever reaches the port has the account.
Limitations
Only saved addresses can be chosen.
configure_checkoutswitches between the points in the account's address book; adding one from the map is not implemented.A selection's cover cannot be uploaded; that is a multipart upload and is not covered.
Per-shipment destinations are handled but unverified against a live order Ozon actually split across addresses.
pay_ordercannot complete a card charge: that happens on Ozon's bank domain and needs banking credentials this server does not hold.Placing an order spends real money, and is gated separately from every other write.
Development
make check-all # ruff format --check, ruff check, ty, pytest with a coverage gatety runs with all = "error", ruff with select = ["ALL"] and a curated ignore
list. Tests mirror src/; the service layer is driven through a stand-in for
get_session() and the transport through a stand-in for the HTTP session, so
nothing in the suite touches the network.
Disclaimer
Scraping a personal account is against Ozon's Terms of Service. This project is for personal, low-volume use, and you are responsible for how you use it.
License
Available Tools
42 toolsadd_to_selectionA
[GATED by writes_enabled] Add products to a «Подборка», keeping what is
already in it. Products must be in favorites first — Ozon draws only from
there and drops the rest silently, so check the items count that comes back.
Inputs: skus (required), uuid (required). Returns: One «Подборка» — a curated list of products, publishable to a profile. A differ…
cancel_orderA
[GATED by writes_enabled] Cancel an order, by default returning its items
to the cart.
skus cancels only those lines and leaves the rest of the order standing — an
order can be cancelled item by item, in as many passes as it has items. Omit
it to cancel the whole order. The selection is checked against what Ozon
reports back and refused on a mismatch, since cancelling the wrong line is
not undoable.
reason_id from list_cancel_reasons; "504" (изменить заказ и оформить заново)
is the neutral default, "508" needs a comment.
Check cancelled in the result — Ozon may answer with a retention offer
instead, and detail then carries what it asked.
Inputs: order (required), skus, comment, reason_id, +1 more. Returns: Result of a cancellation, with what Ozon reported back. ``skus`` is empty when…
check_favorite_price_dropsA
Record the current favorites prices and return the diff against the last run: {drops, rises, added, removed}. Call it periodically — the comparison is only as old as the previous call.
Inputs: no named parameters. Returns: added, drops, rises, removed
configure_checkoutA
Set checkout options and return the recomputed order — one call can set several. Omit an argument to leave that option alone. Every value comes from a get_checkout() answer, so read that first; passing something Ozon does not offer is refused with the list of what it does. Applied in the order Ozon recalculates them: destination, payment, pay-on-delivery, points. Turning points on can withdraw the pay-on-delivery offer, so check the result rather than assuming.
Inputs: points, payment, split_key, pickup_point, +1 more. Returns: The whole checkout state, read-only. ``pay_after_receipt`` is a switch, not a p…
create_listA
[GATED by writes_enabled] Create an empty wishlist and return it with its list_id, ready for set_list_membership(). An empty name is refused by Ozon. Wishlists are the only list this account can create — «Подборки» are not made this way.
Inputs: name (required). Returns: A collection (подборка) or wishlist (вишлист). ``list_id`` is what ``set_list_m…
create_selectionA
[GATED by writes_enabled] Create a «Подборка» around one product and
return it with its uuid. More products go in with add_to_selection().
public defaults to false on purpose: publishing puts it on the account
owner's public profile, so ask them before passing true.
Inputs: sku (required), name (required), public, description. Returns: One «Подборка» — a curated list of products, publishable to a profile. A differ…
delete_listA
[GATED by writes_enabled] Delete a wishlist. The products in it are not deleted — they stay in favorites and in any other list. Not undoable: the list itself is gone, so confirm with the user first.
Inputs: list_id (required). Returns: The outcome of a change to the account. Ozon answers a mutation with a page fra…
delete_selectionA
[GATED by writes_enabled] Delete a «Подборка». The products in it are not deleted. Ozon's own warning: «Восстановить её не получится» — so confirm with the user first.
Inputs: uuid (required). Returns: The outcome of a change to the account. Ozon answers a mutation with a page fra…
delivery_estimateA
When a product would arrive, to which of the account's addresses, and from which warehouse ("Завтра, 2 сентября" / "ул. Данилова, 17" / "Со склада Ozon"). The date is relative to that address, so quote both. This is per product, before ordering; for an existing order the dates are in list_orders(), and for an order being formed in get_checkout().
Inputs: sku_or_url (required). Returns: When a product would arrive, and what that estimate is relative to. The date me…
edit_selectionA
[GATED by writes_enabled] Rename a selection, and optionally replace its description. Visibility is preserved: an edit carries it, so a public selection stays public and a private one stays private.
Inputs: name (required), uuid (required), description. Returns: One «Подборка» — a curated list of products, publishable to a profile. A differ…
find_cheaperA
The cheapest lots of the same thing, ranked by payable price — top limit
(default 10).
Looks in both places Ozon keeps them: its own «Есть дешевле или быстрее»
offers for this exact product, and a price-sorted search by the card's
title, which reaches the same product listed separately. Entries carry
seller and delivery when they came from the offers list, and offers have
no title — confirm the model with product_details() before quoting one.
Raises when the base price cannot be read, rather than answering "nothing is
cheaper" for a product it failed to price.
Inputs: sku_or_url (required), limit. Returns: base, cheaper
get_cartA
The whole cart (paginated through): items with title, price, quantity and
checked — the tick is what decides the order's contents — plus Ozon's own
group headings ("Доступны для заказа", «Бронирование товаров»).
Inputs: no named parameters. Returns: The whole cart, paginated through. ``groups`` are Ozon's own headings ("Доступн…
get_checkoutA
The order being formed from the selected cart items, with everything that
can be changed: payment methods (each with its payment_type), the
pay-on-delivery switch, one entry per destination in deliveries (each with
its shipments in split_keys and its selectable pickup_points), the
shipments with their delivery dates, points choices and the money
breakdown — totals.total is what Ozon charges today (0 ₽ on a fully
deferred order) and totals.order_total is what the order costs.
Pay-on-delivery does not always cover the whole order. pay_after_receipt
.scope is one of "full", "partial" (some items must be paid up front:
prepayment_amount now, post_payment_amount on receipt) or "none", and .note
states it in words. On a partial order pay_now_items and pay_on_receipt_items
name the lines on each side, as Ozon itself splits them, and the shipments
are loaded with their items, each carrying prepaid (null when it holds
items from both sides). shipment_items forces that loading on (true) or off
(false); by default it happens only when scope is "partial".
Forms the checkout itself if Ozon has not yet. If it reports
available=false, reason says what to fix — usually: select cart items.
Change it with configure_checkout, submit with place_order.
Inputs: shipment_items. Returns: The whole checkout state, read-only. ``pay_after_receipt`` is a switch, not a p…
get_descriptionB
Product description text plus the images embedded in it.
Inputs: sku_or_url (required). Returns: sku, images, description
get_financesA
Ozon Card balance and the total points. This balance is what a card payment draws on, so it is what decides whether pay_order() will need a top-up. Breakdown by point type → get_points().
Inputs: no named parameters. Returns: points, ozon_card_balance
get_listsA
The account's wishlists (вишлисты), each with its size and list_id — which
is what set_list_membership() and delete_list() take. With a sku, every list
also carries contains, so a product already in a list is not added twice.
Ozon's «Подборки» are a different thing, kept elsewhere and not exposed.
Inputs: sku. Returns: result
get_pointsB
Points by type (Ozon points, miles, WOW points, stars) with amounts, burning points, and per-store seller bonuses.
Inputs: no named parameters. Returns: burning, by_type, seller_bonuses
get_reviewsC
Reviews on their own: overall score, individual reviews (author/score/text/date) and review photos.
Inputs: sku_or_url (required). Returns: count, score, photos, reviews
get_search_filtersA
Facets available for a query: {name, key, type, options|range}. Apply them with search(filters={key: value}) — checkbox/category value = option.value, range value = "min;max". Flow: search → get_search_filters → search(filters).
Inputs: query (required). Returns: result
get_selectionA
One «Подборка» in full: name, description, product count, status and
public — whether it is published to the account's public profile.
list_selections() does not carry the description or public; this does, and
it is the only reliable read of visibility, because a selection under review
is listed as "На модерации" either way.
Inputs: uuid (required). Returns: One «Подборка» — a curated list of products, publishable to a profile. A differ…
list_cancel_reasonsA
Reasons Ozon will accept for cancelling an order, with their reason_id. The catch-all one (needs_comment=true) is refused without a comment.
Inputs: order (required). Returns: result
list_favoritesA
Favorites as product tiles: sku, title, price, old price, url.
Paginated through for you — limit caps how many come back, there is no
page to ask for.
Inputs: limit. Returns: result
list_ordersA
Orders with status, state (active/received/cancelled), pickup point, delivery slot/ETA and their items. No order total: Ozon states none on the list. Money is amount_due_at_pickup («К оплате при получении», what is owed on collection) per entry, and price plus paid true/false per item. An item's price is not the order's; paid null means unknown, not unpaid. An entry is a delivery group: items arriving together share it, so order_numbers can hold several and its items can be paid and unpaid. Giving either date searches the archive and stops paginating once past the window — cheaper than pulling the whole history for one month. Pass an entry's order number or detail_link on to order_products(), cancel_order() or pay_order().
Inputs: limit, scope, date_to, date_from. Returns: result
list_returnsA
Returns this account has opened, newest first: the return number, the
date of the application, Ozon's own status badge ("Деньги отправлены", "Ждём
товар") and the sentence under it, the amount, and the products going back.
Paginated through — limit caps how many come back.
Inputs: limit. Returns: result
list_selectionsA
The account's «Подборки» — curated, publishable lists of products, a different thing from the wishlists in get_lists(). Each carries the uuid every other selection tool takes, how many products it holds, and Ozon's own status: "Личная подборка" while private, "N сохранений" once public, "На модерации" while a publication is being reviewed.
Inputs: no named parameters. Returns: result
order_productsB
Items of one order: sku, title, price paid, chosen variant, seller and a product-card link. Accepts an order number ("44563249-0865") or a detail_link from list_orders()[].detail_link.
Inputs: order (required). Returns: result
pay_orderA
[GATED] Ask Ozon to charge an order left in «Ожидаем оплаты», and report
what the user must still do.
The result spells it out: amount_due is the charge, shortfall is set when
the Ozon Card balance does not cover it (top up by that much), and
next_step is the instruction to relay. payment_url is where the payment
is completed — that page signs the account in to Ozon Bank, which is the
owner's step, so this server never finishes the charge itself.
Tell the user plainly: top up by shortfall if present, then finish at
payment_url. Ordering with pay-on-delivery avoids all of it.
Inputs: order (required). Returns: Where a card payment stands after asking Ozon to charge it. The charge runs on…
place_orderA
[GATED by orders_enabled — SPENDS REAL MONEY] Submit the order that get_checkout() describes. Not undoable from here except by cancel_order(). Read get_checkout() immediately before, show the user totals.order_total — what the order costs — and pass back the figure they agreed to. The call is refused if that no longer matches, and nothing is ordered. Returns once the order actually exists, with its order_number: keep it, it is what cancel_order() and pay_order() need. Paying from the Ozon Card balance settles with the order; pay-on-delivery leaves nothing to pay today.
Inputs: confirm_total (required). Returns: Result of a submitted order, once Ozon has actually created it. Paying from the…
product_detailsA
Product card: title, three prices, variants, characteristics, gallery photos.
Money as Ozon prints it: price is «С банками» — what the account actually
pays and the figure to compare lots on — price_regular is «С другими
банками», price_old the struck-through comparison. available says whether
it is on sale at all. cheaper_offers / cheaper_from are Ozon's own count
and lowest price for other offers of this product; find_cheaper() lists them.
Each variant carries its own sku, price and availability — that sku is what
goes into the cart, and for apparel it is the only one that will add.
with_description / with_reviews fetch those too (separate requests each);
get_description() and get_reviews() do the same on their own.
Inputs: sku_or_url (required), with_reviews, with_description. Returns: Everything on a product card. ``description`` and ``reviews`` come from separat…
purchasesA
Everything ever bought, as product tiles (sku/title/price/url) — the
answer to "have I bought this before" and "buy that thing again".
With query Ozon searches its own purchase history server-side, which is
much cheaper than paging through all of it.
Orders, statuses and totals are not here: that is list_orders().
Inputs: sort, limit, query. Returns: result
remove_from_selectionA
[GATED by writes_enabled] Take products out of a «Подборка», keeping the rest. Fails if the selection holds none of them, rather than reporting a removal that did not happen.
Inputs: skus (required), uuid (required). Returns: One «Подборка» — a curated list of products, publishable to a profile. A differ…
searchA
Storefront search → product tiles (sku/title/price/url). Give a text query, a category slug ("produkty-dlya-doma-9200"), or both. limit is depth, not page size: pages are walked until there are that many results, so raise it when looking for the cheapest — a page holds a few dozen and the cheapest lot is often further down. sort="cheap" ranks on the payable price (Ozon's own order goes by a different figure). Ozon's text search is literal about words: a lot whose own title omits the brand does not come back for a query that includes it, so search the model ("Basilisk V3 X HyperSpeed") rather than brand-plus-model, and confirm what a lot actually is with product_details() — a tile's title is the seller's wording and may name no model at all. A tile is not a card: for variants, characteristics, photos and stock call product_details() with its sku. filters comes from get_search_filters() — {key: option_value} for a checkbox or category facet, {key: "min;max"} for a range, e.g. {"currency_price": "200;600"}. Narrowing by price is a filter, not a sort.
Inputs: page, sort, limit, query, +2 more. Returns: result
select_cart_itemsA
[GATED by writes_enabled] Choose which cart items make up the order. This is the step that composes a checkout: get_checkout() reports nothing orderable until something is ticked, and it orders exactly what is ticked — so "buy these two" is mode="only" with those two skus, whatever else the cart holds. Returns the cart as it is afterwards, so the ticks can be checked before ordering.
Inputs: mode, skus. Returns: The whole cart, paginated through. ``groups`` are Ozon's own headings ("Доступн…
selection_productsA
The products a «Подборка» holds: sku, title, price and a card link. Neither list_selections() nor get_selection() carries them — they state a count only — so this is the read to make before changing what is in there.
Inputs: uuid (required). Returns: result
session_statusA
Start here. Reports whether the stored session still acts as the account and what this server is allowed to do: writes_enabled covers cart, favorites and lists, orders_enabled covers placing an order. Both are the operator's settings and no tool can change them — plan around them. When signed_in is false, every other tool raises instead of answering, because a signed-out session otherwise looks exactly like an empty account: no orders, no balances, no explanation. Recovery is start_login() + submit_login_code(), and backup_available=true means it will most likely recover by itself on the next call.
Inputs: no named parameters. Returns: Whether the stored session can still act as the account, and what for. ``backup…
set_cart_quantityA
[GATED by writes_enabled] Set how many of a product are in the cart.
Adding, changing and removing are all this one call: quantity=0 removes.
Ozon reports nothing about the outcome, so the cart is read back: ok=false
with the reason in detail means the call was accepted and changed nothing
— an out-of-stock product, or an apparel base SKU with no size chosen (use
the variant sku from product_details()).
Being in the cart is not being in the order: tick it with
select_cart_items() before checkout.
Inputs: sku (required), quantity (required). Returns: The outcome of a change to the account. Ozon answers a mutation with a page fra…
set_favoriteA
[GATED by writes_enabled] Add a product to favorites or remove it.
Ozon reports no outcome for this, so detail names the read that confirms
it — list_favorites(). Favorites are what check_favorite_price_drops()
watches.
Inputs: sku (required), add. Returns: The outcome of a change to the account. Ozon answers a mutation with a page fra…
set_list_membershipA
[GATED by writes_enabled] Put a product into a collection or wishlist,
or take it out. Ozon reports no outcome for this, so detail names the read
that confirms it — get_lists(sku).
Inputs: sku (required), list_id (required), add. Returns: The outcome of a change to the account. Ozon answers a mutation with a page fra…
set_selection_itemsA
[GATED by writes_enabled] Set which products a selection holds, as the
whole list. For "add this" / "take that out" use add_to_selection() and
remove_from_selection(), which read the current products first; this one
replaces them, and an empty list empties the selection.
Products must be in favorites first — Ozon draws only from there, and a
product that is not is dropped silently, so check the items count that
comes back.
Inputs: skus (required), uuid (required). Returns: One «Подборка» — a curated list of products, publishable to a profile. A differ…
set_selection_publicA
[GATED by writes_enabled] Publish a selection to the account owner's public profile, or unpublish it. Outward-facing — confirm with them before publishing. Publication is reviewed by Ozon, so the status comes back as "На модерации" rather than public.
Inputs: uuid (required), public (required). Returns: One «Подборка» — a curated list of products, publishable to a profile. A differ…
start_loginA
Ask Ozon to send a one-time login code to login (account email or
phone). Use this when session_status() reports signed_in=false and the kept
profile copy did not recover it.
Ozon delivers the code out of band (email, SMS or a flash call), so ask the
user for it and pass it to submit_login_code().
Inputs: login (required). Returns: Where an interactive login has got to. Ozon sends a one-time code out of band,…
submit_login_codeA
Finish the login with the code Ozon sent, and keep a copy of the restored profile so the next sign-out costs nobody a code. Codes expire and are single-use: if it is refused, call start_login() again.
Inputs: code (required). Returns: Whether the stored session can still act as the account, and what for. ``backup…
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 42 tools
Each tool targets a distinct resource or action, and the descriptions carefully separate cart operations, search, checkout, orders, favorites, wishlists, selections, and auth. A few near-boundary cases like get_lists vs list_selections and product_details vs get_reviews/get_description are only disambiguated by reading the descriptions rather than the names.
Most tools follow a get_/list_/set_/create_/delete_ verb-object pattern, but the set mixes standalone nouns like purchases, session_status, and selection_products with noun phrases like product_details and delivery_estimate. The inconsistency is not chaotic, but it is noticeable across 42 tools.
42 tools is well beyond the 25+ threshold and makes the server feel heavy even for a broad e-commerce account domain. The surface is highly granular, with 10 selection tools and separate get_reviews/get_description calls that could plausibly be consolidated.
The core shopping lifecycle is well covered: search, product details, cart, checkout, payment, ordering, cancellation, favorites, wishlists, selections, purchases, and returns listing. The main gap is that returns can only be listed, not opened or managed, and there is no address management.
Maintenance
Related MCP Connectors
Let AI agents use any website you're logged in to. Sessions stay encrypted on your machine.
AI-agent product catalog: search, lookup & purchase routing over verified merchant data.
Live product discovery for AI shopping agents, with shopper-approved retailer handoff.
The E-commerce Marketplace For Every AI Agent. A Governed Catalog Of 600M+ US Products, Plus Four Agent Ready Capabilities Search, Price Comparison, Product-Vs-Product, And Detailed Q&A.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceConnects AI assistants to Wildberries and Ozon seller accounts for real-time access to sales, stocks, prices, finances, and reviews through official APIs.MIT
- AlicenseAqualityCmaintenanceEnables AI agents to read and work with Wildberries, Ozon, and Yandex Market seller accounts through typed tools, multi-account support, unified data schemas, rate limiting, audit, and encrypted credential storage.1765 npmMIT
- AlicenseAqualityCmaintenanceEnables read-only analytics and product search for a Wildberries seller account, covering items, advertising, sales, stock, reviews, finances, and tariffs through MCP clients like ChatGPT or Cursor.12MIT
- AlicenseAqualityBmaintenanceOzon Seller API in your AI assistant: products, FBS and FBO orders, prices, stocks, finance and reviews. 441 methods live in a YAML catalog the server executes, the agent searches it in plain language and calls a method through three generic tools, and every method carries an access class so writes and irreversible calls ask for confirmation.2558 PyPI3MIT