Сначала крик души
Каждый раз, когда я вижу 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-сервер на 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 передаст параметры.
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. Пробуйте, ломайте, присылайте пул-реквесты.