{
  "markdown": "<div align=\"center\">\n\n[![Русский](https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-8B949E?style=for-the-badge)](README.ru.md)\n![English](https://img.shields.io/badge/English-0A66C2?style=for-the-badge)\n[![中文](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-8B949E?style=for-the-badge)](README.zh.md)\n\n</div>\n\n# Ozon MCP Server\n\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n[![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)\n[![MCP tools](https://img.shields.io/badge/MCP%20tools-156-orange.svg)](docs/tools.md)\n[![PyPI](https://img.shields.io/pypi/v/ozon-mcp-server.svg)](https://pypi.org/project/ozon-mcp-server/)\n[![Transport](https://img.shields.io/badge/transport-stdio%20%7C%20SSE-lightgrey.svg)](#how-it-works)\n\nRun your Ozon stores straight from a chat with an AI assistant: prices, promos,\nadvertising, orders, returns, reviews, finances — 156 tools on top of the Ozon\nSeller API and Performance API (Ozon is Russia's largest marketplace; the Seller\nAPI covers catalogue and operations, the Performance API covers paid ads).\nBuilt for sellers who run **more than one store**: every call takes a `shop_id`,\nand API keys stay encrypted on your own server — nothing leaves it.\nWhat sets it apart from other Ozon MCP servers: it covers advertising as well as\nthe Seller API, and its built-in diagnostics tell you which Ozon endpoints broke\nbefore your assistant runs into them.\n\nSelling on Wildberries too? There is the same server for WB —\n[wb-mcp-server](https://github.com/DeviceIngineering/wb-mcp-server).\n\nThis is the author's own working tool: more than five months of daily use, around\ntwenty seller accounts, 156 tools. It gets updated when he needs it updated — see\n[Updates and support](#updates-and-support) for what that means for you.\n\n> The per-client installation guides in `docs/` are currently **Russian only**.\n> The configuration in them is ready-to-paste JSON, which reads the same in any\n> language: file paths, the URL `http://localhost:8000/sse`, and the header\n> `Authorization: Bearer <MCP_AUTH_TOKEN>`.\n\n```\nYou: Which of my products is Ozon planning to pull into a promo?\nYou: Show ad campaign spend for the week and stop the ones burning money.\nYou: Which products have a worse price index than their competitors?\nYou: Reply with a thank-you to every new 5-star review.\n```\n\n![Ozon MCP Server dashboard](https://raw.githubusercontent.com/DeviceIngineering/ozon-mcp-server/main/docs/img/dashboard.png)\n\n## What it does\n\n| Group | Tools | What's inside |\n|-------|-------|---------------|\n| Promotions and discounts | 14 | Ozon promotions (list, candidates, join/leave), seller's own promotions, \"I want a discount\" buyer requests |\n| Prices and pricing strategies | 14 | setting prices and the minimum price, price index, minimum-price timer, automatic strategies that track competitors |\n| Advertising (Performance API) | 22 | \"Trafarety\" CPC campaigns (Ozon's sponsored-placement format), bids and budgets, \"Pay per order\" (CPO), per-product and daily statistics |\n| Products | 21 | listings and cards, attributes, stock, import and bulk updates, media, archive, certificates |\n| FBS and FBO orders | 17 | unfulfilled orders, packing (v4), labels, cancellations, handover acts, country of origin |\n| Returns and cancellations | 10 | unified FBO+FBS returns list, rFBS claims that need a seller decision, cancellation requests |\n| Reviews, questions, chats | 13 | reviews and replies, buyer questions, chat conversations (v3) |\n| Warehouses and reports | 8 | FBS warehouses, delivery methods, generating and downloading reports |\n| Finances | 7 | balance, transactions, accruals, realization report, mutual settlements, cash flow |\n| Categories, brands, certificates | 7 | category tree, attributes and their allowed values, certificates |\n| Analytics | 5 | SKU analytics, stock and turnover, product positions in Ozon search, top search queries |\n| FBO supplies | 4 | supply orders (v3), counters, timeslots |\n| Rating | 2 | current seller rating and its history |\n| Diagnostics | 2 | self-check of Ozon API availability, degradation detector |\n| Notifications | 2 | push webhook subscriptions and the event-type reference |\n| Company | 2 | seller details and tariffs |\n| Stores | 1 | list of connected stores and their `shop_id` |\n\nFBO and FBS are Ozon's fulfilment models: FBO ships from Ozon's warehouses,\nFBS from yours, rFBS is FBS with your own delivery.\n\nThe full numbered list, with a description and the parameters of every tool, is in\n**[docs/tools.md](docs/tools.md)**. It is generated from `ozon_mcp/server.py` (the\n`TOOLS` constant) — the same thing `tools/list` returns to any MCP client.\n\n## Quick start\n\n### Option 1: one command, no Docker\n\nThe server speaks stdio, which is how Claude Desktop, Cursor, VS Code and other\nMCP clients connect to it. Nothing to build:\n\n```bash\nuvx ozon-mcp-server\n```\n\nOr via pip:\n\n```bash\npip install ozon-mcp-server\nozon-mcp\n```\n\nClient configuration (for example `claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"ozon\": {\n      \"command\": \"uvx\",\n      \"args\": [\"ozon-mcp-server\"],\n      \"env\": {\n        \"OZON_CLIENT_ID\": \"your Client-Id\",\n        \"OZON_API_KEY\": \"your API key\",\n        \"DATA_DIR\": \"~/.ozon-mcp\"\n      }\n    }\n  }\n}\n```\n\nPoint `DATA_DIR` at any writable directory — it holds stores, keys and statistics.\nThe default is `/data`, which is the path used inside Docker.\n\n### Option 2: Docker with the web dashboard\n\nUse this if you want the dashboard, Ozon API diagnostics and browser-based store\nmanagement. Five commands:\n\n```bash\ngit clone https://github.com/DeviceIngineering/ozon-mcp-server.git\ncd ozon-mcp-server\ncp .env.example .env               # fine as-is for a trusted local network\ndocker compose up -d --build       # builds the image, serves on port 8000\nopen http://localhost:8000/shops   # add a store and its Ozon API keys\n```\n\nWhat each step does:\n\n- `.env` — every variable is optional. Store keys are easier to enter in the web\n  UI than here. The one thing worth setting up front, if the server is reachable\n  by anyone but you, is `MCP_AUTH_TOKEN` (generate one with `openssl rand -hex 32`).\n- `docker compose up -d --build` — builds the image from the `Dockerfile`, maps\n  port `8000:8000` and creates the `ozon_data` volume for stores, keys, call\n  statistics and diagnostics history. `restart: unless-stopped` brings the\n  container back up after a reboot.\n- `/shops` — the add-store form: `shop_id` (the handle you'll use in chat), a\n  display name, Client-Id + Api-Key for the Seller API, and Client-Id +\n  Client-Secret for the Performance API. The \"Проверить\" (Test) button makes a\n  live request to Ozon and tells you whether the keys were accepted.\n\nOnce it's running:\n\n| Address | What it is |\n|---------|------------|\n| `http://localhost:8000/` | dashboard: call counters, errors, degradations |\n| `http://localhost:8000/shops` | stores and keys |\n| `http://localhost:8000/diagnostics` | Ozon API diagnostics |\n| `http://localhost:8000/api/health` | health endpoint, JSON |\n| `http://localhost:8000/sse` | **the MCP endpoint** — this is what clients point at |\n\nNote that the web UI is in Russian.\n\nTo stop: `docker compose down` (the data stays in the `ozon_data` volume).\nLogs: `docker compose logs -f`.\n\n### Without Docker\n\n```bash\npython3 -m venv .venv && source .venv/bin/activate\npip install .\nDATA_DIR=./data PORT=8000 ozon-mcp-web\n```\n\n`DATA_DIR` defaults to `/data`, so when running locally be sure to point it at a\ndirectory you can write to.\n\n## Installing into clients\n\nThe transport is SSE at `http://<host>:8000/sse`. SSE support differs between\nclients: some speak it directly, others need the `mcp-remote` bridge. There is one\nguide per client, with config paths for macOS, Linux and Windows and ready-made\nJSON — **in Russian**, but the JSON blocks are language-neutral:\n\n| Client | SSE directly | Guide |\n|--------|--------------|-------|\n| Claude Code | yes | [docs/install-claude-code.md](docs/install-claude-code.md) |\n| Claude Desktop | no, `mcp-remote` bridge | [docs/install-claude-desktop.md](docs/install-claude-desktop.md) |\n| Cursor | yes | [docs/install-cursor.md](docs/install-cursor.md) |\n| Windsurf / Devin Desktop | yes | [docs/install-windsurf.md](docs/install-windsurf.md) |\n| VS Code (GitHub Copilot) | yes | [docs/install-vscode-copilot.md](docs/install-vscode-copilot.md) |\n| Cline | yes | [docs/install-cline.md](docs/install-cline.md) |\n| Continue.dev | yes | [docs/install-continue.md](docs/install-continue.md) |\n| Zed | unconfirmed, bridge recommended | [docs/install-zed.md](docs/install-zed.md) |\n| JetBrains AI Assistant / Junie | yes | [docs/install-jetbrains.md](docs/install-jetbrains.md) |\n| Gemini CLI | yes | [docs/install-gemini-cli.md](docs/install-gemini-cli.md) |\n| OpenAI Codex CLI | no, `mcp-remote` bridge | [docs/install-codex.md](docs/install-codex.md) |\n\nThe shortest example, Claude Code:\n\n```bash\nclaude mcp add --transport sse ozon http://localhost:8000/sse \\\n  --header \"Authorization: Bearer <MCP_AUTH_TOKEN>\"\n```\n\nClient summary and the bridge reference: [docs/README.md](docs/README.md).\n\n## Multi-store and security\n\nStores are added in the web UI, and every tool takes a required `shop_id`;\n`ozon_list_shops` tells you which ones exist. In chat it looks like this:\n\"show me the stock in store `alpha`\".\n\nThe real gain is not the switching itself but that **a strategy is written once and\nrolled out to every account**: a rule about prices, review replies or ad bids\napplies to all stores at once — no logging in and out of seller accounts, no\ncopying keys between client configs.\n\nThe price you pay is a shared IP. Every account talks to Ozon from one address:\nthe server the MCP runs on. Ozon's rate limits are counted per address among other\nthings, so the more accounts you have and the harder your strategies work them, the\ncloser the combined traffic gets to the threshold where throttling or a block kicks in.\n\n- there is **no** limit on the number of stores in the code;\n- the real ceiling comes from Ozon's per-IP limits, not from this server;\n- around twenty accounts is the author's own estimate of where the traffic still\n  stays in the safe zone;\n- beyond that, spread the stores across several servers with different addresses.\n\nYou can see the limit approaching, and the place to see it is the web UI: failed\npings and diagnostics warnings start piling up, and the error share in the call\nstatistics jumps. The dashboard also tells the two cases apart — mass throttling\nlooks like many tools degrading at once, a broken endpoint like a single one.\n\nHow keys are stored:\n\n- on first use, `.encryption_key` — a Fernet key — is created in `DATA_DIR`;\n- store keys are encrypted with it and kept in `DATA_DIR/shops.json`;\n- the web UI shows keys masked (`abc***xyz`), and saving a masked value does not\n  overwrite the real one;\n- under Docker all of this lives in the `ozon_data` volume. To move to another\n  machine, copy the whole volume — otherwise you lose the encryption key\n  (see [DEPLOY.md](DEPLOY.md), Russian).\n\nWhat to know about access:\n\n- `MCP_AUTH_TOKEN` protects **only** `/sse`. Pass it as the\n  `Authorization: Bearer …` header or as a `?token=…` query parameter.\n- An empty `MCP_AUTH_TOKEN` means no authentication at all. Only acceptable on a\n  trusted network.\n- The web UI (`/`, `/shops`, `/diagnostics`) and `/api/*` are **not** behind the\n  token: anyone who can reach the port sees the dashboard and can add stores.\n- Do not expose port 8000 to the internet directly. For remote access use\n  Tailscale or a VPN.\n- The server does not terminate HTTPS. If you need TLS from outside, put a reverse\n  proxy in front.\n\n## The web UI: every call is visible\n\nWith a typical MCP server, calls vanish into the void: the assistant did\nsomething, but what exactly, how long it took and what error it hit is known only\nto the assistant. Here every call gets a line in the log, and every broken tool\ngets a marker on the dashboard. For a tool that moves real money in a store, that\nis not decoration — it is the condition for trusting it.\n\nThe call statistics and check history are not synthetic: they come from more than\nfive months of daily use across roughly twenty seller accounts. The list of caught\nOzon API changes in the limitations section comes from the same place — it was\nread off the degradation log, not copied from the documentation.\n\n### Dashboard `/`\n\nThe screenshot is at the top of this page.\n\n- Four counters at the top: total calls, calls today, errors, and average call\n  duration in milliseconds.\n- Top 10 tools: call count, average duration, and how many of those calls failed.\n- A feed of the last 50 calls: timestamp, `shop_id`, tool name, duration, success\n  or failure, and the error text.\n- A per-store filter (`/?shop=alpha`) — the same figures for a single account.\n- Two banners surface at the top: degraded tools, and \"the last Ozon API check\n  found problems\".\n\n### Stores `/shops`\n\n![Stores page](https://raw.githubusercontent.com/DeviceIngineering/ozon-mcp-server/main/docs/img/shops.png)\n\nAccounts are added and removed right in the browser, with no file editing and no\ncontainer restart. The \"Проверить\" (Test) button makes a live request to both APIs\n(`POST /api/shops/{shop_id}/test`), so keys are verified when you add them rather\nthan during the first real call in the middle of a task. Tokens are encrypted with\nFernet, the encryption key lives in `DATA_DIR/.encryption_key`, and the UI shows\nkeys masked.\n\n### Diagnostics `/diagnostics`\n\n![Diagnostics page](https://raw.githubusercontent.com/DeviceIngineering/ozon-mcp-server/main/docs/img/diagnostics.png)\n\n*(the screenshot shows a demo store with deliberately invalid keys, which is why\nevery probe is red)*\n\n- Per store: whether keys are set, Ozon host availability, 12 Seller API category\n  probes, and a Performance API key check.\n- A background check every `HEALTH_CHECK_INTERVAL_MIN` minutes (30 by default,\n  `0` disables it), plus a \"Проверить сейчас\" (Check now) button for an immediate\n  run (`POST /api/diagnostics/run`).\n- Check history: time, store, status, number of failed pings, number of failed\n  probes, and the warning texts. The UI shows the last 30 entries; up to 1000 are\n  kept in the database with automatic rotation.\n- The same data is available from chat through the `ozon_diagnostics` tool.\n\n### Degradation detector\n\nThe server notices on its own that Ozon broke or switched off an endpoint — not\nfrom the documentation and not from work that failed, but from its own statistics.\nA tool whose last three calls in a row failed while earlier calls succeeded lands\nin the degradation list, which shows the tool name, the time of the last successful\ncall, the number of consecutive errors, and the text of the latest one. On the\ndashboard that is a red banner; on the diagnostics page, a table.\n\nWhat this buys you: a change on Ozon's side becomes visible the day it happens,\nnot a week later when you discover prices haven't been updating. The same list is\navailable from chat via `ozon_degradations`.\n\n### JSON for external monitoring\n\nAll of the above can be scraped programmatically, not just looked at:\n\n| Endpoint | What it returns |\n|----------|-----------------|\n| `GET /api/health` | service status, whether authentication is on, recent checks, degraded tools |\n| `GET /api/stats` | the same summary as the dashboard; `?shop=` narrows it to one store |\n| `GET /api/diagnostics/{shop_id}` | a full live diagnostic run for one store |\n\nThat is enough to wire the server into Zabbix, Uptime Kuma, or a plain `curl` in cron.\n\n## Context budget\n\nTwo things are paid in tokens: tool definitions, loaded once per session, and tool\nresponses, paid on every call. Both were measured on a live seller account rather\nthan estimated — `scripts/collect_corpus.py` takes a snapshot of read-only tools\n(PII masked before anything is written to disk, the corpus stays out of the repo),\n`scripts/measure_corpus.py` reports what it costs.\n\n**Definitions.** 156 tools cost **12 300 tokens** with a single store configured,\ndown from 18 386: one-sentence descriptions, `shop_id` dropped from the schemas when\nonly one store exists, no empty schema fields.\n\n**Responses.** The real problem turned out to be a handful of giant payloads:\n\n| tool | before | after |\n|---|---:|---:|\n| `ozon_category_tree` — a 9 797-node tree | 266 324 | 979 |\n| `ozon_get_prices` — 93 % of the weight is promo history | 52 879 | 8 679 |\n| `ozon_returns_fbo` — 50 returns with full logistics | 24 277 | 8 458 |\n| `ozon_warehouse_list` — 99 % of the weight is a year of timetables | 20 132 | 370 |\n| **corpus of 16 live responses** | **476 158** | **63 845** |\n\nWhat the server does about it:\n\n- **`view: compact | full`.** Heavy tools return the fields they are called for;\n  `view=\"full\"` gives the raw API response, and the answer states which fields were\n  hidden so the model knows what it can ask for.\n- **Truncation signal.** When exactly `limit` records come back, the answer says the\n  data is partial — otherwise the model reasons about a slice and presents it as the\n  whole catalogue.\n- **Size guard.** A response that would not fit the client's output ceiling\n  (`MAX_MCP_OUTPUT_TOKENS`, 25 000 by default in Claude Code) is cut server-side with\n  a count of what was left out, instead of being silently truncated on arrival.\n- **Search and depth for the category tree.** The API has neither and returns all\n  9 797 nodes; `search` finds a category, `depth` controls the level, and the default\n  is the top level only.\n\nNotes arrive as separate content blocks rather than a field inside the JSON: several\nOzon endpoints return an array at the top level, and wrapping it would break every\npath into the data.\n\n**Tool profiles.** A client without tool search pays for the whole catalogue on every\nrequest. `OZON_TOOLSETS` keeps only the profiles you use, cut along working tasks\nrather than documentation sections:\n\n| `OZON_TOOLSETS` | tools | tokens |\n|---|---:|---:|\n| empty (default) | 151 | 12 686 |\n| `pricing,ads` | 57 | 5 425 |\n| `orders` | 33 | 2 611 |\n\nThe `core` profile — stores, diagnostics, degradations, company — is always on:\ndiagnostics are needed exactly when something is broken. Disabled profiles are listed\nin the `ozon_list_shops` description, and calling a disabled tool answers which profile\ncontains it, so the assistant names the reason instead of saying \"this is not possible\".\n\nClaude Code needs none of this: it has tool search enabled by default. Cursor, Cline,\nContinue and Claude Desktop fetch `tools/list` whole — profiles are for them.\n\n## Design decisions\n\n- **151 narrow tools, not a few generic ones.** Collapsing them into `action`-style\n  endpoints would save definition tokens and change the class of failure: instead of\n  \"no such tool\" you get a wrong call with a side effect, and some of these tools set\n  prices and start ad campaigns.\n- **The server diagnoses itself.** `ozon_diagnostics` checks host availability and\n  runs a light real request across 12 Seller API categories plus the Performance API\n  keys; `ozon_degradations` reports which tools used to work and now fail steadily.\n  Ozon retires endpoints without notice, so \"is it my keys or did the API move\" has to\n  be answerable in one call.\n- **`compact` is the default for heavy tools.** The corpus showed the hidden fields are\n  promo history, warehouse timetables and logistics internals — not the data decisions\n  are made from — and the response says what was hidden.\n- **Response shaping hangs on a contextvar.** The dispatcher here is one long if-chain\n  with 150 `_json` calls; threading the tool name and arguments through every branch by\n  hand would be 150 edit sites and 150 chances to miss one.\n- **`shop_id` disappears from schemas with one store**, and comes back the moment a\n  second one appears.\n- **`mcp<2` is pinned deliberately** — the 2.0 low-level API drops the decorator\n  handlers this server is built on; the pin is documented where it is set.\n- **Keys are encrypted at rest** (Fernet, key in the data volume) and masked in the UI;\n  the corpus collector masks personal data before writing a file.\n\n## How it works\n\nA single Docker container running a FastAPI application that is both the MCP\nserver and the web UI.\n\n- **`ozon_mcp/server.py`** — the MCP server itself. The `TOOLS` list describes all\n  156 tools (name, description, JSON schema for the arguments) and the `call_tool`\n  handler routes each call to the right Ozon client method. Clients are pooled per\n  `shop_id`, so switching stores reconnects nothing.\n- **`ozon_mcp/client.py`** — two HTTP clients: `OzonSellerClient` (`Client-Id` /\n  `Api-Key` headers) and `OzonPerformanceClient` (a `client_credentials` token that\n  lives 30 minutes and refreshes itself).\n- **`ozon_mcp/app.py`** — FastAPI: the `/sse` endpoint on top of\n  `SseServerTransport`, Bearer-token checking, the dashboard/stores/diagnostics\n  pages, and the background health-check task.\n- **`ozon_mcp/settings.py`** — stores and keys: Fernet encryption, masking for the\n  UI, picking up keys from environment variables as a store called `default`, and\n  migrating the old single-store `settings.json` into `shops.json`.\n- **`ozon_mcp/diagnostics.py`** — probes: pinging Ozon hosts plus lightweight real\n  requests across 12 Seller API categories, and a Performance API key check.\n- **`ozon_mcp/stats.py`** — SQLite via `aiosqlite`: every tool call with its\n  duration and outcome, health-check history, degradation calculation.\n\nThe hosts the server talks to:\n\n| API | Base URL | Authorization |\n|-----|----------|---------------|\n| Seller API | api-seller.ozon.ru | `Client-Id` and `Api-Key` headers |\n| Performance API (ads) | api-performance.ozon.ru | OAuth `client_credentials`, 30-minute token |\n\nNon-obvious things:\n\n- Ozon returns ad bids and budgets in **micro-rubles**: `1000000` = 1 ₽. Don't be\n  surprised by seven-digit numbers.\n- A `403` on reviews and questions is not a breakage — it means no Premium Plus\n  subscription. Diagnostics does not count those as errors.\n- Ozon API keys became time-limited after the 2026-02-13 rotation — 180 days. The\n  expiry is exposed explicitly: `POST /v1/roles` returns `expires_at`, so you can\n  warn ahead of time instead of catching a `401` in the probes.\n- Asynchronous ad statistics: one report at a time, ≤10 campaigns, ≤62 days. The\n  tool waits up to about 2 minutes for the report to be ready.\n- Supply-order statuses in API v3 are integers 1–8, not strings.\n\n## Environment variables\n\n| Variable | Default | Purpose |\n|----------|---------|---------|\n| `MCP_AUTH_TOKEN` | empty | Bearer token for `/sse`. Empty = no authentication |\n| `HEALTH_CHECK_INTERVAL_MIN` | `30` | background diagnostics interval, `0` disables it |\n| `PORT` | `8000` | HTTP server port |\n| `OZON_TOOLSETS` | empty | comma-separated tool profiles: `pricing`, `ads`, `catalog`, `orders`, `analytics`, `feedback`, `finance`; empty means all 156 |\n| `OZON_MAX_RESPONSE_CHARS` | `60000` | size-guard threshold for a single response |\n| `DATA_DIR` | `/data` | directory holding `shops.json`, `stats.db`, `.encryption_key` |\n| `OZON_CLIENT_ID`, `OZON_API_KEY` | empty | Seller API keys for the `default` store, if you'd rather not use the UI |\n| `OZON_PERF_CLIENT_ID`, `OZON_PERF_CLIENT_SECRET` | empty | the same for the Performance API |\n\n## Known Ozon API limitations (as of August 2026)\n\n- Advertising: the API can only create \"Trafarety\" CPC campaigns; budgets and bids\n  are in micro-rubles; there is no official way to read the ad account balance.\n- \"Pay per order\": bids have been fixed since February 2025 — you can only turn\n  the promotion on or off.\n- Reviews, questions and part of analytics require a Premium Plus subscription\n  (error code 7).\n- Funnel metrics in `ozon_analytics` are marked deprecated by Ozon — use\n  `ozon_product_queries` for search positions.\n- **Endpoints Ozon is switching off in autumn 2026.** Dates from the official\n  @OzonSellerAPI channel, verified against live seller accounts\n  ([issue #6](https://github.com/DeviceIngineering/ozon-mcp-server/issues/6),\n  thanks to [@standlord-prog](https://github.com/standlord-prog)):\n\n  | path | goes dark | replacement |\n  |---|---|---|\n  | `/v3/posting/fbs/list` | 2026-08-31 | `/v4/posting/fbs/list` — **done in v2.1.0** |\n  | `/v2/posting/fbo/list` | 2026-08-31 | `/v3/posting/fbo/list` — **done in v2.1.0** |\n  | `/v3/posting/fbs/unfulfilled/list` | 2026-08-31 | no replacement: filtered out of `/v4/posting/fbs/list` by status — **done in v2.1.0** |\n  | `/v2/posting/fbs/act/create` | 2026-09-07 | `/v1/carriage/create` + `/v1/carriage/approve` — in progress |\n  | `/v3/finance/transaction/list` | 2026-09-08 | `/v1/finance/accrual/by-day` — in progress |\n  | `/v3/finance/transaction/totals` | 2026-09-08 | same — in progress |\n\n  `/v4/posting/fbs/list` is not a rename of v3: `postings` sit at the top level\n  rather than under `result`, and pagination is cursor-based (`has_next` + `cursor`)\n  instead of `offset`.\n- `ozon_finance_cash_flow` and `ozon_finance_accruals` already run on the new paths\n  (`/v1/finance/cash-flow-statement/list`, `/v1/finance/accrual/by-day`).\n- `ozon_product_stocks_by_warehouse` uses v2 because v1 is switched off on 2026-04-07.\n- Digital FBS handover acts were removed by Ozon on 2026-03-22 — the regular act\n  is used instead.\n- The Ozon API has no \"edit a review reply\" method: the reply is deleted and\n  written again.\n\nThis list is not a rewrite of the reference: it comes from the degradation log and\nfive months of daily calls, cross-checked against docs.ozon.ru as of August 2026.\n\n## What changed in version 2.0\n\nA full revision against the June 2026 Ozon API, verified by running real requests\nrather than reading docs: the unified returns list, cancellations v2, realization\nv2, ship v4, supply-order v3, real pricing strategies and \"I want a discount\",\nthe seller's own promotions, the new advertising model (Trafarety CPC + \"Pay per\norder\"), diagnostics with a degradation detector, and authentication on the MCP\nendpoint.\n\n## Project layout\n\n```\nozon-mcp-server/\n├── docker-compose.yml   # port 8000, ozon_data volume\n├── Dockerfile           # python:3.12-slim, uvicorn\n├── DEPLOY.md            # deploying to a dedicated machine, moving data\n├── docs/                # client connection guides + tool reference\n└── ozon_mcp/\n    ├── server.py        # MCP server: 156 tools, multi-store\n    ├── client.py        # Seller API + Performance API\n    ├── app.py           # FastAPI: SSE, web, auth, health loop\n    ├── diagnostics.py   # category probes, degradation detector\n    ├── settings.py      # stores and keys (Fernet)\n    ├── stats.py         # call statistics and check history (SQLite)\n    └── templates/       # dashboard, diagnostics, shops\n```\n\nDeploying to a dedicated machine and moving stores across:\n[DEPLOY.md](DEPLOY.md) (Russian).\n\n## The same server for Wildberries\n\n[**wb-mcp-server**](https://github.com/DeviceIngineering/wb-mcp-server) is the same\ntool for the other marketplace (Wildberries is the other large Russian\nmarketplace): same architecture, same web UI with dashboard and diagnostics, same\nmulti-store model via `shop_id`, same SSE transport, same ways of connecting\nclients.\n\n|  | Ozon MCP Server | WB MCP Server |\n|---|---|---|\n| Port | 8000 | 8001 |\n| Tools | 151 | 202 |\n| API | Ozon Seller API + Performance API (ads) | Wildberries Seller API |\n\nIn practice that means two things:\n\n- **The second server takes no new learning.** Once you have set up one, the other\n  starts the same way; only the port (8001 instead of 8000) and the tool set differ.\n- **You can run both on one machine.** Different ports, data in separate Docker\n  volumes, no conflict. In your client they are simply two MCP servers: `ozon` at\n  `http://localhost:8000/sse` and `wb` at `http://localhost:8001/sse`.\n\nSharing one machine does not hurt on rate limits either: both go out from the same\nIP, but Ozon and Wildberries count limits on their own side — they are different\nmarketplaces. The cap on the number of seller accounts described in the multi-store\nsection applies within each marketplace separately.\n\n## From API access to a working repricer\n\nThis server gives a model access to the seller account. Deciding *what* the price\nshould be is a separate job, and [**ozon-wildberries-repricer**](https://github.com/DeviceIngineering/ozon-wildberries-repricer)\ndoes it: it holds a reference price, computes a break-even floor from each\nmarketplace's real fees, pulls products out of promotions that would push them\nunder cost, and can search for a better price by running a controlled experiment\non live sales.\n\nIt covers Ozon, Wildberries and Yandex Market in one place, and it has its own\nHTTP control plane for LLM agents — with the rails a model needs: a price move\nthat would trigger Wildberries quarantine is walked over several runs, prices are\nread back three minutes later because marketplaces report success for changes they\ndid not make, and several agents working the same catalogue cannot overwrite each\nother's decisions.\n\nRun it without a marketplace account: `npm run demo` seeds a synthetic catalogue\nand starts the app.\n\n## Updates and support\n\nOzon changes its API constantly: endpoints get added, renamed and switched off\n(the limitations section above lists what has been caught so far). This server is\nthe author's working tool, and it gets updated **when he needs it updated** — that\nis, when a change breaks something in his own stores. More than five months of\ndaily use, and commits appear when Ozon breaks something, not on a schedule: a gap\nbetween commits usually means everything is working. The upside is that the code is\nproven by real daily use rather than published and forgotten; the downside is that\nthere is no release schedule and no commitment on turnaround.\n\nIf you need a fix urgently, write to **d0371153@gmail.com**.\nIssues and pull requests are welcome too, and they do get read.\n\n## Acknowledgements\n\n- [@standlord-prog](https://github.com/standlord-prog):\n  - [issue #6](https://github.com/DeviceIngineering/ozon-mcp-server/issues/6) — the breakdown\n    of Ozon endpoints being switched off, verified against live seller accounts: dates,\n    replacements and three gotchas in the move to `/v4`. Separately — the warning that\n    `/v1/carriage/create` has no required fields and an empty `{}` body creates a real\n    shipment, and the correction about `POST /v1/roles` returning `expires_at`.\n    Version **v2.1.0** is built on that work.\n  - [PR #7](https://github.com/DeviceIngineering/ozon-mcp-server/pull/7) — found and fixed\n    blind diagnostics: in stdio mode call statistics were never initialised, so\n    `ozon_degradations` answered \"no degradations\" to every question — even when every\n    single call was failing. The tool is marked [P0] and is asked precisely when something\n    has broken, which makes a silent false negative worse than having no tool at all. The\n    PR does not just fix the wiring: it also separates \"no data\" from \"no degradations\"\n    and adds an integration test over stdio with a real MCP client. Shipped in **v2.1.2**.\n\nVersion history: [CHANGELOG.md](CHANGELOG.md).\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\n## MCP Registry\n\nPublished in the official [MCP Registry](https://registry.modelcontextprotocol.io/):\n\n```\nmcp-name: io.github.DeviceIngineering/ozon-mcp-server\n```\n",
  "bytes": 31527,
  "sha": "4a81882d8c3d2b94694854ab506c54abaea66aff1ca475305956776fc268469a",
  "repo_slug": "deviceingineering/ozon-mcp-server",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_deviceingineering_ozon_mcp_ser_a7e0584b/readme"
}