Skip to main content
Glama
README.md
# avito-mcp

**English** · [Русский](README.ru.md)

Buyer-side MCP server for **Avito**, focused on **used PC components in
Yekaterinburg**. Search live listings, inspect a full card, and compare exact
offers. No Avito account or API key is required.

It reads the public storefront in a normal Playwright Chromium session. It is
unofficial and does not use Avito's seller/business API.

**Experimental:** live search and card parsing were observed, but Avito blocked
the subsequent MCP card call during validation. Full live coverage is incomplete;
see [the actual checks and limitations](VALIDATION.md).

## What it gets right

- Search results can include other cities. `local_only=true` (default) checks
  each listing's URL city and excludes those offers. The page heading alone is
  not proof that every result is local.
- `search_used_parts` opens full cards and only returns an explicit **Б/у** in
  the structured condition field. It does not infer used condition from a
  snippet, and does not pretend an ignored native condition parameter worked.
- A used part can be broken. Repair, mining, defect and testing mentions carry
  excerpts and are **text signals**, not a diagnosis or a fraud score. Negated
  phrases such as “без артефактов” can also produce a mention.
- Prices are asking prices, excluding unknown shipping/fees. From-prices and
  instalments are not passed off as a full item price. Missing values stay null.
- Seller ratings are labelled as seller ratings, not hardware-model ratings.
- Native price, seller-type and sort controls are checked in the returned page;
  ignored or changed filters fail explicitly. Requested price bounds are also
  applied locally to parsed prices.
- Every network result includes its source URL and `fetched_at`. Unexpected
  layouts fail instead of returning a fake empty result.

## Tools

| Tool | Purpose |
|---|---|
| `search_listings(query, city, page, sort, price_min, price_max, seller_type, local_only, limit)` | One page of computer-goods listings (new/used/unknown), asking prices, seller ratings, snippets and links. Sort: `relevance`, `price_asc`, `price_desc`, `newest`. Seller: `all`, `private`, `company`. |
| `search_used_parts(query, city, page, sort, price_min, price_max, seller_type, local_only, offset, inspect_limit)` | Search plus **up to 5 full card reads**, default 3. Returns only seller-declared used items; reports new/unknown/unavailable exclusions. Continue with `next_offset` on the same page, then `next_page`. |
| `get_listing(url)` | Full title, price, structured model/condition/specs, description, seller rating/type, date text, views and quoted hardware signals. |
| `compare_listings(urls)` | Refresh up to five exact listings side by side. Does not calculate a misleading “market value” from unrelated parts. |
| `get_status()` | Local default city, saved-session presence and remaining cooldown; no network request or credentials. |

Example requests:

```text
Find used RTX 3060 12GB in Yekaterinburg under 25,000 rubles.
Check the full cards; separate 3060 Ti and broken/repair offers.

Compare these two listing URLs: exact model, memory, seller, condition,
price and what the seller says about repairs or tests.
```

## Install

Python **3.11+**, Playwright Chromium and its system libraries are required.
Docker and an account are not required. A display is needed only for the
optional manual browser window.

With uv:

```bash
uvx --from playwright playwright install chromium
codex mcp add avito --env AVITO_CITY=ekaterinburg -- uvx --from git+https://github.com/SZhukovWork/avito-mcp avito-mcp
```

The Chromium version must match the installed Playwright package. If the
server reports a missing browser, use the checkout instructions below to
install it with that exact Python environment. On a minimal Debian/Ubuntu
machine, `python -m playwright install --with-deps chromium` installs OS
dependencies as well (requires the appropriate system privileges).

From a checkout:

```bash
python3 -m venv .venv
.venv/bin/python -m pip install -e '.[dev]'
.venv/bin/python -m playwright install chromium
.venv/bin/avito-mcp
```

Any stdio MCP client:

```json
{
  "mcpServers": {
    "avito": {
      "command": "/absolute/path/to/.venv/bin/avito-mcp",
      "env": {"AVITO_CITY": "ekaterinburg"}
    }
  }
}
```

For Codex, allow enough time for a bounded used-parts scan:

```toml
[mcp_servers.avito]
command = "/absolute/path/to/.venv/bin/avito-mcp"
startup_timeout_sec = 30
tool_timeout_sec = 420

[mcp_servers.avito.env]
AVITO_CITY = "ekaterinburg"
```

## Configuration

| Variable | Default | Meaning |
|---|---|---|
| `AVITO_CITY` | `ekaterinburg` | Avito city slug. `Екатеринбург` and `Екб` also work. Per-call `city` overrides it. |
| `AVITO_MIN_INTERVAL` | `4` | Seconds between page reads in this process; minimum 3. Parallel tools share one browser lock. |
| `AVITO_HEADLESS` | `1` | `0` shows the server's browser; requires a desktop display. |
| `AVITO_PROXY` | unset | Optional Playwright proxy server URL. No proxy rotation. |
| `AVITO_TIMEOUT` | `45` | Navigation timeout in seconds (10–90), plus up to 15 seconds for rendering. |
| `AVITO_IDLE_TIMEOUT` | `300` | Close Chromium after this many idle seconds (minimum 30). |
| `AVITO_COOLDOWN` | `600` | Pause after an unresolved access challenge (minimum 60), persisted across restarts. |
| `AVITO_CACHE_DIR` | `$XDG_CACHE_HOME/avito-mcp` or `~/.cache/avito-mcp` | Browser state and pause marker. Created directory mode 0700; session files 0600. |

The browser launches lazily, not during MCP initialization. No browser downloads
occur silently at startup. Ordinary tool calls make only reads of public pages;
they do not reveal phone numbers, contact sellers, add to cart, order, or pay.

## Access checks and manual setup

Avito can limit an IP, show a captcha, or warn about a VPN. If usable listing
data is absent, the server stops and reports the problem. It never solves a
captcha automatically or rotates proxies. A warning overlay alongside parsed
data is reported in `site_warning`, not hidden.

Check the site in a browser, wait for the restriction to expire, or run:

```bash
avito-mcp browser
```

Complete any challenge yourself in the separate window, then press Enter in
the terminal to save its session and close it. This explicit setup clears the
local cooldown; it does not guarantee that Avito accepts the next request.
Use `avito-mcp status` to see the local state. Account login is not required and
this server does not expose account actions. If you log in manually, the saved
browser state can contain account credentials: do not share or commit it.

## Limits and interpretation

- **Computer goods only** in this release. Search can match irrelevant products
  or accessories: check the full model; “RTX 3060” also matches Ti and other cards.
- **Used search is a bounded scan**, not a site-wide native Б/у filter. It may
  return no items after inspecting three new listings even when used ones exist
  later. `next_offset` and `next_page` make this explicit. Pages can shift, so
  deduplicate by listing id when continuing.
- `total_found` is Avito's total **before** local filtering. `limit` truncates
  the current page; increase it to 50 to inspect the rest rather than assuming
  `next_page` resumes after the last returned item.
- Local filtering uses URL city, not distance or a pickup address. Delivery
  estimates and landed cost are not quoted.
- No claim of availability beyond the rendered page, no guarantee of working
  condition, no completed-sales history, no image diagnosis, no full seller
  review feed and no reliable promoted-listing detection.
- Contact patterns in descriptions are masked. Public seller display names and
  profile links are retained; phone-reveal endpoints are never called.
- Rate spacing is per process. Avoid running multiple clients against the same
  IP at once. Avito may change its UI/markers at any time.

## Development

```bash
python -m pytest                 # offline parsers + real MCP stdio handshake
python -m pytest -m live -s      # optional: one search + one card against Avito
```

Offline tests use synthetic markup matching observed DOM structures; no browser
session, private data, captured page scripts or cookies are shipped. Live tests
are excluded from the CI template in `ci/github-actions.yml`. To enable GitHub
Actions, copy it to `.github/workflows/tests.yml` and push with a credential that
allows workflow changes. It is not active in this initial repository publication.
See [VALIDATION.md](VALIDATION.md) for the release checks.

Not affiliated with Avito. Intended for occasional personal buyer research.
Respect the site's terms and request limits. MIT license.

TDQS

A4/5.0

Scored across 5 tools

Disambiguation4/5

Most tools have clearly distinct roles: get_listing reads one listing, compare_listings compares up to five, get_status reports local state. search_listings and search_used_parts overlap because both search Avito, but the specialized condition-filtered purpose of search_used_parts is explained well enough to reduce confusion.

Naming Consistency4/5

Names consistently use snake_case with a leading verb (get_, search_, compare_), which is predictable. Minor deviations include singular/plural noun forms (get_listing vs search_listings) and the extra adjective in search_used_parts, but the pattern remains readable.

Tool Count5/5

Five tools are well-scoped for a read-only Avito listing research server: general search, used-parts search, single-listing read, comparison, and local status. Each tool has a clear place and the set is neither thin nor bloated.

Completeness4/5

The surface covers the core read-only workflow: search listings, find used parts by condition, inspect a listing, compare exact listings, and check local status. Minor gaps remain, such as no dedicated seller/profile retrieval or category discovery, but agents can work around these via existing search and listing reads.

Maintenance

ActivityMaintained
ResponsivenessNo issues