{
  "markdown": "<p align=\"center\">\n  <img src=\"assets/logo-128.png\" width=\"96\" height=\"96\" alt=\"seo-tools-mcp\" />\n</p>\n\n# seo-tools-mcp\n\n[![CI](https://github.com/antohins/seo-tools-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/antohins/seo-tools-mcp/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![GitHub MCP Registry](https://img.shields.io/badge/GitHub%20MCP%20Registry-8%20servers-24292e?logo=github)](https://github.com/mcp/antohins/seo-tools-mcp-gsc)\n\n**Русский** | [English](README.en.md)\n\nВосемь **универсальных** stdio MCP-серверов для SEO: доступ к SERP, Wordstat, Google Search Console, Google Analytics 4, Яндекс.Вебмастеру, Яндекс.Метрике и self-hosted A-Parser прямо из Claude Code (и любого MCP-клиента). Все инструменты **read-only** — ничего не публикуют и не меняют в твоих аккаунтах, вывод — строгий JSON. Машиночитаемо это заявлено аннотацией `readOnlyHint`; её намеренно **нет** у двадцати инструментов, каждый вызов которых тратит платный ресурс (запрос к XMLStock/XMLRiver, прокси-трафик A-Parser) — иначе клиент счёл бы их безобидными и перестал спрашивать подтверждение перед прогоном по большому пулу. К конкретному сайту не привязаны: дефолты (свойство GSC, свойство GA4, хост Вебмастера, счётчик Метрики) настраиваются на лету.\n\n> 🛰 Эти серверы мы используем в продакшене в **[PBN Workers](https://pbn-workers.com/ru/tools/seo-tools-mcp/)** — инфраструктура поискового топа: семантика, PBN и сателлиты, автоматизация SEO. Нужен стабильный органический трафик — [приходите](https://pbn-workers.com/ru/tools/seo-tools-mcp/).\n\n| Сервер | Рабочие инструменты | Авторизация |\n|---|---|---|\n| `xmlstock` | `xmlstock_serp`, `xmlstock_images`, `xmlstock_news`, `xmlstock_video`, `xmlstock_wordstat`, `xmlstock_wordstat_dynamics`, `xmlstock_wordstat_regions`, `xmlstock_wordstat_regions_tree`, `xmlstock_balance` | API-ключ |\n| `xmlriver` | `xmlriver_serp`, `xmlriver_images`, `xmlriver_news`, `xmlriver_maps`, `xmlriver_check_index`, `xmlriver_suggest`, `xmlriver_related_questions`, `xmlriver_balance` | API-ключ |\n| `wordstat` | `wordstat_frequency`, `wordstat_dynamics`, `wordstat_regions`, `wordstat_regions_tree` | Api-Key Yandex Cloud |\n| `gsc` | `gsc_query`, `gsc_inspect_url`, `gsc_list_sites`, `gsc_get_site`, `gsc_list_sitemaps`, `gsc_get_sitemap` | OAuth (все свойства аккаунта) / service account |\n| `ga4` | `ga4_list_properties`, `ga4_property_details`, `ga4_metadata`, `ga4_check_compatibility`, `ga4_report`, `ga4_bytime`, `ga4_traffic_sources`, `ga4_geo`, `ga4_devices`, `ga4_top_pages`, `ga4_events`, `ga4_funnel`, `ga4_annotations`, `ga4_realtime` | OAuth (все свойства аккаунта) / service account |\n| `ywm` | `ywm_hosts`, `ywm_summary`, `ywm_search_queries`, `ywm_queries_history`, `ywm_recommended_queries`, `ywm_popular`, `ywm_indexing_history`, `ywm_sqi_history`, `ywm_external_links`, `ywm_broken_links`, `ywm_diagnostics`, `ywm_important_urls`, `ywm_sitemaps` | OAuth (авто-refresh) |\n| `metrika` | `metrika_report`, `metrika_bytime`, `metrika_counters`, `metrika_goals`, `metrika_traffic_sources`, `metrika_geo`, `metrika_devices`, `metrika_landing_behavior`, `metrika_search_phrases`, `metrika_top_landings` | OAuth (авто-refresh) |\n| `aparser` | `aparser_ping`, `aparser_status`, `aparser_proxies`, `aparser_parsers`, `aparser_parser_fields`, `aparser_get_preset`, `aparser_serp_google`, `aparser_serp_yandex`, `aparser_suggest`, `aparser_request`, `aparser_bulk_request` | self-hosted A-Parser (URL + пароль API) |\n\nГде опубликовано: [npm](https://www.npmjs.com/search?q=seo-tools-mcp) (восемь пакетов), [официальный MCP Registry](https://registry.modelcontextprotocol.io), **[GitHub MCP Registry](https://github.com/mcp/antohins/seo-tools-mcp-gsc)** (все восемь серверов), маркетплейс плагинов Claude Code (см. ниже) и `.mcpb`-бандлы в [релизах](https://github.com/antohins/seo-tools-mcp/releases/latest).\n\nУ каждого сервера дополнительно есть auth-инструменты `<server>_auth_status` и `<server>_set_credentials` (см. [Интерактивная авторизация](#интерактивная-авторизация-в-любой-сессии)).\n\n## Инструменты по сервисам\n\n### xmlstock — SERP Google/Яндекс\n- `xmlstock_serp` — веб-выдача Google/Яндекса (органика + подсветки + SERP-фичи): регион, устройство, safe search, сортировка (Яндекс), период, рекламные блоки; третий движок `yandex_xml` — официальный Яндекс XML (groupby до 100 за 1 запрос, hlword на любых устройствах, статистика found/found-docs; тариф от 24 ₽/1000)\n- `xmlstock_images` — поиск картинок Google (url страницы + url изображения + заголовок)\n- `xmlstock_news` — новости Google (заголовок, источник, дата, сниппет)\n- `xmlstock_video` — видео Google (url, заголовок, превью, хост, канал, длительность)\n- `xmlstock_wordstat` — Яндекс Wordstat: топ + похожие запросы с частотностью (можно по региону), операторы Wordstat\n- `xmlstock_wordstat_dynamics` — динамика частотности по времени (день/неделя/месяц)\n- `xmlstock_wordstat_regions` — спрос по регионам (count, share, affinity index + имена регионов)\n- `xmlstock_wordstat_regions_tree` — дерево регионов Wordstat (id + имя + путь)\n- `xmlstock_balance` — баланс аккаунта / проверка ключа (бесплатно)\n\n> Wordstat через XMLStock — тем же ключом `XMLSTOCK_*`, что и SERP; **не нужен Yandex Cloud** (в отличие от отдельного сервера `wordstat`).\n\n### xmlriver — SERP Google/Яндекс + проверка индексации\n- `xmlriver_serp` — органика Google/Яндекса (глубина добирается пагинацией: каждые 10 позиций = 1 платный запрос), флаг наличия AI Overview; опция `includeAIOverview` — полный текст Обзора от ИИ + цитируемые ссылки (платный `ai=1`, только Google); `includeAdditional` — доп. SERP-блоки Google из `<addresults>` (knowledge_graph, localresultsplace, rs и др.; наполнение зависит от платных опций кабинета XMLRiver, непришедшие блоки — в `additional.unavailable`); гео-таргетинг Google — `location` (город → `loc`, «Moscow»/«1011969») и `country` (ISO/числовой id, автовыводится из города); `device` — desktop/mobile/tablet, `os` (ios/android) отправляется только при `device=mobile`\n- `xmlriver_images` — картинки Google (страница + url картинки + заголовок + источник + размеры); гео — `location`/`country`\n- `xmlriver_news` — новости Google (заголовок, источник, дата, сниппет), фильтр по времени; гео — `location`/`country`\n- `xmlriver_maps` — поиск заведений по Google Maps (`setab=maps`, обязательные `zoom` 1–15 и `coords` «широта,долгота», `count` 5–50): название, рейтинг, адрес, телефон, сервисы, координаты, place_id, число отзывов. ВАЖНО: формат по доке, лайвом не подтверждён (на тестовом аккаунте эндпоинт устойчиво отвечает кодом 500 — вероятно, нужна платная опция кабинета)\n- `xmlriver_check_index` — проверка индексации URL в Google/Яндексе (`inindex`)\n- `xmlriver_suggest` — поисковые подсказки Google (до 50 фраз за вызов, платно за каждую фразу); гео подсказок — `location`/`country`\n- `xmlriver_related_questions` — блок «Вопросы по теме» / People Also Ask Google (вопросы всегда; ответы — только при включённой платной опции «Related Questions с ответами» в кабинете)\n- `xmlriver_balance` — баланс аккаунта / проверка ключа (бесплатно)\n\n### wordstat — частотности Яндекса\n- `wordstat_frequency` — широкая и точная частотность, уточняющие запросы (related) и ассоциации\n- `wordstat_dynamics` — частотность по времени (день/неделя/месяц)\n- `wordstat_regions` — распределение по регионам с индексом аффинити и именами регионов\n- `wordstat_regions_tree` — полное дерево регионов Вордстата (id + имя)\n\n### gsc — Google Search Console\n- `gsc_query` — Search Analytics (клики/показы/CTR/позиция), авто-пагинация, `dataState` final/all, произвольные фильтры измерений (`filters`, AND-семантика) и `aggregationType` (auto/byProperty/byPage)\n- `gsc_inspect_url` — URL Inspection: статус индексации, покрытие, canonical, последний обход, mobile usability, rich results\n- `gsc_list_sites` — свойства, доступные авторизации\n- `gsc_get_site` — уровень доступа к свойству\n- `gsc_list_sitemaps` — отправленные sitemap со статусом\n- `gsc_get_sitemap` — детали одного sitemap\n\nДаты Search Analytics — по Pacific Time (не МСК); история ~16 месяцев; финальные данные отстают на ~2-3 дня (свежие — `dataState=all`); `ctr` в ответе — доля 0..1.\n\n### ga4 — Google Analytics 4\n- `ga4_list_properties` — свойства GA4, доступные авторизации (отсюда берётся `propertyId` — это **не** Measurement ID `G-XXXXXXX`)\n- `ga4_metadata` — какие измерения и метрики доступны в ЭТОМ свойстве, включая кастомные (`customEvent:…`); поиск подстрокой, `blockedReasons` (по такой метрике отчёт вернёт нули) и `type` (целое/дробное для `metricFilters`)\n- `ga4_check_compatibility` — совместима ли связка измерений/метрик в этом свойстве, без тяжёлого отчёта; при несовместимости — какие поля убрать\n- `ga4_report` — произвольный отчёт: любые измерения × метрики, фильтры по измерениям, сортировка (полный Data API `runReport`)\n- `ga4_bytime` — динамика метрик по времени (день/час/неделя/месяц)\n- `ga4_traffic_sources` — источники трафика: группа каналов, source/medium, кампания; `organicOnly` — только органика\n- `ga4_geo` — страна/регион/город\n- `ga4_devices` — тип устройства/ОС/браузер\n- `ga4_top_pages` — топ страниц по `pagePath`, странице входа или заголовку; фильтры `organicOnly` и `pathContains`\n- `ga4_events` — события по `eventName`; `keyEventsOnly` — только ключевые события (бывшие конверсии)\n- `ga4_realtime` — отчёт в реальном времени (последние 30 минут)\n- `ga4_funnel` — воронка (`runFunnelReport`): сколько дошло до каждого шага и где отвалились; шаг = событие и/или условия по измерениям, разбивка по измерению. Внутри шагов действует схема Exploration API (`pagePath` там недоступен), корзина квоты отдельная и запрос дороже обычного отчёта\n- `ga4_annotations` — аннотации свойства: пометки на датах, включая созданные самой GA4 (`systemGenerated`) — частое объяснение необъяснимого скачка в динамике\n- `ga4_property_details` — карточка свойства: таймзона отчётов, валюта, уровень сервиса (STANDARD/360) и потоки данных с их Measurement ID `G-XXXXXXX`\n\nВо всех отчётных инструментах есть `includeQuota` — сколько «токенов» Data API съел запрос и сколько осталось на час/сутки.\n\nЕдиницы и даты: `bounceRate`/`engagementRate` GA4 отдаёт **долей 0..1** (не процентами); даты считаются в таймзоне **свойства** — принимаются `YYYY-MM-DD` и ключевые слова GA4 (`today`, `yesterday`, `28daysAgo`), фактическая таймзона возвращается в ответе. В ответах есть `totalRows`/`truncated`, а `thresholded: true` означает, что часть данных скрыта порогом конфиденциальности GA4.\n\n### ywm — Яндекс.Вебмастер\n- `ywm_hosts` — id пользователя + подтверждённые сайты\n- `ywm_summary` — ИКС, страниц в поиске, исключено, проблемы сайта по важности\n- `ywm_search_queries` — аналитика запросов по URL (~2 недели по умолчанию; переопределяется dateFrom/dateTo)\n- `ywm_queries_history` — суммарные показы/клики/позиции по времени\n- `ywm_recommended_queries` — приближённые рекомендованные запросы (спрос + недобор кликов)\n- `ywm_popular` — популярные запросы хоста\n- `ywm_indexing_history` — страниц в поиске по времени\n- `ywm_sqi_history` — ИКС по времени\n- `ywm_external_links` — выборка внешних ссылок + общее число\n- `ywm_broken_links` — битые внутренние/внешние ссылки\n- `ywm_diagnostics` — проблемы сайта\n- `ywm_important_urls` — отслеживаемые URL со статусом индексации/поиска\n- `ywm_sitemaps` — sitemap со статусом\n\n### metrika — Яндекс.Метрика\n- `metrika_report` — произвольный отчёт: любые dimensions × metrics, фильтры, сортировка (полный Stat API)\n- `metrika_bytime` — метрики по времени (день/неделя/месяц/час)\n- `metrika_traffic_sources` — визиты/пользователи/отказы по источникам трафика\n- `metrika_geo` — визиты по стране/региону/городу\n- `metrika_devices` — визиты по устройству/ОС/браузеру\n- `metrika_goals` — список целей (конверсий)\n- `metrika_counters` — доступные счётчики\n- `metrika_landing_behavior` — поведение на посадочных + достижения целей\n- `metrika_search_phrases` — поисковые фразы (органика)\n- `metrika_top_landings` — топ органических посадочных\n\n### aparser — мост к self-hosted A-Parser\n- `aparser_ping` — проверка связи с инстансом и пароля API\n- `aparser_status` — вердикт готовности: версия, установленные парсеры, очередь, живые прокси\n- `aparser_proxies` — живые прокси инстанса (можно по пачкам proxy checkers; креды прокси не выводятся)\n- `aparser_parsers` — парсеры, установленные на инстансе\n- `aparser_parser_fields` — поля результата, которые умеет вернуть парсер (flat + arrays)\n- `aparser_get_preset` — опции config-пресета парсера (чувствительные значения маскируются)\n- `aparser_serp_google` — органика Google (парсер `SE::Google`); прокси по умолчанию + preflight живых прокси\n- `aparser_serp_yandex` — органика Яндекса (`SE::Yandex`); регион через `lr`\n- `aparser_suggest` — поисковые подсказки Google/Яндекса\n- `aparser_request` — универсальный синхронный запрос к любому парсеру (`oneRequest`)\n- `aparser_bulk_request` — пакетный запрос: один парсер, много запросов в N потоков (`bulkRequest`)\n\n> Нужен **свой** запущенный инстанс [A-Parser](https://a-parser.com/?ref=38832) (лицензия + сервер): мост им управляет, но не хостит и не проксирует его. Прокси и прокси-чекеры (пачки) настраиваются один раз в GUI A-Parser — мост их читает, проверяет (preflight) и выбирает (`checkers`), но не создаёт. v1 синхронный и read-only: очередь задач и большие асинхронные выгрузки не подключены.\n\n## Быстрый старт\n\n### Вариант 1 — в один клик для Claude Desktop (.mcpb)\n\nСамый простой способ, ничего ставить руками не нужно: скачай нужный `.mcpb` со [страницы релиза](https://github.com/antohins/seo-tools-mcp/releases/latest) и **открой двойным кликом** — Claude Desktop поставит сервер сам и спросит ключи в диалоге установки.\n\n- Серверы с API-ключом (`xmlstock`, `xmlriver`, `wordstat`, `aparser`) — ключи вводятся прямо в установщике.\n- Серверы на OAuth (`gsc`, `ga4`, `ywm`, `metrika`) ничего не спрашивают: авторизация проходит в чате (`<server>_oauth_start` → `<server>_oauth_finish`).\n\nБандлы самодостаточны (~0.2 МБ, зависимости внутри), Node.js 20+ нужен только для варианта с npx. Собрать самому: `pnpm build:mcpb`.\n\n### Вариант 2 — плагин для Claude Code (маркетплейс)\n\nАналог `.mcpb`, но для Claude Code: сервер, ключи и подсказки ставятся одной командой, ключи спрашиваются диалогом, секреты уходят в системное хранилище, а не в открытый файл.\n\n```bash\nclaude plugin marketplace add antohins/seo-tools-mcp\n```\n\nДальше — **только те источники, которые нужны**; каждый плагин тянет ровно один сервер:\n\n```bash\nclaude plugin install xmlstock@seo-tools-mcp\nclaude plugin install gsc@seo-tools-mcp\nclaude plugin install ga4@seo-tools-mcp\n```\n\nДоступны `xmlstock`, `xmlriver`, `wordstat`, `gsc`, `ga4`, `ywm`, `metrika`, `aparser` — и `seo-tools`, который ставит все восемь сразу. Бандл удобен, но это ~100 инструментов в каждой сессии: если работаешь только с Вебмастером и Метрикой, ставь два плагина, а не бандл.\n\nКлючи можно ввести сразу (`--config KEY=VALUE`) или потом через `/plugin configure <плагин>@seo-tools-mcp`:\n\n```bash\nclaude plugin install xmlstock@seo-tools-mcp --config XMLSTOCK_USER=12345 --config XMLSTOCK_KEY=...\n```\n\nПоля, помеченные как секретные (API-ключи, OAuth-секреты), Claude Code кладёт в системное хранилище; в `settings.json` они не попадают. Плагины на OAuth (`gsc`, `ga4`, `ywm`, `metrika`) при установке спрашивают только client_id/secret — сам вход проходит в чате через `<сервер>_oauth_start` → `<сервер>_oauth_finish`.\n\nВместе с сервером плагин приносит **навыки** — процедурные инструкции по своему источнику:\nкак не сжечь баланс на снятии позиций, почему `freq_broad` завышает трафик в разы, отчего\nGA4 молча отдаёт нули, чем усреднённая позиция GSC отличается от снятой из выдачи. В контексте\nони всегда занимают ~110 токенов на навык и разворачиваются, только когда действительно нужны.\n\n### Вариант 3 — через npx (без клонирования)\n\nКаждый сервер — самодостаточный npm-пакет `seo-tools-mcp-<сервер>`; ставится одной командой:\n\n```bash\nclaude mcp add xmlstock --scope user -- npx -y seo-tools-mcp-xmlstock\nclaude mcp add xmlriver --scope user -- npx -y seo-tools-mcp-xmlriver\nclaude mcp add wordstat --scope user -- npx -y seo-tools-mcp-wordstat\nclaude mcp add gsc      --scope user -- npx -y seo-tools-mcp-gsc\nclaude mcp add ga4      --scope user -- npx -y seo-tools-mcp-ga4\nclaude mcp add ywm      --scope user -- npx -y seo-tools-mcp-ywm\nclaude mcp add metrika  --scope user -- npx -y seo-tools-mcp-metrika\nclaude mcp add aparser  --scope user -- npx -y seo-tools-mcp-aparser\n```\n\n#### Нужен только один сервер?\n\nСерверы **не связаны** между собой: возьмите один пакет и игнорируйте остальные. Каждый самодостаточен — общий код `@seo-tools/shared` вшит в сборку, так что лишних зависимостей и «хвоста» монорепы не тянется. Достаточно установить нужный пакет с npm — там уже всё из коробки (`npx -y` скачает и запустит его сам):\n\n| Пакет (npm) | Сервер |\n|---|---|\n| [`seo-tools-mcp-xmlstock`](https://www.npmjs.com/package/seo-tools-mcp-xmlstock) | SERP Google/Яндекс + Wordstat |\n| [`seo-tools-mcp-xmlriver`](https://www.npmjs.com/package/seo-tools-mcp-xmlriver) | SERP Google/Яндекс + проверка индексации |\n| [`seo-tools-mcp-wordstat`](https://www.npmjs.com/package/seo-tools-mcp-wordstat) | частотности Яндекса (Yandex Cloud) |\n| [`seo-tools-mcp-gsc`](https://www.npmjs.com/package/seo-tools-mcp-gsc) | Google Search Console |\n| [`seo-tools-mcp-ga4`](https://www.npmjs.com/package/seo-tools-mcp-ga4) | Google Analytics 4 |\n| [`seo-tools-mcp-ywm`](https://www.npmjs.com/package/seo-tools-mcp-ywm) | Яндекс.Вебмастер |\n| [`seo-tools-mcp-metrika`](https://www.npmjs.com/package/seo-tools-mcp-metrika) | Яндекс.Метрика |\n| [`seo-tools-mcp-aparser`](https://www.npmjs.com/package/seo-tools-mcp-aparser) | мост к self-hosted A-Parser |\n\n```bash\n# добавить один сервер в Claude Code\nclaude mcp add xmlstock --scope user -- npx -y seo-tools-mcp-xmlstock\n\n# или запустить напрямую (ключи через env)\nXMLSTOCK_USER=... XMLSTOCK_KEY=... npx -y seo-tools-mcp-xmlstock\n```\n\nВ любом MCP-клиенте (Claude Desktop, Cursor…) — прописывается один блок в `mcpServers`:\n\n```json\n{\n  \"mcpServers\": {\n    \"xmlstock\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"seo-tools-mcp-xmlstock\"],\n      \"env\": { \"XMLSTOCK_USER\": \"...\", \"XMLSTOCK_KEY\": \"...\" }\n    }\n  }\n}\n```\n\n> Прямая установка одного пакета по GitHub-ссылке (`npm i github:antohins/seo-tools-mcp`) **не поддерживается**: это pnpm-монорепа, отдельный подпакет так не ставится. Для установки из исходников — вариант Б ниже (клонировать + собрать). Готовые пакеты живут на npm.\n\n### Вариант 4 — из исходников\n\n```bash\ngit clone https://github.com/antohins/seo-tools-mcp.git && cd seo-tools-mcp\npnpm install && pnpm build\nROOT=$(pwd)\nfor s in xmlstock xmlriver wordstat gsc ga4 ywm metrika aparser; do\n  claude mcp add \"$s\" --scope user -- node \"$ROOT/servers/$s/dist/index.js\"\ndone\n```\n\nДальше (любой вариант) — **прямо в диалоге Claude Code**: «настрой доступ к xmlstock» → агент вызовет `xmlstock_auth_status`, подскажет, какие ключи нужны и где их взять, примет их через `xmlstock_set_credentials` и сохранит. После этого спрашивайте данные обычным языком: «сними топ-10 Яндекса по запросу X», «частотность фраз …», «клики/показы из GSC за месяц». Ключи и OAuth настраиваются один раз (см. [Получение доступов](#получение-доступов-по-сервису)).\n\n## Интерактивная авторизация (в любой сессии)\n\nУ каждого сервера есть auth-инструменты — ключи можно выдавать прямо в диалоге, без правки файлов и перезапуска:\n\n- `<server>_auth_status` — вызывается в начале работы: показывает, какие ключи заданы (маскированно), каких не хватает и как их получить (шаги регистрации).\n- `<server>_set_credentials` — сохраняет переданные значения в `~/.config/seo-tools-mcp/.env` (права 600) и применяет сразу.\n- `gsc_save_sa_json` — принимает содержимое JSON-ключа сервис-аккаунта, кладёт его в конфиг-директорию и возвращает email, который нужно добавить в GSC.\n- `ywm_oauth_start` / `metrika_oauth_start` → ссылка авторизации Яндекса; пользователь открывает, разрешает, копирует код → `*_oauth_finish` обменивает код на access+refresh токены. Дальше токен **обновляется автоматически** при протухании (code flow, не implicit).\n\nТиповой сценарий новой сессии: «настрой доступ к xmlstock» → агент вызывает `xmlstock_auth_status` → просит недостающие ключи → `xmlstock_set_credentials` → работает.\n\n⚠ Ключи, переданные через чат, проходят через контекст модели. Для максимальной гигиены можно по-прежнему вписать их в `~/.config/seo-tools-mcp/.env` руками — серверы подхватят файл сами.\n\n## Мультиаккаунт\n\nКлиентские сайты раскиданы по разным аккаунтам Google/Яндекса — поддерживаются **именованные профили**:\n\n- Каждый рабочий инструмент принимает опциональный параметр **`account`** («clientX», «agency»...). Без него используется основной профиль — обратная совместимость полная.\n- Ключи профиля хранятся в том же конфиге с суффиксом: `GSC_REFRESH_TOKEN__clientX`, `YANDEX_OAUTH_TOKEN__clientX`, `XMLSTOCK_KEY__clientX`…\n- Добавление профиля: `gsc_oauth_start(account=\"clientX\")` → пользователь авторизуется под **другим** Google-аккаунтом → `gsc_oauth_finish(account=\"clientX\")`. Аналогично `ywm_oauth_start/finish(account=...)` для Яндекса; API-ключи — `<server>_set_credentials(account=\"clientX\", ...)`.\n- **OAuth-приложения общие**: один Google-client и одно Яндекс-приложение обслуживают все профили (клиент создаётся один раз, авторизаций — сколько угодно). Per-account хранятся только токены; refresh обновляет токен своего профиля.\n- Резолв строгий: `account=\"clientX\"` без настроенных ключей → ошибка со списком настроенных профилей (никаких тихих фолбэков в чужой аккаунт). Дефолты (`GSC_SITE_URL__clientX`, `YWM_HOST_ID__clientX`, `METRIKA_COUNTER_ID__clientX`) — тоже per-account.\n- `<server>_auth_status` показывает все профили и их ключи (маскированно).\n- Альтернатива для жёсткой изоляции: отдельный env-файл через `SEO_TOOLS_MCP_ENV` (при заданном пути домашний конфиг НЕ читается).\n\n## Установка\n\n```bash\ncd seo-tools-mcp\npnpm install\npnpm build\n```\n\n## Секреты\n\nЕдиный env-файл: `~/.config/seo-tools-mcp/.env` (права 600). Все серверы читают его при старте, а `*_set_credentials`/`*_oauth_finish` пишут в него сами — ручная правка не обязательна. Шаблон — [.env.example](.env.example). Переменные из окружения процесса имеют приоритет над файлом. Альтернативный путь к файлу — `SEO_TOOLS_MCP_ENV` (так один хост может держать несколько независимых профилей: разные `claude mcp add` с разным `SEO_TOOLS_MCP_ENV`).\n\n## Регистрация в Claude Code\n\n```bash\nROOT=/path/to/seo-tools-mcp\nclaude mcp add xmlstock --scope user -- node $ROOT/servers/xmlstock/dist/index.js\nclaude mcp add wordstat --scope user -- node $ROOT/servers/wordstat/dist/index.js\nclaude mcp add gsc      --scope user -- node $ROOT/servers/gsc/dist/index.js\nclaude mcp add ga4      --scope user -- node $ROOT/servers/ga4/dist/index.js\nclaude mcp add ywm      --scope user -- node $ROOT/servers/ywm/dist/index.js\nclaude mcp add metrika  --scope user -- node $ROOT/servers/metrika/dist/index.js\n```\n\n`--scope user` — доступно во всех сессиях/проектах. Для шаринга на команду — `--scope project` (создаст `.mcp.json` в репозитории; секреты подставлять только через `${VAR}`).\n\n## Получение доступов (по сервису)\n\n> Всё из этого раздела продублировано в ответах `<server>_auth_status` — агент сам подскажет шаги. Ниже — для чтения человеком.\n\n### XMLStock (приоритет 1) — SERP Google + Яндекс\n\n1. Регистрация: https://xmlstock.com → личный кабинет, пополнить баланс (Google XML и Яндекс Live — от 12 ₽/1000 запросов).\n2. Взять ID пользователя и API-ключ → `XMLSTOCK_USER`, `XMLSTOCK_KEY` (или через `xmlstock_set_credentials`).\n3. Проверка: `xmlstock_balance`.\n\nНюансы (выяснено на живых ответах):\n- подсветки выдачи (`text_bolds`) — параметр `hlword=1`, тег `<hlword>` вложенным XML (парсится через stopNodes, соседние слова склеиваются во фразы); PAA и related searches — `related=1` (PAA только у Google);\n- **mobile-выдача не отдаёт hlword/PAA/related** — мобильный слепок только позиции+сниппеты, подсветки снимать с desktop;\n- страницы с 0 у обоих движков; органики на странице бывает <10 — сервер сам добирает страницей (+1 платный запрос);\n- `lr` принимает id регионов Яндекса для обоих движков (XMLStock маппит на Google сам);\n- ошибки HTTP 200 + `<error code>`: 20–25/101/110/111/500 ретраятся, 55 — rate-limit с паузой, 15 = пустая выдача (деньги списаны), 31/42 — фатальные (авторизация);\n- **Wordstat у XMLStock НЕТ** — частотности через отдельный сервер (официальный API Вордстата Яндекса).\n\n### Wordstat (приоритет 1) — частотности Яндекса\n\nОфициальный **Wordstat API v2** (в составе Yandex Cloud Search API) — бесплатный, без заявок и OAuth. Один раз в https://console.yandex.cloud:\n\n1. Создать каталог (folder) или взять существующий → его ID в `WORDSTAT_FOLDER_ID`.\n2. Создать сервисный аккаунт с ролью **`search-api.webSearch.user`**.\n3. Выпустить для него **API-ключ** с областью действия **`yc.search-api.execute`** → `WORDSTAT_API_KEY`.\n4. Проверка: `wordstat_frequency` по любой фразе.\n\nНюансы: точная частотность = операторы `\"!слово !слово\"` (поддерживаются в topRequests/regions; в dynamics — только при period=daily); данные topRequests — за последние 30 дней; `count` приходит строками (парсится); квоты **10 rps / 100 запросов в час** (429 ретраится, но для массового съёма закладывать троттлинг); associations максимум 20.\n\n### Google Search Console (приоритет 1)\n\nДва пути; **рекомендуемый — OAuth**: токен наследует доступ твоего Google-аккаунта и видит **все его свойства GSC разом** (включая будущие), добавлять пользователя в каждое свойство не нужно.\n\n**Путь A — OAuth (один раз):**\n\n1. https://console.cloud.google.com → проект → APIs & Services → Library → включить **Google Search Console API**.\n2. **OAuth consent screen**: тип External; себя — в Test users. (Для refresh-токена дольше 7 дней — нажать **Publish app**; предупреждение «unverified» при авторизации — норма для личного использования.)\n3. **Credentials → Create credentials → OAuth client ID → Desktop app** → взять client ID + secret.\n4. В чате: `gsc_oauth_start` (передать clientId+secret) → открыть ссылку → разрешить → браузер редиректнется на `localhost:8585`, код подхватится автоматически → `gsc_oauth_finish`.\n5. Проверка: `gsc_list_sites` — покажет все свойства аккаунта.\n\n**Путь B — сервис-аккаунт (для headless-кронов):** IAM → Service Accounts → JSON-ключ → `gsc_save_sa_json` (или путь в `GSC_SA_JSON`) → добавить email аккаунта в **каждое** нужное свойство GSC (Настройки → Пользователи и права, «Полный»).\n\nЕсли заданы оба — приоритет у OAuth.\n\n### Google Analytics 4 (приоритет 1)\n\nАвторизация та же, что у GSC, и **OAuth-приложение общее** (`GOOGLE_CLIENT_ID`/`GOOGLE_CLIENT_SECRET` переиспользуются). Но scope у GA4 свой, поэтому нужна отдельная авторизация — один раз.\n\n1. В том же проекте console.cloud.google.com → APIs & Services → Library → включить **Google Analytics Data API** и **Google Analytics Admin API**.\n2. В чате: `ga4_oauth_start` (если client ID/secret уже сохранены для GSC — без аргументов) → открыть ссылку → разрешить → браузер редиректнется на `localhost:8586` (порт отличается от GSC, чтобы серверы не конфликтовали), код подхватится автоматически → `ga4_oauth_finish`.\n3. Проверка: `ga4_list_properties` — покажет все свойства аккаунта и их `propertyId`.\n4. Удобно сохранить свойство по умолчанию: `ga4_set_credentials` → `GA4_PROPERTY_ID` (числовой id из п. 3), иначе передавать `propertyId` в каждом вызове.\n\n**Путь B — сервис-аккаунт:** JSON-ключ → `ga4_save_sa_json` → добавить email аккаунта в свойство GA4 (Администратор → Управление доступом к ресурсу, роль «Просмотр»).\n\n### Яндекс OAuth (Вебмастер + Метрика — одно приложение, один токен)\n\n1. Один раз: https://oauth.yandex.ru/client/new → «Веб-сервисы», Redirect URI: `https://oauth.yandex.ru/verification_code`. Права (scope): **Яндекс.Вебмастер** — «Получение информации о сайтах» (`webmaster:hostinfo`) + «Управление сайтами» (`webmaster:verify`); **Яндекс.Метрика** — «Получение статистики» (`metrika:read`). Взять ClientID и Client secret.\n2. Дальше — интерактивно в чате: `ywm_oauth_start` (передать ClientID + secret, сохранятся) → открыть ссылку под аккаунтом-владельцем сайта/счётчика → скопировать код → `ywm_oauth_finish`. Получатся access+refresh токены, общие для ywm и metrika; **обновляются автоматически**.\n3. Дефолты: `YWM_HOST_ID` (список — `ywm_hosts`), `METRIKA_COUNTER_ID` (список — `metrika_counters`) — задать через `*_set_credentials`, либо передавать в каждом вызове.\n4. Ручная альтернатива: получить токен implicit-flow (`response_type=token`) и сохранить в `YANDEX_OAUTH_TOKEN` — но без refresh он протухнет (Вебмастер ~6 мес, Метрика ~1 год).\n\nОграничения API Яндекса (не баги серверов): фильтр по URL в Вебмастере есть только в query-analytics (данные ~2 недели); эндпоинта «рекомендованные запросы» в API v4 нет — `ywm_recommended_queries` аппроксимирует через спрос (DEMAND) + недобор кликов; поисковые фразы в Метрике в основном «Не определено» (шифрование).\n\n### A-Parser (self-hosted) — SERP и сотни парсеров через свою коробку\n\n1. Свой запущенный инстанс [A-Parser](https://a-parser.com/?ref=38832) (лицензия + сервер) — мост им управляет, но не хостит и не проксирует его.\n2. В A-Parser: **Settings → API** — включить API-сервер, запомнить порт (обычно 9091) и пароль.\n3. `APARSER_URL` = `http://<IP-инстанса>:<порт>/API` (обязательно с путём `/API`), `APARSER_PASSWORD` = пароль оттуда же → `aparser_set_credentials`.\n4. Проверка: `aparser_ping`, затем `aparser_status` (готовность инстанса + живые прокси).\n\nНюансы: прокси и прокси-чекеры (пачки) настраиваются один раз в GUI — без живых прокси Google/Яндекс быстро банят, поэтому serp/suggest-инструменты делают preflight и предупреждают (`use_proxy=false` — на свой риск); пресеты и пачки по умолчанию задаются env (`APARSER_GOOGLE_PRESET`, `APARSER_YANDEX_PRESET`, `APARSER_PROXY_CHECKERS`, `APARSER_USE_PROXY`); v1 синхронный и read-only — очередь задач и мутирующие методы API не подключены.\n\n## Формат дат и регионы\n\nДаты — `YYYY-MM-DD` (МСК). Регионы: имя из встроенного списка частых регионов («Москва», «спб», «Казахстан»…) **или** числовой id региона Яндекса (`213`, `225`…) — числовой id работает всегда. Несколько регионов через запятую поддерживает только сервер `wordstat`; SERP-инструменты `xmlstock_*`/`xmlriver_*` принимают ОДИН регион. Полный справочник id — инструмент `wordstat_regions_tree`.\n\n## Где и как использовать\n\nСерверы — обычные stdio-процессы без привязки к машине. Четыре сценария:\n\n### 1. Claude Code, локально\n\nЗарегистрировать через `claude mcp add --scope user` (блок «Регистрация в Claude Code» выше) — доступно во всех проектах и сессиях.\n\n### 2. Claude Code, другая машина\n\n```bash\ngit clone https://github.com/antohins/seo-tools-mcp.git && cd seo-tools-mcp\npnpm install && pnpm build\n# зарегистрировать серверы (блок «Регистрация в Claude Code» выше)\n# ключи: скопировать ~/.config/seo-tools-mcp/.env со старой машины (chmod 600)\n# ЛИБО выдать в диалоге через <server>_auth_status → <server>_set_credentials\n```\n\n### 3. Claude Desktop (локально)\n\nВ `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/`):\n\n```json\n{\n  \"mcpServers\": {\n    \"xmlstock\": { \"command\": \"node\", \"args\": [\"/ABS/PATH/seo-tools-mcp/servers/xmlstock/dist/index.js\"] },\n    \"wordstat\": { \"command\": \"node\", \"args\": [\"/ABS/PATH/seo-tools-mcp/servers/wordstat/dist/index.js\"] }\n  }\n}\n```\n\nКлючи подхватятся из `~/.config/seo-tools-mcp/.env` автоматически.\n\n### 4. Удалённо: claude.ai / Claude Code с любого места\n\nclaude.ai (web/mobile) умеет только **remote MCP** (Streamable HTTP по публичному HTTPS). Наши stdio-серверы выносятся на VPS через мост [supergateway](https://github.com/supercorp-ai/supergateway):\n\n```bash\n# на сервере: клонировать/собрать как в сценарии 2, ключи в ~/.config/seo-tools-mcp/.env\nnpx -y supergateway --stateful --outputTransport streamableHttp --port 8801 \\\n  --stdio \"node /opt/seo-tools-mcp/servers/xmlstock/dist/index.js\"   # и так для каждого сервера, порты 8801–8805\n```\n\nДальше nginx: TLS + proxy_pass на `127.0.0.1:880X` под **секретным путём** (например `/mcp-<длинный-случайный-токен>/xmlstock/`) — supergateway слушать только на localhost. Подключение:\n\n- **Claude Code**: `claude mcp add --transport http xmlstock https://host/<секретный-путь>/xmlstock/mcp`\n- **claude.ai**: Settings → Connectors → Add custom connector → тот же URL.\n\n⚠ Секретный путь — минимальный гейт (custom connectors claude.ai не передают произвольные заголовки авторизации). За эндпоинтом — все ключи сервисов, поэтому: только HTTPS, длинный токен в пути, отдельный access-лог.\n\nАльтернатива для Claude Code без HTTP-моста — stdio через ssh:\n\n```bash\nclaude mcp add xmlstock --scope user -- ssh root@SERVER node /opt/seo-tools-mcp/servers/xmlstock/dist/index.js\n```\n\n## Разработка\n\n```bash\npnpm build        # собрать все воркспейсы\npnpm typecheck    # только типы\npnpm test         # юнит-тесты (vitest, без сети)\npnpm test:live    # лайв-смоук по реальным API (нужны креды в конфиге; free-эндпоинты)\nnode servers/xmlstock/dist/index.js   # ручной запуск (stdio)\n```\n\nЮнит-тесты покрывают чистую логику: маскирование секретов, классификацию OAuth-ошибок, пагинацию Метрики/GSC (дедуп, `truncated`), фильтры, парсер SERP, регионы. Лайв-смоук поднимает каждый сервер и дёргает бесплатный инструмент (`xmlstock_balance`, `xmlriver_balance`, `wordstat_frequency`, `gsc_list_sites`, `ywm_hosts`, `metrika_counters`, `aparser_ping`) — проверка авторизации end-to-end.\n\nОбщий код (`shared/`): HTTP-клиент с ретраями на 429/5xx (3 попытки, экспоненциальный backoff, Retry-After), загрузчик env + персистентный конфиг, фабрика auth-инструментов, Яндекс-OAuth с авто-refresh, JSON-хелперы MCP, счётчик расхода платных вызовов. XMLStock дополнительно ретраит свои «временные» коды из тела XML, код 15 («ничего не найдено») трактуется как пустая выдача.\n\nСборка серверов — `tsup`: `shared/` вбивается в единый `dist/index.js` каждого сервера (рантайм-зависимости остаются external), поэтому npm-пакет самодостаточен.\n\n## Публикация в npm (мейнтейнерам)\n\nКаждый сервер публикуется как отдельный пакет `seo-tools-mcp-<сервер>`; `shared/` приватный и в npm не уходит (вбит в серверы). Версии всех серверов держим синхронно.\n\n```bash\nnpm login\npnpm -r build                 # shared (tsc) → серверы (tsup-бандл)\npnpm -r publish --access public   # публикует 8 серверов; private-пакеты (shared, корень) пропускаются\n```\n\n`pnpm publish` сам подставляет реальные версии вместо `workspace:*` и не даст опубликовать при грязном рабочем дереве.\n\nБамп версии — **только через корневой `package.json`**: правишь версию там и запускаешь `pnpm version:sync`, который разносит её по всем 42 местам (`package.json` и `server.json` каждого сервера, литерал в `new McpServer({ version })`, манифесты плагинов). `pnpm -r exec npm version patch` для этого НЕ годится: он обновит только пакеты серверов, остальное останется на старой версии, и `pnpm version:check` в CI упадёт. Проверить без записи — `pnpm version:check`.\n\n## Контрибьютинг\n\nPR приветствуются — см. [CONTRIBUTING.md](CONTRIBUTING.md). История изменений — [CHANGELOG.md](CHANGELOG.md). Уязвимости — приватно через [Security Advisories](https://github.com/antohins/seo-tools-mcp/security/advisories/new) (детали — [SECURITY.md](SECURITY.md)).\n\n## Лицензия\n\n[MIT](LICENSE) © antohins\n",
  "bytes": 35402,
  "sha": "d460b24aafa27adac9eb24ee6cf2c7103b6b566dc916bf61d89264b010f89e97",
  "repo_slug": "antohins/seo-tools-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_antohins_seo_tools_mcp_ywm_c17435a7/readme"
}