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

**MCP-сервер, через который LLM работает на Авито как человек**: ищет и разбирает объявления, читает личный кабинет и статистику, переключает профили, кликает, заполняет формы. Необратимые действия выполняются только после явного подтверждения.

Сервер управляет отдельным окном Google Chrome по протоколу CDP. В этом окне вы один раз входите в свой аккаунт, и сессия сохраняется между перезапусками. Ключи API Авито и платный тариф не нужны. Подтверждать каждый клик, как в браузерных расширениях, тоже не нужно.

> Неофициальный проект, с Авито не связан. Используйте его для своего аккаунта и в рамках [правил Авито](https://www.avito.ru/legal/rules). Подробнее — в разделе [«Ограничения и ответственность»](#ограничения-и-ответственность).

*English summary at the [end of this file](#english-summary).*

---

## Содержание

- [Зачем это нужно](#зачем-это-нужно)
- [Что умеет](#что-умеет)
- [Как это устроено](#как-это-устроено)
- [Установка](#установка)
- [Подключение к клиентам MCP](#подключение-к-клиентам-mcp)
- [Первый запуск и вход в аккаунт](#первый-запуск-и-вход-в-аккаунт)
- [Справочник инструментов](#справочник-инструментов)
- [Безопасность](#безопасность)
- [Настройки](#настройки)
- [Вспомогательные скрипты](#вспомогательные-скрипты)
- [Примеры запросов к агенту](#примеры-запросов-к-агенту)
- [Решение проблем](#решение-проблем)
- [Разработка](#разработка)
- [Ограничения и ответственность](#ограничения-и-ответственность)

---

## Зачем это нужно

Дать LLM доступ к Авито можно несколькими способами, и у каждого есть недостатки:

| Способ | Проблема |
|---|---|
| Официальное API Авито | Нужен активный платный тариф и ключи `client_id`/`client_secret`. Поиска по чужим объявлениям в API нет |
| Браузерные расширения (Claude in Chrome и др.) | Просят подтверждение почти на каждое действие, долгие сценарии становятся мучительными |
| Встроенные браузеры агентов, headless-скрейперы | Авито быстро показывает «Доступ ограничен: проблема с IP» |
| Готовые MCP с жёсткими парсерами | Ломаются при любом изменении вёрстки и не видят кабинет Авито Pro |

**avito-mcp** выбирает середину:

- **Живой браузер с вашим входом.** Это обычный Chrome со своим профилем, а не headless-скрейпер.
- **Универсальные инструменты:** открыть, прочитать, кликнуть, ввести текст. Агент справится, даже если Авито поменяет вёрстку.
- **Готовые извлекатели данных** поверх них: выдача, объявление, кабинет, профили. Это экономит токены и время.
- **Защитный слой:** только домен avito.ru, подтверждение необратимых действий, передача капчи человеку.

## Что умеет

- **Поиск.** Выдача по запросу, городу, разделу, цене и сортировке. По каждой карточке: позиция, цена, строка прайса, продавец, рейтинг, число отзывов, бейджи, признак платного продвижения.
- **Разбор объявления.** Заголовок, цена, прайс-лист, параметры («Подробности»), описание, адрес, продавец, бейджи, отрывок отзывов. Фото в полном размере: сервер проходит по миниатюрам галереи. Если объявление ваше, ещё и статистика за 7 дней: показы, просмотры, избранное, контакты, расходы.
- **Личный кабинет.** Свои объявления во всех вкладках (активные, с ошибками, архив и т. д.) со строкой статистики. Работает и в обычном кабинете, и в Авито Pro.
- **Профили аккаунта.** Список профилей из меню аватара и переключение между ними, например между обычным профилем и профилем Pro.
- **Любая страница Авито:** сообщения, избранное, настройки продвижения, форма редактирования. Через снимок кликабельных элементов со стабильными ссылками `ref`.
- **Редактирование форм.** Ввод в поля с масками (цены), выпадающие списки, точечная замена фраз в описании. Описание на Авито сделано на Draft.js, и сервер сверяет итоговый текст, а не перезаписывает его целиком.
- **Фото.** Скачивание фото объявлений с `avito.st` в папку, снимки экрана страницы.

## Как это устроено

```text
 LLM-клиент (Claude Code, Claude Desktop, Codex, Cursor…)
        │  MCP по stdio
        ▼
 avito-mcp (Python, FastMCP + Playwright)
   ├─ браузер: запуск Chrome, подключение по CDP, паузы между переходами
   ├─ извлекатели: JS по атрибутам data-marker с запасным вариантом «текст страницы»
   ├─ защита: белый список доменов, confirm=true для рискованных кнопок, детектор капчи
   └─ инструменты: 20 штук (см. справочник)
        │  CDP только на 127.0.0.1:9333
        ▼
 Google Chrome со своим профилем ~/.avito-mcp/chrome-profile  ←  вход выполняет человек
        │
        ▼
 avito.ru
```

- **Chrome живёт отдельно от сервера.** При первом вызове сервер запускает его как обычный процесс со своим профилем и портом отладки. Окно и вход в аккаунт переживают перезапуск MCP-клиента. Если окно закрыть, сервер откроет его снова.
- **Никаких обходов защиты.** Сервер не подменяет user-agent, не ставит «стелс»-патчи, не решает капчи. Между переходами по страницам держится пауза: по умолчанию 2,5 с плюс случайные 0–1,5 с.
- **Извлекатели опираются на атрибуты `data-marker`.** Они стабильнее CSS-классов. Если разметка не распознана, инструмент возвращает текст страницы: модель продолжит работу, а не упадёт.

## Установка

Требования: macOS или Linux, Python 3.12+, [uv](https://docs.astral.sh/uv/), установленный Google Chrome.

```bash
git clone https://github.com/ermizin/avito-mcp.git
cd avito-mcp
uv sync
```

Ставить браузеры Playwright не нужно: используется ваш Google Chrome. На Linux или при нестандартном пути к Chrome задайте `AVITO_MCP_CHROME` (см. [настройки](#настройки)).

Проверка, что сервер запускается и видит Авито:

```bash
uv run python scripts/smoke.py avito_status
```

Откроется окно Chrome с Авито, а в терминале появится JSON: `url`, `title`, `blocked`, `logged_in`.

## Подключение к клиентам MCP

Во всех примерах `/ABS/PATH/avito-mcp` — абсолютный путь к клонированному репозиторию.

**Claude Code**

```bash
claude mcp add -s user avito -- /ABS/PATH/avito-mcp/.venv/bin/python -m avito_mcp
claude mcp get avito   # Status: ✔ Connected
```

**Claude Desktop, Cursor и другие клиенты с JSON-конфигом**

```json
{
  "mcpServers": {
    "avito": {
      "command": "/ABS/PATH/avito-mcp/.venv/bin/python",
      "args": ["-m", "avito_mcp"]
    }
  }
}
```

**Codex** (`~/.codex/config.toml`)

```toml
[mcp_servers.avito]
command = "/ABS/PATH/avito-mcp/.venv/bin/python"
args = ["-m", "avito_mcp"]
```

Через `uv` без активации окружения:

```bash
uv run --directory /ABS/PATH/avito-mcp avito-mcp
```

## Первый запуск и вход в аккаунт

1. Попросите агента вызвать `avito_status` или запустите `scripts/smoke.py avito_status`. Откроется окно Chrome с профилем `~/.avito-mcp/chrome-profile`.
2. Если `logged_in: false`, войдите в Авито **сами** в этом окне. Агент пароли и коды из СМС не вводит.
3. Если Авито показал «Доступ ограничен» (`blocked: true`), пройдите проверку в окне сами. Агент вызывает `avito_wait_for_user` и ждёт.
4. Готово: дальше сессия сохраняется в профиле.

Уже есть отдельный профиль Chrome, где выполнен вход в Авито? Укажите его в `AVITO_MCP_PROFILE`. Профиль, который сейчас открыт в другом окне Chrome, не подойдёт: Chrome блокирует профиль.

## Справочник инструментов

### Состояние и навигация

| Инструмент | Параметры | Что делает |
|---|---|---|
| `avito_status` | — | Запускает Chrome, если нужно. Возвращает `url`, `title`, `blocked`, `logged_in` |
| `avito_open` | `url` — полный адрес или путь (`/profile`, `/profile/messenger`, `/favorites`) | Открывает страницу avito.ru, соблюдая паузу между переходами |
| `avito_back` | — | Назад по истории |
| `avito_wait_for_user` | `reason`: `captcha` / `login` / `any`; `timeout_s` (до 600) | Выводит окно на передний план и ждёт, пока человек пройдёт проверку или войдёт |

### Чтение страницы

| Инструмент | Параметры | Что делает |
|---|---|---|
| `avito_read` | `mode`: `text` / `snapshot`; `selector`; `offset`, `max_chars`; `viewport_only`; `wait_for_text` | `text` — видимый текст по частям. `snapshot` — компактный список кнопок, ссылок и полей вида `e12 button 'Найти' #search-form/submit-button`. `ref` стабильны между снимками. `wait_for_text` ждёт до 15 с, пока на странице, которая подгружается после открытия, появится нужный текст |
| `avito_screenshot` | `full_page` | Снимок страницы; копия сохраняется в `~/.avito-mcp/screenshots` |

### Действия

| Инструмент | Параметры | Что делает |
|---|---|---|
| `avito_click` | `ref` или `text`; `confirm` | Клик. Для рискованных кнопок нужен `confirm=true`, см. [безопасность](#безопасность) |
| `avito_hover` | `ref` | Наведение курсора: открывает выпадающие меню, например меню аватара |
| `avito_type` | `ref`, `text`; `clear`; `submit`; `confirm` | Вставляет значение целиком, поэтому поля с масками не теряют символы, и возвращает итоговое значение поля. `submit=true` нажимает Enter; в сообщениях это отправка, нужен `confirm=true` |
| `avito_replace_text` | `ref`, `find`, `replace`; `drop_empty_line` | Меняет один точный фрагмент в поле или в редакторе описания (Draft.js), остальной текст не трогает. Ошибка, если фрагмент не найден или встречается несколько раз |
| `avito_press` | `key`; `confirm` | Клавиша (`Enter`, `Escape`, `PageDown`…). Enter в сообщениях — только с `confirm=true` |
| `avito_scroll` | `direction`: `down` / `up` / `top` / `bottom`; `screens` | Прокрутка, нужна для подгрузки лент и отзывов |

### Данные Авито

| Инструмент | Параметры | Что возвращает |
|---|---|---|
| `avito_search` | `query`; `city` (`sankt-peterburg`, `moskva`, `rossiya`…); `category` (часть адреса раздела, например `predlozheniya_uslug`); `page`; `sort`: `default` / `date` / `price_asc` / `price_desc`; `price_min`, `price_max` | До 50 карточек на страницу: `id`, `position`, `title`, `url`, `price`, `price_list`, `location`, `promoted`, `seller`, `seller_url`, `seller_score`, `seller_reviews`, `badges`, `photos_in_card` |
| `avito_listing` | `url` (без него — текущая страница) | `id`, `title`, `price`, `price_list`, `params`, `description`, `address`, `date`, `views`, `seller`, `badges`, `reviews_excerpt`, `photos` (полный размер). Для своих объявлений ещё `owner_stats_7d` |
| `avito_my_items` | `path` (по умолчанию `/profile`; `""` — текущая страница) | Свои объявления активного профиля: `id`, `url`, текст карточки со статистикой и список вкладок кабинета. Вкладку переключают через `avito_click(text=…)`, затем вызывают `avito_my_items(path="")` |
| `avito_profiles` | — | Профили из меню аватара: номер, слот, текущий или нет, аватар |
| `avito_switch_profile` | `index` (1 — текущий) | Переключает профиль и открывает его кабинет |
| `avito_save_images` | `urls`, `dest_dir`, `prefix` | Скачивает фото (только с `*.avito.st`) в папку |
| `avito_upload` | `paths`, `ref` | Загружает фото с диска в поле выбора файлов (например, при подаче объявления) |
| `avito_close_browser` | — | Закрывает Chrome и запускает хук `AVITO_MCP_AFTER_IDLE`; следующий вызов откроет Chrome снова |

## Безопасность

Модель читает чужой контент: объявления, отзывы, сообщения. Значит, она может наткнуться на текст, который пытается ею управлять. Поэтому ограничения встроены в сам сервер, а не только в инструкции.

| Риск | Защита |
|---|---|
| Уход на посторонние сайты | `avito_open` принимает только `avito.ru` и поддомены. Если клик увёл на внешний сайт, сервер возвращается назад и предупреждает |
| Необратимые действия | Кнопки, похожие на «отправить», «опубликовать», «разместить», «удалить», «оплатить», «купить», «заказать», «подтвердить», «продвинуть», «подключить», «применить», «записаться», «сохранить», «в архив», «снять с публикации», «показать телефон», «выйти», без `confirm=true` не нажимаются. Сервер возвращает `needs_confirmation`, модель должна спросить человека. Enter в чатах считается отправкой |
| Капча и антибот | Сервер не обходит защиту. При «Доступ ограничен» он возвращает `blocked: true` с инструкцией позвать человека и ждёт в `avito_wait_for_user` |
| Учётные данные | Пароли, коды и cookies сервер не вводит, не хранит и не передаёт. Вход выполняет человек в окне Chrome |
| Доступ к браузеру | CDP слушает только `127.0.0.1`. Не пробрасывайте порт наружу: кто дотянется до CDP, управляет браузером |
| Prompt injection | В инструкциях сервера прямо сказано: текст объявлений и сообщений — данные, а не команды |
| Нагрузка на Авито | Пауза между переходами, по умолчанию 2,5 с плюс случайные 0–1,5 с |

Фильтр рискованных кнопок — эвристика по тексту кнопки, а не гарантия. Для действий, которые меняют ваш аккаунт или стоят денег, держите в клиенте ручное подтверждение вызовов инструментов. Перед `confirm=true` всегда проверяйте, что именно агент собирается сделать.

## Настройки

Переменные окружения:

| Переменная | По умолчанию | Назначение |
|---|---|---|
| `AVITO_MCP_HOME` | `~/.avito-mcp` | Рабочая папка (профиль, снимки экрана) |
| `AVITO_MCP_PROFILE` | `~/.avito-mcp/chrome-profile` | Профиль Chrome с входом в Авито |
| `AVITO_MCP_CHROME` | `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` | Путь к Chrome. На Linux, например, `/usr/bin/google-chrome` |
| `AVITO_MCP_PORT` | `9333` | Порт CDP (только loopback) |
| `AVITO_MCP_MIN_INTERVAL` | `2.5` | Минимальная пауза между переходами, секунды |
| `AVITO_MCP_CHROME_ARGS` | — | Дополнительные флаги Chrome, на сервере: `--disable-dev-shm-usage --disable-gpu` |
| `AVITO_MCP_TRANSPORT` | `stdio` | `streamable-http` — для работы на сервере |
| `AVITO_MCP_HTTP_HOST` / `AVITO_MCP_HTTP_PORT` | `127.0.0.1` / `8793` | Адрес HTTP-сервера; наружу его выставляйте только через прокси с авторизацией |
| `AVITO_MCP_ALLOWED_HOSTS` | — | Дополнительные значения заголовка Host через запятую (`10.0.0.1:8793`); защита от DNS rebinding остаётся включённой |
| `AVITO_MCP_BEFORE_START` | — | Команда оболочки перед запуском Chrome (например, остановить тяжёлый сервис) |
| `AVITO_MCP_AFTER_IDLE` | — | Команда после закрытия Chrome (например, запустить этот сервис обратно) |
| `AVITO_MCP_IDLE_SECONDS` | `0` (выкл.) | Закрыть Chrome после стольких секунд без запросов |

### Развёртывание на сервере

На Linux-сервере Chrome работает на виртуальном дисплее (Xvfb). Для входа в аккаунт и капчи к дисплею подключаются через x11vnc и noVNC по SSH-туннелю. Пример юнита systemd:

```ini
[Service]
User=ubuntu
Environment=DISPLAY=:91
Environment=AVITO_MCP_TRANSPORT=streamable-http
Environment=AVITO_MCP_CHROME=/opt/google/chrome/chrome
Environment="AVITO_MCP_CHROME_ARGS=--disable-dev-shm-usage --disable-gpu --window-size=1100,800"
Environment="AVITO_MCP_BEFORE_START=sudo -n /usr/bin/systemctl stop game-server"
Environment="AVITO_MCP_AFTER_IDLE=sudo -n /usr/bin/systemctl start game-server"
Environment=AVITO_MCP_IDLE_SECONDS=900
ExecStart=/home/ubuntu/avito-mcp/.venv/bin/python -m avito_mcp
```

Хуки нужны, если серверу не хватает ресурсов. На время работы с Авито они освобождают процессор от другого сервиса, а после простоя возвращают его. Инструмент `avito_close_browser` освобождает ресурсы сразу, не дожидаясь простоя. HTTP-порт держите на `127.0.0.1` и выставляйте наружу только через обратный прокси с OAuth или токеном: этот сервер управляет вашим аккаунтом.

## Вспомогательные скрипты

Все скрипты запускают сервер по stdio и вызывают его инструменты, как это делал бы MCP-клиент. Они удобны для отладки и пакетных задач.

| Скрипт | Назначение |
|---|---|
| `scripts/smoke.py 'tool:{json}' …` | Вызвать любые инструменты по очереди и напечатать ответы (`SMOKE_LIMIT` — сколько символов показывать) |
| `scripts/collect.py <папка> <url…>` | Сохранить полные карточки объявлений в `listings.json` и скачать фото |
| `scripts/serp.py <out.json> <запрос…>` | Сохранить выдачу (2 страницы) по нескольким запросам |
| `scripts/verify.py <url…>` | Напечатать прайс и статистику объявлений со страниц |
| `scripts/promo_read.py <id…>` | Прочитать (не меняя) настройки цены просмотра у объявлений |
| `scripts/fill_pricelist.py plan.json` | Заполнить прайс-лист объявления по плану, **без сохранения** |
| `scripts/apply_edits.py plan.json` | Внести правки в заголовок и описание и сверить итоговый текст с ожидаемым, **без сохранения** |
| `scripts/save_listing.py [--stay]` | Нажать «Сохранить изменения» на открытой форме; без `--stay` пропускает следующий шаг с продвижением |
| `examples/brand_prices.py` | Пример анализа рынка: цены конкретных препаратов в объявлениях конкурентов |

Скрипты правок нарочно разделены на «заполнить» и «сохранить»: сначала человек проверяет результат, потом сохраняет.

## Примеры запросов к агенту

- «Найди на Авито в Москве объявления по запросу „ремонт iPhone“, отсортируй по цене и покажи топ-10 с рейтингом продавцов».
- «Открой мои объявления, переключись на профиль Pro и сведи в таблицу просмотры, контакты и конверсию по каждому».
- «Разбери 15 первых объявлений конкурентов по запросу „увеличение губ“: цены, длина описаний, сколько отзывов, у кого продвижение».
- «Посмотри, что во вкладке „С ошибками“, и объясни, почему объявление отклонено».
- «Подготовь новый прайс-лист для объявления X, заполни форму, но не сохраняй — покажи, что получилось».

## Решение проблем

| Симптом | Что делать |
|---|---|
| `Chrome не открыл порт отладки за 30 с` | Профиль уже открыт в другом окне Chrome без отладки. Закройте это окно или укажите другой `AVITO_MCP_PROFILE` |
| `blocked: true` / «Доступ ограничен: проблема с IP» | Пройдите проверку в окне сами. Помогает отключить VPN и реже открывать страницы (увеличьте `AVITO_MCP_MIN_INTERVAL`) |
| `logged_in: false` | Войдите в Авито в окне Chrome сервера |
| В кабинете «Ошибка. Попробуйте обновить страницу» | Это сбой на стороне Авито. Нажмите «Обновить» через `avito_click(text="Обновить")` или повторите позже |
| `avito_my_items` вернул пустой список и `page_text` | Вкладка кабинета ещё грузится. Повторите `avito_my_items(path="")` |
| `Элемент не найден: обновите snapshot` | Страница перерисовалась. Снова вызовите `avito_read(mode="snapshot")` |
| Список вариантов в выпадающем меню не виден | Откройте меню кликом, затем `avito_read(mode="snapshot", viewport_only=true)`: варианты появляются как `…/custom-option(…)` |
| В клиенте не видно инструментов | Перезапустите клиент или проверьте `claude mcp get avito`. Новые MCP-серверы подключаются к новой сессии |

## Разработка

```bash
uv sync
uv run pytest -q                       # тесты логики без браузера
uvx ruff check --select E,F,B,I src    # линтер
```

Код:
- `src/avito_mcp/browser.py` — запуск Chrome, CDP, паузы, белый список доменов, детектор капчи и входа;
- `src/avito_mcp/extract.py` — JS-извлекатели, снимок элементов со стабильными `ref`, замена фрагмента в Draft.js;
- `src/avito_mcp/server.py` — инструменты MCP и защитный слой.

Авито регулярно меняет вёрстку. Если извлекатель сломался, начните с `avito_read(mode="snapshot")` и `avito_read(mode="text")` на нужной странице: найдите новые `data-marker` и обновите JS в `extract.py`.

## Ограничения и ответственность

- **Проект неофициальный.** Он работает через веб-интерфейс, поэтому может сломаться при изменениях на Авито.
- **Вы отвечаете за соблюдение правил Авито.** Правила запрещают автоматизированный массовый сбор данных и рассылки. Используйте сервер для своего аккаунта и разумных объёмов, не убирайте паузы и не пытайтесь обходить капчу или ограничения.
- **Персональные данные.** Сообщения и данные чужих объявлений — персональные данные. Не сохраняйте и не публикуйте их без необходимости.
- **Проверено** на macOS с Google Chrome на страницах avito.ru (выдача услуг, объявления, кабинет и кабинет Pro, форма редактирования, настройки цены просмотра) в октябре 2026. Мобильная версия и приложение не поддерживаются.

Лицензия — [MIT](LICENSE).

---

## English summary

**avito-mcp** is an unofficial MCP server that lets an LLM operate [Avito](https://www.avito.ru) (Russia's largest classifieds site) the way a person would. It drives a dedicated Google Chrome window over CDP. You sign in once yourself and the session persists. No Avito API keys or paid plan are needed, and the agent doesn't need click-by-click approval.

- **20 tools.** Generic ones: `open`, `read` (text or a compact snapshot with stable refs), `click`, `hover`, `type`, `replace_text`, `press`, `scroll`, `screenshot`. Avito-specific ones: search results with positions and promotion flags, full listing parsing including full-size photos and owner stats, your own listings across cabinet tabs (incl. Avito Pro), and account profile switching.
- **Safety built into the server:**
  - navigation is limited to avito.ru;
  - irreversible controls (send, publish, delete, pay, save…) require `confirm=true`;
  - CAPTCHAs are handed to the human, never bypassed;
  - no credential handling; CDP stays on loopback;
  - every page navigation is rate-limited.
- **Install:** `uv sync`, then register `/ABS/PATH/avito-mcp/.venv/bin/python -m avito_mcp` as a stdio MCP server.

MIT licensed. Use it with your own account and within Avito's terms of service.

TDQS

B3.4/5.0

Scored across 18 tools

Disambiguation4/5

Tools largely target distinct browser actions (click, type, press, scroll, hover), page extraction (read, listing, search), and account/profile management. Some potential overlap exists between avito_type vs avito_replace_text and avito_status vs avito_read, but descriptions clarify boundaries well enough.

Naming Consistency4/5

All names use the avito_ prefix and snake_case, which is consistent and predictable. Most are verb-based, though a few noun-based names (avito_listing, avito_my_items, avito_profiles) are minor deviations that remain readable.

Tool Count4/5

18 tools is slightly above the typical 3-15 range but reasonable for a browser-automation server with both low-level primitives and Avito-specific helpers. Each tool appears to serve a distinct role rather than being redundant.

Completeness3/5

The surface covers browsing, searching, listing parsing, own items, profiles, and image saving. However, there are no dedicated high-level tools for posting/editing/deleting listings or managing messages, so core transactional Avito workflows rely on manual click/type/press workarounds.

Maintenance

ActivityMaintained
ResponsivenessNo issues