Odel
yandex direct mcp

yandex direct mcp

Local
@ai-hub-open6TypeScriptApache-2.0Updated 1w ago

MCP server for Yandex Direct API: campaigns, ads, keywords, bids, reports. Click.ru or OAuth.

Yandex Direct MCP

MCP для Yandex Direct JSON API v5 на Bun + TypeScript.

51 инструмент: кабинеты, кампании (включая ЕПК, стратегии торгов и цели Метрики), группы объявлений, объявления (текстовые и комбинаторные), расширения (быстрые ссылки, уточнения), изображения, ставки и прогноз цены клика, корректировки ставок, ключевые фразы, отчёты, справочники.

Требования

Установка

git clone https://github.com/ai-hub-open/yandex-direct-mcp.git
cd yandex-direct-mcp
bun install

Настройка

Скопируйте .env.example в .env и заполните один из двух режимов (переменные окружения имеют приоритет над .env):

A. Прямой режим — свой OAuth-токен Яндекс.Директа:

YANDEX_DIRECT_TOKEN=y0__...
YANDEX_DIRECT_SANDBOX=false   # true — песочница

По умолчанию используется версия API v501 (обязательна для ЕПК); переключить можно через YANDEX_DIRECT_API_VERSION=v5.

B. Через прокси click.ru — OAuth-токен Яндекса не нужен:

CLICK_RU_PROXY=true
CLICK_RU_TOKEN=<API-токен из профиля click.ru>
CLICK_RU_CLIENT_LOGIN=<логин аккаунта Яндекс.Директа>
CLICK_RU_USER_ID=<ID пользователя click.ru>   # только при работе из мастер-аккаунта

Токен создаётся в профиле https://click.ru/userinfo.html → поле «API Token» → «Создать». Аккаунт Яндекс.Директа должен быть подключён в click.ru. Справка: https://help.click.ru/81, https://api.click.ru/V0/docs/.

Прокси click.ru работает только с продакшн-API Яндекса (песочница недоступна).

Запуск

bun run src/index.ts                                  # stdio — для локальных MCP-клиентов
MCP_AUTH_TOKEN=<секрет> bun run src/index.ts --http   # HTTP-сервер на :3000

В HTTP-режиме нужно выбрать способ авторизации — см. HTTP-режим.

Тесты

Обязательная проверка перед изменениями — мок-тесты (тела запросов к API, без сети) и тесты HTTP-транспорта (авторизация, CORS):

bun run test

Релизы

Публикация автоматическая: при выпуске релиза на GitHub конвейер GitHub Actions прогоняет проверки, публикует пакет в npm и обновляет запись в реестре MCP. Тег релиза должен совпадать с версией в package.json (версия 0.5.0 → тег v0.5.0), иначе публикация останавливается. Версию поднимают в трёх местах: package.json, server.json (корневое поле и packages[0].version) и CHANGELOG.md.

Авторизация в обоих реестрах идёт через доверенного издателя по GitHub OIDC — долгоживущих токенов в секретах нет.

E2E-прогон на песочнице (создаёт и удаляет тестовые кампании; нужны YANDEX_DIRECT_TOKEN и YANDEX_DIRECT_SANDBOX=true):

bun run test:sandbox

Ограничения песочницы Яндекса (в продакшн-API их нет; часть лечится пересозданием песочницы в кабинете — Инструменты → Настройки API → Песочница). E2E помечает такие шаги как «пропуск», а не как ошибку:

  • adgroups.add возвращает ID, но группа не появляется в adgroups.get, а ads.add отвечает «Группа объявлений не найдена» — заливка объявлений в песочнице непроверяема;
  • bidmodifiers.add возвращает ID, но bidmodifiers.get всегда пуст при любом фильтре;
  • сервис sitelinks отвечает «Сервис временно недоступен».

Подключение к Claude Code

.mcp.json в корне вашего проекта (см. также .mcp.json.example):

{
  "mcpServers": {
    "yandex-direct": {
      "command": "bun",
      "args": ["run", "/абсолютный/путь/к/yandex-direct-mcp/src/index.ts"],
      "env": {
        "CLICK_RU_PROXY": "true",
        "CLICK_RU_TOKEN": "<ваш токен>",
        "CLICK_RU_CLIENT_LOGIN": "<логин Директа>"
      }
    }
  }
}

Для прямого режима в env вместо CLICK_RU_* укажите YANDEX_DIRECT_TOKEN.

📋 Инструкция для ИИ-агента — скопируйте и передайте своему агенту (Claude Code / Codex), подставив ключи:

Установи и подключи MCP «Yandex Direct»: склонируй https://github.com/ai-hub-open/yandex-direct-mcp.git, проверь Bun (bun --version, если нет — установи с https://bun.sh), выполни bun install в корне репозитория. Зарегистрируй локальный stdio-MCP: команда bun, аргументы run <абсолютный_путь_к_репо>/src/index.ts, переменные окружения — мои ключи: CLICK_RU_PROXY=true, CLICK_RU_TOKEN=<...>, CLICK_RU_CLIENT_LOGIN=<...> (или YANDEX_DIRECT_TOKEN=<...> для прямого режима). Проверь tools/list и сообщи результат.

HTTP-режим

MCP_TRANSPORT=http MCP_PORT=3000 MCP_AUTH_TOKEN=<секрет> bun run src/index.ts

Сервер не стартует без выбранного способа авторизации — открытый эндпоинт даёт полный доступ к рекламному кабинету. Вариантов три:

ПеременнаяКого пускает
MCP_AUTH_TOKEN=<секрет>Общий ключ шлюза: запросы несут Authorization: Bearer <секрет>
MCP_REQUIRE_CLIENT_CREDENTIALS=click-ruПропуск — сам токен Click.ru в заголовках запроса, отдельный ключ шлюза не нужен
MCP_ALLOW_ANONYMOUS=trueНикого не проверяет. Только если сервер закрыт обратным прокси-сервером или слушает localhost

Режим MCP_REQUIRE_CLIENT_CREDENTIALS рассчитан на multi-tenant: клиент присылает свои креды, они же служат пропуском, на сервере ничего не хранится. Значения: click-ru (только Click.ru), yandex (только OAuth Яндекса), any (любые). Запрос без кред или с кредами другого типа получает 401 с объяснением, каких заголовков не хватает.

Выбор кабинета

Любой инструмент, работающий с кабинетом, принимает необязательный client_login — логин кабинета для этого вызова. Не указан — берётся кабинет из подключения, то есть прежнее поведение сохраняется полностью.

yandex_direct_accounts_get                        → список доступных кабинетов
yandex_direct_campaigns_get { client_login: "…" } → кампании именно этого кабинета

Так одно подключение обслуживает несколько кабинетов: ссылка задаёт кабинет по умолчанию, а параметр перекрывает его при необходимости. В прямом режиме параметр включает заголовок Client-Login, которого раньше не было вовсе — это открывает работу с клиентами агентства по OAuth-токену.

Параметра нет у справочников, прогноза ставок и самого списка кабинетов: они к конкретному кабинету не относятся.

Список кабинетов через click.ru скрывает привязанные (createdType: LINKED): click.ru может не обслуживать их — например, если закончился пробный период, — и запросы отклоняются с «Customer not found». Скрытые не пропадают бесследно: в ответе указано их количество и как показать (include_linked: true).

Подключение одной ссылкой

Клиенты, которые добавляют коннектор по URL и не дают задать заголовки (например форма custom connector), подключаются адресом с кредами в пути:

https://direct-mcp.example.com/c/<логин Директа>/<токен Click.ru>
https://direct-mcp.example.com/c/<логин Директа>/<токен Click.ru>/<ID пользователя>   # мастер-аккаунт
https://direct-mcp.example.com/y/<OAuth-токен Яндекса>                                # прямой режим

Хвост /mcp допускается, флаги — в query: ?sandbox=true, ?api_version=v5. Такой адрес работает как обычная точка /mcp, только креды берутся из пути, а не из заголовков; при MCP_REQUIRE_CLIENT_CREDENTIALS он же служит пропуском.

Адрес равносилен паролю. Секрет в пути попадает в логи обратных прокси-серверов, историю браузера и заголовок Referer — в отличие от заголовков, которые нигде не оседают. Выдавайте такие ссылки только по TLS, не публикуйте их и отзывайте токен в Click.ru при утечке. Где клиент умеет заголовки — используйте заголовки.

Переменные: MCP_PORT (3000), MCP_HOST (0.0.0.0), MCP_AUTH_TOKEN, MCP_REQUIRE_CLIENT_CREDENTIALS, MCP_ALLOW_ANONYMOUS, MCP_ALLOWED_ORIGIN. CORS-заголовки по умолчанию не выдаются (MCP-клиенты ходят не из браузера) — разрешите конкретный источник через MCP_ALLOWED_ORIGIN, если он действительно нужен.

Метод + путьНазначение
POST /mcpJSON-RPC 2.0 запрос (или батч)
GET /healthzпроверка доступности
GET /mcp/toolsсписок инструментов (отладка)

Несколько аккаунтов: данные доступа можно передавать заголовками на каждый запрос (перекрывают .env) — один сервер обслуживает несколько клиентов:

X-Yandex-Token: <OAuth>              X-Click-Ru-Token: <токен>
X-Yandex-Sandbox: true|false         X-Click-Ru-User-Id: <ID>
X-Yandex-Api-Version: v501|v5        X-Client-Login: <логин Директа>
                                     X-Click-Ru-Base-Url: <база API click.ru>

В режиме click.ru обязательны X-Click-Ru-Token и X-Client-Login; X-Click-Ru-User-Id нужен только при работе из мастер-аккаунта. Сервер можно запустить без данных доступа в .env — тогда каждый запрос обязан нести заголовки.

⚠️ Безопасность: при публикации в сеть держите MCP_AUTH_TOKEN заданным и закройте порт за обратным прокси-сервером с TLS. Запуск с MCP_ALLOW_ANONYMOUS=true на MCP_HOST=0.0.0.0 открывает кабинет всем, у кого есть доступ к порту.

Docker

cp .env.example .env   # заполните ключи и MCP_AUTH_TOKEN
docker compose up -d --build
curl http://localhost:3000/healthz

Инструменты (51)

  • Campaigns: get / add / update / delete / suspend / resume — текстовые кампании и ЕПК (единая перформанс-кампания), стратегии торгов с недельным бюджетом, счётчики Метрики и приоритетные цели
  • AdGroups: get / add / update / delete — включая группы ЕПК (UnifiedAdGroup)
  • Ads: get / add / add_responsive (комбинаторное объявление ЕПК) / update (текстовые и комбинаторные) / delete / suspend / resume / moderate
  • AdImages: add (с кропом) / get
  • Accounts: get — список доступных кабинетов Директа
  • Sitelinks: add / get / delete — наборы быстрых ссылок
  • AdExtensions: add / get / delete — уточнения
  • BidModifiers: devices / regional / retargeting / demographics / set / delete / get — корректировки ставок на запись и чтение
  • Keywords: get / add / update / delete / suspend / resume
  • KeywordBids: set / get — ставки по фразам и данные аукциона (объёмы трафика, цены)
  • Forecast: forecast_bids — прогноз цены клика и трафика по произвольным фразам без создания кампании
  • Reports: campaign / ad / search_queries / custom (произвольный тип и набор столбцов)
  • Dictionaries: regions / currencies / interests / all

Предпросмотр записи (dry-run)

Любой инструмент, меняющий кабинет, принимает dry_run: true — вернёт тело запроса, которое ушло бы в API, и ничего не изменит. Валидация параметров при этом выполняется полностью, так что предпросмотр ловит ошибки до записи:

{ "name": "yandex_direct_campaigns_add", "arguments": { "name": "Тест", "start_date": "2026-08-01", "dry_run": true } }

Отказы API

Любой отказ Директа возвращается агенту помеченным как ошибка, с причиной и следующим шагом — включая частичные отказы, когда запрос выполнен, но отдельные объекты не приняты. Пример реального ответа:

Запрос к API Яндекс.Директа отклонён (код 152).
Причина: Недостаточно баллов
Баллы API: потрачено 0, осталось 0 из 64000
Что делать: Закончились баллы API (units) — это дневной лимит на обращения
к Директу, а не проблема MCP. Баллы начисляются раз в 60 минут...

Ниже подсказки всегда идёт полный ответ API — для разбора нестандартных случаев.

Расход контекста

Полное описание инструментов (tools/list) занимает около 16 800 токенов — 8,4% окна на 200k и 1,7% на 1M. Три четверти веса приходятся на JSON-схемы параметров, на текстовые описания — 17%.

Сокращённый режим (отдельные методы «список / описание / вызов») решено не вводить: выигрыш не оправдывает потерю штатной валидации аргументов на стороне клиента и лишние обмены запрос-ответ, а доля окна не критична. Решение пересмотреть, если число инструментов заметно вырастет.

Замер воспроизводится:

bun run scripts/measure-context.ts

Что осознанно вне этого MCP

MCP покрывает только API Яндекс.Директа. Смежные задачи живут в других контурах и сюда не встраиваются:

  • Вордстат (подбор семантики, частота запросов, спрос) — отдельный API Яндекса, не Директ. Остаётся за скиллом или отдельным MCP. Граница проходит по смыслу данных: частота и спрос — там, аукционные деньги (прогноз цены клика) — здесь, инструментом forecast_bids.
  • Метрика как сервис (создание целей, чтение статистики, сегменты) — отдельный API Метрики. MCP Директа привязывает цель по готовому ID (counter_ids, priority_goals, goal_id в стратегиях) — на этом граница.

Причина: «одно подключение к Яндексу = Директ + Метрика + Вордстат» — это уровень пакета или прокси (например Click.ru), а не одного сервера. Смешение трёх API в одном MCP увеличивает связность и зону отказа.

Лицензия

Apache License 2.0