Featured

Deploy OpenClaw in 60 seconds — 20% off logoDeploy OpenClaw in 60 seconds — 20% off

Launch OpenClaw on Hostinger in about 60 seconds and keep your agent live 24/7. Our referral link gives you 20% off, no coupon code needed.

Launch on Hostinger
Run your Hermes agent on Hostinger, fully managed logoRun your Hermes agent on Hostinger, fully managed

Launch Hermes on Hostinger in one click, fully managed, no VPS knowledge needed. Use code ZACAARON10 for 10% off.

Launch on Hostinger
Crawl and scrape any site into clean data, 10% off logoCrawl and scrape any site into clean data, 10% off

Firecrawl crawls and scrapes any site into clean markdown for your agent. Get 1,000 free credits, and new users get 10% off their first purchase.

Try Firecrawl free
Your own AI agent, running 24/7 with QwikClaw logoYour own AI agent, running 24/7 with QwikClaw

QwikClaw sets up and runs an always-on OpenClaw agent for you. One click, no config files, no server setup.

Deploy now
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you
SEO data APIs for your agent, $1 free credit logoSEO data APIs for your agent, $1 free credit

DataForSEO gives your agent live access to SERP results, keyword data, backlinks, and on-page SEO data through one API. New accounts get a $1 credit, good for up to 20,000 keyword or backlink lookups.

Try DataForSEO free
Reach 47,000+ AI builders

A flat monthly placement in front of developers actively installing AI tools. No lock-in, cancel anytime.

Advertise here

Works with

Claude CodeClaude DesktopCursorVS CodeClineCodex CLIOpenClaw+ any MCP client

Install to Claude Code

This server doesn't publish a one-line install command. Follow the setup in the source repository.

Summary

Universal MCP server for the Avito API - 145 tools, safe-by-default (dry-run, idempotency).

README.md

avito-mcp

![npm version](https://www.npmjs.com/package/avito-mcp) ![npm downloads](https://www.npmjs.com/package/avito-mcp) ![CI](https://github.com/elchin92/avito-mcp/actions/workflows/ci.yml) ![Tests](./test) ![TypeScript](./tsconfig.json) ![License: MIT](LICENSE) ![Node](package.json) ![MCP](https://modelcontextprotocol.io) ![Glama score](https://glama.ai/mcp/servers/elchin92/avito-mcp) ![GitHub stars](https://github.com/elchin92/avito-mcp/stargazers) ![Avito API snapshot](./swaggers)

Give your AI agents hands and feet on Avito. An MCP server that lets Claude, Cursor, Cline and any other AI assistant do real work on Avito for you — answer customers, manage listings, run promotions, fulfil orders, analyse stats. 138 Swagger endpoint tools + 3 local/convenience tools + 7 meta tools = up to 148 MCP tools across 18 official Avito APIs. Runs locally over stdio or as a shared remote MCP over HTTP (OAuth 2.1), with a built-in webhook receiver for real-time chat events. One npx command to install.

🇷🇺 Русская версия / Russian version →

<a href="https://glama.ai/mcp/servers/elchin92/avito-mcp"><img width="380" height="200" src="https://glama.ai/mcp/servers/elchin92/avito-mcp/badges/card.svg" alt="avito-mcp MCP server" /></a>

New in v1.3.3 — a lease directory left behind by a process killed before it wrote its owner marker no longer blocks its domain forever, and rate-limit snapshots are persisted through a queue instead of an unawaited lock. See the CHANGELOG for details.

---

What it does

Avito is Russia's largest classifieds marketplace (~250M monthly visits). Selling there involves dozens of repetitive operations every day: replying in chats, refreshing listings, applying paid promotion, generating shipping labels, watching stats.

avito-mcp exposes every public Avito API as a tool your AI agent can call. Plug it into your favourite MCP client and your agent can run an entire Avito storefront — autonomously — from natural language.

  • 🔌 Universal — works with 15+ MCP clients (Claude Desktop, Cursor, Cline, Continue, Windsurf, Zed, ChatGPT, …)
  • 🔒 Local-first — stdio transport by default, your OAuth credentials never leave your machine (optional remote HTTP mode for shared/team deployments)
  • 🤖 Built for autonomy — dry-run, idempotency keys, a confirmation flow and risk-tagged tools make it safe to leave an agent running unattended
  • Zero installnpx -y avito-mcp, no clone/build, no Docker

---

Quick start (≈90 seconds)

1. Get OAuth credentials from the Avito Developer Portal: Client_id, Client_secret, and your Profile_id (your numeric account ID, shown on the same page).

2. Add this snippet to your MCP client's config (the JSON is the same for every client — only the file path differs, see Connect your AI client):

{
  "mcpServers": {
    "avito": {
      "command": "npx",
      "args": ["-y", "avito-mcp"],
      "env": {
        "Client_id": "YOUR_CLIENT_ID",
        "Client_secret": "YOUR_CLIENT_SECRET",
        "Profile_id": "YOUR_PROFILE_ID"
      }
    }
  }
}

3. Restart your client. Ask your agent:

_"What's my Avito balance and how many unread chats do I have?"_

Done. Two API calls, real answer.

---

Built for autonomous workflows

Most MCP servers are designed to be called by hand from a chat window. avito-mcp is designed to be left running — picked up by multi-agent runtimes and scheduled agents that operate without you watching.

Typical deployment patterns:

  • Reactive agent — a Claude/Cursor session permanently open, monitoring chats and replying to customers in your tone of voice. Pair with the webhook receiver to react the instant a customer writes instead of polling.
  • Cron-scheduled agent — a runtime fires up your agent every N minutes to triage new orders, top up promotion budgets, refresh stats.
  • Multi-agent swarm — separate agents for "support", "promotion", "logistics", each holding only the tools they need (via AVITO_MCP_ALLOW_TOOLS / safety modes).
  • Team / hosted deployment — one remote MCP instance behind OAuth 2.1, shared by several clients and humans.

The stdio transport keeps every credential and API response on your machine. No proxy. No SaaS in the middle.

→ See the full list of compatible runtimes at modelcontextprotocol.io/clients.

---

What's included — up to 148 tools

| Configuration | Tools visible | | -------------------------------------------------- | ---------------------------------------------- | | Default (AVITO_MCP_MODE=full_access, no opt-ins) | 144 | | + AVITO_MCP_EXPOSE_AUTH_TOOLS=1 | 147 (+3 auth) | | + AVITO_MCP_ALLOWED_UPLOAD_DIRS=… | 145 (+1 upload) | | + Both opt-ins | 148 | | AVITO_MCP_CONFIRMATION_MODE=off | −3 (hides meta_*_action) | | AVITO_MCP_MODE=read_only | 80 (only risk=read) | | AVITO_MCP_MODE=guarded | 120 (read + write; hides money/public) |

138 tools map one-to-one to bundled Swagger operations, 3 are local/convenience tools, and 7 are meta tools — meta_get_rate_limits, three meta_*_action tools for the confirmation flow, plus meta_health, meta_auth_status and meta_capabilities for introspection. The authoritative, active-policy inventory lives in dist/manifest.json (regenerate with npm run generate:manifest).

Every public endpoint from Avito's 18 OpenAPI specs is exposed. Click any group to expand.

Avito API snapshot date: 25 May 2026. The bundled swaggers (./swaggers/) reflect Avito's public API as of that date. Avito occasionally adds or revises endpoints — if you spot drift (404 on a known method, new method missing), open an issue and we'll bump the snapshot.

<details> <summary>📋 <b>Listings</b> — 11 tools (items_*)</summary>

  • items_get_items_info — list your listings (pagination, status, category filters)
  • items_get_item_info — full details of one listing
  • items_post_calls_stats — call statistics per item per day
  • items_post_vas_prices — promotion service prices for given items
  • items_post_item_stats_shallow — basic views/contacts/calls over a period
  • items_post_item_analytics — extended analytics with grouping & sorting
  • items_post_account_spendings — spend breakdown by service type
  • items_update_price ⚠️ — change listing price
  • items_put_item_vas ⚠️ — apply one paid VAS service
  • items_put_item_vas_package_v2 ⚠️ — apply a VAS package
  • items_apply_vas ⚠️ — apply multiple VAS slugs at once

</details>

<details> <summary>💬 <b>Messenger</b> — 16 tools (messenger_*)</summary>

  • messenger_get_chats_v2 — list chats (filters: unread, item_ids, chat_types)
  • messenger_get_chat_by_id_v2 — details of one chat
  • messenger_get_messages_v3 — message history in a chat (paginated)
  • messenger_get_voice_files — download URLs for voice messages
  • messenger_get_subscriptions — current webhook subscriptions
  • messenger_post_send_message ⚠️ — send a real text reply to a customer
  • messenger_post_send_image_message ⚠️ — send an image (use upload first)
  • messenger_upload_images — multipart upload, returns image_ids
  • messenger_delete_message ⚠️ — delete a message
  • messenger_chat_read — mark all unread in a chat as read
  • messenger_post_blacklist_v2 ⚠️ — block users (with reason codes)
  • messenger_post_webhook_v3 ⚠️ — subscribe to push notifications (needs public URL)
  • messenger_post_webhook_unsubscribe — unsubscribe
  • messenger_get_webhook_events — drain events received by the built-in webhook receiver
  • messenger_get_webhook_status — receiver stats: retained / total received / last received
  • messenger_register_webhook ⚠️ — subscribe the configured public URL with Avito in one call

</details>

<details> <summary>📦 <b>Orders</b> — 12 tools (orders_*)</summary>

  • orders_get_orders — list orders with filters
  • orders_get_courier_delivery_range — available courier time slots
  • orders_download_label — fetch generated label PDF
  • orders_markings ⚠️ — submit "Честный знак" (mandatory product marking)
  • orders_accept_return_order ⚠️ — choose Russian Post office for return
  • orders_apply_transition ⚠️ — change order status (confirm/ship/cancel)
  • orders_check_confirmation_code — verify pickup code
  • orders_cnc_set_details ⚠️ — click-and-collect order details
  • orders_set_courier_delivery_range ⚠️ — pick a courier time slot
  • orders_set_tracking_number ⚠️ — set carrier tracking number
  • orders_generate_labels — generate labels (≤100 orders)
  • orders_generate_labels_extended — generate labels (≤1000 orders)

</details>

<details> <summary>🔄 <b>Autoload</b> — 17 tools (autoload_*)</summary>

XML/YML/CSV feed uploads, report retrieval, ID mapping, category schema lookup. Includes both v1 (deprecated, kept for compatibility) and v2/v3.

  • autoload_upload ⚠️ — trigger a feed upload (rate-limited to 1/hour)
  • autoload_get_profile_v2, autoload_create_or_update_profile_v2 ⚠️ — manage feed profile
  • autoload_get_reports_v2 — list upload reports with pagination
  • autoload_get_report_by_id_v3, autoload_get_last_completed_report_v3 — report details
  • autoload_get_report_items_by_id, autoload_get_report_items_fees_by_id — per-item results
  • autoload_get_ad_ids_by_avito_ids, autoload_get_avito_ids_by_ad_ids — ID mapping
  • autoload_user_docs_tree, autoload_user_docs_node_fields — category schema reference
  • - 5 legacy endpoints (deprecated v1 and early v2), kept under their original names for compatibility

</details>

<details> <summary>🚚 <b>Delivery</b> — 31 tools (delivery_*) <i>· 3PL partner API</i></summary>

Avito's logistics partner API for delivery service providers. Most users will never call these — they're for shipping companies integrating with Avito Delivery. Includes both production endpoints and sandbox endpoints for partner testing. Full list in the source: src/domains/delivery.ts. </details>

<details> <summary>📈 <b>Promotion & CPA</b> — 25 tools (promotion_, cpa_, cpa_target_, cpa_auction_)</summary>

  • BBIP promotion (7) — promotion_get_bbip_forecasts_by_items_v1, promotion_create_bbip_order_for_items_v1 ⚠️, promotion_get_order_status_v1, …
  • CPA (11) — chats/calls by time, balance v2/v3, complaints, phone info — cpa_*
  • CPA target action (5) — cpa_target_get_bids, cpa_target_save_auto_bid ⚠️, cpa_target_save_manual_bid ⚠️, …
  • CPA auction (2) — cpa_auction_get_user_bids, cpa_auction_save_item_bids ⚠️

</details>

<details> <summary>👤 <b>Profile, Stock, Hierarchy, Reviews</b> — 14 tools</summary>

  • User (3) — user_get_user_info_self, user_get_user_balance, user_post_operations_history
  • Stock (2) — stock_get_stocks_info, stock_update_stocks ⚠️
  • Hierarchy (5) — sub-accounts, employees, item assignment (multi-employee setups)
  • Reviews (4) — reviews_get_reviews_v1, reviews_create_review_answer_v1 ⚠️, reviews_remove_review_answer_v1 ⚠️, reviews_get_ratings_info_v1

</details>

<details> <summary>🛠️ <b>Misc</b> — 12 tools (tariffs_, trxpromo_, calltracking_, msg_discounts_)</summary>

  • Tariffs (1) — transport-category tariff reference
  • TrxPromo (3) — transactional promotion: commissions / apply / cancel
  • CallTracking (3) — call records and audio retrieval
  • Messenger discounts (5, beta) — bulk discount campaigns in chats

</details>

<details> <summary>🔐 <b>Auth & Meta</b> — 4 tools</summary>

  • Auth (3) — auth_get_access_token (debug; the server manages tokens automatically), auth_get_access_token_authorization_code, auth_refresh_access_token_authorization_code
  • Meta (1) — meta_get_rate_limits — observe X-RateLimit-* across all domains

</details>

⚠️ marks methods that spend real money or affect live data (price changes, paid promotion, customer-facing messages, blocked users). Safe read-only smoke tools: user_get_user_balance, items_get_items_info, messenger_get_chats_v2, meta_get_rate_limits.

---

MCP resources & prompts

Beyond tools, the server exposes MCP resources (data your agent can fetch without an API call) and prompts (canned workflows that orchestrate the right tools in the right order).

Resources

| URI | Type | What's in it | | ------------------------------- | ------------------ | --------------------------------------------------------------------------------- | | avito://docs/safety | text/markdown | Safety modes + confirmation guide | | avito://manifest | application/json | Live tool catalogue (risk / domain / title / annotations) | | avito://state/config | application/json | Active config snapshot — secrets redacted | | avito://state/rate-limits | application/json | Latest X-RateLimit-* per Avito domain | | avito://state/pending-actions | application/json | Pending confirmations — subscribable, emits notifications/resources/updated | | avito://webhook/events | application/json | Buffered Avito webhook events — subscribable | | avito://swaggers/{slug} | application/json | One resource per file in swaggers/ (autocomplete via complete) |

Subscribe to avito://state/pending-actions and your client sees every create/confirm/cancel/expire in real time — perfect for UIs that want a "things waiting for human" indicator. Subscribe to avito://webhook/events and the client is notified the moment Avito delivers a new chat event.

Prompts

| Name | Args | Purpose | | -------------------------- | --------------------- | -------------------------------------------------------------------------- | | avito_daily_overview | days? (default 7) | Balance + active items + spendings (read-only, no confirmation) | | avito_check_unread_chats | limit? (default 20) | Triage unread chats; explicit "don't send / don't blacklist" guard | | avito_safety_report | — | Self-describe via state/config + manifest + docs/safety | | avito_explain_tool | tool_name | Cross-reference one tool's manifest entry + matching swagger | | avito_promote_item | item_id | Gather everything needed before a paid VAS purchase; explicit "не покупай" |

Structured tool outputs

Every tool returns structuredContent alongside the text block — clients can parse Avito responses as JSON without regex:

  • Objects → { status, ...data, http_status }; legacy/API-owned status is preserved, while http_status is always the HTTP code
  • Arrays → { status, http_status, items, count }
  • Binary (PDF labels, audio) → { status, http_status, mimeType, sizeBytes, base64 }; text content also keeps base64 for pre-2025-06-18 MCP clients
  • Errors → { error: { type, message, retryable, retryAfter?, httpStatus? }, error_kind } with isError: true — see Structured error taxonomy

MCP logging

Selected pino events (mode changes, hidden-tool reports, confirmation lifecycle, rate-limit warnings) are forwarded to the client as notifications/message with logger: "avito-mcp", with sensitive fields censored. Pino → stderr is preserved.

How a client asks for them depends on the protocol revision the connection speaks:

  • 2025-11-25logging/setLevel sets a threshold for the whole connection, as before.
  • 2026-07-28logging/setLevel no longer exists. The level is declared per request in _meta["io.modelcontextprotocol/logLevel"]; the notifications arrive on that request's own response stream, a request that declares no level receives none at all, and an unrecognised level is answered -32602.

Protocol revisions

AVITO_MCP_PROTOCOL_ERA selects which revisions this process serves: legacy (the default — 2025-11-25 only, byte-for-byte the 1.3.x behaviour), dual (both), modern (2026-07-28 only). Nothing changes for an existing client unless you set it.

| | 2025-11-25 | 2026-07-28 | | ----------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Handshake | initialize | none — server/discover, plus a per-request _meta envelope | | Watching a resource | resources/subscribenotifications/resources/updated | subscriptions/listen with resourceSubscriptions: [...]; the stream opens with notifications/subscriptions/acknowledged naming what it will actually deliver | | Subscribable URIs | avito://state/pending-actions, avito://webhook/events | the same two | | tools / prompts / resources listChanged | advertised true | advertised false | | tools/list and the other list verbs | one answer (≈225 KB); a cursor is ignored | paginated — a page of at most 48 KiB of descriptors plus nextCursor; end of list is the ABSENCE of that field, and a cursor this server did not mint is -32602 Invalid cursor | | Prompt arguments (prompts/get) | any string; a blank required one renders a "…is required" stub as a SUCCESSFUL result, and days/limit go through parseInt(...) || default | validatedtool_name and item_id are allowlists, days/limit are bounded integers, and every value is swept for control characters and bidi/zero-width formatting before it enters prompt text; anything else is -32602 | | Cancelling a call | notifications/cancelled | closing the response stream — the outgoing Avito call is aborted and its idempotency lease and rate-limiter slot are released |

stdio: the era is decided once per connection. On stdio there is no header layer, so a connection's revision is read from its FIRST classifiable message and held for the life of that connection — the rule and the code are the SDK's (serveStdio, classifyOpeningMessage). A 2026 client whose first frame carries no _meta envelope is therefore served as a 2025 client until it reconnects, even if every later frame carries one. Under AVITO_MCP_PROTOCOL_ERA=dual the server writes one protocol era pinned to legacy line to stderr when this happens, naming the method that pinned it; grep for it while rolling dual out. The fix is on the client side: send io.modelcontextprotocol/protocolVersion in params._meta on the first message. HTTP is unaffected — there every request is classified on its own. Full rationale, and why we do not fork the SDK entry to change it, in docs/adr/0001-protocol-era-limitations.md.

The listChanged difference is deliberate. This server's tool, prompt and resource sets are fixed for the life of the process (membership is decided once, at registration, by the active safety policy), so it never emits a list_changed notification on either revision. On 2025-11-25 the advertised true is inert, and it is kept for wire compatibility with 1.3.x. On 2026-07-28 it is not inert: subscriptions/listen narrows a client's requested filter against exactly these bits, so advertising true would acknowledge a subscription to updates that never come, and the client would wait instead of polling. false tells it the truth immediately.

Tool input schemas

inputSchema and outputSchema are emitted in JSON Schema draft-07 ($schema: "http://json-schema.org/draft-07/schema#") on both revisions. 2026-07-28 relaxes the constraint to full JSON Schema 2020-12 but does not require it, and for this catalogue the two dialects render identical bodies apart from that one string — while meta_capabilities.schemaHash is computed over the schemas as emitted, so moving the dialect would break every consumer watching that hash for drift. No schema contains a $ref to a network URI.

---

Universal safety primitives

Opt-in primitives that make the package safe to use in any automation context — manual chat, scheduled jobs, multi-agent runtimes, server farms — without committing to a specific orchestrator or backend.

Dry-run

Every destructive tool (risk: write | money | public) accepts an optional dryRun: boolean parameter. When true, the tool returns a structured preview of the HTTP request it _would have_ made — no call to Avito. Useful both for human inspection ("what is the agent about to do?") and for agents that want to think before acting.

{
  "name": "items_update_price",
  "arguments": { "item_id": 12345, "price": 1400, "dryRun": true }
}

structuredContent: { dryRun: true, operation: { tool, method, path, ... }, request_preview: { ... } } and fetch is never called.

You can flip the default for the entire server: AVITO_MCP_DRY_RUN_DEFAULT=true or --dry-run. Then every destructive tool short-circuits unless the agent explicitly passes dryRun: false.

Idempotency

Every destructive tool also accepts an optional idempotencyKey: string. The server keeps a durable account-scoped ledger keyed by bounded SHA-256 fingerprints of (tool, key) plus hash(args); long keys are never retained verbatim:

  • First call with a key: executes, caches the result.
  • Repeat call with the same key + identical args within TTL: returns the cached result, marked structuredContent.idempotent_replay: true. No second HTTP call.
  • Repeat call with the same key + different args: returns a structured IdempotencyConflictError (the dedupe contract was violated).

The reservation is written before the upstream mutation. A completed result replays after restart and across stdio processes; an abandoned reservation fails closed for remote reconciliation instead of repeating a possibly charged action. TTL via AVITO_MCP_IDEMPOTENCY_TTL_SEC (default 1 hour), storage via AVITO_MCP_RUNTIME_STATE_DIR.

Structured error taxonomy

All errors return both human text and a machine envelope:

{
  "isError": true,
  "structuredContent": {
    "error": {
      "type": "AVITO_RATE_LIMIT",
      "message": "Avito API 429 for POST ...",
      "retryable": true,
      "retryAfter": 60,
      "httpStatus": 429
    }
  }
}

typeAVITO_BAD_REQUEST | AVITO_UNAUTHORIZED | AVITO_FORBIDDEN | AVITO_NOT_FOUND | AVITO_RATE_LIMIT | AVITO_SERVER_ERROR | AVITO_API_ERROR | NETWORK_ERROR | TIMEOUT | CONFIG_ERROR | INTERNAL_ERROR.

Agents can branch on retryable and retryAfter programmatically — no regex over English text.

Health / auth / capabilities meta-tools

| Tool | What it returns | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | meta_health | Overall health snapshot: version, uptime, capabilities, safety mode, counters (pending actions, idempotency entries, rate-limit snapshots) | | meta_auth_status | OAuth token _metadata_ only — tokenPresent, expiresInSec, last error. The token value is NEVER exposed. With probe: true will attempt a refresh. | | meta_capabilities | Machine-readable config: mode, allow/deny counts, feature flags (dryRun, idempotency, confirmation, hardConfirmation, fileUploads, sensitiveAuthTools) |

All three have strict outputSchema (zod) — clients can validate against the contract.

Cross-process token lock

If you run multiple avito-mcp processes against the same token file (cron + chat + CLI), they never hit Avito's /token endpoint in parallel. The first to acquire {tokenFile}.lock refreshes; the rest wait, then read the freshly-refreshed token from disk. Ownership-checked leases reclaim dead/corrupt locks without stealing a live process's lock. Tunable via AVITO_MCP_TOKEN_LOCK_TIMEOUT_MS (default 30s).

CLI flags

Convenience shortcuts that translate to env vars (env wins if both set):

avito-mcp --readonly             # AVITO_MCP_MODE=read_only
avito-mcp --guarded              # AVITO_MCP_MODE=guarded
avito-mcp --dry-run              # AVITO_MCP_DRY_RUN_DEFAULT=true
avito-mcp --no-confirmation      # AVITO_MCP_CONFIRMATION_MODE=off
avito-mcp --http | --both        # AVITO_MCP_TRANSPORT=http | both
avito-mcp --health               # print JSON health snapshot and exit

--health does not start the server; it is a local configuration/capability diagnostic, not a liveness probe for an already running process. The bundled Docker image checks PID 1 in stdio mode and the live /readyz endpoint in HTTP/webhook mode. For Kubernetes or a supervisor, probe:

/readyz returns 200 only while the listener is open, HTTP-mode Avito credentials are complete, the token and runtime-state directories are writable, the OAuth store lease is healthy, and configured webhook persistence has not failed. Its public body remains only { "ok": boolean }.

httpGet:
  path: /readyz
  port: 3000

---

Remote MCP over HTTP (OAuth 2.1)

By default avito-mcp speaks stdio — perfect for a local client. It can also run as a remote MCP server: the same 148 tools served over the network via Streamable HTTP, so a hosted agent, a team, or a phone-based client can connect to one shared instance. Access is gated by OAuth 2.1 (authorization-code + PKCE + Dynamic Client Registration), with a human-in-the-loop consent screen.

Turn it on

AVITO_MCP_TRANSPORT=http            # stdio (default) | http | both   (CLI: --http)
AVITO_MCP_HTTP_HOST=127.0.0.1       # loopback by default; TLS is the proxy's job
AVITO_MCP_HTTP_PORT=3000
AVITO_MCP_HTTP_PUBLIC_URL=https://mcp.example.com   # your public TLS domain, NO trailing slash
AVITO_MCP_HTTP_AUTH=oauth           # oauth (default) | bearer | none
AVITO_MCP_OAUTH_OWNER_PASSWORD=…    # REQUIRED, random, at least 32 bytes
# Client_id / Client_secret / Profile_id as usual (the Avito credentials the remote server acts with)

both runs stdio and HTTP at once — handy when one process serves a local client and a remote one simultaneously.

How the OAuth flow works

  1. A client hits /.well-known/oauth-protected-resource/mcp (the RFC 9728 path-suffixed URL the 401's WWW-Authenticate header points to), discovers the authorization server, and reads /.well-known/oauth-authorization-server.
  2. The client self-registers via Dynamic Client Registration (POST /register) — no manual client setup.
  3. It runs authorization-code + PKCE: opens /authorize in a browser.
  4. A human approves at /authorize by entering AVITO_MCP_OAUTH_OWNER_PASSWORD. This is the gate — without the owner password no token is ever issued, and the approval endpoint is rate-limited against brute force.
  5. The client exchanges the code at /token for a bearer token (TTL AVITO_MCP_OAUTH_TOKEN_TTL_SEC, default 3600s), and that token guards every /mcp request.

Tokens are accepted only with the exact avito:mcp scope and this deployment's exact resource URL; each MCP session is bound to the OAuth principal that initialized it. Consent transactions and authorization codes are one-time and short-lived.

| Endpoint | Purpose | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | /mcp | Streamable HTTP MCP transport (the tools) | | /.well-known/oauth-authorization-server | OAuth 2.1 AS metadata | | /.well-known/oauth-protected-resource/mcp | Resource-server metadata for /mcp (RFC 9728 path-suffixed) | | /authorize | Consent screen — human enters the owner password (rate-limited) | | /token | Authorization-code → bearer token exchange | | /register | Dynamic Client Registration (DCR) — redirect_uris must be https, or http on a loopback address, with no fragment | | /revoke | Token revocation (RFC 7009) | | /healthz | Liveness probe (no auth — answers only {ok, name, version}) | | /readyz | Readiness probe (no auth — answers only {ok}) |

All HTTP / OAuth env vars

| Variable | Default | Meaning | | ------------------------------------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | AVITO_MCP_TRANSPORT | stdio | stdio \| http \| both (CLI flag --http) | | AVITO_MCP_HTTP_HOST | 127.0.0.1 | Bind address — keep it loopback behind a proxy | | AVITO_MCP_HTTP_PORT | 3000 | Listen port | | AVITO_MCP_HTTP_PUBLIC_URL | — | Public TLS base used to build OAuth issuer / resource metadata. No trailing slash. Must be https in oauth mode (loopback excepted). Changing it changes the OAuth issuer identifier, i.e. it is a different authorization server: registered clients and issued tokens are dropped and every client re-registers | | AVITO_MCP_HTTP_AUTH | oauth | oauth \| bearer \| none | | AVITO_MCP_OAUTH_OWNER_PASSWORD | — | Required in oauth mode; at least 32 bytes. Gates /authorize — the only secret that mints a token. | | AVITO_MCP_OAUTH_TOKEN_TTL_SEC | 3600 | Issued bearer-token lifetime | | AVITO_MCP_OAUTH_STORE_FILE | — | Optional durable token/client store. It has an exclusive process lease: one running server per file | | AVITO_MCP_HTTP_AUTH_TOKEN | — | bearer mode: shared secret(s), comma-separated; each at least 32 bytes | | AVITO_MCP_HTTP_ALLOW_INSECURE_PUBLIC_URL | 0 | Development only. Allow a cleartext http: public URL on a routable host in oauth mode. The SDK additionally requires MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL=true | | AVITO_MCP_HTTP_ALLOW_NO_AUTH | 0 | Allow auth=none on a non-loopback host (discouraged) | | AVITO_MCP_HTTP_ALLOWED_HOSTS | derived | CSV — DNS-rebinding protection (accepted Host values). Derived from public URL + bind address; an under-specified wildcard bind fails startup | | AVITO_MCP_HTTP_ALLOWED_ORIGINS | derived | CSV — accepted Origin values. Same fail-closed derivation as above | | AVITO_MCP_HTTP_MAX_SESSIONS | 100 | Legacy era only. Max concurrent Streamable HTTP sessions — initialize beyond it → 503 | | AVITO_MCP_HTTP_SESSION_IDLE_SEC | 1800 | Legacy era only. Sessions idle longer than this are reaped (clients that vanished without DELETE) | | AVITO_MCP_HTTP_MAX_INFLIGHT | 64 | 2026 era. Concurrent /mcp exchanges, counting open subscription streams; beyond it → 503 + Retry-After | | AVITO_MCP_HTTP_MAX_STREAMS | 32 | 2026 era. How many of those may be long-lived subscriptions/listen streams, so streams cannot starve ordinary tool calls |

Security model. Node binds 127.0.0.1 by default and speaks plain HTTP. TLS is terminated by a reverse proxy (nginx / Caddy) on your domain, which forwards to http://127.0.0.1:3000. Never expose port 3000 directly to the internet. Host/Origin validation protects MCP and OAuth routes. auth=none on a public host is refused unless you set AVITO_MCP_HTTP_ALLOW_NO_AUTH=1.

v1.2.0 migration: OAuth artifacts issued without the exact scope/resource binding are intentionally rejected. Reauthorize affected clients. A configured AVITO_MCP_OAUTH_STORE_FILE is now single-writer; give each concurrent server its own file.

Reverse-proxy snippets (terminate TLS for https://mcp.example.com)

Both proxy the MCP endpoint, the OAuth discovery/flow endpoints, and the webhook path, and preserve the Host header (the OAuth metadata is built from it).

<details open> <summary><b>nginx</b></summary>

server {
    listen 443 ssl;
    server_name mcp.example.com;

    ssl_certificate     /etc/letsencrypt/live/mcp.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem;

    # MCP transport + OAuth (discovery, authorize, token, register, revoke) + webhook receiver.
    location ~ ^/(mcp|\.well-known/oauth-authorization-server|\.well-known/oauth-protected-resource|authorize|token|register|revoke|avito/webhook|healthz|readyz) {
        proxy_pass         http://127.0.0.1:3000;
        proxy_http_version 1.1;

        proxy_set_header   Host              $host;          # preserve Host — OAuth metadata depends on it
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;

        # Streamable HTTP keeps long-lived responses open:
        proxy_buffering    off;
        proxy_read_timeout 3600s;
    }
}

</details>

<details> <summary><b>Caddy</b></summary>

mcp.example.com {
    # Caddy obtains and renews the TLS cert automatically.
    # Caddy preserves the Host header by default (no header_up needed).
    reverse_proxy /mcp* http://127.0.0.1:3000
    reverse_proxy /.well-known/oauth-authorization-server* http://127.0.0.1:3000
    reverse_proxy /.well-known/oauth-protected-resource*   http://127.0.0.1:3000
    reverse_proxy /authorize* http://127.0.0.1:3000
    reverse_proxy /token*     http://127.0.0.1:3000
    reverse_proxy /register*  http://127.0.0.1:3000
    reverse_proxy /revoke*    http://127.0.0.1:3000
    reverse_proxy /avito/webhook* http://127.0.0.1:3000
    reverse_proxy /healthz* http://127.0.0.1:3000
    reverse_proxy /readyz*  http://127.0.0.1:3000
}

</details>

Quicker: bearer mode

If you control both ends and don't need the full OAuth dance, set AVITO_MCP_HTTP_AUTH=bearer and a shared secret:

AVITO_MCP_TRANSPORT=http
AVITO_MCP_HTTP_PUBLIC_URL=https://mcp.example.com
AVITO_MCP_HTTP_AUTH=bearer
AVITO_MCP_HTTP_AUTH_TOKEN=long-random-secret,another-secret   # one or more, comma-separated

Clients then send Authorization: Bearer long-random-secret to /mcp. The same reverse-proxy config applies.

bearer and none do not claim conformance with the MCP authorization specification. They are a shared-secret door and an open door, for deployments that control both ends. Neither publishes RFC 9728 protected-resource metadata, neither runs an authorization server, and the 401 they return is a bare Bearer realm="avito-mcp" with no resource_metadata — so an MCP client built for revision 2026-07-28 cannot discover where to authorize and will not complete a flow it has to start itself. Use oauth for MCP clients. bearer is for a caller you configure by hand with a secret you generated.

---

Avito webhook receiver

Polling messenger_get_chats_v2 works, but for real-time reactions (reply the instant a customer writes) Avito can push events to you. The server ships a built-in receiver: point Avito at a secret URL and every event is buffered for your agent to read.

This works even in pure stdio mode — Avito only needs a public URL to POST to; your MCP client never touches it. (If AVITO_MCP_TRANSPORT=stdio and a webhook secret is set, the server still starts a tiny HTTP listener just for the receiver.)

Turn it on

AVITO_MCP_WEBHOOK_SECRET=…                              # random, at least 32 bytes
AVITO_MCP_WEBHOOK_PUBLIC_URL=https://mcp.example.com    # public base Avito POSTs to (defaults to the HTTP public URL)
# AVITO_MCP_WEBHOOK_PATH=/avito/webhook                 # default
# AVITO_MCP_WEBHOOK_BUFFER=100                          # ring-buffer size (events kept in memory)
# AVITO_MCP_WEBHOOK_LOG_FILE=/var/lib/avito-mcp/webhook-events.jsonl

Avito then delivers to:

POST {AVITO_MCP_WEBHOOK_PUBLIC_URL}{AVITO_MCP_WEBHOOK_PATH}/{AVITO_MCP_WEBHOOK_SECRET}
  → 200 {"ok":true}      (answered in well under Avito's 2-second deadline)

The secret is part of the path, so the URL is unguessable — that's the auth. Generate at least 32 random bytes. The URL must be public HTTPS. Both webhook registration tools accept only the exact receiver URL derived from operator configuration; an agent cannot redirect future messages to an arbitrary host. Their dry-run output redacts the secret URL.

| Variable | Default | Meaning | | ------------------------------ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | | AVITO_MCP_WEBHOOK_SECRET | — | Enables the receiver; random path segment of at least 32 bytes. Required — without it the receiver stays disabled | | AVITO_MCP_WEBHOOK_ENABLED | 1 when a secret is set | Explicit toggle: set 0 to disable without unsetting the secret. 1 without a secret does nothing (warned at startup) | | AVITO_MCP_WEBHOOK_PUBLIC_URL | (HTTP public URL) | Public base Avito POSTs to | | AVITO_MCP_WEBHOOK_PATH | /avito/webhook | Path prefix before the secret segment | | AVITO_MCP_WEBHOOK_BUFFER | 100 | In-memory ring-buffer size | | AVITO_MCP_WEBHOOK_LOG_FILE | — | Optional 0600 JSONL containing normalized metadata only (no message text/raw payload); rotates at 10 MiB and retains one backup |

Consuming events

| Surface | What it gives you | | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | messenger_get_webhook_events (tool, read) | Drain buffered events — filter by chat_id, since, limit | | messenger_get_webhook_status (tool, read) | Receiver stats: retained / total received / last received at / buffer size | | messenger_register_webhook (tool, ⚠️ public) | Subscribe only the operator-configured public URL with Avito; confirmed by default | | avito://webhook/events (resource, subscribable) | The same events as an MCP resource; resources/subscribe (2025-11-25) or subscriptions/listen (2026-07-28) for live push to your client |

A typical loop: subscribe to avito://webhook/events (resources/subscribe on 2025-11-25, subscriptions/listen on 2026-07-28), and on each notifications/resources/updated read the new event, draft a reply, and (after confirmation) send it with messenger_post_send_message.

---

Connect your AI client

The JSON snippet from the Quick Start section above works in every MCP-compatible client — only the path to the config file changes. Pick yours below:

<details> <summary><b>Claude Desktop</b> (macOS / Windows / Linux)</summary>

| OS | Path | | ------- | ----------------------------------------------------------------- | | macOS | ~/Library/Application Support/Claude/claude_desktop_config.json | | Windows | %APPDATA%\Claude\claude_desktop_config.json | | Linux | ~/.config/Claude/claude_desktop_config.json |

Create the file if it doesn't exist; otherwise add the avito entry to the existing mcpServers block. Fully quit Claude Desktop (system tray) and reopen — a 🔌 avito indicator should appear at the bottom of the chat.

Logs: ~/Library/Logs/Claude/mcp-server-avito.log (macOS). </details>

<details> <summary><b>Claude Code</b> (CLI)</summary>

Easiest — one command:

claude mcp add avito npx -y avito-mcp \
  -e Client_id=YOUR_CLIENT_ID \
  -e Client_secret=YOUR_CLIENT_SECRET \
  -e Profile_id=YOUR_PROFILE_ID

Or add .mcp.json to your project root (use the JSON from Quick Start, plus "type": "stdio"). Verify with claude mcp list. </details>

<details> <summary><b>Cursor</b></summary>

Path: ~/.cursor/mcp.json (global) or <project>/.cursor/mcp.json (per-project). Use the Quick Start JSON as-is. Reload window after saving (Cmd/Ctrl + Shift + P → "Reload Window"). </details>

<details> <summary><b>ChatGPT Desktop</b> (Connectors / MCP)</summary>

OpenAI's Desktop app added MCP server support via the Connectors UI. Settings → Connectors → Add custom MCP server → fill in:

  • Name: Avito
  • Type: stdio
  • Command: npx
  • Arguments: -y avito-mcp
  • Environment variables: Client_id, Client_secret, Profile_id

</details>

<details> <summary><b>Windsurf</b> (Codeium)</summary>

Path: ~/.codeium/windsurf/mcp_config.json. Use the Quick Start JSON. Alternative: Settings → Cascade → MCP Servers → Add Server (UI). </details>

<details> <summary><b>Cline</b> (VS Code extension)</summary>

In VS Code: Cline icon → ⚙️ → MCP Servers → Edit cline_mcp_settings.json.

| OS | Path | | ------- | --------------------------------------------------------------------------------------------------------------- | | macOS | ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json | | Windows | %APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json | | Linux | ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json |

Use the Quick Start JSON. Cline auto-reloads without VS Code restart. </details>

<details> <summary><b>Continue</b> (VS Code / JetBrains)</summary>

Add to ~/.continue/config.json:

{
  "experimental": {
    "modelContextProtocolServers": [
      {
        "transport": {
          "type": "stdio",
          "command": "npx",
          "args": ["-y", "avito-mcp"],
          "env": { "Client_id": "...", "Client_secret": "...", "Profile_id": "..." }
        }
      }
    ]
  }
}

</details>

<details> <summary><b>Zed</b></summary>

Open Settings (Cmd+,), find the context_servers block:

{
  "context_servers": {
    "avito": {
      "command": {
        "path": "npx",
        "args": ["-y", "avito-mcp"],
        "env": { "Client_id": "...", "Client_secret": "...", "Profile_id": "..." }
      }
    }
  }
}

</details>

<details> <summary><b>VS Code</b> (GitHub Copilot Chat with MCP)</summary>

Microsoft added MCP support to Copilot Chat in 2025. Create .vscode/mcp.json in your workspace or use the Command Palette → "MCP: Add Server". Same Quick Start JSON. </details>

<details> <summary><b>Codex CLI</b> (OpenAI)</summary>

OpenAI's CLI assistant supports MCP via ~/.codex/config.toml:

[mcp_servers.avito]
command = "npx"
args = ["-y", "avito-mcp"]
env = { Client_id = "...", Client_secret = "...", Profile_id = "..." }

</details>

<details> <summary><b>JetBrains AI Assistant</b></summary>

Settings → Tools → AI Assistant → MCP → Add server. Fill the same fields (command npx, args -y avito-mcp, env variables). Applies to IntelliJ IDEA, PyCharm, WebStorm, GoLand, Rider. </details>

<details> <summary><b>Goose</b> (Block)</summary>

Block's open-source CLI agent. Add via goose configure → MCP server → paste the Quick Start JSON. Config lives in ~/.config/goose/config.yaml. </details>

<details> <summary><b>Roo Code / Kilo Code</b> (Cline forks, VS Code)</summary>

Both are forks of Cline and use the same config format and path patterns — replace saoudrizwan.claude-dev in the path with the fork's extension ID (rooveterinaryinc.roo-cline or kilocode.kilo-code). JSON is identical. </details>

<details> <summary><b>LibreChat</b> (self-hosted ChatGPT alternative)</summary>

Edit librechat.yaml:

mcpServers:
  avito:
    type: stdio
    command: npx
    args: ['-y', 'avito-mcp']
    env:
      Client_id: '...'
      Client_secret: '...'
      Profile_id: '...'

</details>

<details> <summary><b>Cherry Studio</b></summary>

Settings → MCP Servers → Add. UI fields: name avito, command npx, args -y avito-mcp, env vars same as above. </details>

<details> <summary><b>Any other MCP client</b></summary>

The server speaks stock stdio MCP. Universal parameters:

  • command: npx
  • args: ["-y", "avito-mcp"]
  • env: { Client_id, Client_secret, Profile_id }
  • transport: stdio

Browse the MCP clients directory for new ones. </details>

---

Example prompts

Drop these into your AI client to see what's possible:

📊 Analyse

  • _"What's my Avito balance and how much did I spend on promotion this month?"_
  • _"Top 10 listings by contacts last week — table with views/contacts/conversion."_
  • _"Find listings whose calls dropped 50%+ compared to the previous week."_

💬 Communicate

  • _"Show me unread chats from the last 24 hours and reply with: 'Hi! Yes, still available, where would you like delivery?'"_
  • _"Read the full conversation in chat X and suggest the best next reply in my tone."_

💰 Promote

  • _"Forecast a 1000₽ BBIP boost on item 12345 — is it worth it?"_
  • _"Set a manual CPA bid of 500₽ on top-10 listings in category 'Electronics'."_

📦 Fulfil

  • _"List all orders with status ready_to_ship and generate labels in a single PDF."_
  • _"For order ABCD, find an available courier slot tomorrow morning."_

🤖 Automate

  • _"Every weekday at 9am, send me Telegram with: balance, new orders count, unread chats count, top promotion spends."_
  • _"If any chat has been unread for 6+ hours, draft a reply and ping me to approve."_

---

What's NOT supported

Avito provides separate APIs for the following verticals — their swagger specs are not bundled:

| Category | Where to find | | -------------------------------------- | ------------------------------------------------------------------------------------------------ | | 🏷️ Auction | Avito Auction API | | 🤖 Auto-strategies (automated bidding) | Avito Autostrategy API | | 🚗 Autoteka (vehicle history) | Avito Autoteka API | | 💼 Jobs / Vacancies | Avito Jobs API | | 📊 Real-estate reports | Avito Realty Reports API | | 🏠 Short-term rent | Avito STR API |

Also out of scope: the authorization_code OAuth flow against Avito itself (no public redirect URI on a local CLI) and the Avito sandbox (Avito issues no sandbox credentials — every call hits production).

---

Security

  • Local stdio by default — no proxy, no remote endpoints, no telemetry. The optional remote HTTP mode is opt-in (AVITO_MCP_TRANSPORT=http), binds loopback, and is guarded by OAuth 2.1 (or a bearer secret) behind your own TLS proxy, with DNS-rebinding protection on by default.
  • Credentials live in your MCP client's env block or local .env. They're never sent anywhere except api.avito.ru.
  • OAuth tokens are account-bound and cached in a per-user state directory (0600; parent 0700). A cache created for another API origin/client/profile, or a legacy unbound record, is ignored:
  • Linux: $XDG_STATE_HOME/avito-mcp/token.json (≈ ~/.local/state/avito-mcp/token.json)
  • macOS: ~/Library/Application Support/avito-mcp/token.json
  • Windows: %APPDATA%\avito-mcp\token.json
  • Override with AVITO_TOKEN_FILE. Delete the file to force a refresh.
  • Three-layer safety model (every layer opt-in via env vars; the defaults keep trivial reads frictionless but harden everything destructive):
  • AVITO_MCP_MODE (read_only / guarded / full_access) — registration-time gate. Hidden tools never appear in tools/list. read_only exposes 80 tools, guarded exposes 120, and full_access exposes 145 ordinary tools (plus the 3 explicitly opt-in sensitive tools).
  • AVITO_MCP_ALLOW_TOOLS / AVITO_MCP_DENY_TOOLS — per-tool gating. Deny wins over allow.
  • AVITO_MCP_CONFIRMATION_MODE (off / money_public (default) / all_destructive) — runtime gate. Destructive tools return {requires_confirmation: true, confirmation_id: ...}; pending state is durable, account-scoped, TTL'd (default 15 min), and one-shot. AVITO_MCP_CONFIRMATION_SECRET enables hard confirmation; AVITO_MCP_APPROVAL_MODE=external additionally requires that secret-provider identity to differ from the initiator.
  • AVITO_MCP_EXPOSE_AUTH_TOOLS (default: 0) — auth_* tools return OAuth tokens; classed as sensitive and hidden by default even in full_access.
  • AVITO_MCP_ALLOWED_UPLOAD_DIRSmessenger_upload_images reads files from disk; without an explicit directory allowlist it doesn't register at all. It opens and validates the same file descriptor, rejects parent/final symlink races, enforces file-count and aggregate-size limits, checks jpg/jpeg/png/webp magic bytes, and redacts local paths from errors.
  • Every tool is tagged with one of five risks (sensitive: 3 / read: 80 / write: 40 / money: 9 / public: 16), exposed as MCP ToolAnnotations and _meta.risk, and listed in dist/manifest.json. The default money_public confirmation mode covers all externally visible, paid, and irreversible public actions, including webhook registration, blacklist changes, and CPA complaints.
  • Environment parsing is fail-fast: unknown enum/boolean values, partial numbers, weak remote secrets, and out-of-range limits stop startup instead of silently falling back. AVITO_MCP_MAX_BINARY_MB is retained for compatibility but caps every HTTP response body, including JSON/text.
  • See docs/safety.md for ready-to-paste configs (analytics-only, customer-support with confirmation, listings-only, full admin) and a frank discussion of what the confirmation flow is and isn't (it's a server-side two-step + audit layer, not a cryptographic human-approval mechanism — unless you add the hard-confirmation secret).
  • Upgrading a live deployment? docs/adr/0002-canary-protocol.md is the canary protocol: five read-only tools to re-run against the live account after every risky rollout, with AVITO_MCP_MODE=read_only set.
  • Treat every tool marked environment: prod as production. Writes may spend real m

See related servers & alternatives →

Related MCP servers

Browse all →

Related guides

Hand-picked reading to help you choose and use Other servers.