Metadata-Version: 2.4
Name: yandex-mcp
Version: 2.1.0
Summary: Read-only MCP server for Yandex Metrika, Webmaster and Direct (incl. Wordstat)
Author: Anton Nozikov
License: MIT
Project-URL: Homepage, https://github.com/nozikov/yandex-mcp
Project-URL: Issues, https://github.com/nozikov/yandex-mcp/issues
Project-URL: Changelog, https://github.com/nozikov/yandex-mcp/blob/main/CHANGELOG.md
Project-URL: Source, https://github.com/nozikov/yandex-mcp
Keywords: mcp,model-context-protocol,yandex,metrika,webmaster,direct,wordstat,analytics
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
Classifier: Topic :: Office/Business
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: build>=1; extra == "dev"
Requires-Dist: twine>=5; extra == "dev"
Dynamic: license-file

# yandex-mcp

<!-- mcp-name: io.github.nozikov/yandex-mcp -->

**Спрашивай свою аналитику словами.** MCP-сервер к Яндекс Метрике, Вебмастеру, Директу
и Вордстату: 15 инструментов, **ноль зависимостей**, токен лежит в хранилище ОС и не
появляется ни в одном ответе.

[![Install in VS Code](https://img.shields.io/badge/VS_Code-Установить-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=yandex&config=%7B%22type%22%3A%20%22stdio%22%2C%20%22command%22%3A%20%22uvx%22%2C%20%22args%22%3A%20%5B%22yandex-mcp%22%5D%7D)
[![Install in Cursor](https://img.shields.io/badge/Cursor-Установить-000000?style=flat-square&logo=cursor&logoColor=white)](cursor://anysphere.cursor-deeplink/mcp/install?name=yandex&config=eyJjb21tYW5kIjogInV2eCIsICJhcmdzIjogWyJ5YW5kZXgtbWNwIl19)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](./LICENSE)

```
> Как изменился трафик за последний месяц и откуда пришёл рост?
> По каким запросам мы на второй странице — там, где до топа осталось чуть-чуть?
> Сколько стоила заявка в Директе на прошлой неделе по каждой кампании?
```

## Кому это

| Кому | Что закрывает |
|---|---|
| **Маркетологу, аналитику** | Метрика: сводка, произвольный отчёт, сравнение периодов, цели, счётчики |
| **SEO-специалисту** | Вебмастер: ИКС, страницы в поиске, поисковые запросы с позициями, динамика индексации, sitemap, переобход. Вордстат: частотности и что ищут вместе |
| **PPC-специалисту** | Директ: кампании, остаток баллов API, отчёты Reports API v5 — расход, клики, CTR, средняя цена клика |

## Безопасность в трёх фразах

Сервер работает только на вашем компьютере: он ходит в API Яндекса напрямую, никаких
посредников. Токен хранится в Keychain, GNOME Keyring или файле с правами `0600` —
и не появляется ни в ответе инструмента, ни в тексте ошибки. Единственное необратимое
действие — постановка страниц на переобход — требует явного `confirm: true`.

## Установка

### Claude Code — плагин, две команды

```
/plugin marketplace add nozikov/yandex-mcp
/plugin install yandex-mcp@nozikov
```

Плагин ставит сервер и скиллы разом, обновляется через `/plugin update`, а пути
подставляет сам. Нужен только Python 3.8+, установка пакета не требуется.

| | Плагин | Руками через `claude mcp add` |
|---|---|---|
| Установка | две команды | команда + указание путей |
| Скиллы в комплекте | да | нет |
| Пути | подставляет `${CLAUDE_PLUGIN_ROOT}` | прописываете сами |
| Обновление | `/plugin update` | `git pull` и проверка путей |

### Любой MCP-клиент — из PyPI

```bash
uvx yandex-mcp              # запуск без установки
# или: pipx install yandex-mcp
```

```bash
claude mcp add yandex -s user -e YANDEX_MCP_DEFAULT_COUNTER=12345678 -- uvx yandex-mcp
```

Или вручную в конфиге клиента — см. [`.mcp.json.example`](./.mcp.json.example):

```json
{
  "mcpServers": {
    "yandex": {
      "command": "uvx",
      "args": ["yandex-mcp"],
      "env": { "YANDEX_MCP_DEFAULT_COUNTER": "12345678" }
    }
  }
}
```

`YANDEX_MCP_DEFAULT_COUNTER` опционален: без него `counter_id` придётся передавать
в каждом вызове явно.

## Вход

Терминал не нужен — просто попросите агента:

```
> Подключи Яндекс
```

Он вызовет `yandex_login`, покажет ссылку, вы подтвердите доступ в браузере и вернёте
код в чат. Токен ляжет в хранилище ОС, перезапускать сервер не нужно.

Если предпочитаете терминал:

```
yandex-mcp setup     # регистрация приложения Яндекса, по шагам
yandex-mcp login     # вход в браузере
yandex-mcp status    # какие токены есть, где лежат, когда истекут
yandex-mcp logout    # удалить токены из хранилища
```

### Приложение Яндекса

Яндекс выдаёт токен только зарегистрированному приложению, поэтому один раз нужно
создать своё — это бесплатно и занимает пять минут. `yandex-mcp setup` открывает нужную
страницу и проводит по шагам. Client secret не нужен: используется PKCE, где подлинность
подтверждается тем, что только ваш процесс знает `code_verifier`.

При создании Яндекс спрашивает тип приложения — подходят оба, разница в способе входа:

| Тип приложения | Redirect URI | Вход |
|---|---|---|
| «Для авторизации пользователей» | задаёте сами: `http://localhost:8765/callback` | `login` или `yandex_login` с `mode: localhost` |
| «Для доступа к API или отладки» | зафиксирован на `https://oauth.yandex.ru/verification_code` | `login --manual` или `yandex_login` (по умолчанию) |

Права добавляются в разделе «Доступ к данным» по названию:

```
metrika:read
webmaster:hostinfo
webmaster:verify
direct:api           ← требует одобренной заявки в кабинете Директа, до 7 дней
```

**Что именно доступно, решает панель Яндекса, а не настройки здесь.** Вход за одну
авторизацию просит все права сразу. Если `direct:api` ещё не одобрен, Яндекс отклонит
авторизацию с ним — `login` заметит это, **сам войдёт без Директа** и скажет об этом.
Метрика и Вебмастер заработают сразу, а когда заявку одобрят, тот же `login` подхватит
Директ.

Если нужен least privilege — `login --service metrika` выдаст отдельный узкий токен
только на неё; такой токен имеет приоритет над общим.

## Инструменты

| Инструмент | Что делает |
|---|---|
| `metrika_summary` | Сводка за период: визиты, посетители, отказы, длительность визита, глубина, достижения всех целей счётчика |
| `metrika_report` | Произвольный отчёт Reporting API Метрики — любые метрики, измерения, фильтры |
| `metrika_compare` | Сравнение метрик между двумя периодами (по умолчанию — с предыдущим такой же длины), опционально построчно по измерению |
| `metrika_counters` | Список доступных счётчиков с сайтами и статусом |
| `webmaster_summary` | ИКС, страниц в поиске, исключено, активные проблемы диагностики по всем подтверждённым сайтам |
| `webmaster_queries` | Поисковые запросы: показы, клики, средняя позиция |
| `webmaster_indexing` | Динамика количества страниц в поиске по датам |
| `webmaster_sitemaps` | Sitemap-файлы, которые видит Яндекс: URL, число адресов, ошибки, дата обращения робота |
| `webmaster_recrawl` | Постановка URL в очередь на переобход. **Единственный мутирующий вызов**, до 20 URL, требует `confirm: true` |
| `direct_campaigns` | Список кампаний Директа и остаток баллов API |
| `direct_report` | Отчёт Reports API v5 — расход, показы, клики, CTR по кампаниям, объявлениям, группам или поисковым запросам |
| `wordstat_phrases` | Частотности Вордстата через Live v4 API Директа |
| `yandex_login` | Шаг 1 входа: ссылка авторизации, терминал не нужен |
| `yandex_submit_code` | Шаг 2 входа: обмен кода на токен |
| `yandex_auth_status` | Что подключено, где лежат токены, когда истекают |

### Скиллы

Ставятся вместе с плагином, вызываются как обычные слэш-команды:

| Скилл | Что делает |
|---|---|
| `/yandex-mcp:site-weekly` | Недельный отчёт по сайту: трафик, источники, поиск, реклама — и что с этим делать |
| `/yandex-mcp:seo-opportunities` | Запросы на границе топа: где до первой страницы осталось чуть-чуть |

## Почему 15 инструментов, а не 130

Спецификация инструментов уходит в контекст модели **при каждом запросе**, пока сервер
подключён. У нас это ≈1 800 токенов. У серверов со 130–150 инструментами — 40 000 и
больше, причём 85% приходится на JSON-схемы параметров. Это постоянный налог на каждый
диалог и лишний шум при выборе инструмента.

Здесь сознательно оставлено то, на что реально смотрят: цифры и их динамика. Управление
кампаниями, ставками и объявлениями не входит в задачу — для этого есть кабинет Директа,
и цена ошибки там другая.

## Где лежат токены

Хранилище выбирается автоматически, по убыванию защищённости:

| Условие | Хранилище |
|---|---|
| macOS | Keychain (`security`) |
| Linux с libsecret | Secret Service (`secret-tool` → GNOME Keyring, KWallet) |
| Windows, headless-сервер, Docker | файл `secrets.json` с правами `0600` в конфиг-директории |

Принудительно — переменной `YANDEX_MCP_KEYSTORE=keychain|secret-tool|file`.

Все записи лежат под префиксом `yandex-mcp-`, чтобы в глобальном пространстве имён
Keychain ничего не пересекалось и `logout` не задел чужое:

```
yandex-mcp-token             общий токен единого входа
yandex-mcp-metrika-token     узкий токен одного сервиса
yandex-mcp-client-id         ID приложения Яндекса
```

Любой секрет можно прокинуть через окружение, минуя хранилище:
`yandex-mcp-metrika-token` → `YANDEX_MCP_SECRET_METRIKA_TOKEN`, общий токен →
`YANDEX_MCP_SECRET_TOKEN`. Это основной способ для Docker и CI.

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

| Переменная | Зачем |
|---|---|
| `YANDEX_MCP_DEFAULT_COUNTER` | ID счётчика Метрики по умолчанию |
| `YANDEX_MCP_CLIENT_ID` | ID приложения Яндекса, если не хотите хранить его в хранилище |
| `YANDEX_MCP_KEYSTORE` | `keychain`, `secret-tool` или `file` — форсировать хранилище |
| `YANDEX_MCP_SECRET_*` | Прокинуть готовый секрет мимо хранилища (Docker, CI) |
| `YANDEX_MCP_DIRECT_SANDBOX` | `1` — все вызовы Директа идут в песочницу, баллы API не тратятся |
| `YANDEX_MCP_DIRECT_CLIENT_LOGIN` | Логин клиента для агентских аккаунтов |
| `YANDEX_MCP_WORDSTAT_WAIT` | Сколько секунд ждать отчёт Вордстата в одном вызове, по умолчанию 170 |

## Принципы

- **Токен не хранится в открытом виде там, где есть системное хранилище**, и не появляется
  ни в одном ответе инструмента, ни в тексте ошибки — есть отдельный `scrub()`, вычищающий
  Bearer/OAuth-заголовки и Яндекс-токены (`y0_...`, `y1_...`) из любого текста. `status`
  печатает только sha256-отпечаток.
- **Почти всё — чтение.** Единственный мутирующий вызов — `webmaster_recrawl`, ограниченный
  20 URL за раз и требующий `confirm: true`: у Вебмастера квота 150 в сутки на весь сайт.
- **Данные из API считаются недоверенными.** Поисковые фразы, UTM-метки и названия кампаний
  пишут посторонние люди; каждый ответ снабжается пометкой, что это данные для анализа,
  а не инструкции агенту.
- **Официальный MCP SDK не используется намеренно** — он тянет `httpx`, `pydantic`, `anyio`
  и их транзитивные зависимости, а через этот процесс проходит OAuth-токен к вашей аналитике
  и рекламному кабинету. Меньше чужого кода в рантайме — меньше supply-chain поверхность.

## Структура проекта

```
src/yandex_mcp/
  cli.py               # yandex-mcp: без аргументов сервер, с аргументами настройка
  server.py            # JSON-RPC поверх stdio
  registry.py          # реестр инструментов: сборка TOOLS/HANDLERS
  httpclient.py        # urllib-обёртка: заголовки, единая обработка ошибок
  scrub.py             # вычищение секретов из ответов и ошибок
  auth/
    store.py           # выбор хранилища: Keychain / secret-tool / файл 0600
    tokens.py          # токен сервиса, с фолбэком на общий
    flow.py            # PKCE-вход: begin/complete — тихие, save_tokens — терминальный
    callback.py        # приём redirect на localhost
  tools/               # по модулю на сервис: metrika, webmaster, direct, wordstat, auth
tests/
  conftest.py          # изолированное файловое хранилище вместо системного
  auth/ tools/         # структура повторяет исходники
.claude/skills/        # скиллы, которые ставятся вместе с плагином
.claude-plugin/        # манифесты плагина и маркетплейса Claude Code
```

Код лежит в `src/`, а не в корне: при таком раскладе `import yandex_mcp` берёт
**установленный** пакет, а не случайно подхваченную рабочую директорию — иначе тесты
могут проходить на коде, которого нет в собранном колесе.

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

```bash
pip install -e ".[dev]"
pytest
```

Тесты не ходят в сеть и не трогают системное хранилище: сеть и Keychain подменяются через
`monkeypatch`, секреты пишутся во временный файл. CI гоняет их на Linux, macOS и Windows.

## Ограничения

- `direct_campaigns`, `direct_report` и `wordstat_phrases` требуют одобренного доступа к API
  Директа — до одобрения Директ отвечает кодом ошибки 58.
- `wordstat_phrases`: отчёт готовится у Яндекса около трёх минут. Если вызов вернул «ещё
  готовится» — повторите его с теми же фразами, готовый результат подхватится сразу.
- `direct_report` при офлайн-обработке может готовиться минуты — тул сам ждёт, но упирается
  в квоту Директа: не больше 5 офлайн-отчётов в очереди на аккаунт.
- `webmaster_sitemaps` отдаёт первые 100 sitemap хоста (без пагинации).
- Ответ каждого инструмента обрезается до 20000 символов — для больших выгрузок сужайте
  период или `limit`.
- Файловое хранилище (Windows, headless, Docker) держит токен в открытом виде под правами
  0600 — уровень `~/.aws/credentials` или SSH-ключа без пароля. На Windows права наследуются
  от профиля пользователя, `chmod` там условен.
- На macOS запись в Keychain идёт через `security add-generic-password -w <value>`, то есть
  на время работы подпроцесса значение видно в `ps` — ограничение самого CLI. На Linux
  `secret-tool` читает значение из stdin, там этой проблемы нет.
- Обновление токена (`refresh`) у Яндекса требует client_secret, которого у PKCE-приложения
  нет. Практического значения это не имеет: выданный так токен живёт около года, после чего
  достаточно повторить `yandex-mcp login`.

## Лицензия

MIT
