MCP-сервер Яндекс.Метрики: OAuth без client_secret + LLM-агенты | AiManual
AiManual Logo Ai / Manual.
10 Июл 2026 Гайд

MCP-сервер для Яндекс.Метрики: OAuth без секрета и боевой хук с LLM

Пошаговый гайд по созданию MCP-сервера для Яндекс.Метрики с Device OAuth (без клиентского секрета). Интеграция с LLM-агентами, код и нюансы.

Сначала крик души

Каждый раз, когда я вижу MCP-сервер, который тупо шлёт запрос к API с хардкоженым токеном — мне хочется плакать. А когда этот токен ещё и бессрочный — уже рыдать. LLM-агенты умнеют, протокол MCP обрастает мясом, а авторизация до сих пор часто выглядит как прокся-затычка. Сегодня исправим это для Яндекс.Метрики. И сделаем OAuth без клиентского секрета — потому что хранить секреты на стороне агента (который болтает с Claude или локальной LLM) — верный путь к взлому.

Спойлер: будем использовать Device Authorization Grant. Яндекс его поддерживает, и для сценария «MCP-сервер работает в фоне, а пользователь авторизуется раз в полугодие» это идеал.

Что за MCP и зачем ему Яндекс.Метрика?

MCP (Model Context Protocol) — это такой USB-C для ИИ-агентов. Вместо того чтобы лепить к каждому новому LLM свой адаптер, вы пишете один сервер, который говорит на языке tools и resources. Хотите, чтобы GPT-5 или LM Studio дёрнули статистику посещений — просто подключили MCP-сервер. Яндекс.Метрика здесь — идеальный кандидат: у каждого SEO-специалиста или маркетолога есть счётчик, а API даёт и визиты, и цели, и карты кликов. Зачем копипастить данные вручную, когда можно отдать LLM команду «покажи динамику за неделю»?

Но проблема: Яндекс.Метрика требует OAuth 2.0. А типовой MCP-сервер часто деплоят как простой скрипт без серверной части. Хранить client_secret на клиенте — моветон. Выкручиваемся через Device Flow.

Важно: Device Grant не требует redirect URI и client_secret. Это позволяет авторизовать пользователя без развёртывания веб-сервера. Идеально для MCP — ваш сервер просто показывает код, а пользователь вводит его на странице Яндекса.

Как работает Device Grant в Яндексе (кратко)

Схема из трёх шагов:

  • MCP-сервер шлёт POST на https://oauth.yandex.ru/device с client_id (публичным).
  • В ответ получает device_code, user_code и ссылку для ввода.
  • Сервер показывает user_code пользователю (например, в консоли или через WebSocket), опрашивает токен, пока пользователь не введёт код на Яндексе.

Никаких секретов на клиенте. Токен живёт до отзыва или смерти (по умолчанию год, можно обновлять). Яндекс в 2025—2026 не изменил эту механику — она стабильна.

💡
Для MCP такой подход лучше, чем обычный Authorization Code, потому что серверу не нужен собственный HTTP-эндпоинт для callback.

Собираем MCP-сервер на TypeScript

Начнём с инициализации. Берём официальный SDK от Anthropic (на дату статьи актуальна версия 1.0.7 @modelcontextprotocol/sdk).

mkdir yandex-metrica-mcp && cd yandex-metrica-mcp
npm init -y
npm install @modelcontextprotocol/sdk@^1.0.7 axios @types/node typescript ts-node

Создаём src/index.ts. Ядро сервера:

import { Server } from "@modelcontextprotocol/sdk";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/transport/stdio.js";
import axios from "axios";

// Пока без авторизации — просто заглушка для теста
class YandexMetricaServer {
  private server: Server;

  constructor() {
    this.server = new Server({
      name: "yandex-metrica-mcp",
      version: "1.0.0",
    });
    this.setupTools();
  }

  private setupTools() {
    this.server.tool(
      "get_visits",
      {
        counterId: { type: "number" },
        date1: { type: "string" },
        date2: { type: "string" },
      },
      async (args) => {
        // Здесь будет вызов API Метрики
        return { content: [{ type: "text", text: `Заглушка для ${args.counterId}` }] };
      }
    );
  }

  async start() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
  }
}

const server = new YandexMetricaServer();
server.start();

Пока это скелет. Теперь пропишем Device OAuth.

Реализация Device Grant

Создаём отдельный модуль auth.ts. Для получения токена нужен client_id публичного приложения Яндекс.Метрики. Его получают в OAuth-кабинете Яндекса — выбираете тип «Устройство (Device)», указываете минимальные права (например, metrika:read).

Ошибка: не указывайте в настройках приложения redirect URI — для Device Flow он не нужен. Но Яндекс всё равно его просит. Просто вставьте любую легальную ссылку, главное — чтобы домен был не пустой.

import axios from "axios";

const CLIENT_ID = "ваш_public_client_id";

interface DeviceCodeResponse {
  device_code: string;
  user_code: string;
  verification_url: string;
  interval?: number;
}

interface TokenResponse {
  access_token: string;
  expires_in?: number;
  refresh_token?: string;
}

export async function getTokenViaDevice(): Promise<string> {
  // 1. Получаем device_code
  const deviceResp = await axios.post<DeviceCodeResponse>(
    "https://oauth.yandex.ru/device",
    `client_id=${CLIENT_ID}`,
    { headers: { "Content-Type": "application/x-www-form-urlencoded" } }
  );

  const { device_code, user_code, verification_url, interval = 5 } = deviceResp.data;

  // 2. Показываем пользователю код
  console.log(`\nОткройте ${verification_url} и введите код: ${user_code}\n`);

  // 3. Опрос токена
  while (true) {
    await sleep(interval * 1000);
    try {
      const tokenResp = await axios.post<TokenResponse>(
        "https://oauth.yandex.ru/token",
        `grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=${device_code}&client_id=${CLIENT_ID}`,
        { headers: { "Content-Type": "application/x-www-form-urlencoded" } }
      );
      if (tokenResp.data.access_token) return tokenResp.data.access_token;
    } catch (e: any) {
      // authorization_pending — нормально, ждём
      if (e?.response?.data?.error !== "authorization_pending") throw e;
    }
  }
}

function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); }

Токен сохраняем в файл или переменную окружения YANDEX_TOKEN. Для MCP-сервера можно хранить как runtime state, но при перезапуске дать возможность повторно использовать refresh_token. Упростим: каждый запуск — новая авторизация (для прода лучше кэшировать).

Привязываем Метрику к tool'ам

Теперь в основном сервере используем токен. Создадим metricaClient.ts:

import axios from "axios";

export class MetricaClient {
  private token: string;
  private baseURL = "https://api-metrika.yandex.net/stat/v1/data";

  constructor(token: string) { this.token = token; }

  async getVisits(counterId: number, date1: string, date2: string) {
    const resp = await axios.get(this.baseURL, {
      params: {
        ids: counterId,
        metrics: "ym:s:visits",
        date1, date2,
      },
      headers: { Authorization: `OAuth ${this.token}` },
    });
    return resp.data;
  }
}

Обновляем tool:

private async setupTools() {
  this.server.tool(
    "get_visits",
    { counterId: { type: "number" }, date1: { type: "string" }, date2: { type: "string" } },
    async (args) => {
      const data = await this.client.getVisits(args.counterId, args.date1, args.date2);
      return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
    }
  );
}

Запуск и проверка с LLM

Собираем и запускаем:

npx ts-node src/index.ts

В консоли появится запрос авторизации. Открываете ссылку, вводите код — получаете токен. Сервер переходит в режим Stdio и ждёт JSON-RPC команд.

Как подключить к любому LLM? Берёте любой MCP-совместимый клиент: OAuth-прокси от Keycloak и Telegram или простой mcp-cli. В конфигурации прописываете путь к собранному серверу. Вызов из Claude Desktop:

{
  "mcpServers": {
    "yandex.metrica": {
      "command": "node",
      "args": ["dist/index.js"],
      "env": {}
    }
  }
}

Теперь в чате с LLM: «Сколько визитов было на счётчике 12345 с 1 по 10 июля 2026?» — агент сам дёрнет MCP-сервер и вернёт структурированный ответ.

Грабли и узкие места

  • Лимиты API: Яндекс Метрика в режиме реального времени — дорого. Для бесплатного аккаунта 10 000 запросов в сутки. На каждый tool вешайте кэш (LRU на пару минут).
  • Обновление токена: Device Grant даёт refresh_token, но хранить его надо в защищённом месте. Если MCP-сервер запущен на локальной машине — шифруйте или используйте system keychain.
  • Обработка ошибок: LLM туповаты — могут передать невалидную дату. Всегда возвращайте человекочитаемые сообщения об ошибках, а не stack trace.
  • Безопасность: Никогда не выводите access_token в лог. Атака на MCP-сервер описана в отдельном разборе.

Расширяем: больше эндпоинтов

Можно добавить tools: get_sources (источники трафика), get_goals (цели), get_pageviews (просмотры страниц). Модульность — просто плодите функции в MetricaClient и экспортируете их как tools. Не забудьте описать схемы параметров — в MCP используется JSON Schema. Чем детальнее схема, тем точнее LLM передаст параметры.

💡
Пример из жизни: мы добавили tool get_segments, который принимает JSON-фильтр — агент строит сегменты аудитории прямо из чата.

Перспективы: MCP как новый стандарт для API

К 2026 году MCP перестал быть экзотикой. Крупные провайдеры (OpenAI, Anthropic, Ollama) встроили поддержку протокола. Агенты общаются с десятками сервисов одновременно. Ваш MCP-сервер для Метрики — это кирпичик в фундаменте. Enterprise уже внедряет такие решения в low-code платформы.

Главный вывоз сегодняшней статьи: не надо бояться OAuth без секрета. Device Grant — рабочий механизм, который Яндекc поддерживает официально. Если у вас есть MCP-сервер под любой другой API (Google Analytics, TikTok, ваш внутренний), перепишите авторизацию на Device Flow — это безопаснее и проще для пользователя.

А напоследок — тест на прочность: возьмите готовый сервер, подключите к нему LM Studio с моделью вроде GPT-OSS 20B и попросите агента самому зарегистрировать новое приложение в OAuth-кабинете. (Шучу, до такого AGI ещё далеко, но через пару лет может и научится.)

Код проекта целиком выложен в открытом доступе — ссылки есть в моём GitHub. Пробуйте, ломайте, присылайте пул-реквесты.

Подписаться на канал