{
  "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# WB 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-197-orange.svg)](docs/tools.md)\n[![PyPI](https://img.shields.io/pypi/v/wb-mcp-server.svg)](https://pypi.org/project/wb-mcp-server/)\n[![Transport](https://img.shields.io/badge/transport-stdio%20%7C%20SSE-lightgrey.svg)](#how-it-works)\n\n**Run your Wildberries stores from a chat with an AI assistant.**\n197 tools covering the Wildberries Seller API — product cards, prices, ads, shipments,\nreviews, finance, analytics — exposed to Claude, Cursor, Copilot, Gemini CLI and any\nother MCP client. Built for WB sellers (Wildberries is Russia's largest marketplace)\nwho run one or several seller accounts and would rather ask a question than click\nthrough the seller portal.\n\nSelling on Ozon too? There is [the same server for Ozon](https://github.com/DeviceIngineering/ozon-mcp-server).\n\nThe server has been in daily use for more than five months across roughly twenty WB seller\naccounts, with 197 tools. It is the author's own working tool and is updated as the author\nneeds it — [details here](#updates-and-support).\n\n```\nYou: Which of my product cards are blocked, and why?\nYou: Show ad cost share for every campaign this week and pause the ones above 15%.\nYou: Which warehouses currently have an intake coefficient of 0 or 1?\nYou: Reply to every new 5-star review with a thank-you note.\n```\n\n![WB MCP Server dashboard](https://raw.githubusercontent.com/DeviceIngineering/wb-mcp-server/main/docs/img/dashboard.png)\n\n---\n\n## What it can do\n\n197 tools, grouped by Wildberries Seller API area.\nThe full numbered list with a description of each one is in **[docs/tools.md](docs/tools.md)**.\n\n| Area | Tools | What it covers |\n|---|---:|---|\n| Product cards | 26 | card list and details, create and update, SEO text, attributes, barcodes, media, tags, trash bin, **cards with errors and blocks** |\n| Prices and discounts | 7 | current prices, setting prices and discounts, price quarantine, WB Club (WB's paid membership discounts), B2B, upload status |\n| Promotions | 7 | promotion calendar, auto-promotions, an audit of \"where WB has already enrolled your products\", joining and leaving a promotion |\n| Advertising | 22 | campaign list and creation, statistics and ad cost share, bids and bid recommendations, keyword clusters and negative phrases, balance and top-ups |\n| Analytics | 25 | sales funnel v3 (per-product views → cart → order conversion), day-by-day history, stock, anti-fraud, paid intake, measurement penalties, brand share, sales by region, search queries |\n| Statistics | 3 | sales, orders, stock (statistics-api) |\n| FBS orders | 29 | new and all assembly tasks, statuses, cancellation, labels, supplies, boxes, warehouse passes, KIZ marking codes (Russia's mandatory product marking). FBS = fulfilled by seller from WB warehouse pickup |\n| DBS orders | 10 | delivery by seller: orders, statuses, actions, delivery dates, metadata |\n| Click & collect | 9 | pickup orders, buyer identity confirmation, actions and metadata |\n| FBW supplies | 6 | shipments into WB warehouses, goods in a shipment, warehouses, **intake coefficients for the next 14 days** |\n| Seller warehouses and stock | 8 | seller warehouses, updating and reading stock |\n| Finance | 7 | sales reports, detailed breakdown, acquiring, balance, seller profile |\n| Tariffs and storage | 6 | box and pallet tariffs, return tariffs, commissions, FBW transit, paid storage |\n| Reviews and questions | 18 | reviews and questions, replies, per-period counters, archive, pinned reviews, seller rating |\n| Returns | 3 | return requests, answering a request, returns report |\n| Buyer chats | 4 | chats, events, sending messages, downloading attachments |\n| Documents | 4 | document categories, list, single and bulk download |\n| Users | 2 | staff members and invitations |\n| WB Jam | 1 | WB Jam subscription status (WB's paid analytics add-on) |\n| Shops | 1 | list of connected seller accounts |\n| Diagnostics | 4 | self-diagnostics, token inspection, tool degradations, WB API news |\n\nThree things similar servers usually do not have:\n\n- **Multi-store.** Every call takes a `shop_id`, so two WB seller accounts live in one\n  conversation. With a single store you can omit `shop_id` entirely.\n- **WB API diagnostics.** The server pings WB hosts by itself, sends one cheap probe\n  request per API category, decodes the token's expiry and scopes, and highlights\n  \"degradations\": a tool that used to work and now fails consistently — a reliable sign\n  that WB changed the API.\n- **Encrypted tokens.** WB tokens are stored encrypted (Fernet), not in your client's config.\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 wb-mcp-server\n```\n\nOr via pip:\n\n```bash\npip install wb-mcp-server\nwb-mcp\n```\n\nClient configuration (for example `claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"wildberries\": {\n      \"command\": \"uvx\",\n      \"args\": [\"wb-mcp-server\"],\n      \"env\": {\n        \"WB_API_TOKEN\": \"your Wildberries API token\",\n        \"DATA_DIR\": \"~/.wb-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, WB API diagnostics and browser-based store\nmanagement. You need Docker (Docker Desktop or OrbStack) and a Wildberries Seller\nAPI token.\n\n```bash\ngit clone https://github.com/DeviceIngineering/wb-mcp-server.git\ncd wb-mcp-server\ncp .env.example .env          # fine as-is for a local run\ndocker compose up -d --build\n```\n\nCheck:\n\n```bash\ncurl -s http://localhost:8001/api/health\n# {\"status\":\"ok\",\"auth_enabled\":false,\"health_check_interval_min\":30,...}\n```\n\nWhat you now have:\n\n| Address | What it is |\n|---|---|\n| <http://localhost:8001> | dashboard: tool calls, errors, response times |\n| <http://localhost:8001/shops> | stores: add a WB seller account, test its token |\n| <http://localhost:8001/diagnostics> | diagnostics: tokens, WB host pings, probes, history |\n| <http://localhost:8001/api/health> | JSON summary for external monitoring |\n| `http://localhost:8001/sse` | **the MCP endpoint** — this is what you give to the client |\n\nNext:\n\n1. Open <http://localhost:8001/shops> → **Добавить магазин** (Add store) → paste the WB\n   token → **Проверить** (Test). The token comes from the WB Seller Portal\n   (seller.wildberries.ru): **Настройки → Доступ к API → Создать токен**\n   (Settings → API access → Create token). It is valid for 180 days; the remaining\n   lifetime is shown on the diagnostics page.\n2. Connect an MCP client — see the next section.\n3. Ask your assistant: \"list my Wildberries stores\" — the `wb_list_shops` tool should fire.\n\nThe start command, flag by flag:\n\n| Flag | Why |\n|---|---|\n| `up` | start the service described in `docker-compose.yml` |\n| `-d` | in the background, without holding the terminal |\n| `--build` | build the image from `Dockerfile` — needed on the first run and after code updates |\n\nStop it with `docker compose down` (data stays in the `wb_data` volume).\nLogs: `docker compose logs -f`.\n\n<details>\n<summary>Running without Docker</summary>\n\n```bash\ngit clone https://github.com/DeviceIngineering/wb-mcp-server.git\ncd wb-mcp-server\npython3 -m venv .venv && source .venv/bin/activate\npip install .\nDATA_DIR=./data PORT=8001 python -m wb_mcp.app\n```\n\n`DATA_DIR` is mandatory here: by default the server writes to `/data`, a path that only\nexists inside the container.\n</details>\n\n## Installing into clients\n\nThe server speaks MCP over **SSE**: `GET /sse` is the event stream, `POST /messages`\ncarries the client's messages. SSE support differs from client to client, so each one\nhas its own guide — with config paths for macOS, Linux and Windows, ready-to-paste JSON,\nand variants with and without an auth token.\n\n> The per-client guides in `docs/` are currently **in Russian only**. The configuration\n> in them is ready-made JSON with file paths and flags, which is readable regardless\n> of language.\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 or local stdio | [docs/install-claude-desktop.md](docs/install-claude-desktop.md) |\n| Cursor | yes | [docs/install-cursor.md](docs/install-cursor.md) |\n| Windsurf | 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 | by URL; SSE support is not officially stated | [docs/install-zed.md](docs/install-zed.md) |\n| JetBrains AI Assistant | yes (SSE as legacy) | [docs/install-jetbrains.md](docs/install-jetbrains.md) |\n| Gemini CLI | yes | [docs/install-gemini-cli.md](docs/install-gemini-cli.md) |\n| Codex CLI | no → `mcp-remote` bridge | [docs/install-codex.md](docs/install-codex.md) |\n\nOverview and compatibility table: [docs/README.md](docs/README.md).\n\nWhere a client has a command that configures the connection by itself, the guide starts with\nthat command and treats editing JSON as the second option. The shortest setup of all —\nClaude Code:\n\n```bash\nclaude mcp add --transport sse wildberries http://localhost:8001/sse\nclaude mcp list      # expected: wildberries ... ✔ Connected\n```\n\n## Multi-store and security\n\n**Several seller accounts.** Stores are added on `/shops`; each one gets its own `shop_id`.\n`wb_list_shops` returns the list, and 200 of the 197 tools take `shop_id` as their first\nparameter (the exceptions are `wb_list_shops` and `wb_degradations`).\nWith a single store the parameter can be omitted — the server substitutes the only one available.\n\nThe point is not \"it supports two accounts\" but that **a strategy is written once and rolled\nout to every account**: a pricing rule, a review-reply template, an advertising bid ceiling\napply to all stores inside one conversation — no account switching, no scattering API keys\nacross different clients' configs.\n\n**How many accounts you can connect.** There is no limit in the code: `shops.json` is a plain\ndictionary, add as many as you like. The ceiling is set by Wildberries, not by this server:\nall accounts reach WB **from a single IP address** — the one running this server — and rate\nlimits are counted per address as well. The author's own estimate: around twenty accounts per\naddress stay in the safe zone. Beyond that, split them across several servers with different\naddresses.\n\nWhy this matters more than it looks — see the [WB limits](#wildberries-api-limits):\nseveral methods allow **3 requests per minute**, and **any 4XX response counts as 10 requests**.\nWith a dozen accounts on one server, a handful of malformed requests in a row burns the quota\nten times faster — and **every store hits the wall at once**, not just the one that erred.\n\nThere are ways to watch for it:\n\n- **Background diagnostics** send one `/ping` per host per run (the limit is 3 requests per\n  30 seconds per host) and record failed checks and warnings into a history. You see the limit\n  approaching in advance, instead of learning about it from a block.\n- **The degradation detector** tells two cases apart: many tools degrading at once means\n  per-address throttling, while a single tool degrading means one WB endpoint broke.\n  The dashboard makes the difference obvious at a glance.\n\n**Where the tokens live.** In the `wb_data` volume (`/data` inside the container):\n\n- `shops.json` — stores, with tokens encrypted using Fernet;\n- `.encryption_key` — the encryption key, generated on first start;\n- `stats.db` — SQLite with call statistics and diagnostics history.\n\nThe key sits next to the encrypted data, so the encryption protects against an accidental\nleak of the single `shops.json` file (a backup, a copy-paste) but not against anyone who\ngets access to the whole volume. Move the data as a whole volume — see [DEPLOY.md](DEPLOY.md).\n\n**MCP authorization.** The `MCP_AUTH_TOKEN` variable in `.env`:\n\n```bash\nopenssl rand -hex 32   # put the value into .env → MCP_AUTH_TOKEN=\ndocker compose up -d\n```\n\n- empty (the default) — `/sse` is open to anyone with network access to the port;\n- set — the client must send `Authorization: Bearer <token>` **or** `?token=<token>`\n  in the URL. The second form rescues clients that cannot send custom headers.\n\nThe token is checked on both MCP endpoints — on `GET /sse` and on `POST /messages`.\n\n**What the server does not do:**\n\n- The web UI (`/`, `/shops`, `/diagnostics`) is **not** protected by the token — it is open\n  to anyone with network access to the port.\n- Port 8001 is not meant to be exposed to the internet. For remote access use Tailscale or a VPN.\n- The server does not terminate HTTPS. If you need TLS from outside, put a reverse proxy in front.\n\n## The web UI: every call is visible\n\nWith a typical MCP server, calls vanish into thin air: you cannot see what the assistant\nactually did, how long it took or what the marketplace answered, and you learn about a problem\nonly when something fails. Here every call has a record and every store has a state.\nFor a tool that moves real money in a real shop, this is a precondition for trust,\nnot decoration. Five months of daily use across some twenty accounts is precisely what\nfilled these pages — and produced the WB limits section further down.\n\n### Dashboard — `/`\n\nThe screenshot is at the top of this page.\n\nA summary of all tool calls (`stats.get_summary()`):\n\n- total calls, calls today, number of errors, average call duration;\n- **top 10 tools**: call count, average time, error count;\n- **a feed of the last 50 calls**: timestamp, store, tool, duration in milliseconds,\n  success or failure, error text;\n- **a per-store filter** — an \"All / specific account\" switch above the summary.\n\n### Stores — `/shops`\n\n![The stores page](https://raw.githubusercontent.com/DeviceIngineering/wb-mcp-server/main/docs/img/shops.png)\n\nAccounts are added and removed right in the browser, with no file editing and no container\nrestart. Each store has a **Проверить** (\"Test\") button: it makes one cheap real request to WB\nand tells you immediately whether the token is alive — instead of letting you find out during\nthe first real call. Tokens are shown masked in the list (`abc***xyz`).\n\nTokens are encrypted with Fernet and stored in `shops.json` inside the data volume; the key\nis in `.encryption_key` next to it. The HTTP client pool is reset when a store is saved or\ndeleted, so a new token takes effect immediately.\n\n### Diagnostics — `/diagnostics`\n\n![The diagnostics page](https://raw.githubusercontent.com/DeviceIngineering/wb-mcp-server/main/docs/img/diagnostics.png)\n\n*(the screenshot shows a demo store with a made-up token: WB answers `401` to every ping and\nevery probe, so the whole page is red. That is what a failed check looks like — the server\nitself is fine. With a working token the \"Проверка …\" line reads `ping 13/13, пробы 20/20`\nand the store status is \"✅ Здоров\".)*\n\nA background check every `HEALTH_CHECK_INTERVAL_MIN` minutes (30 by default), per store:\n\n- **the token** — expiry, access categories, read-only and sandbox flags;\n- **pings of 13 WB API hosts** — availability and latency of each;\n- **20 probes** — one cheap real GET per API category. These are what catch\n  \"the endpoint returns 404 because WB renamed it\";\n- **warnings in plain language**: \"the token expires in N days\",\n  \"Content: 404 on /content/v2/... — WB may have changed the API\";\n- **check history** with automatic rotation (the last 1000 records are kept);\n- a **\"check now\"** button to run everything immediately.\n\n### The degradation detector\n\nThe most useful thing the accumulated statistics give you. The server finds, by itself, tools\nthat **used to work and now fail consistently**: the last three calls failed while successful\ncalls exist in the history. For each such tool it shows the time of the last successful call,\nthe number of consecutive errors, the text of the latest error and the moment things broke.\n\nIn other words, the server detects from its own statistics that Wildberries broke or switched\noff an endpoint — and tells you before you run into it at work. Next to the\n[section on limits and endpoint shutdown dates](#wildberries-api-limits) this is its practical\ncontinuation: that section lists what WB announced, this one catches what WB did quietly.\n\nYou can look at it on the dashboard, or call `wb_degradations` straight from the chat.\n\n### JSON for external monitoring\n\nEverything visible to a human is also readable by a machine:\n\n| Endpoint | What it returns |\n|---|---|\n| `GET /api/health` | service status, whether authorization is on, the check interval, the last 5 health checks, the list of degraded tools |\n| `GET /api/stats` | the same summary as the dashboard; accepts `?shop=<shop_id>` |\n| `POST /api/diagnostics/run` | run diagnostics for all stores now and return the result |\n| `GET /api/diagnostics/<shop_id>` | full live diagnostics of a single store |\n\nSo the server can be wired into Uptime Kuma, Zabbix or any other monitoring system, and you\nlearn about a dead token before the assistant tells you about it.\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.** 197 tools cost **17 700 tokens** with a single store configured,\ndown from 27 460. Descriptions are one sentence each, `shop_id` is dropped from the\nschemas when only one store exists (the server fills it in), and empty schema fields\nare not serialised.\n\n**Responses.** The real problem turned out to be a handful of giant payloads:\n\n| tool | before | after |\n|---|---:|---:|\n| `wb_tariffs_commission` — the whole 7 408-category reference | 621 802 | 23 023 |\n| `wb_cards_list` — 78 % of the weight is photo URLs and descriptions | 73 827 | 3 232 |\n| `wb_finance_report` — 90 fields per row | 23 540 | 7 156 |\n| `wb_advert_list` — 110 campaigns with timestamps | 20 528 | 12 084 |\n| **corpus of 27 live responses** | **770 506** | **74 947** |\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. Which fields were hidden is stated in\n  the response itself, so the model knows what it can ask for.\n- **Truncation signal.** When exactly `limit` records come back, the answer carries\n  a warning that the data is partial. Without it the model reasons about a slice\n  and presents it as the 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,\n  saying how many records are left out of how many — instead of being silently\n  truncated on arrival.\n- **Server-side filters where the API has none.** WB returns the commission\n  reference in full; the `subject` parameter narrows it here.\n\nNotes arrive as separate content blocks rather than a field inside the JSON: half\nof the WB endpoints return an array at the top level, and wrapping it would break\nevery path into the data.\n\n**Tool profiles.** A client without tool search pays for the whole catalogue on\nevery request. `WB_TOOLSETS` keeps only the profiles you use — they are cut along\nworking tasks, not along WB documentation sections, because auditing promotions\nneeds promotions, prices and the price quarantine at once:\n\n| `WB_TOOLSETS` | tools | tokens |\n|---|---:|---:|\n| empty (default) | 202 | 18 011 |\n| `pricing,ads` | 49 | 4 925 |\n| `pricing,ads,analytics` | 73 | 7 313 |\n| `orders` | 71 | 5 801 |\n\nThe `core` profile — stores, diagnostics, degradations, token info — is always on:\ndiagnostics are needed exactly when something is broken. Disabled profiles are\nlisted in the `wb_list_shops` description, and calling a disabled tool answers\nwhich profile contains it — so the assistant names the reason instead of saying\n\"this is not possible\".\n\nClaude Code needs none of this: it has tool search enabled by default and loads\nschemas on demand. Cursor, Cline, Continue and Claude Desktop fetch `tools/list`\nwhole — profiles are for them.\n\n## Design decisions\n\n- **202 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- **Dispatch through dictionaries, not an if-chain.** `NO_CLIENT_DISPATCH`,\n  `CLIENT_DISPATCH` and `SHOP_DISPATCH` map names to handlers, and a test asserts that\n  every tool has one and no handler is orphaned. With 197 tools an if-chain rots quietly.\n- **The server diagnoses itself.** `wb_diagnostics` pings every WB host and runs a\n  light real request per API category; `wb_degradations` reports which tools used to\n  work and now fail steadily. Marketplace APIs change without notice — the question\n  \"is it my token or did WB move the endpoint\" has to be answerable in one call.\n- **`compact` is the default for heavy tools.** The corpus showed the hidden fields\n  are photo URLs, promo history and warehouse timetables — not the data decisions are\n  made from. The response says what was hidden, so nothing is lost silently.\n- **`shop_id` disappears from schemas with one store.** The same parameter block\n  repeated across 200 schemas cost 3 400 tokens per session for no information;\n  the server substitutes the only store and puts the parameter back as soon as 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 migration is a separate task, and the pin\n  is documented where it is set rather than discovered at runtime.\n- **Tokens are encrypted at rest** (Fernet, key in the data volume) and masked in the\n  UI; the corpus collector masks personal data before writing a file, because order\n  and chat payloads carry customer names, phones and addresses.\n\n## How it works\n\nOne Docker container running a FastAPI application that plays two roles at once:\nan MCP server over SSE, and a small web UI. One paragraph per file:\n\n- **`wb_mcp/server.py`** — the MCP server itself. The `TOOLS` list of 202 `Tool` objects\n  (name, description, JSON schema of arguments) is exactly what the client receives in\n  response to `tools/list`. Calls are routed by three dictionaries: `NO_CLIENT_DISPATCH`\n  (no WB access needed), `CLIENT_DISPATCH` (needs the store's HTTP client) and\n  `SHOP_DISPATCH` (needs the `shop_id` as well). The stdio entry point `main()` lives here\n  too, for clients that only speak stdio.\n- **`wb_mcp/client.py`** — HTTP clients for the 14 Wildberries hosts. One `WBClient` per\n  store, wrapping an `httpx.AsyncClient` with the token; clients are cached in a pool keyed\n  by `shop_id`.\n- **`wb_mcp/app.py`** — FastAPI: `GET /sse` and `POST /messages` for MCP, the dashboard,\n  stores and diagnostics pages, the `/api/*` JSON API, the `MCP_AUTH_TOKEN` check, and the\n  background health-check loop.\n- **`wb_mcp/settings.py`** — stores and keys: reading and writing `shops.json`, Fernet\n  encryption, migration of the old single-store `settings.json`, masking tokens for the UI.\n  There is a fallback: if `WB_API_TOKEN` is set, a store named `default` appears.\n- **`wb_mcp/diagnostics.py`** — pinging WB hosts, decoding the JWT token (expiry, scopes,\n  sandbox flag), \"probes\" — one cheap real request per API category — and WB news.\n- **`wb_mcp/stats.py`** — SQLite via aiosqlite: every tool call is recorded with its\n  duration, success flag and `shop_id`; this feeds the degradation detector and the\n  health-check history.\n- **`wb_mcp/templates/`** — three PicoCSS pages, no frontend build step.\n\nNon-obvious details:\n\n- **`shop_id` is filled in automatically while there is only one store.** Convenient day\n  to day, but the moment you add a second account, calls without `shop_id` start returning\n  \"Укажите shop_id\" (\"specify shop_id\").\n- **Every call is written to the statistics**, failures included. That is what powers the\n  degradation detector: \"used to work, now fails consistently\" is a signal that WB changed\n  the API, not that you made a mistake. Check `wb_degradations` or the dashboard.\n- **Background diagnostics every 30 minutes** make real requests to WB and consume your\n  rate limits. If that is in the way, set `HEALTH_CHECK_INTERVAL_MIN=0` in `.env`.\n- **Responses are returned as-is**, the raw JSON from WB, with no repackaging. That keeps\n  the tools predictable, but large reports should be requested with filters or the answer\n  will eat your context window.\n- **`POST /messages` is mounted as a separate ASGI app** (`Mount`) rather than as an\n  ordinary FastAPI route: `handle_post_message` sends the ASGI response itself, and inside\n  a route the framework would send it a second time — the connection would be dropped on\n  every POST. That is why authorization for this endpoint is checked manually inside the app.\n- **The `mcp` library version is pinned to `>=1.0.0,<2`.** The server is written against\n  the decorator API of `mcp` 1.x (`@app.list_tools()`), removed in `mcp` 2.0. Do not lift\n  the upper bound in `pyproject.toml`: with `mcp` 2.x the server crashes on start with\n  `AttributeError: 'Server' object has no attribute 'list_tools'`.\n\n## Environment variables\n\n| Variable | Default | Meaning |\n|---|---|---|\n| `WB_API_TOKEN` | empty | token for the `default` store; adding stores via `/shops` is more convenient |\n| `MCP_AUTH_TOKEN` | empty | Bearer token for `/sse`; empty means authorization is off |\n| `HEALTH_CHECK_INTERVAL_MIN` | `30` | background diagnostics interval, `0` disables it |\n| `DATA_DIR` | `/data` | directory holding `shops.json`, `.encryption_key`, `stats.db` |\n| `PORT` | `8001` | HTTP server port |\n| `WB_TOOLSETS` | empty | comma-separated tool profiles: `pricing`, `ads`, `catalog`, `orders`, `analytics`, `feedback`, `finance`; empty means all 197 |\n| `WB_MAX_RESPONSE_CHARS` | `60000` | size-guard threshold for a single response |\n\n## Wildberries API limits\n\nThese are limits of WB itself, not of this server — but the assistant will hit them\nregularly, and it is better to know them in advance. This list was not copied out of the\ndocumentation: it comes from five months of daily calls across some twenty accounts, plus\nthe diagnostics log.\n\n- `GET /adv/v3/fullstats` (advertising statistics) — **3 requests per minute**, period\n  no longer than 31 days.\n- Sales funnel v3 — **3 requests per minute**; day-by-day history is available for the\n  last week at most.\n- `/ping` — 3 requests per 30 seconds per host (the background diagnostics accounts for this).\n- **Any 4XX response counts as 10 requests** against the limit (a rule in force since\n  2026-06-04). One wrong parameter inside a loop and you are rate-limited.\n- `reportDetailByPeriod` **was removed by Wildberries on 2026-07-15**. The server calls\n  finance-api; the fallback to the old endpoint is gone, since it is dead anyway. The\n  realization report needs the **Finance** category in the token — without it you get a\n  clear error telling you what to reissue, not an opaque refusal.\n- FBW supplies cannot be created through the API — only in the seller portal.\n  The `wb_fbw_*` tools are informational.\n- A WB token lives for 180 days. `wb_token_info` and the `/diagnostics` page show\n  the remaining time.\n- A `429` from WB means a rate limit, not a failure. Retry in a minute.\n\nVerified against the dev.wildberries.ru documentation August 2026.\n\n## Technical reference\n\n### Wildberries Seller API hosts\n\n| API | Base URL |\n|-----|-------------|\n| Content | content-api.wildberries.ru |\n| Marketplace (FBS/DBS/DBW) | marketplace-api.wildberries.ru |\n| Supplies (FBW) | supplies-api.wildberries.ru |\n| Statistics | statistics-api.wildberries.ru |\n| Analytics | seller-analytics-api.wildberries.ru |\n| Prices | discounts-prices-api.wildberries.ru |\n| Promotions calendar | dp-calendar-api.wildberries.ru |\n| Advert | advert-api.wildberries.ru |\n| Finance | finance-api.wildberries.ru |\n| Feedbacks + Questions | feedbacks-api.wildberries.ru |\n| Returns | returns-api.wildberries.ru |\n| Tariffs / News / Seller | common-api.wildberries.ru |\n| Buyer Chat | buyer-chat-api.wildberries.ru |\n| Documents | documents-api.wildberries.ru |\n\n### Diagnostics\n\n- **The `/diagnostics` page** — per store: token expiry and scopes, pings of all WB API\n  hosts, per-category probes, check history, and a \"check now\" button.\n- **Automatic background checks** every `HEALTH_CHECK_INTERVAL_MIN` minutes.\n- **Degradation detector** — highlights on the dashboard the tools that stopped working.\n- **MCP tools**: `wb_diagnostics`, `wb_token_info`, `wb_degradations`, `wb_api_news`.\n- **`GET /api/health`** — JSON summary for external monitoring.\n- **`POST /api/diagnostics/run`** — run a check of all stores right now.\n- **`GET /api/diagnostics/<shop_id>`** — full diagnostics of a single store.\n\n### Project layout\n\n```\nwb-mcp-server/\n├── docker-compose.yml          # port 8001, wb_data volume\n├── Dockerfile                  # python:3.12-slim\n├── pyproject.toml\n├── DEPLOY.md                   # deploying to a dedicated machine, moving the data\n├── docs/                       # client setup guides + tool reference\n└── wb_mcp/\n    ├── server.py       # MCP server: 197 tools, dispatch tables, stdio mode\n    ├── client.py       # HTTP clients for the 14 Wildberries APIs\n    ├── app.py          # FastAPI: SSE + web UI + auth + health loop\n    ├── diagnostics.py  # pings, JWT decoder, probes, API news\n    ├── settings.py     # stores and keys (Fernet)\n    ├── stats.py        # call statistics and check history (SQLite)\n    └── templates/      # PicoCSS: dashboard, diagnostics, shops\n```\n\n### Deployment\n\nMoving the server to a dedicated machine, migrating stores, setting up autostart —\nsee **[DEPLOY.md](DEPLOY.md)** (in Russian).\n\n## The same server for Ozon\n\n[**DeviceIngineering/ozon-mcp-server**](https://github.com/DeviceIngineering/ozon-mcp-server)\nis the same tool for the other marketplace (Ozon is Russia's other large marketplace):\nsame architecture, same web UI with dashboard and diagnostics, same multi-store handling via\n`shop_id`, same SSE transport, same ways of connecting clients. Once you have set up one,\nthe second one follows the same instructions; only the port and the tool set differ.\n\n|  | WB MCP Server | Ozon MCP Server |\n|---|---|---|\n| Port | 8001 | 8000 |\n| Tools | 202 | 151 |\n| API | Wildberries Seller API | Ozon Seller API + Performance API (advertising) |\n\n**They can run side by side on one machine**: different ports, different Docker volumes,\nno conflict.\n\nLiving on the same server does not hurt on the rate-limit side either: both go out through\none IP, but Wildberries and Ozon count their limits separately — they are different\nplatforms. The per-address ceiling on the number of accounts, described in the multi-store\nsection, applies within each platform on its own.\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\nWildberries changes its API constantly: endpoints are added, renamed and switched off —\nthe limits section above lists what has already been caught in practice.\nThis server is the author's working tool: more than five months of daily use across roughly\ntwenty seller accounts. It is updated **as the author needs it** — when the next change breaks\nsomething in his own stores, not on a schedule. That is why the gaps between commits can be\nlong: it means WB broke nothing in the meantime. There is no commitment on timing.\n\nIf you need a fix urgently, write to **d0371153@gmail.com**.\nIssues and pull requests are welcome and do get reviewed.\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/wb-mcp-server\n```\n",
  "bytes": 33979,
  "sha": "c1a06b1a10d206454dee9946c8e7938cc1d1749914f38fec23a78211907e52ce",
  "repo_slug": "deviceingineering/wb-mcp-server",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_deviceingineering_wb_mcp_serve_19f937c8/readme"
}