{
  "markdown": "# Yandex Metrika MCP Server\n\nMCP-сервер к API Яндекс Метрики. Покрыты все 108 методов; по умолчанию объявляются **десять** —\nте, которыми считают. Остальное включается одной переменной.\n\nmcp-name: io.github.artgas1/yandex-metrika-mcp-server\n\n[![npm](https://img.shields.io/npm/v/yandex-metrika-mcp-server)](https://www.npmjs.com/package/yandex-metrika-mcp-server)\n[![CI](https://github.com/artgas1/yandex-metrika-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/artgas1/yandex-metrika-mcp/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)\n\n*[English](./README.en.md)*\n\n<img src=\"https://raw.githubusercontent.com/artgas1/yandex-metrika-mcp/main/assets/case.svg\" alt=\"Слева семь шагов в интерфейсе Метрики, справа тот же результат одним вопросом: таблица источников с визитами, целями и конверсией\" width=\"100%\">\n\n```bash\nnpx -y yandex-metrika-mcp-server\n```\n\nФорк [atomkraft/yandex-metrika-mcp](https://github.com/atomkraft/yandex-metrika-mcp) (апстрим — Vadim Bezymianyi, MIT).\nС версии 2.0.0 инструменты не пишутся руками, а порождаются из спеки, собранной по официальной документации.\n\n## Покрытие\n\n| API | методов | из них в профиле `core` | примеры инструментов |\n| --- | ---: | ---: | --- |\n| Management | 95 (21 ресурс) | 4 | `metrika_counter_list`, `metrika_goal_create`, `metrika_segment_update` |\n| Logs | 7 | — | `metrika_logs_create`, `metrika_logs_get`, `metrika_logs_download` |\n| Stat | 6 | 6 | `metrika_stat_data`, `metrika_stat_bytime`, `metrika_stat_pivot` |\n\nИмя инструмента — `metrika_<ресурс>_<действие>`, где ресурс взят из URL самого API без переименований.\nПоэтому `metrika_goal_list` однозначно отображается в `GET /management/v1/counter/{id}/goals`\nи в свою страницу документации.\n\n## Контракт\n\nСервер переписан из-за двух наблюдавшихся отказов: он отдавал не то, что просили, и молча\nподмешивал фильтр. Отсюда четыре правила, каждое закрыто тестом.\n\n1. **Никакой молчаливой подмены.** Что попросили — то и уходит в API. Сервер не досочиняет\n   ни измерений, ни периода, ни фильтров.\n2. **Всё, что сервер добавил от себя, видно в ответе.** Ответ приходит как\n   `{\"_meta\": {...}, \"data\": {...}}`, где `_meta.applied_by_server` перечисляет добавленное,\n   а `_meta.notes` — принятые за вызывающего решения.\n3. **Отказ остаётся отказом.** Ошибка API возвращается с `isError: true` и телом ответа Метрики.\n   Повтор делается по статусу (429/500/502/503/504 и сетевые сбои), а не по подстроке\n   в тексте; у 429 соблюдается `Retry-After` с потолком 30 секунд. Число повторов всегда\n   видно в `_meta.retries`.\n4. **Обрезание выдачи видно.** В `_meta` едут `rows_returned`, `rows_total` и `truncated` —\n   Метрика режет ответ по умолчанию, и молчать об этом нельзя. Если сервер сам урезал ответ\n   по потолку длины, это отдельно объявлено в `_meta.truncated_by_server` с числом\n   выброшенных строк.\n5. **Секреты не уезжают в ответ.** У `metrika_measurement_delete` есть параметр `token`;\n   в показанном `_meta.request_url` его значение заменено на `REDACTED`. Сам OAuth-токен\n   уходит только заголовком и в ответе не появляется никогда.\n\n### Фильтр роботов\n\nВ отчётах Stat API по умолчанию применяется собственный флаг робота Метрики, и только он:\n\n```\nym:s:isRobot=='no'\n```\n\nОн **объявлен**: виден в схеме инструмента, отключается параметром `human_traffic_only: false`\nи всегда перечислен в `_meta.applied_by_server`. Если в запросе есть метрики `ym:ad:` или\n`ym:ev:`, фильтр не применяется (Метрика отвечает на такое сочетание 400) — и это попадает\nв `_meta.notes`, а не остаётся молчаливым исключением.\n\nСвоё условие задаётся переменной `METRIKA_TRAFFIC_FILTER` — **целиком**, включая `isRobot`,\nесли он нужен:\n\n```\nMETRIKA_TRAFFIC_FILTER=\"ym:s:isRobot=='no' AND ym:s:browserName!='HeadlessChrome'\"\n```\n\nЭто образец формы, а не рекомендация. Какой рез верен — зависит от того, какие боты ходят\nименно к вам: отсечка по стране, по заголовку браузера или по подсети осмысленна только на\nсвоих данных. Копировать чужой список бессмысленно и опасно: он вырежет живой трафик.\n\nЗаданное своё условие сервер называет в stderr при старте — оно меняет числа в каждом\nотчёте, и молчать об этом нельзя.\n\n### Сравнение периодов: ответ, который выглядит валидным\n\nУ `metrika_stat_comparison` и `metrika_stat_comparison_drilldown` даты периодов\n**необязательны**, и Метрика на их отсутствие не ругается. Она подставляет собственное окно\n(последняя неделя) в **оба** набора и возвращает сравнение периода с самим собой:\n\n```\nmetrika_stat_comparison(ids, metrics)  →  totals a == b\n                                          query  date1_a == date1_b\n```\n\nОтказывать сервер не будет — запрос ушёл ровно тем, каким его собрали. Но такой ответ\nприходит с пометкой в `_meta.notes`: и когда даты не заданы, и когда периоды совпали явно.\n\n## Как устроена спека\n\nПубличного `openapi.json` у Метрики нет, но каждая страница метода сгенерирована из OpenAPI\nдвижком Diplodoc и отдаётся как `text/markdown`. Семантика (тип, `required`, комбинатор,\nассертация) лежит в CSS-классах вида `{.json-schema-property}`, поэтому спека собирается\nпострочным сканером по классам, а не markdown-парсером.\n\n```bash\nnpm run spec:fetch   # скачать llms.txt и 108 страниц в .cache/docs/\nnpm run spec:build   # разобрать их в spec/metrika-api.json\nnpm test             # тесты спеки и схем инструментов\nnpm run smoke        # живые вызовы к API (нужен YANDEX_API_KEY)\n```\n\n`spec/metrika-api.json` коммитится — это состав API на момент сборки. Тест на дрейф сверяет\nего с `llms.txt`: Яндекс добавил или удалил метод — тест краснеет.\n\nРазбор привязан к версии генератора (`Diplodoc Platform v5.57.3`): вся семантика висит на его\nклассах, поэтому расхождение версии останавливает сборку спеки, а не молча портит её.\n\n## Запуск\n\n> **По умолчанию объявляются десять инструментов из 108** — те, которыми считают. Управление\n> счётчиками и целями, доступы и Logs API включаются переменной `METRIKA_PROFILE`; подробности\n> ниже, в разделе [«Почему по умолчанию не всё»](#почему-по-умолчанию-не-всё).\n>\n> Спросить у самого сервера тоже можно: инструмент `metrika_catalog_list` перечисляет, что\n> объявлено, что скрыто и как это включить.\n\n```bash\nnpm install\nnpm run build\nYANDEX_API_KEY=<OAuth-токен с scope direct:api / metrika> npm start\n```\n\nТокен — OAuth Яндекса, тот же, что используется для Директа и Вебмастера.\n\n### Подключение к клиенту\n\n```json\n{\n  \"mcpServers\": {\n    \"yandex-metrika-mcp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"yandex-metrika-mcp-server@3\"],\n      \"env\": { \"YANDEX_API_KEY\": \"...\" }\n    }\n  }\n}\n```\n\nИз локальной сборки — то же самое, но `\"command\": \"node\"` и путь до `build/index.js`.\n\nМажор в строке запуска закреплён намеренно: смена мажорной версии меняет набор\nинструментов по умолчанию, и получать это молча при старте агента не нужно.\n\n### Переменные окружения\n\n| Переменная | По умолчанию | Что делает |\n| --- | --- | --- |\n| `YANDEX_API_KEY` | — | OAuth-токен. Без него сервер не стартует. |\n| `METRIKA_PROFILE` | `core` | Какая часть каталога объявляется: `core` (10 инструментов), `read` (все 51 читающих), `all` (все 108). Неизвестное значение роняет старт. |\n| `METRIKA_ALLOW_WRITES` | не задана | `1` разрешает и **объявляет** 57 инструментов, меняющих данные. Пока не задана — их нет в `tools/list` вовсе. |\n| `METRIKA_TOOLS` | пусто | Своя выборка через запятую: раздел (`stat`, `logs`, `management`), префикс имени (`metrika_goal`) или точное имя. Задана — побеждает профиль. |\n| `METRIKA_TRAFFIC_FILTER` | `ym:s:isRobot=='no'` | Условие сегментации, добавляемое к отчётам Stat. Задаётся целиком. |\n| `METRIKA_MAX_OUTPUT_CHARS` | `120000` | Потолок длины ответа одного вызова. Выгрузка Logs API в него обычно не помещается — сутки визитов это сотни тысяч символов; урезание объявляется в `_meta.truncated_by_server`. |\n| `METRIKA_API_BASE` | пусто | Подмена адреса API (прокси, заглушка в тестах). Факт подмены печатается в stderr. |\n\n### Как узнать, что скрыто, не открывая README\n\nИнструмент **`metrika_catalog_list`** объявлен в любом профиле и отвечает из спеки, лежащей в\nпакете, — ни токена, ни сети ему не нужно:\n\n```json\n{\n  \"profile\": \"METRIKA_PROFILE=core\",\n  \"api_methods_total\": 108,\n  \"api_methods_declared\": 10,\n  \"api_methods_hidden\": 98,\n  \"writes_enabled\": false,\n  \"declared_tools\": { \"Stat API — отчёты\": [\"metrika_stat_data\", \"…\"] },\n  \"hidden_tools\": { \"Management API — …\": [\"metrika_goal_create\", \"…\"] },\n  \"how_to_widen\": [\"METRIKA_PROFILE=read — …\", \"METRIKA_PROFILE=all вместе с METRIKA_ALLOW_WRITES=1 — …\"]\n}\n```\n\nОн существует по простой причине: **сервер, который что-то скрыл, обязан уметь сказать, что\nименно и как это включить.** `instructions` видит модель, но не человек — в интерфейс клиента\nони не показываются; стартовую строку в stderr в обычной работе тоже никто не открывает. Без\nэтого инструмента узнать про остальные 98 можно было только придя сюда.\n\nСписок инструментов в ответе строится из того же отбора, по которому они регистрируются, —\nразойтись с реальностью ему негде, и это проверено тестом.\n\n### Почему по умолчанию не всё\n\n<img src=\"https://raw.githubusercontent.com/artgas1/yandex-metrika-mcp/main/assets/surface.svg\" alt=\"Из 108 инструментов по умолчанию объявляются 10, остальные 98 вычеркнуты\" width=\"100%\">\n\nОписания объявленных инструментов лежат в контексте модели, когда клиент их загрузил. Это\nцена сервера, которую платят за сам факт подключения, а не за вызовы. Замер `tools/list`\n(09.09.2026):\n\n| Профиль | Инструментов | `tools/list` | токенов |\n| --- | ---: | ---: | ---: |\n| `core` (по умолчанию) | 10 + каталог | 32 181 Б | **14,8 тыс.** |\n| `read` | 51 + каталог | 68 074 Б | ~31 тыс. — оценка |\n| `all` + `METRIKA_ALLOW_WRITES=1` | 108 + каталог | 158 301 Б | ~73 тыс. — оценка |\n\nЗамер `core` — 14,5 тысячи до появления каталога и 14,8 после: сам инструмент стоит около\n670 байт схемы, примерно 2% набора. Его ответ не входит в эту цену — он платится только при\nвызове.\n\nБайты точные, их воспроизведёт любой: сериализуй ответ `tools/list` и посчитай длину.\nС токенами сложнее, и здесь стоит сказать прямо.\n\n⚠️ **Замер честный только у `core`** — его дал `/context` клиента, который считает\nсобственным токенизатором. Две другие строки пересчитаны из байтов по калибровке\n**2,17 байта на токен**, снятой с той же строки `core`.\n\nХодовая эвристика «4 символа на токен» здесь **врёт почти вдвое**: она выведена на\nанглийском тексте, а описания у этого сервера русские, и кириллица в BPE токенизируется\nпримерно вдвое хуже латиницы. Первая редакция этой таблицы была построена именно на ней и\nназывала для `core` 7,9k вместо 14,5k. Если считаешь бюджет контекста для сервера с\nне-английскими описаниями — считай токенизатором, а не делением на четыре.\n\nСостав `core` выведен из замера реального использования, а не из вкуса: шесть отчётов Stat\nплюс справочники, без которых отчёт не собрать (`metrika_counter_list`, `metrika_counter_get`,\n`metrika_goal_list`, `metrika_segment_list`). Порог веса стоит тестом — манифест не может\nподорожать молча. Порог в тесте стоит на **байтах**: они не зависят ни от токенизатора, ни\nот языка описаний.\n\n## Безопасность\n\n- **Запись выключена по умолчанию, и меняющие инструменты не объявляются вовсе.** Среди\n  методов четырнадцать `DELETE` и пять удаляющих `POST` (`.../measurement/delete`,\n  `.../expense/delete`, `.../logrequest/{id}/clean` и т. д.). Цена ошибочного вызова —\n  удалённый счётчик или цель без возможности восстановить историю. Модель не может позвать\n  то, чего не видит в `tools/list`; как включить — сказано в `instructions` сервера.\n- **Аннотации проставлены на всех инструментах** (`readOnlyHint`, `destructiveHint`,\n  `idempotentHint`, `openWorldHint`). Клиент по ним отличает чтение от удаления: удаление под\n  глаголом `POST` помечено разрушающим, `PUT` — тоже, потому что заменяет сущность целиком.\n- **Ответы Метрики — недоверенные данные.** В отчётах лежат поисковые фразы, заголовки\n  страниц, реферера и значения UTM, то есть строки, которые пишут посетители сайта. Любой\n  может зайти на сайт по ссылке с текстом внутри и увидеть его в отчёте. У всех инструментов\n  `openWorldHint: true`, а в `_meta.notes` отчётов и выгрузок едет напоминание, что это данные,\n  а не инструкции.\n- **Транспорт — только stdio**, токен передаётся переменной окружения; сетевого слушателя\n  сервер не открывает.\n\n## Политика приватности\n\nСервер не собирает, не хранит и никуда не передаёт данные о вас. Ни телеметрии, ни аналитики,\nни обращений к серверам автора — их не существует: под этот пакет не поднято никакой\nинфраструктуры.\n\nЕдинственный сетевой адресат — `https://api-metrika.yandex.net`. Токен читается из\n`YANDEX_API_KEY` в память процесса и никуда не пишется: ни в файл, ни в stdout, ни в тело\nответа. Данные отчётов не кэшируются на диск и не переживают процесс.\n\nДанные, которые вы запрашиваете, обрабатывает Яндекс как оператор Метрики — на это\nраспространяется [его политика](https://yandex.ru/legal/confidential/), а не эта.\n\nПолный текст: [`PRIVACY.md`](./PRIVACY.md).\n\n## Установка одним файлом (MCPB)\n\nДля Claude Desktop и других клиентов, понимающих MCP-бандлы, есть `.mcpb`-файл — он лежит в\n[релизах](https://github.com/artgas1/yandex-metrika-mcp/releases). Открываете файл, вводите\nтокен в окне установки — всё.\n\nБандл собирается из того же кода тем же тегом (`npm run mcpb`), а его манифест **генерируется**\nиз `package.json` и профиля — не пишется руками, поэтому разойтись с сервером ему негде; это\nпроверяется тестом.\n\n⚠️ **В бандле нельзя включить запись.** Цена ошибочного вызова — удалённый счётчик или цель\nбез возможности восстановить историю, и щёлкать таким переключателем в окне установки нечего.\nНужна запись — ставьте пакет с npm и включайте её осознанно, переменной окружения.\n\n## Проверки\n\n```bash\nnpm test          # 67 тестов: спека, схемы, протокол MCP, поверхность и её бюджет\nnpm run protocol  # только протокольные: stdio, tools/list, tools/call, отказы\nnpm run smoke     # живые вызовы к API (нужен YANDEX_API_KEY)\n```\n\nПротокольные тесты поднимают сервер как подпроцесс и говорят с ним по JSON-RPC — тем же\nспособом, каким это делает клиент. Сеть при этом не нужна: `METRIKA_API_BASE` уводит запросы\nна заглушку. Проверяется в том числе то, чего не видно изнутри: что в stdout не попадает\nничего, кроме JSON-RPC, что отказ API приезжает как `isError`, а не как успешный текст, и что\nзапись действительно заблокирована.\n\n### Чего в проверках НЕТ\n\n**Евала выбора инструмента.** Это единственная проверка, которую не заменяют ни снапшот\nсхемы, ни протокольный тест: описания могут быть синтаксически безупречны, а модель всё\nравно возьмёт не тот инструмент. Тесты этого не видят по построению — они зовут инструмент\nпо имени, то есть выбор уже сделан за модель.\n\nЗдесь это осознанный пропуск, а не забытый пункт. Профиль по умолчанию — десять\nинструментов, из них шесть отчётов Stat различаются формой ответа, а не темой, и путать их\nмодели особо не с чем. Евал становится нужен, когда поверхность по умолчанию расширяется\nили когда в неё попадают инструменты с пересекающимися описаниями, — тогда его надо писать\n**до** расширения, а не после.\n\n## Что изменилось в 2.0.0\n\nУдалены 26 инструментов-обёрток над пресетами Stat API (`get_visits`, `sources_summary`,\n`get_page_performance` и прочие). Они покрывали малую часть API, зашивали измерения и период\nв код и не давали задать произвольный запрос. Их заменяют `metrika_stat_*`, принимающие\nпараметры Stat API как есть.\n\nПоявились методы, которых не было вовсе: список счётчиков, цели, сегменты, фильтры,\nразрешения, расходы, офлайн-конверсии и весь Logs API. Раньше идентификатор счётчика\nприходилось знать заранее — теперь его можно найти.\n\n## Что изменилось в 2.1.0\n\nСервер довели до состояния, в котором его не страшно оставить агенту.\n\n- **Аннотации на всех 108 инструментах.** До этого клиент не отличал `metrika_counter_list`\n  от `metrika_counter_delete`.\n- **Запись выключена по умолчанию** (`METRIKA_ALLOW_WRITES`).\n- **Найден и починен дефект разбора документации.** Ассертации размечены строкой, где\n  значение стоит *после* закрывающей скобки класса, — распознаватель свойств заякорен на\n  конец строки и такие строки не матчил вовсе. В итоге до спеки не доезжало **ни одного**\n  примера, значения по умолчанию или границы, а часть их падала в описание соседнего поля.\n  Сейчас в спеке 288 примеров, 69 значений по умолчанию и 155 ограничений; ограничения\n  переносятся в схему инструмента, примеры и значения по умолчанию — в описания параметров.\n- **Найдена и починена потеря обязательности.** Параметры вида «один из N типов» (`goal`\n  у создания и правки цели, `grant` у выдачи доступа) собирались как `z.unknown()`, а он\n  в zod необязателен, — обязательное поле уезжало клиенту как опциональное. Теперь это\n  объединение реальных форм, и обязательность на месте.\n- **Ссылки на сущности разворачиваются** на один уровень: у 23 параметров тела вместо\n  свободного объекта видны настоящие поля.\n- **Послабления на входе там, где они безвредны.** Число строкой, булево словом, список\n  через запятую в строке запроса — принимаются; в теле запроса, где важен точный JSON,\n  не принимаются.\n- **Потолок длины ответа** с объявленным урезанием: выгрузка Logs API бывает в сотни мегабайт.\n- **Вычистка секретов** из показанного `request_url`.\n- **Повтор на 429** с соблюдением `Retry-After`.\n- **SDK обновлён** до 1.30 — на 1.17 висели три опубликованных уязвимости, две высокие;\n  `npm audit --audit-level=high` теперь часть CI.\n- **Починена джоба дрейфа в CI.** Она запускала тесты через `| tee` без `pipefail`, поэтому\n  код возврата брался у `tee` и джоба оставалась зелёной при любом падении теста.\n",
  "bytes": 17628,
  "sha": "5a5c3be2d5df3666815b42289ea88d5aa1c4e879207cc859cc40fe36e12551db",
  "repo_slug": "artgas1/yandex-metrika-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_artgas1_yandex_metrika_mcp_ser_4359d59e/readme"
}