Back to the catalog

io.github.artgas1/yandex-metrika-mcp-server

Полное покрытие API Яндекс Метрики: Stat, Management и Logs — 108 методов, 108 инструментов

Open source Open in the app JSON README (API)

About

Полное покрытие API Яндекс Метрики: Stat, Management и Logs — 108 методов, 108 инструментов

Details

Kind
MCP servers
Topic
No topic detected
Publisher
artgas1
Origin
official
Category
ferramentas
Transport
local
Version
3.2.1
Last push
2026-09-08T22:49:00Z
Repository state
ativo
Language
JavaScript
License
NOASSERTION
Added
2026-09-08 11:16:12
Updated
2026-09-08 22:05:06
Origin id
io.github.artgas1/yandex-metrika-mcp-server

README

# Yandex Metrika MCP Server

MCP-сервер к API Яндекс Метрики. Покрыты все 108 методов; по умолчанию объявляются **десять** —
те, которыми считают. Остальное включается одной переменной.

mcp-name: io.github.artgas1/yandex-metrika-mcp-server

[![npm](https://img.shields.io/npm/v/yandex-metrika-mcp-server)](https://www.npmjs.com/package/yandex-metrika-mcp-server)
[![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)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)

*[English](./README.en.md)*

<img src="https://raw.githubusercontent.com/artgas1/yandex-metrika-mcp/main/assets/case.svg" alt="Слева семь шагов в интерфейсе Метрики, справа тот же результат одним вопросом: таблица источников с визитами, целями и конверсией" width="100%">

```bash
npx -y yandex-metrika-mcp-server
```

Форк [atomkraft/yandex-metrika-mcp](https://github.com/atomkraft/yandex-metrika-mcp) (апстрим — Vadim Bezymianyi, MIT).
С версии 2.0.0 инструменты не пишутся руками, а порождаются из спеки, собранной по официальной документации.

## Покрытие

| API | методов | из них в профиле `core` | примеры инструментов |
| --- | ---: | ---: | --- |
| Management | 95 (21 ресурс) | 4 | `metrika_counter_list`, `metrika_goal_create`, `metrika_segment_update` |
| Logs | 7 | — | `metrika_logs_create`, `metrika_logs_get`, `metrika_logs_download` |
| Stat | 6 | 6 | `metrika_stat_data`, `metrika_stat_bytime`, `metrika_stat_pivot` |

Имя инструмента — `metrika_<ресурс>_<действие>`, где ресурс взят из URL самого API без переименований.
Поэтому `metrika_goal_list` однозначно отображается в `GET /management/v1/counter/{id}/goals`
и в свою страницу документации.

## Контракт

Сервер переписан из-за двух наблюдавшихся отказов: он отдавал не то, что просили, и молча
подмешивал фильтр. Отсюда четыре правила, каждое закрыто тестом.

1. **Никакой молчаливой подмены.** Что попросили — то и уходит в API. Сервер не досочиняет
   ни измерений, ни периода, ни фильтров.
2. **Всё, что сервер добавил от себя, видно в ответе.** Ответ приходит как
   `{"_meta": {...}, "data": {...}}`, где `_meta.applied_by_server` перечисляет добавленное,
   а `_meta.notes` — принятые за вызывающего решения.
3. **Отказ остаётся отказом.** Ошибка API возвращается с `isError: true` и телом ответа Метрики.
   Повтор делается по статусу (429/500/502/503/504 и сетевые сбои), а не по подстроке
   в тексте; у 429 соблюдается `Retry-After` с потолком 30 секунд. Число повторов всегда
   видно в `_meta.retries`.
4. **Обрезание выдачи видно.** В `_meta` едут `rows_returned`, `rows_total` и `truncated` —
   Метрика режет ответ по умолчанию, и молчать об этом нельзя. Если сервер сам урезал ответ
   по потолку длины, это отдельно объявлено в `_meta.truncated_by_server` с числом
   выброшенных строк.
5. **Секреты не уезжают в ответ.** У `metrika_measurement_delete` есть параметр `token`;
   в показанном `_meta.request_url` его значение заменено на `REDACTED`. Сам OAuth-токен
   уходит только заголовком и в ответе не появляется никогда.

### Фильтр роботов

В отчётах Stat API по умолчанию применяется собственный флаг робота Метрики, и только он:

```
ym:s:isRobot=='no'
```

Он **объявлен**: виден в схеме инструмента, отключается параметром `human_traffic_only: false`
и всегда перечислен в `_meta.applied_by_server`. Если в запросе есть метрики `ym:ad:` или
`ym:ev:`, фильтр не применяется (Метрика отвечает на такое сочетание 400) — и это попадает
в `_meta.notes`, а не остаётся молчаливым исключением.

Своё условие задаётся переменной `METRIKA_TRAFFIC_FILTER` — **целиком**, включая `isRobot`,
если он нужен:

```
METRIKA_TRAFFIC_FILTER="ym:s:isRobot=='no' AND ym:s:browserName!='HeadlessChrome'"
```

Это образец формы, а не рекомендация. Какой рез верен — зависит от того, какие боты ходят
именно к вам: отсечка по стране, по заголовку браузера или по подсети осмысленна только на
своих данных. Копировать чужой список бессмысленно и опасно: он вырежет живой трафик.

Заданное своё условие сервер называет в stderr при старте — оно меняет числа в каждом
отчёте, и молчать об этом нельзя.

### Сравнение периодов: ответ, который выглядит валидным

У `metrika_stat_comparison` и `metrika_stat_comparison_drilldown` даты периодов
**необязательны**, и Метрика на их отсутствие не ругается. Она подставляет собственное окно
(последняя неделя) в **оба** набора и возвращает сравнение периода с самим собой:

```
metrika_stat_comparison(ids, metrics)  →  totals a == b
                                          query  date1_a == date1_b
```

Отказывать сервер не будет — запрос ушёл ровно тем, каким его собрали. Но такой ответ
приходит с пометкой в `_meta.notes`: и когда даты не заданы, и когда периоды совпали явно.

## Как устроена спека

Публичного `openapi.json` у Метрики нет, но каждая страница метода сгенерирована из OpenAPI
движком Diplodoc и отдаётся как `text/markdown`. Семантика (тип, `required`, комбинатор,
ассертация) лежит в CSS-классах вида `{.json-schema-property}`, поэтому спека собирается
построчным сканером по классам, а не markdown-парсером.

```bash
npm run spec:fetch   # скачать llms.txt и 108 страниц в .cache/docs/
npm run spec:build   # разобрать их в spec/metrika-api.json
npm test             # тесты спеки и схем инструментов
npm run smoke        # живые вызовы к API (нужен YANDEX_API_KEY)
```

`spec/metrika-api.json` коммитится — это состав API на момент сборки. Тест на дрейф сверяет
его с `llms.txt`: Яндекс добавил или удалил метод — тест краснеет.

Разбор привязан к версии генератора (`Diplodoc Platform v5.57.3`): вся семантика висит на его
классах, поэтому расхождение версии останавливает сборку спеки, а не молча портит её.

## Запуск

> **По умолчанию объявляются десять инструментов из 108** — те, которыми считают. Управление
> счётчиками и целями, доступы и Logs API включаются переменной `METRIKA_PROFILE`; подробности
> ниже, в разделе [«Почему по умолчанию не всё»](#почему-по-умолчанию-не-всё).
>
> Спросить у самого сервера тоже можно: инструмент `metrika_catalog_list` перечисляет, что
> объявлено, что скрыто и как это включить.

```bash
npm install
npm run build
YANDEX_API_KEY=<OAuth-токен с scope direct:api / metrika> npm start
```

Токен — OAuth Яндекса, тот же, что используется для Директа и Вебмастера.

### Подключение к клиенту

```json
{
  "mcpServers": {
    "yandex-metrika-mcp": {
      "command": "npx",
      "args": ["-y", "yandex-metrika-mcp-server@3"],
      "env": { "YANDEX_API_KEY": "..." }
    }
  }
}
```

Из локальной сборки — то же самое, но `"command": "node"` и путь до `build/index.js`.

Мажор в строке запуска закреплён намеренно: смена мажорной версии меняет набор
инструментов по умолчанию, и получать это молча при старте агента не нужно.

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

| Переменная | По умолчанию | Что делает |
| --- | --- | --- |
| `YANDEX_API_KEY` | — | OAuth-токен. Без него сервер не стартует. |
| `METRIKA_PROFILE` | `core` | Какая часть каталога объявляется: `core` (10 инструментов), `read` (все 51 читающих), `all` (все 108). Неизвестное значение роняет старт. |
| `METRIKA_ALLOW_WRITES` | не задана | `1` разрешает и **объявляет** 57 инструментов, меняющих данные. Пока не задана — их нет в `tools/list` вовсе. |
| `METRIKA_TOOLS` | пусто | Своя выборка через запятую: раздел (`stat`, `logs`, `management`), префикс имени (`metrika_goal`) или точное имя. Задана — побеждает профиль. |
| `METRIKA_TRAFFIC_FILTER` | `ym:s:isRobot=='no'` | Условие сегментации, добавляемое к отчётам Stat. Задаётся целиком. |
| `METRIKA_MAX_OUTPUT_CHARS` | `120000` | Потолок длины ответа одного вызова. Выгрузка Logs API в него обычно не помещается — сутки визитов это сотни тысяч символов; урезание объявляется в `_meta.truncated_by_server`. |
| `METRIKA_API_BASE` | пусто | Подмена адреса API (прокси, заглушка в тестах). Факт подмены печатается в stderr. |

### Как узнать, что скрыто, не открывая README

Инструмент **`metrika_catalog_list`** объявлен в любом профиле и отвечает из спеки, лежащей в
пакете, — ни токена, ни сети ему не нужно:

```json
{
  "profile": "METRIKA_PROFILE=core",
  "api_methods_total": 108,
  "api_methods_declared": 10,
  "api_methods_hidden": 98,
  "writes_enabled": false,
  "declared_tools": { "Stat API — отчёты": ["metrika_stat_data", "…"] },
  "hidden_tools": { "Management API — …": ["metrika_goal_create", "…"] },
  "how_to_widen": ["METRIKA_PROFILE=read — …", "METRIKA_PROFILE=all вместе с METRIKA_ALLOW_WRITES=1 — …"]
}
```

Он существует по простой причине: **сервер, который что-то скрыл, обязан уметь сказать, что
именно и как это включить.** `instructions` видит модель, но не человек — в интерфейс клиента
они не показываются; стартовую строку в stderr в обычной работе тоже никто не открывает. Без
этого инструмента узнать про остальные 98 можно было только придя сюда.

Список инструментов в ответе строится из того же отбора, по которому они регистрируются, —
разойтись с реальностью ему негде, и это проверено тестом.

### Почему по умолчанию не всё

<img src="https://raw.githubusercontent.com/artgas1/yandex-metrika-mcp/main/assets/surface.svg" alt="Из 108 инструментов по умолчанию объявляются 10, остальные 98 вычеркнуты" width="100%">

Описания объявленных инструментов лежат в контексте модели, когда клиент их загрузил. Это
цена сервера, которую платят за сам факт подключения, а не за вызовы. Замер `tools/list`
(09.09.2026):

| Профиль | Инструментов | `tools/list` | токенов |
| --- | ---: | ---: | ---: |
| `core` (по умолчанию) | 10 + каталог | 32 181 Б | **14,8 тыс.** |
| `read` | 51 + каталог | 68 074 Б | ~31 тыс. — оценка |
| `all` + `METRIKA_ALLOW_WRITES=1` | 108 + каталог | 158 301 Б | ~73 тыс. — оценка |

Замер `core` — 14,5 тысячи до появления каталога и 14,8 после: сам инструмент стоит около
670 байт схемы, примерно 2% набора. Его ответ не входит в эту цену — он платится только при
вызове.

Байты точные, их воспроизведёт любой: сериализуй ответ `tools/list` и посчитай длину.
С токенами сложнее, и здесь стоит сказать прямо.

⚠️ **Замер честный только у `core`** — его дал `/context` клиента, который считает
собственным токенизатором. Две другие строки пересчитаны из байтов по калибровке
**2,17 байта на токен**, снятой с той же строки `core`.

Ходовая эвристика «4 символа на токен» здесь **врёт почти вдвое**: она выведена на
английском тексте, а описания у этого сервера русские, и кириллица в BPE токенизируется
примерно вдвое хуже латиницы. Первая редакция этой таблицы была построена именно на ней и
называла для `core` 7,9k вместо 14,5k. Если считаешь бюджет контекста для сервера с
не-английскими описаниями — считай токенизатором, а не делением на четыре.

Состав `core` выведен из замера реального использования, а не из вкуса: шесть отчётов Stat
плюс справочники, без которых отчёт не собрать (`metrika_counter_list`, `metrika_counter_get`,
`metrika_goal_list`, `metrika_segment_list`). Порог веса стоит тестом — манифест не может
подорожать молча. Порог в тесте стоит на **байтах**: они не зависят ни от токенизатора, ни
от языка описаний.

## Безопасность

- **Запись выключена по умолчанию, и меняющие инструменты не объявляются вовсе.** Среди
  методов четырнадцать `DELETE` и пять удаляющих `POST` (`.../measurement/delete`,
  `.../expense/delete`, `.../logrequest/{id}/clean` и т. д.). Цена ошибочного вызова —
  удалённый счётчик или цель без возможности восстановить историю. Модель не может позвать
  то, чего не видит в `tools/list`; как включить — сказано в `instructions` сервера.
- **Аннотации проставлены на всех инструментах** (`readOnlyHint`, `destructiveHint`,
  `idempotentHint`, `openWorldHint`). Клиент по ним отличает чтение от удаления: удаление под
  глаголом `POST` помечено разрушающим, `PUT` — тоже, потому что заменяет сущность целиком.
- **Ответы Метрики — недоверенные данные.** В отчётах лежат поисковые фразы, заголовки
  страниц, реферера и значения UTM, то есть строки, которые пишут посетители сайта. Любой
  может зайти на сайт по ссылке с текстом внутри и увидеть его в отчёте. У всех инструментов
  `openWorldHint: true`, а в `_meta.notes` отчётов и выгрузок едет напоминание, что это данные,
  а не инструкции.
- **Транспорт — только stdio**, токен передаётся переменной окружения; сетевого слушателя
  сервер не открывает.

## Политика приватности

Сервер не собирает, не хранит и никуда не передаёт данные о вас. Ни телеметрии, ни аналитики,
ни обращений к серверам автора — их не существует: под этот пакет не поднято никакой
инфраструктуры.

Единственный сетевой адресат — `https://api-metrika.yandex.net`. Токен читается из
`YANDEX_API_KEY` в память процесса и никуда не пишется: ни в файл, ни в stdout, ни в тело
ответа. Данные отчётов не кэшируются на диск и не переживают процесс.

Данные, которые вы запрашиваете, обрабатывает Яндекс как оператор Метрики — на это
распространяется [его политика](https://yandex.ru/legal/confidential/), а не эта.

Полный текст: [`PRIVACY.md`](./PRIVACY.md).

## Установка одним файлом (MCPB)

Для Claude Desktop и других клиентов, понимающих MCP-бандлы, есть `.mcpb`-файл — он лежит в
[релизах](https://github.com/artgas1/yandex-metrika-mcp/releases). Открываете файл, вводите
токен в окне установки — всё.

Бандл собирается из того же кода тем же тегом (`npm run mcpb`), а его манифест **генерируется**
из `package.json` и профиля — не пишется руками, поэтому разойтись с сервером ему негде; это
проверяется тестом.

⚠️ **В бандле нельзя включить запись.** Цена ошибочного вызова — удалённый счётчик или цель
без возможности восстановить историю, и щёлкать таким переключателем в окне установки нечего.
Нужна запись — ставьте пакет с npm и включайте её осознанно, переменной окружения.

## Проверки

```bash
npm test          # 67 тестов: спека, схемы, протокол MCP, поверхность и её бюджет
npm run protocol  # только протокольные: stdio, tools/list, tools/call, отказы
npm run smoke     # живые вызовы к API (нужен YANDEX_API_KEY)
```

Протокольные тесты поднимают сервер как подпроцесс и говорят с ним по JSON-RPC — тем же
способом, каким это делает клиент. Сеть при этом не нужна: `METRIKA_API_BASE` уводит запросы
на заглушку. Проверяется в том числе то, чего не видно изнутри: что в stdout не попадает
ничего, кроме JSON-RPC, что отказ API приезжает как `isError`, а не как успешный текст, и что
запись действительно заблокирована.

### Чего в проверках НЕТ

**Евала выбора инструмента.** Это единственная проверка, которую не заменяют ни снапшот
схемы, ни протокольный тест: описания могут быть синтаксически безупречны, а модель всё
равно возьмёт не тот инструмент. Тесты этого не видят по построению — они зовут инструмент
по имени, то есть выбор уже сделан за модель.

Здесь это осознанный пропуск, а не забытый пункт. Профиль по умолчанию — десять
инструментов, из них шесть отчётов Stat различаются формой ответа, а не темой, и путать их
модели особо не с чем. Евал становится нужен, когда поверхность по умолчанию расширяется
или когда в неё попадают инструменты с пересекающимися описаниями, — тогда его надо писать
**до** расширения, а не после.

## Что изменилось в 2.0.0

Удалены 26 инструментов-обёрток над пресетами Stat API (`get_visits`, `sources_summary`,
`get_page_performance` и прочие). Они покрывали малую часть API, зашивали измерения и период
в код и не давали задать произвольный запрос. Их заменяют `metrika_stat_*`, принимающие
параметры Stat API как есть.

Появились методы, которых не было вовсе: список счётчиков, цели, сегменты, фильтры,
разрешения, расходы, офлайн-конверсии и весь Logs API. Раньше идентификатор счётчика
приходилось знать заранее — теперь его можно найти.

## Что изменилось в 2.1.0

Сервер довели до состояния, в котором его не страшно оставить агенту.

- **Аннотации на всех 108 инструментах.** До этого клиент не отличал `metrika_counter_list`
  от `metrika_counter_delete`.
- **Запись выключена по умолчанию** (`METRIKA_ALLOW_WRITES`).
- **Найден и починен дефект разбора документации.** Ассертации размечены строкой, где
  значение стоит *после* закрывающей скобки класса, — распознаватель свойств заякорен на
  конец строки и такие строки не матчил вовсе. В итоге до спеки не доезжало **ни одного**
  примера, значения по умолчанию или границы, а часть их падала в описание соседнего поля.
  Сейчас в спеке 288 примеров, 69 значений по умолчанию и 155 ограничений; ограничения
  переносятся в схему инструмента, примеры и значения по умолчанию — в описания параметров.
- **Найдена и починена потеря обязательности.** Параметры вида «один из N типов» (`goal`
  у создания и правки цели, `grant` у выдачи доступа) собирались как `z.unknown()`, а он
  в zod необязателен, — обязательное поле уезжало клиенту как опциональное. Теперь это
  объединение реальных форм, и обязательность на месте.
- **Ссылки на сущности разворачиваются** на один уровень: у 23 параметров тела вместо
  свободного объекта видны настоящие поля.
- **Послабления на входе там, где они безвредны.** Число строкой, булево словом, список
  через запятую в строке запроса — принимаются; в теле запроса, где важен точный JSON,
  не принимаются.
- **Потолок длины ответа** с объявленным урезанием: выгрузка Logs API бывает в сотни мегабайт.
- **Вычистка секретов** из показанного `request_url`.
- **Повтор на 429** с соблюдением `Retry-After`.
- **SDK обновлён** до 1.30 — на 1.17 висели три опубликованных уязвимости, две высокие;
  `npm audit --audit-level=high` теперь часть CI.
- **Починена джоба дрейфа в CI.** Она запускала тесты через `| tee` без `pipefail`, поэтому
  код возврата брался у `tee` и джоба оставалась зелёной при любом падении теста.

More