MCP server for Yandex Direct API v5: 113 methods from the machine-readable schema
MCP server for Yandex Direct API v5 that exposes 113 methods derived from a machine-readable schema. It supports usage as an MCP server for clients such as Claude Code, Cursor, and Codex, and it can also be used as a standard command-line tool when MCP is not required.
🛠️ Key Features
Yandex Direct API v5 integration
113 methods generated from a machine-readable schema
“Narrow surface by default”; change is disabled
🚀 Use Cases
Run Yandex Direct API v5 operations via MCP-capable clients
Execute the same functionality via a CLI command when MCP is unnecessary
⚡ Developer Benefits
Deterministic method set from the schema (“113 methods”)
Can be launched with npx yandex-direct-api-mcp help
OAuth-based access using the required scope direct:api
⚠️ Limitations
Requires an approved Yandex OAuth token with scope direct:api
Change is disabled; the surface is narrow by default
MCP-сервер и командная строка к API Яндекс Директа v5. Покрыты все 113 методов,
порождённые из машиночитаемой схемы; по умолчанию объявляются девять — те, которыми
читают. Остальное включается одной переменной, изменение выключено.
Работает и как MCP-сервер для Claude Code, Cursor, Codex и других клиентов, и как
обычная команда — если MCP не нужен.
Что это даёт — за пять секунд
Обе колонки настоящие: левая — тело ответа, разобранное обычным JSON.parse,
то есть так, как его получил бы любой клиент; правая — то, что вернул сервер по JSON-RPC.
Строка с идентификатором самодоказательна: слева он испорчен не потому, что так нарисовано,
а потому что его действительно портит разбор. Ни токена, ни сети: запросы уводятся на локальную
заглушку, поэтому прогон повторяется где угодно, включая CI. Повторить у себя — npm run demo, переснять — npm run demo:record
(нужен vhs).
bash
npx -y yandex-direct-api-mcp
Покрытие
что покрыто
служб
методов
из них в core
примеры инструментов
Кампании и объявления
9
37
3
direct_adgroups_get, direct_ads_get
Таргетинг
9
45
1
direct_keywords_get
Ставки и стратегии
4
15
2
direct_bidmodifiers_get, direct_keywordbids_get
Отчёты и справочники
6
9
2
direct_dictionaries_get, direct_reports_get
Клиенты и агентства
2
7
1
direct_clients_get
всего
30
113
9
плюс четыре служебных: direct_catalog, direct_fields, direct_schema, direct_inventory
Таблица считается из спеки (npm run coverage), а не пишется руками: числа в
прозе расходятся со схемой молча, и неправда выглядит ровно как правда.
Быстрый старт
Нужен OAuth-токен Яндекса со scope direct:api — https://oauth.yandex.ru/
(приложению требуется одобренная заявка на доступ к API Директа).
export YANDEX_DIRECT_TOKEN="ваш-токен"
yandex-direct-mcp catalog --service campaigns
yandex-direct-mcp describe campaigns.get
yandex-direct-mcp call campaigns.get --FieldNames Id --FieldNames Name
Что этот сервер делает за вас
Не удобства. Каждый пункт — место, где прямой запрос к Директу ошибается
молча: ответ выглядит нормальным, ошибки нет, а число или вывод неверны.
Всё перечисленное снято прогоном живого API, а не прочитано в документации.
Суммы приходят умноженными на миллион — всегда
code
DailyBudget.Amount = 1000000000 ← это 1000 единиц валюты счёта
Cost = 1234500000 ← это 1234,50 единицы валюты счёта
Ошибка ровно в миллион раз, и она не выглядит ошибкой: число правдоподобное,
его можно сложить, поделить и построить по нему график. Заголовок
returnMoneyInMicros, который выключает микро-единицы в отчётах, на обычные
службы не действует — проверено на campaigns, значение не изменилось.
Сервер приводит суммы к валюте счёта и перечисляет в ответе, какие именно поля
пересчитал:
Идентификаторы объявлений не помещаются в число JavaScript
Типичный Id объявления: 1234567890123456789 — девятнадцать цифр.
JSON.parse держит пятнадцать и превращает его в 1234567890123456800.
И это не единичный курьёз: девятнадцатизначные идентификаторы встретились в
каждом проверенном кабинете, а не в одном экземпляре. Схема Яндекса объявляет
358 полей типом xsd:long — то есть диапазон до 19 цифр нормален по контракту.
Опасен не сдвиг, а то, как он выходит наружу: испорченный идентификатор Директ
принимает и отвечает HTTP 200 с телом {"result":{}}. То есть отказа нет —
есть сообщение «такого объявления нет». Пустота как доказательство отсутствия.
Сервер разбирает тело так, что длинные целые остаются точными. Отдельно
проверено, что API принимает идентификатор строкой, поэтому точность держится
на всём пути — и на чтении, и на записи.
Успех определяется телом, а не кодом ответа
что спросили
код
что в теле
неверный FieldNames
200
error_code: 8000
неизвестный метод
202
error_code: 55
ошибка в отчёте
400
error_code: "8000" — строкой, не числом
отчёт поставлен в очередь
201
пусто, retryIn: 1
отчёт считается
202
пусто, retryIn: 10
Один и тот же код 202 означает отказ у campaigns и «ещё считается» у
reports. Проверка res.ok пропускает первые три строки таблицы: отказ уходит
модели как удачный ответ.
Отчёт приходит не сразу
201 → 202 → 200. Замер: до готовности потребовалось три запроса. Повтор идёт с тем же
ReportName — имя и есть ключ поставленной задачи. Сервер ждёт сам.
Версия пути меняет данные
Один и тот же запрос:
code
/json/v5/campaigns → N кампаний, у всех Type = TEXT_CAMPAIGN
/json/v501/campaigns → те же N, у всех Type = UNIFIED_CAMPAIGN
Это разные представления с разными наборами глубоких полей, и несовпадение
отнимает их без всякого признака:
путь
набор полей
глубокие поля
v5
TextCampaignFieldNames
приходят
v5
UnifiedCampaignFieldNames
пусто, ошибки нет
v501
TextCampaignFieldNames
пусто, ошибки нет
v501
UnifiedCampaignFieldNames
приходят
Стратегия, настройки, счётчики просто отсутствуют — читается как «у кампании
ничего не настроено». Умолчание v501 (документация называет адресом только
его), переключается DIRECT_API_VERSION=v5, выбранная версия печатается в
каждом ответе, а несовпадающий набор полей вызывает предупреждение.
Список кампаний неполон
Кампании Мастера кампаний не отдаются методом campaigns.get вовсе — ни
списком, ни по явному Ids; ответ пустой и без ошибки. Ни v5, ни v501 этого
не меняют.
Поэтому состав кабинета собирает отдельный инструмент direct_inventory:
он склеивает список кампаний и отчёт и помечает каждую строку источником.
Предупреждения в описании тут мало — оно требует, чтобы читатель помнил про него
в момент вывода, а вывод делается по данным, которые выглядят нормально.
Прогон на живом кабинете: объединение оказалось на кампанию длиннее списка, и
эта строка была видна только отчёту. Невидимая для campaigns.get кампания при
этом откручивается и может нести основную долю показов — по списку кампаний
этого не заметить.
code
ВНИМАНИЕ: 1 кампаний откручивались, но методом campaigns.get НЕ отдаются
(10000017). Управлять ими через API нельзя — только в интерфейсе.
Предупреждение — это применено, а не отклонено
В ответе на add/update каждому входному элементу отвечает выходной.
Различать надо по Errors; Warnings означает «применено с замечанием».
Счёт по наличию любого содержимого даёт «отклонено всё» там, где применилось
всё. Сервер приводит итог отдельной строкой:
code
UpdateResults: применено 2, отклонено 1, с предупреждениями 1
Обе формы одинаковы и на чтении, и на записи. Сервер снимает и ставит обёртку
по графу типов, а не по виду значения, поэтому круг «прочитал → поправил →
записал» не рвётся. Вам обе формы видны как обычные массивы.
Кабинет называется в каждом ответе
Client-Login переключает кабинет по-настоящему, и ошибиться в нём можно молча.
Несуществующий логин отбивается кодом 8800 — это видно сразу. А существующий,
но не тот, отдаёт полные и правильные данные, просто из другого кабинета:
по виду ответа это неотличимо.
Поэтому сервер спрашивает у API, кто отвечает, и пишет ответ в каждый конверт:
Спрашивается один раз за запуск и кешируется — clients.get стоит 10 баллов.
Поверхность
Описания всех объявленных инструментов лежат в контексте модели на каждом
ходу, вызываете вы их или нет. Поэтому по умолчанию объявляется не всё, что
умеет API, а то, чем пользуются.
Список всех 113 методов API Директа: девять оставлены и выделены, 104 вычеркнуты. Манифест по умолчанию — 27 028 байт против 143 706 у полного каталога.
Замер tools/list на собранном сервере (npm run surface):
профиль
инструментов
байт
≈ токенов
core (умолчание)
13
27 028
12 455
read
37
61 158
28 183
all + DIRECT_ALLOW_WRITES=1
117
143 706
66 224
Умолчание в 5,3 раза легче полного набора. Главный рычаг — вложенные типы не
разворачиваются в схему: транзитивно campaigns.add это 1083 поля и 54 КБ на
один инструмент. Вместо разворачивания состав типа назван словами в описании,
а точная схема выдаётся инструментом direct_schema по запросу.
Чего не видно — расскажет сам сервер: инструмент direct_catalog перечисляет
все 113 методов и говорит, какие скрыты и как их включить.
Изменение выключено по умолчанию
Из 113 методов 80 меняют данные, 16 удаляют. У Директа нет подтверждающего
шага: suspend останавливает показы в момент вызова, archive убирает кампанию
из работы, delete необратим, а на другом конце — деньги.
Меняющие инструменты не объявляются вовсе, пока не задан
DIRECT_ALLOW_WRITES=1. Объявлять их и отказывать на вызове — худший вариант:
контекст за них платится полностью, а позвать всё равно нельзя.
Неизвестное имя профиля — отказ на старте, а не откат к полной поверхности:
неверная настройка ограничения не должна превращаться в отсутствие ограничения.
Есть песочница: DIRECT_SANDBOX=1 (нужны отдельная регистрация и отдельный
токен). Факт включения печатается при старте и в каждом ответе.
Настройки
переменная
по умолчанию
что делает
YANDEX_DIRECT_TOKEN
—
OAuth-токен, scope direct:api. Обязательна
YANDEX_DIRECT_LOGIN
—
логин кабинета (не почта). Переключает кабинет по-настоящему: под одним токеном отдаёт другой аккаунт со своей квотой. Фактический кабинет сервер называет в каждом ответе
DIRECT_PROFILE
core
core, read, all
DIRECT_TOOLS
—
явный список служб или инструментов, побеждает профиль
npm run spec:fetch # скачать WSDL и документацию в .cache/
npm run spec:build # собрать spec/direct-api.json
⚠️ Перечисления из схемы отстают от живого API и поэтому не становятся
жёстким фильтром, а идут подсказкой в описание. Сверка с живым API: campaigns
принимает CreateTime, keywords — AutotargetingBrief,
AutotargetingBriefSuggests, AutotargetingMode, которых в схеме нет. Фильтр
по отстающему списку запретил бы то, что API умеет, и отказ выглядел бы как
отсутствие возможности. Право решать остаётся за API.
Проверки
bash
npm test# 70 тестов, включая отрицательные контроли
npm run surface # замер поверхности по профилям
npm run coverage # таблица покрытия для README
npm run graphic # пересобрать графику поверхности (SVG и GIF)
npm run smoke # прогон собранного сервера против живого API (нужен токен)
Тесты содержат отрицательные контроли на каждый инвариант — то есть могут
упасть на том дефекте, ради которого написаны: на порче идентификатора, на
ошибке с кодом 202, на пустой схеме get, на пропаже службы и на чтении
предупреждений как отказов.
Скилл — работа без MCP
Для агентов, которым MCP не нужен или недоступен:
bash
npx skills add artgas1/yandex-direct-mcp
Ставит один канонический экземпляр в .agents/skills/yandex-direct/ и
связывает его с каталогами агентов. Скилл — тонкая надстройка над той же
командой: своей логики у него нет, поэтому расходиться с сервером ему нечем.
Если выбираете между серверами
Серверов к Директу написано много. Полезные вопросы к любому из них — те же,
что перечислены выше: приводит ли суммы из микро-единиц; переживают ли
девятнадцатизначные идентификаторы разбор; считается ли HTTP 200 с телом
error успехом; ждёт ли он отчёт после 201; отличает ли Warnings от
Errors; что делает при опечатке в имени профиля. Ответы стоят одного вызова.
Лицензия
MIT.
Install
Configuration
Environment variables
YANDEX_DIRECT_TOKENrequiredsecret
OAuth-токен Яндекса со scope direct:api (https://oauth.yandex.ru/)
YANDEX_DIRECT_LOGIN
Логин кабинета для заголовка Client-Login. Нужен агентствам и представителям; для собственного кабинета не задаётся
DIRECT_PROFILEdefault core
Набор объявляемых инструментов: core — узкий набор по умолчанию, read — все читающие, all — вся поверхность
DIRECT_TOOLS
Явный список инструментов или служб через запятую. Побеждает DIRECT_PROFILE
DIRECT_ALLOW_WRITESdefault 0
Разрешить изменяющие методы (add/update/delete). Пока выключено, они не объявляются вовсе. Значения: 1, true, yes
DIRECT_API_VERSIONdefault v501
Версия пути API. Разные версии отдают разные наборы полей одной и той же кампании
DIRECT_SANDBOXdefault 0
Отправлять запросы в песочницу Директа вместо боевого кабинета. Значения: 1, true, yes
DIRECT_MAX_OUTPUT_CHARSdefault 60000
Потолок размера ответа инструмента в символах; сверх него ответ усекается с пометкой