{
  "markdown": "# yandex-direct-mcp\n\nMCP-сервер и командная строка к API Яндекс Директа v5. Покрыты все **113 методов**,\nпорождённые из машиночитаемой схемы; по умолчанию объявляются девять — те, которыми\nчитают. Остальное включается одной переменной, изменение выключено.\n\nmcp-name: io.github.artgas1/yandex-direct-api-mcp\n\n[![npm](https://img.shields.io/npm/v/yandex-direct-api-mcp)](https://www.npmjs.com/package/yandex-direct-api-mcp)\n[![CI](https://github.com/artgas1/yandex-direct-mcp/actions/workflows/test.yml/badge.svg)](https://github.com/artgas1/yandex-direct-mcp/actions/workflows/test.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)\n\n*[English](./README.en.md)*\n\nРаботает и как MCP-сервер для Claude Code, Cursor, Codex и других клиентов, и как\nобычная команда — если MCP не нужен.\n\n## Что это даёт — за пять секунд\n\n<img src=\"https://raw.githubusercontent.com/artgas1/yandex-direct-mcp/main/assets/demo.gif\" alt=\"Запись прогона в терминале: вызов direct_campaigns_get и две колонки — слева тело ответа, разобранное обычным JSON.parse, справа то же самое после сервера. Бюджет 1000000000 против 1000, идентификатор объявления, испорченный разбором, против точного, обёртка Items против обычного списка и отказ с кодом HTTP 202, распознанный как отказ.\" width=\"100%\">\n\n<sup>Обе колонки настоящие: левая — тело ответа, разобранное обычным <code>JSON.parse</code>,\nто есть так, как его получил бы любой клиент; правая — то, что вернул сервер по JSON-RPC.\nСтрока с идентификатором самодоказательна: слева он испорчен не потому, что так нарисовано,\nа потому что его действительно портит разбор. Ни токена, ни сети: запросы уводятся на локальную\nзаглушку, поэтому прогон повторяется где угодно, включая CI. Повторить у себя — <code>npm run demo</code>, переснять — <code>npm run demo:record</code>\n(нужен <a href=\"https://github.com/charmbracelet/vhs\">vhs</a>).</sup>\n\n\n```bash\nnpx -y yandex-direct-api-mcp\n```\n\n## Покрытие\n\n| что покрыто | служб | методов | из них в `core` | примеры инструментов |\n| --- | ---: | ---: | ---: | --- |\n| Кампании и объявления | 9 | 37 | 3 | `direct_adgroups_get`, `direct_ads_get` |\n| Таргетинг | 9 | 45 | 1 | `direct_keywords_get` |\n| Ставки и стратегии | 4 | 15 | 2 | `direct_bidmodifiers_get`, `direct_keywordbids_get` |\n| Отчёты и справочники | 6 | 9 | 2 | `direct_dictionaries_get`, `direct_reports_get` |\n| Клиенты и агентства | 2 | 7 | 1 | `direct_clients_get` |\n| **всего** | **30** | **113** | **9** | плюс четыре служебных: `direct_catalog`, `direct_fields`, `direct_schema`, `direct_inventory` |\n\nТаблица считается из спеки (`npm run coverage`), а не пишется руками: числа в\nпрозе расходятся со схемой молча, и неправда выглядит ровно как правда.\n\n## Быстрый старт\n\nНужен OAuth-токен Яндекса со scope `direct:api` — https://oauth.yandex.ru/\n(приложению требуется одобренная заявка на доступ к API Директа).\n\n**MCP:**\n\n```json\n{\n  \"mcpServers\": {\n    \"yandex-direct\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"yandex-direct-api-mcp\"],\n      \"env\": { \"YANDEX_DIRECT_TOKEN\": \"ваш-токен\" }\n    }\n  }\n}\n```\n\n**Командная строка:**\n\n```bash\nexport YANDEX_DIRECT_TOKEN=\"ваш-токен\"\n\nyandex-direct-mcp catalog --service campaigns\nyandex-direct-mcp describe campaigns.get\nyandex-direct-mcp call campaigns.get --FieldNames Id --FieldNames Name\n```\n\n## Что этот сервер делает за вас\n\nНе удобства. Каждый пункт — место, где прямой запрос к Директу ошибается\n**молча**: ответ выглядит нормальным, ошибки нет, а число или вывод неверны.\nВсё перечисленное снято прогоном живого API, а не прочитано в документации.\n\n### Суммы приходят умноженными на миллион — всегда\n\n```\nDailyBudget.Amount = 1000000000     ← это 1000 единиц валюты счёта\nCost               = 1234500000     ← это 1234,50 единицы валюты счёта\n```\n\nОшибка ровно в миллион раз, и она не выглядит ошибкой: число правдоподобное,\nего можно сложить, поделить и построить по нему график. Заголовок\n`returnMoneyInMicros`, который выключает микро-единицы в отчётах, **на обычные\nслужбы не действует** — проверено на `campaigns`, значение не изменилось.\n\nСервер приводит суммы к валюте счёта и перечисляет в ответе, какие именно поля\nпересчитал:\n\n```json\n\"_мета\": { \"суммы_переведены_из_микроединиц\": [\"Amount\", \"Refund\", \"Spend\"] }\n```\n\n### Идентификаторы объявлений не помещаются в число JavaScript\n\nТипичный `Id` объявления: `1234567890123456789` — девятнадцать цифр.\n`JSON.parse` держит пятнадцать и превращает его в `1234567890123456800`.\n\nИ это не единичный курьёз: девятнадцатизначные идентификаторы встретились в\nкаждом проверенном кабинете, а не в одном экземпляре. Схема Яндекса объявляет\n358 полей типом `xsd:long` — то есть диапазон до 19 цифр нормален по контракту.\n\nОпасен не сдвиг, а то, как он выходит наружу: **испорченный идентификатор Директ\nпринимает** и отвечает `HTTP 200` с телом `{\"result\":{}}`. То есть отказа нет —\nесть сообщение «такого объявления нет». Пустота как доказательство отсутствия.\n\nСервер разбирает тело так, что длинные целые остаются точными. Отдельно\nпроверено, что API принимает идентификатор строкой, поэтому точность держится\nна всём пути — и на чтении, и на записи.\n\n### Успех определяется телом, а не кодом ответа\n\n| что спросили | код | что в теле |\n|---|---|---|\n| неверный `FieldNames` | **200** | `error_code: 8000` |\n| неизвестный метод | **202** | `error_code: 55` |\n| ошибка в отчёте | **400** | `error_code: \"8000\"` — строкой, не числом |\n| отчёт поставлен в очередь | **201** | пусто, `retryIn: 1` |\n| отчёт считается | **202** | пусто, `retryIn: 10` |\n\nОдин и тот же код `202` означает отказ у `campaigns` и «ещё считается» у\n`reports`. Проверка `res.ok` пропускает первые три строки таблицы: отказ уходит\nмодели как удачный ответ.\n\n### Отчёт приходит не сразу\n\n`201` → `202` → `200`. Замер: до готовности потребовалось три запроса. Повтор идёт с тем же\n`ReportName` — имя и есть ключ поставленной задачи. Сервер ждёт сам.\n\n### Версия пути меняет данные\n\nОдин и тот же запрос:\n\n```\n/json/v5/campaigns    → N кампаний, у всех Type = TEXT_CAMPAIGN\n/json/v501/campaigns  → те же N,     у всех Type = UNIFIED_CAMPAIGN\n```\n\nЭто разные представления с разными наборами глубоких полей, и несовпадение\nотнимает их без всякого признака:\n\n| путь | набор полей | глубокие поля |\n|---|---|---|\n| `v5` | `TextCampaignFieldNames` | приходят |\n| `v5` | `UnifiedCampaignFieldNames` | **пусто, ошибки нет** |\n| `v501` | `TextCampaignFieldNames` | **пусто, ошибки нет** |\n| `v501` | `UnifiedCampaignFieldNames` | приходят |\n\nСтратегия, настройки, счётчики просто отсутствуют — читается как «у кампании\nничего не настроено». Умолчание `v501` (документация называет адресом только\nего), переключается `DIRECT_API_VERSION=v5`, выбранная версия печатается в\nкаждом ответе, а несовпадающий набор полей вызывает предупреждение.\n\n### Список кампаний неполон\n\nКампании Мастера кампаний не отдаются методом `campaigns.get` вовсе — ни\nсписком, ни по явному `Ids`; ответ пустой и без ошибки. Ни `v5`, ни `v501` этого\nне меняют.\n\nПоэтому состав кабинета собирает отдельный инструмент **`direct_inventory`**:\nон склеивает список кампаний и отчёт и помечает каждую строку источником.\nПредупреждения в описании тут мало — оно требует, чтобы читатель помнил про него\nв момент вывода, а вывод делается по данным, которые выглядят нормально.\n\nПрогон на живом кабинете: объединение оказалось на кампанию длиннее списка, и\nэта строка была видна только отчёту. Невидимая для `campaigns.get` кампания при\nэтом откручивается и может нести **основную долю показов** — по списку кампаний\nэтого не заметить.\n\n```\nВНИМАНИЕ: 1 кампаний откручивались, но методом campaigns.get НЕ отдаются\n          (10000017). Управлять ими через API нельзя — только в интерфейсе.\n```\n\n### Предупреждение — это применено, а не отклонено\n\nВ ответе на `add`/`update` каждому входному элементу отвечает выходной.\nРазличать надо по `Errors`; `Warnings` означает «применено с замечанием».\nСчёт по наличию любого содержимого даёт «отклонено всё» там, где применилось\nвсё. Сервер приводит итог отдельной строкой:\n\n```\nUpdateResults: применено 2, отклонено 1, с предупреждениями 1\n```\n\n### Форму списка задаёт тип, а не направление\n\n```\nRegionIds            (maxOccurs=unbounded) → [225, 977]\nRestrictedRegionIds  (тип ArrayOfLong)     → {\"Items\": [225]}\n```\n\nОбе формы одинаковы и на чтении, и на записи. Сервер снимает и ставит обёртку\nпо графу типов, а не по виду значения, поэтому круг «прочитал → поправил →\nзаписал» не рвётся. Вам обе формы видны как обычные массивы.\n\n### Кабинет называется в каждом ответе\n\n`Client-Login` переключает кабинет по-настоящему, и ошибиться в нём можно молча.\nНесуществующий логин отбивается кодом 8800 — это видно сразу. А существующий,\nно не тот, отдаёт полные и правильные данные, просто из другого кабинета:\nпо виду ответа это неотличимо.\n\nПоэтому сервер спрашивает у API, кто отвечает, и пишет ответ в каждый конверт:\n\n```json\n\"_мета\": { \"кабинет\": \"example-login (ClientId 1234567)\", \"версия_api\": \"v501\" }\n```\n\nСпрашивается один раз за запуск и кешируется — `clients.get` стоит 10 баллов.\n\n## Поверхность\n\nОписания всех объявленных инструментов лежат в контексте модели **на каждом\nходу**, вызываете вы их или нет. Поэтому по умолчанию объявляется не всё, что\nумеет API, а то, чем пользуются.\n\n<img src=\"https://raw.githubusercontent.com/artgas1/yandex-direct-mcp/main/assets/surface.gif\" alt=\"Список всех 113 методов API Директа: девять оставлены и выделены, 104 вычеркнуты. Манифест по умолчанию — 27 028 байт против 143 706 у полного каталога.\" width=\"100%\">\n\nЗамер `tools/list` на собранном сервере (`npm run surface`):\n\n| профиль | инструментов | байт | ≈ токенов |\n|---|---|---|---|\n| **`core`** (умолчание) | 13 | 27 028 | 12 455 |\n| `read` | 37 | 61 158 | 28 183 |\n| `all` + `DIRECT_ALLOW_WRITES=1` | 117 | 143 706 | 66 224 |\n\nУмолчание в 5,3 раза легче полного набора. Главный рычаг — вложенные типы не\nразворачиваются в схему: транзитивно `campaigns.add` это 1083 поля и 54 КБ на\nодин инструмент. Вместо разворачивания состав типа назван словами в описании,\nа точная схема выдаётся инструментом `direct_schema` по запросу.\n\nЧего не видно — расскажет сам сервер: инструмент `direct_catalog` перечисляет\nвсе 113 методов и говорит, какие скрыты и как их включить.\n\n## Изменение выключено по умолчанию\n\nИз 113 методов **80 меняют данные, 16 удаляют**. У Директа нет подтверждающего\nшага: `suspend` останавливает показы в момент вызова, `archive` убирает кампанию\nиз работы, `delete` необратим, а на другом конце — деньги.\n\nМеняющие инструменты **не объявляются вовсе**, пока не задан\n`DIRECT_ALLOW_WRITES=1`. Объявлять их и отказывать на вызове — худший вариант:\nконтекст за них платится полностью, а позвать всё равно нельзя.\n\nНеизвестное имя профиля — отказ на старте, а не откат к полной поверхности:\nневерная настройка ограничения не должна превращаться в отсутствие ограничения.\n\nЕсть песочница: `DIRECT_SANDBOX=1` (нужны отдельная регистрация и отдельный\nтокен). Факт включения печатается при старте и в каждом ответе.\n\n## Настройки\n\n| переменная | по умолчанию | что делает |\n|---|---|---|\n| `YANDEX_DIRECT_TOKEN` | — | OAuth-токен, scope `direct:api`. Обязательна |\n| `YANDEX_DIRECT_LOGIN` | — | логин кабинета (**не** почта). Переключает кабинет по-настоящему: под одним токеном отдаёт другой аккаунт со своей квотой. Фактический кабинет сервер называет в каждом ответе |\n| `DIRECT_PROFILE` | `core` | `core`, `read`, `all` |\n| `DIRECT_TOOLS` | — | явный список служб или инструментов, побеждает профиль |\n| `DIRECT_ALLOW_WRITES` | выкл. | объявить меняющие данные инструменты |\n| `DIRECT_API_VERSION` | `v501` | `v501` или `v5` — меняет представление кампаний |\n| `DIRECT_SANDBOX` | выкл. | песочница вместо боевого кабинета |\n| `DIRECT_MAX_OUTPUT_CHARS` | `60000` | потолок ответа; усечение называется вслух |\n\n## Откуда берутся инструменты\n\nНи один метод не описан руками.\n\n| источник | что даёт | почему нужен |\n|---|---|---|\n| WSDL 29 служб + 3 общие XSD | состав, типы, обязательность, массивность, перечисления | единственный полный: в индексе документации нет `vcards`, `smartadtargets`, `dynamictextadtargets`, `dynamicfeedadtargets` |\n| страницы документации | человекочитаемые описания | в WSDL нет ни одного `xs:documentation` |\n| описано явно | служба `reports` | WSDL для неё Директ не отдаёт (404) |\n\nИтог: **30 служб, 113 методов, 609 типов, 240 перечислений.**\n\n```bash\nnpm run spec:fetch    # скачать WSDL и документацию в .cache/\nnpm run spec:build    # собрать spec/direct-api.json\n```\n\n⚠️ **Перечисления из схемы отстают от живого API** и поэтому не становятся\nжёстким фильтром, а идут подсказкой в описание. Сверка с живым API: `campaigns`\nпринимает `CreateTime`, `keywords` — `AutotargetingBrief`,\n`AutotargetingBriefSuggests`, `AutotargetingMode`, которых в схеме нет. Фильтр\nпо отстающему списку запретил бы то, что API умеет, и отказ выглядел бы как\nотсутствие возможности. Право решать остаётся за API.\n\n## Проверки\n\n\n```bash\nnpm test         # 70 тестов, включая отрицательные контроли\nnpm run surface  # замер поверхности по профилям\nnpm run coverage # таблица покрытия для README\nnpm run graphic  # пересобрать графику поверхности (SVG и GIF)\nnpm run smoke    # прогон собранного сервера против живого API (нужен токен)\n```\n\nТесты содержат отрицательные контроли на каждый инвариант — то есть могут\nупасть на том дефекте, ради которого написаны: на порче идентификатора, на\nошибке с кодом 202, на пустой схеме `get`, на пропаже службы и на чтении\nпредупреждений как отказов.\n\n## Скилл — работа без MCP\n\nДля агентов, которым MCP не нужен или недоступен:\n\n```bash\nnpx skills add artgas1/yandex-direct-mcp\n```\n\nСтавит один канонический экземпляр в `.agents/skills/yandex-direct/` и\nсвязывает его с каталогами агентов. Скилл — тонкая надстройка над той же\nкомандой: своей логики у него нет, поэтому расходиться с сервером ему нечем.\n\n## Если выбираете между серверами\n\nСерверов к Директу написано много. Полезные вопросы к любому из них — те же,\nчто перечислены выше: приводит ли суммы из микро-единиц; переживают ли\nдевятнадцатизначные идентификаторы разбор; считается ли `HTTP 200` с телом\n`error` успехом; ждёт ли он отчёт после `201`; отличает ли `Warnings` от\n`Errors`; что делает при опечатке в имени профиля. Ответы стоят одного вызова.\n\n## Лицензия\n\nMIT.\n",
  "bytes": 14251,
  "sha": "0e3dd7abadbeaed634f82394937c87dcee73df630f4e67741c5e8bcff2bddb17",
  "repo_slug": "artgas1/yandex-direct-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_artgas1_yandex_direct_api_mcp_fa52fb35/readme"
}