План: скиллы Docsbook под продажи продукта
Дата: 2026-08-01. Основано на аудите кода, не на предположениях: 65 MCP-тулов в src/app/api/mcp/server/route.ts, markdown-lsp (семантический поиск + граф на эмбеддингах), 43 существующих скилла в index.json на момент написания. (К концу третьей сессии на проде: 84 тула = 66 named + 18 webhook-регистраторов, 52 скилла. Цифры ниже по тексту — на момент написания плана.)
Документ — рабочий план. Разделы 1–3 объясняют, из чего строим. Разделы 4–7 — что делаем: сначала на существующих тулах, потом на новых.
Где мы сейчас (обновлено 2026-08-01, конец третьей сессии). Этапы 1 и 2 выполнены, Этап 3 начат — каталог 43 → 52 скилла. Разделы 5 и 5-бис реализованы. На проде живут и проверены боевым вызовом два новых тула:
fetch_url(T1) иget_page_diff_impact(T7); скиллы, которые они разблокировали, написаны. Следующая работа — T6suggest_internal_links→docs-link-weaver.Продолжаешь в новом чате — читай §6 → «Как продолжить в новом чате»: там состояние одной таблицей, инварианты каталога, порядок добавления скилла, грабли и точка входа. Цифры в §1 и §4 — на момент написания плана; актуальные статусы отмечены ✅ прямо в таблицах.
Одно предупреждение, которое экономит час. Работа идёт в ДВУХ репозиториях: скиллы — здесь, MCP-тулы — в основном (
src/app/api/mcp/server/route.ts+src/lib/mcp/). Приёмка у них разная, и MCP-тул проверяется на проде только с Bearer-токеном: анонимный билдер отдаёт 4 тула из 84, и свежий тул там отсутствует по устройству, а выглядит это как «не задеплоился».
1. Что реально есть под капотом#
1.1 Главная цифра#
65 MCP-тулов существует. Скиллы используют 26. Простаивает 39.
Простаивает почти всё, что относится к продажам и поиску. Ценность уже построена в продукте — но нейросеть про неё не знает, потому что нет скилла, который её оркестрирует. Это дешевле любой новой фичи: писать надо markdown, не код.
1.2 Простаивающие тулы, которые прямо про деньги#
| Тул | Что отдаёт | План | Почему это продажи |
|---|---|---|---|
get_search_rankings |
живой Google Search Console: position, impressions, clicks, queries + готовый набор «позиция 5–20» | free | Единственный источник правды для SEO. Сейчас docs-seo гадает по тексту страницы, не зная ни одной реальной позиции |
get_search_zero_click |
поиск сработал, результаты были, читатель отверг все | PRO | Указывает на заголовки и сниппеты, а не на отсутствие страницы. Zero-result отчёты этот класс пропускают целиком |
get_chat_intent |
разговоры по стадии покупки: evaluation / pricing / integration / support / bug | PRO+ | Прямой ответ на «кто решает купить и что блокирует» |
get_chat_outbound_hosts |
куда чат реально уводит людей, с отметкой целевой страницы воркспейса | PRO | «Чат гонит на pricing или сливает трафик на сторону?» |
get_content_health |
скор 0–100 на страницу (dead-end + негатив) | PRO | Готовая очередь «что чинить первым» без ручной сверки отчётов |
get_insights |
ранжированный дайджест фиксов с оценкой impact | PRO+ | Практически готовый скилл-редактор внутри одного тула |
get_rage_signals |
3+ перезаходов на страницу за визит, A→B→A, повторные поиски | PRO | Где именно сломалось, а не факт, что визит провалился |
get_visit_outcomes |
success / dead end / bounce / partial + self-serve resolution rate | PRO | Метрика №1 для доки как канала самообслуживания |
get_reverse_funnel |
работает назад от успешных визитов: какие входы ведут к успеху | PRO | Не требует гипотезы, в отличие от forward-воронки |
get_route_patterns |
реальные последовательности из 2–4 страниц и чем кончаются | PRO | Отличает задуманный маршрут от трёх случайных заходов |
get_metric_timeseries |
одна метрика по дням. Ровно 6: dead_end_rate, self_serve_resolution_rate, visits, success, dead_ends, median_time_to_first_value |
PRO | «Стало ли хуже» + привязка к дате релиза |
get_retention |
W1/W4 по недельным когортам | BUSINESS | Единственный когортный взгляд в системе; остальное — снимки |
get_change_history |
коммиты по контенту: days, path, sha, limit |
— | Замыкает петлю «правка → эффект», которой сейчас нет вообще |
search_docs / write_docs / get_doc_outline |
чтение и запись контента | — | Позволяет скиллу не только советовать, но и чинить |
1.3 markdown-lsp#
Под капотом уже есть то, что обычно и есть весь продукт конкурента: полнотекстовый и семантический поиск на эмбеддингах с гранулярностью страница / заголовок / строка, граф ссылок, backlinks, resolve-link, get-section, экспорт графа. Это делает возможными скиллы, которые рассуждают о смысловой структуре доки, а не только о тексте: находить дубли по смыслу, предлагать недостающие связи, сравнивать наш корпус с чужим.
2. Чего нет: весь наружный мир#
Проверено по коду: в MCP нет ни одного тула, выходящего за пределы воркспейса. Ни fetch, ни crawl, ни SERP, ни цитирований в LLM.
Последствия, которые уже болят:
- Скиллы, говорящие про конкурентов (
docs-imagine,docs-audience-enricher,docs-from-site,docs-create), опираются на веб-тулы самого агента. Работают у того, у кого они есть, и не работают в вебовом чате Docsbook. get_search_rankingsговорит, на какой мы позиции, но не говорит, кого обгонять.docs-ai-retrievalучит писать под цитирование LLM — а проверить, цитируют ли нас, нечем.- Дока может годами врать про цены и лимиты конкурента, и ни один аудит этого не поймает: все аудиты смотрят внутрь.
Вывод: веб-фетч — не «ещё один тул», а недостающая ось. Без него весь класс рыночных и конкурентных скиллов физически непостроим.
3. Новые тулы: что добавить и что это разблокирует#
По убыванию отдачи.
| # | Тул | Что делает | Разблокирует | Сложность |
|---|---|---|---|---|
| T1 | fetch_url ✅ СДЕЛАН |
одна публичная страница как markdown, с лимитом и явным сообщением об усечении | competitor-gap, trust-audit, pricing-consistency ✅ — все три написаны | оказалась низкой, но не из-за crawl-site.js (это CLI-скрипт): переиспользован боевой safeFetch/robots/htmlToMarkdown из основного репо |
| T2 | fetch_competitor_docs ⏸ отложен |
обёртка над T1: sitemap → outline → карта чужой доки одним вызовом | docs-competitor-gap без ручного обхода 200 страниц — но он и так читает вширь и останавливается рано |
низкая (поверх T1). Ждём доказательства, что ручной обход дорог |
| T3 | get_serp_context |
кто реально в топе по нашему запросу | замыкает get_search_rankings: знаем позицию, узнаём соперника |
средняя (внешний API) |
| T4 | get_ai_citations |
цитируют ли нас ChatGPT / Perplexity / AI Overviews по нашим темам | весь GEO-блок становится измеримым | средняя |
| T5 | compare_docs |
семантический дифф двух корпусов через markdown-lsp | gap-анализ на эмбеддингах вместо «на глазок» | средняя — эмбеддинги уже есть |
| T6 | suggest_internal_links |
по семантическому графу предложить недостающие связи | превращает docs-navigation-linking из аудита в фикс |
низкая |
| T7 | get_page_diff_impact ✅ СДЕЛАН |
композиция get_change_history × get_metric_timeseries + контрольная группа из нетронутых страниц |
docs-change-impact ✅ написан — «моя правка сработала?» отвечается впервые |
низкая, как и ожидалось (композиция существующих) |
Про T1 отдельно: он не требовал новых внешних интеграций и новых секретов. Это была самая дешёвая позиция в таблице и одновременно самая разблокирующая — расчёт подтвердился, тул закрыл весь Этап 2 за одну сессию. По той же логике следующий — T7: он тоже ничего не интегрирует, а собирается из двух уже существующих тулов.
4. Категоризация: 4 бизнес-оси вместо 7 технических категорий#
Нынешние категории (analysis / creation / publishing / observability / growth / planning / automation) описывают, что скилл делает. Продуктовник ищет по тому, какую цель он закрывает. Предлагаю бизнес-оси как основную разметку, технические категории оставить служебным тегом.
Ось A — ACQUISITION: привести людей (SEO + GEO)#
| Скилл | Статус | Стоит на |
|---|---|---|
docs-seo |
СДЕЛАНО ✅ v1.2.0 | get_search_rankings вместо гадания по тексту |
docs-rank-recovery |
СДЕЛАНО ✅ новый | набор «позиция 5–20» из GSC — самый дешёвый рост трафика, который бывает |
docs-query-coverage |
новый | GSC × get_doc_outline: показывают, а страницы под запрос нет |
docs-ai-retrieval |
есть | пишет под цитирование LLM |
docs-geo-visibility |
новый · T4 | замеряет, цитируют ли нас реально |
docs-competitor-gap |
СДЕЛАНО ✅ новый | темы конкурента, которых у нас нет — но выдаёт не дифф оглавлений, а то немногое из него, что нам правда нужно |
docs-serp-positioning |
новый · T3 | под кого именно переписывать страницу |
Ось B — ACTIVATION: чтобы дошли до ценности#
| Скилл | Статус | Стоит на |
|---|---|---|
docs-dead-end-hunter |
есть | get_visit_outcomes |
docs-rage-fixer |
новый | get_rage_signals — где читатель кружит |
docs-title-rewriter |
СДЕЛАНО ✅ новый | get_search_zero_click — заголовки отвергли, страница есть |
docs-journey-repair |
новый | get_route_patterns + get_reverse_funnel |
docs-health-triage |
СДЕЛАНО ✅ новый | get_content_health + get_insights — план работ на неделю одной командой |
docs-link-weaver |
новый · T6 | не находит битые связи, а достраивает недостающие |
Ось C — CONVERSION: чтобы купили#
Фокус на продажи. Сейчас здесь один скилл при том, что данных под ось больше всего.
| Скилл | Статус | Стоит на |
|---|---|---|
docs-sales-conversion |
есть | пишет доку так, чтобы продавала |
docs-buying-blockers |
СДЕЛАНО ✅ новый | get_chat_intent — что спрашивают на стадии pricing и что блокирует |
docs-chat-to-pricing |
новый | get_chat_outbound_hosts — чат ведёт к нам или на сторону |
docs-link-click-analyzer |
есть | CTR по CTA |
docs-objection-answers |
новый | негатив + unanswered × стадия покупки → страницы-ответы на возражения |
docs-pricing-consistency |
СДЕЛАНО ✅ новый | цена в доке против ЖИВОЙ страницы цен (не против константы в репо — это docs-maintenance) |
docs-market-positioning |
новый · T1/T2 | наше позиционирование против того, что конкуренты пишут прямо сейчас |
Ось D — RETENTION & TRUST: чтобы не ушли и верили#
| Скилл | Статус | Стоит на |
|---|---|---|
docs-maintenance |
есть | стухший контент |
docs-sync |
есть | дрейф код↔дока |
docs-change-impact |
СДЕЛАНО ✅ новый · T7 | правка × качество визитов на затронутых страницах ПРОТИВ нетронутых. Петля обратной связи закрыта |
docs-retention-cohorts |
новый | get_retention (BUSINESS) |
docs-trust-audit |
СДЕЛАНО ✅ новый | дока не врёт про интеграции, лимиты и чужие цены, изменившиеся снаружи |
docs-semantic-dedup |
новый · T5 | две страницы про одно и то же — каннибализация выдачи и путаница читателя |
5. Три режима работы: аудит / рефакторинг / письмо по правилам#
Статус: РЕАЛИЗОВАНО (Этап 1, пункты 6–7). Режим объявляется в
metadata.mode, проверяется стражем. Добавился четвёртый —platform(настраивает воркспейс, а не контент): без него 11 скиллов пришлось бы врать про свой режим. Раздел оставлен как обоснование.
Отдельная и недооценённая проблема. Сейчас есть «analysis» (найти) и «creation» (сделать с нуля), а между ними дыра: нет режима «пишу новое, но сразу правильно». Из-за этого docs-analyze находит одни и те же дефекты по кругу — их продолжают порождать.
| Режим | Когда | Что делает | Что ему запрещено |
|---|---|---|---|
| audit | дока есть, надо понять что не так | только отчёт | ничего не меняет |
| refactor | дока есть и плохая | правит существующее, сохраняя смысл | не создаёт новые страницы |
| authoring guardrail | пишем новое | свод правил, подключаемый до написания | не чинит старое |
Третий режим сейчас отсутствует. Предлагаю docs-authoring-rules — один компактный свод (Diátaxis + retrieval-паттерны + стиль + структура + конверсионный минимум), который агент грузит перед написанием любой страницы. (Сделано: 159 строк, правила сжаты из девяти аудиторов, которые ловят эти же дефекты постфактум.)
Это же решает смешение, о котором был вопрос: три непересекающихся режима вместо нынешней каши, где docs-create и docs-analyze частично перекрываются. Каждому скиллу в каталоге проставляется режим — и правило «аудит никогда не пишет» становится проверяемым.
5-бис. Правило: скилл называет потребность, а не тул#
Статус: РЕАЛИЗОВАНО (Этап 1, пункт 0). Цифры ниже — на момент написания плана; по факту нарушителей оказалось 22 из 43, стало 10, и все 10 — тулы-цели. Правило удерживается стражем
scripts/check-catalog.jsв CI, иначе откатилось бы (см. последний пункт §7).
Проблема#
README продаёт принцип «states the need, not the tool». Факт по коду: 19 из 43 скиллов зашивают имена MCP-тулов. То есть README врёт, а половина каталога нарушает собственную архитектуру.
Пример — docs-question-clusterer перечисляет пять тулов-коллекторов (get_ai_questions + get_ai_unanswered + get_negative_feedback + get_failed_searches + get_popular_searches). Всё, что они делают, уже описано в их же description внутри MCP-сервера, и описания там подробные. Дублирование в скилле даёт три дефекта:
- дрейф — тул переименуют или добавят шестой, скилл об этом не узнает;
- сужение — скилл выдал модели закрытый список из 5 источников при 39 простаивающих; сама она нашла бы, например,
get_search_zero_click; - ломка вне Docsbook — скилл становится неработающим без нашего MCP, хотя мог бы отработать на grep.
Правило#
Скилл описывает потребность и критерий приёмки. Имя тула допустимо только там, где тул — цель действия, и никогда — там, где тул это способ добыть данные.
Три исключения, где имена остаются:
- Платформенные скиллы.
docs-setup-workspaceбуквально и есть «выставь branding / seo / geo / languages». Тул тут не деталь реализации, а предмет скилла — без него скилл ни о чём. - Гейты по планам. Фактическая справка «этого нет на free» спасает от падения на середине. Но формулировать как ограничение потребности, а не как «вызови тул X».
- Антидубль-guardrails. «Не пересекайся с
docs-gap-finder» — это про скиллы, не про тулы. Остаётся.
Побочный эффект в нашу пользу: скилл без имён тулов честно работает на голом агенте и становится лучше при подключённом MCP — ровно то, что README и продаёт.
Следствие для этого плана#
В разделах 4 и 6 скиллы описаны через «стоит на get_chat_intent» — это язык плана, а не скилла. В сами SKILL.md имена переносить нельзя.
6. Порядок работ#
Этап 1 — ВЫПОЛНЕН 2026-08-01 (коммит 6281fe2)#
Каталог 43 → 48 скиллов. Что фактически сделано против плана:
| # | Пункт | Итог |
|---|---|---|
| 0 | Ревизия по правилу 5-бис | 22 нарушителя → 10, и все 10 — тулы-цели (мутации: update_*, set_*, register_webhook_*). README исправлен: продавал принцип, который нарушала половина каталога |
| 1 | docs-seo на реальные позиции |
Аудит стартует с фактических позиций/показов/запросов; без данных деградирует в помеченные гипотезы, а не молча гадает |
| 2 | docs-rank-recovery |
Новый. Набор «5–20» → очередь переписывания, отделяет «не тот интент» от «слабая подача» |
| 3 | docs-health-triage |
Новый. Сигналы → один ранжированный план недели, раздаёт работу исполнителям |
| 4 | docs-buying-blockers |
Новый. Разговоры по стадии покупки + конкурентная разведка |
| 5 | docs-title-rewriter |
Новый. «Поиск сработал, читатель отверг все» → заголовки словами читателя |
| 6 | docs-authoring-rules |
Новый. Третий режим — свод правил ПЕРЕД написанием, 159 строк |
| 7 | Режим всем скиллам | metadata.mode в схеме + index.json: audit 24 / platform 11 / authoring 8 / refactor 5 |
Сверх плана — то, без чего пункт 0 откатился бы назад (риск из §7):
scripts/check-catalog.js— тест-страж в CI до publish: имена тулов в телах, наличие режима, существование id метрик и путей словаря, поля frontmatter по схеме, счётчики README против каталога. Проверен A/B: ловит подсаженное нарушение, не только зеленеет.- Баг в
sync-skills.js: git схлопывает целиком новый скилл до папки (?? skills/foo/), поэтому строка не матчила/SKILL.md— релиз с пятью новыми скиллами оценивался какpatchвместоminor.
Долг, обнаруженный по ходу Этапа 1 и закрытый позже в тот же день: в metrics/metric-dictionary.json не было id для позиции/показов/CTR из Search Console, поэтому docs-seo и docs-rank-recovery объявляли только traffic/search_ctr — метрику не выдумывали. Что именно сделано и что при этом вскрылось — ниже, в блоке «Долг по словарю».
Как продолжить в новом чате#
Всё ниже — факты, проверенные в работе, а не намерения. Читать до того, как что-то менять.
Состояние на 2026-08-01 (конец второй сессии).
| Каталог | 52 скилла, 7 категорий. Режимы: audit 28 / platform 11 / authoring 8 / refactor 5 |
| npm | docs-skills@1.8.22 (публикует CI, не руками) |
| Этап 1 | ✅ выполнен — каталог 43 → 48, страж в CI, режимы |
| Долг по словарю метрик | ✅ закрыт — GSC-метрики заведены |
| Этап 2 | ✅ выполнен полностью: T1 fetch_url + все три скилла, которые он разблокировал (docs-competitor-gap, docs-pricing-consistency, docs-trust-audit) |
| Этап 3 | 🟡 начат. T7 get_page_diff_impact + docs-change-impact ГОТОВЫ (см. ниже). Следующий — T6 suggest_internal_links → docs-link-weaver |
| Что осталось от Этапа 2 | T2 fetch_competitor_docs — намеренно не делали: он оправдан, только когда станет видно, что обход руками через fetch_url дорог. Данных на это пока нет |
Индекс — index.json на main; в npm-пакет он не входит намеренно (files в package.json), потребители тянут его с raw.githubusercontent.com.
Где что лежит.
| Что | Где | Замечание |
|---|---|---|
| Скиллы | skills/<name>/SKILL.md, часть в skills/<category>/<name>/ |
Обе раскладки живые. От вложенности зависит путь metric_dictionary (../../ vs ../../../) — страж это проверяет |
| Копия для агентов | .claude/skills/ |
Генерируется, руками не править |
| Индекс | index.json |
Собирается node scripts/build-index.js |
| Схема frontmatter | schema/skill.schema.json |
additionalProperties: false — лишний ключ роняет проверку |
| Словарь метрик | metrics/metric-dictionary.json |
Единственный источник id для measures |
| Страж | scripts/check-catalog.js (= npm test) |
Висит в .github/workflows/publish.yml до publish |
Два инварианта каталога — их легко откатить по невнимательности.
- Скилл называет потребность, не тул. Имя MCP-тула в теле
SKILL.mdдопустимо, только если тул — цель шага (update_branding,register_webhook_*), и никогда — если это способ добыть данные (get_*,list_*,search_*). Во frontmattermetadata.uses_mcp_toolsимена нужны: это машинный контракт дляindex.json. Аллоулист целей — константаGOAL_TOOLSв страже; read-тул туда добавлять нельзя. - Каждый скилл объявляет
metadata.mode—audit/refactor/authoring/platform. Актуальную разбивку смотри в таблице состояния выше (она меняется с каждым скиллом; на конец второй сессии — audit 27 / platform 11 / authoring 8 / refactor 5).
Приёмка любой правки: node scripts/check-catalog.js зелёный. Он же ловит выдуманные id метрик, битые пути словаря и разъехавшиеся счётчики README.
Как добавлять скилл (проверенная последовательность).
- Написать
skills/<name>/SKILL.md. Не называть read-тулы в теле — только потребность; имена идут вmetadata.uses_mcp_tools. - Обновить Related-секции соседей, с которыми скилл граничит, — иначе связь односторонняя, и следующий читатель не поймёт, чем эти скиллы отличаются.
node scripts/sync-skills.js --apply— пересоберётindex.json, счётчики в README каталога и секцию родительского README между<!-- skills:start/end -->. Генератор англоязычный (родительский README англоязычен целиком) — язык не менять.node scripts/check-catalog.jsдолжен быть зелёным. Он же валидирует словарь метрик.git pull --rebase origin main→git push.
Грабли, на которые уже наступили (не повторять).
- Первый
git pushотбиваетсяrejected— это норма, не авария. CI на каждый пуш вmainсам делаетchore: release vX [skip ci]и публикует в npm. Лечитсяgit pull --rebase origin mainи повтором. Свойnpm version/npm publishне гнать — будетversion_existsи путаница с тем, выпущена ли версия. - Проверять публикацию нужно фактом, и
npm packбез версии ВРЁТ. Он отдаёт закэшированный тарбол: показывал 49 скиллов при 51 уже опубликованных, что выглядело как «CI выложил не всё». Правильно —npm pack docs-skills@<версия> --prefer-online, либоgh run view <id> --repo Docsbook-io/docs-skills --log | grep "npm notice". Отсутствиеindex.jsonв тарболе — не дефект, см. выше. sync-skills.jsчинили трижды за одну сессию — если он «ничего не сделал», это скорее его дефект, чем отсутствие изменений. Уже исправлено: (а) генератор README целил в формат до переписывания Этапа 1 и молча не матчил ничего; (б)package.jsonправился, но не попадал в коммит; (в) шаг релиза дублировал CI и всегда падал ПОСЛЕ пуша скиллов; (г) счётчик скиллов убран изpackage.json— он был единственным полем, которое скрипт писал в файле, где CI пишет версию, и давал гарантированный конфликт при каждом новом скилле. Конфликтpackage.jsonпри rebase теперь возникать не должен; если возник — версию берёшь их, остальные поля свои.
Долг по словарю — ЗАКРЫТ 2026-08-01 (43fae3b в каталоге, 9455e5de в родительском репо). search_position, search_impressions, organic_ctr заведены и подключены к docs-seo (1.3.0) и docs-rank-recovery (1.1.0). Что попутно вскрылось и что теперь надо знать:
- GSC не проходит через слой паттернов.
mcp_patternsописывался как id изpatterns.ts, а Search Console приходит прямо изget_search_rankings. Фиктивный паттерн заводить не стали: смысл поля расширен (ключ оставлен прежним, чтобы не двигать скомпилированную копию в продукте), а множество допустимых источников вынесено вmetrics/sources.json. Список продублирован руками намеренно — CI каталога чекаутит только этот репозиторий, и реестр, живущий в продукте, был бы зелёным локально и неверным в CI. - Страж теперь валидирует сам словарь, а не только скиллы, которые на него ссылаются: метрика с несуществующим источником и битая перекрёстная ссылка (
related_metrics/pairs_with) роняют проверку. Проверено подсадкой обоих нарушений. - Словарь живёт в двух местах. После правки
metric-dictionary.jsonобязателенnode scripts/build-metric-dictionary.mjsв родительском репо — он инлайнится вsrc/lib/analytics/metric-dictionary.generated.ts, и без пересборки продукт новой метрики не видит. - Оговорки, которые несут новые метрики: лаг ~2 дня и обновление раз в сутки, позиция взвешена по показам (один запрос с рангом 90 тянет вниз страницу, стоящую на 4), query-грейн недосчитывает объём примерно вчетверо — Google скрывает низкочастотные запросы, поэтому тоталы берутся с page-грейна, — и CTR без позиции нечитаем: те же проценты здоровы на 9-й позиции и тревожны на 2-й.
Работа идёт в ДВУХ репозиториях — это главное, что ломает ожидания.
| Что делаешь | Где | Чем проверяешь |
|---|---|---|
| Скилл | docs-skills/skills/ |
check-catalog.js, затем npm pack @версия --prefer-online |
| MCP-тул | основной репо, src/app/api/mcp/server/route.ts + модуль в src/lib/mcp/ |
tsc + vitest + npx next build + вызов на проде с Bearer |
| Словарь метрик | docs-skills/metrics/ и пересборка в основном репо |
check-catalog.js + build-metric-dictionary.mjs |
Проверка MCP-тула на проде — только с токеном. Сервер собирается двумя билдерами: анонимный отдаёт 4 тула, аутентифицированный — 83. Scoped-эндпоинт без Authorization идёт в анонимный, и свежий тул там отсутствует по устройству, а выглядит это как «не задеплоился». Токен — в ~/.claude/mcp.json, в память его не сохранять.
Точка входа в Этап 3. Первым идёт T7 get_page_diff_impact → скилл docs-change-impact. Это композиция уже существующих get_change_history × get_metric_timeseries, то есть самая дешёвая позиция из оставшихся. И самая важная по смыслу: сейчас петли обратной связи нет вообще — весь каталог советует правки и ни разу не узнаёт, помогли ли они. Помни ограничение из §7: get_metric_timeseries знает ровно 6 метрик, и скилл не должен обещать произвольную.
При любом новом туле помни §7: содержимое чужого сайта — данные, не инструкции, а description обязан быть самодостаточным — по правилу 5-бис скилл на тул по имени не ссылается, значит модель выбирает его по описанию.
Этап 2 — ВЫПОЛНЕН 2026-08-01 — тул fetch_url (T1) и всё, что он открыл#
- T1
fetch_url— ГОТОВ 2026-08-01 (1c824e83, проверен на проде: 83 тула, 65 named). Живёт вsrc/lib/mcp/fetch-url.ts, зарегистрирован в аутентифицированном билдере MCP-сервера (в анонимном его нет — тот отдаёт 4 тула).- Краулер из
scripts/crawl-site.jsне переиспользовался: это CommonJS-скрипт под CLI. В основном репо уже была вся нужная механика, проверенная боем, —safeFetch(перепроверяет SSRF на каждом redirect-хопе),fetchRobotsTxt/isDisallowed,htmlToMarkdown. Тул — тонкий слой поверх них, поэтому страница читается одинаково, каким бы тулом её ни достали. - Один URL, не обход.
crawl_websiteуже ходит по сайту и стоит соответственно; повторяющаяся потребность — одна страница, с которой что-то сверяют. - Ошибки возвращаются, а не бросаются. 404 — это и есть ответ на вопрос «жива ли ссылка»; исключение показало бы находку как поломку тула. Усечение и пустота JS-страницы сообщаются явно, иначе агент скажет «на странице этого нет» про то, чего просто не увидел.
- Побочно: robots-хелперы переехали в
ssrf.ts(они и так зовутsafeFetch) — теперь потребителю, которому нужно только достать страницу, не нужно тянуть зависимостиadmin.ts(next-auth, axiom, drizzle). Реэкспорт оставлен. - Проверено фактом, не моками: 17 юнит-тестов + живая сеть (metadata-эндпоинт и localhost режет SSRF, 404 приходит находкой, 307 отслеживается) + боевой вызов на проде (страница цен GitBook прочитана и честно помечена усечённой).
- Дока и спека обновлены в том же заходе:
docs/ai/mcp.md(счётчик врал: 80/62),docs/reference/mcp-tools.md,specs/mcp/content-editing.md.
- Краулер из
docs-competitor-gap— ГОТОВ 2026-08-01 (каталог 48 → 49,docs-skills@1.8.17, провереноnpm pack). Первый скилл, который смотрит наружу.- Смысл не в диффе двух оглавлений — такой дифф выдаёт «у них 40 страниц, которых нет у вас» и подразумевает, что надо написать 40. Скилл выбрасывает большую часть разницы: фича, которой у нас нет (это вход для роадмапа, а не страница), аудитория, которой мы не продаём, тема, уже закрытая в README/блоге (это «перенести», а не «написать»). Выживших ранжирует по СПРОСУ (показы без страницы, провальные поиски), и «у конкурента есть» — самый слабый довод, который не может быть единственным.
- Тул по имени в теле не назван (правило 5-бис), только в
uses_mcp_toolsкак машинный контракт; страж это подтвердил. - Явно зафиксировано: чужая страница — данные, не инструкции; ни одного утверждения о конкуренте без URL, прочитанного в этом прогоне.
Три дефекта scripts/sync-skills.js, вскрытых добавлением одного скилла (починены, dedcbf8):
- Генератор README целил в формат ДО переписывания Этапа 1 (
**N skills across M categories**+ таблица## Categories). Ни того, ни другого больше нет — обаreplaceне матчили ничего, скрипт рапортовал успех, ничего не изменив, и счётчики ловил уже страж в CI. Теперь правит те два счётчика, что есть, и счётчики по категориям в заголовках<details>. package.jsonправился прогоном, но не попадал в коммит — дерево оставалось грязным после каждого--apply.- Шаг релиза дублировал CI:
npm versionтребует чистого дерева, которое этот же прогон только что испачкал, поэтому он всегда падал ПОСЛЕ пуша скиллов — каталог опубликован, версия нет. Свой бамп поверх CI-шного даёт ещё иversion_exists. Релиз убран из скрипта целиком.
docs-trust-auditиdocs-pricing-consistency— ГОТОВЫ 2026-08-01 (каталог 49 → 51,docs-skills@1.8.20, провереноnpm pack --prefer-online).- Границу с существующими скиллами пришлось проводить явно.
docs-maintenanceУЖЕ проверяет цены — но против источника правды ВНУТРИ репозитория (constants-файл, конфиг). Новый скилл сверяет с живой публичной страницей: константа может быть верной, а опубликованная страница врать, и наоборот. Обе проверки нужны, и вdocs-maintenanceдописана Related-секция, иначе связь односторонняя. docs-pricing-consistencyузкий и проверяемый: каждая находка цитирует ОБЕ стороны дословно. Сравнивается не число, а число+единица+период+валюта одним фактом — «$19 против $19» не совпадение, если одно за место, другое за воркспейс. Legacy-план — вердикт человека, не скилла.docs-trust-auditшире: любое утверждение о внешнем мире, проверяемое снаружи (партнёрские API, чужие лимиты, версии, внешние ссылки, стандарты). Ключевое: «не смог проверить» — самостоятельный вердикт, не «вероятно, ок»; их слияние и делает такой отчёт бесполезным. Утверждения, чей источник правды — свой репозиторий, объявлены вне области (этоdocs-sync).- Оба audit-режима: цену и утверждение о партнёре правит человек, тихо переписывать деньги аудит не должен.
- Границу с существующими скиллами пришлось проводить явно.
Ещё один дефект sync-skills.js (38f41da): счётчик скиллов в package.json был ЕДИНСТВЕННЫМ полем, которое скрипт писал в том же файле, где CI пишет версию, — каждый новый скилл давал гарантированный конфликт при rebase (их версия против нашего счётчика), разрулен руками трижды за день. Число убрано: оно живёт в README, где его сверяет страж, а в описании npm-пакета не проверялось ничем и устаревало.
Грабли верификации: npm pack docs-skills отдаёт закэшированный тарбол и показал 49 скиллов при 51 опубликованных. Это выглядело как «CI опубликовал не всё». Проверять npm pack docs-skills@<версия> --prefer-online либо смотреть npm notice в логе CI (gh run view <id> --log).
- T2
fetch_competitor_docs— СОЗНАТЕЛЬНО ОТЛОЖЕН. Он оправдан ровно тогда, когда станет видно, что обход конкурента вручную черезfetch_urlдорог. Такого сигнала пока нет:docs-competitor-gapнаписан так, чтобы читать вширь и останавливаться, когда новые страницы перестают менять картину, — на 400 справочных страницах это десяток фетчей, а не 400. Делать обёртку до появления доказательства — оптимизация вслепую.
Этап 3 — НАЧАТ — измеримость и семантика#
- T7
get_page_diff_impact+docs-change-impact— ГОТОВЫ 2026-08-01 (34982a92; каталог 51 → 52; на проде 84 тула = 66 named + 18 webhook, проверено с Bearer). Петля обратной связи закрыта: каталог больше не советует правки, ни разу не узнавая, помогли ли они.- Композиция, а не новый источник. Живёт в
src/lib/mcp/page-diff-impact.ts. Сессии за окно тянутся ОДНИМ запросом и нарезаются в памяти по времени и по пути — квота Axiom 600 запросов/час на ОРГ, поэтому «по запросу на коммит» было бы неверной формой, даже если читается естественнее. - Контрольная группа — суть тула, а не украшение. Считает те же метрики по НЕтронутым страницам в тех же окнах и кредитует правку, только если она обогнала тренд сайта. Правка, совпавшая с трендом, отдаётся как «эффект неразличим», а не как победа. Проверено A/B: подсадка
relative = e(выброс контроля) роняет 2 теста — защита реальная. - Матчинг путей чуть не сделали на глазок. Заготовка стрипала
docs/и лепила свои кандидаты. Живой Axiom показал:fields.pathнесёт путь ФАЙЛА как он лежит в репо, и у части воркспейсов этоdocs/reference/api.md, у частиapi.md— стрип склеил бы разные страницы. Правильный ответ уже был в коде:normalizeDocPath(README.md→introduction, README папки→папка). Тест на «не стрипатьdocs/» стоит стражем. - Три отказа вместо нулей. Коммит моложе after-окна; коммит старше горизонта видимости визитов; выборка ниже порога рейтов. Нули там читались бы как обвал трафика. Отдельно: путь коммита, который никто не посещал, уходит в
unmatched_paths— «страницу, которую ты починил, не читают» это находка про навигацию, а не измерение правки. - Грабли, стоившие 13 падений: горизонт видимости данных сначала считался по САМОМУ СТАРОМУ визиту в выдаче. На тихом сайте старейший визит бывает позавчерашним — тул отказывался мерить коммиты с целыми окнами. Горизонт — это граница ЗАПРОСА (
LOOKBACK_DAYS), а не то, что вернулось. - Боевой вызов на проде (ws 41, коммит
6dcdafe):README.mdсматчился какintroduction, второй путь честно ушёл вunmatched, контроль отделился (33/23 против 22/8), вердикт — отказ судить по малой выборке. Ровно то поведение, ради которого писался guard. - Дока и спека в том же заходе:
docs/ai/mcp.md(счётчик врал 83/65; обе «петли» вели в site-wideget_metric_timeseries— заменено),specs/mcp/analytics.md.
- Композиция, а не новый источник. Живёт в
- T6
suggest_internal_links→docs-link-weaver👈 СЛЕДУЮЩИЙ. - T5
compare_docs→docs-semantic-dedup. - T3/T4 — SERP и цитирования в LLM, когда появится бюджет на внешние API.
7. Риски и на что смотреть#
- Планы. Много конверсионных тулов — PRO/PRO+/BUSINESS. Скиллы обязаны деградировать внятно: сообщать, какой план нужен, а не падать. Это же продающая поверхность — скилл показывает, что он бы дал.
get_metric_timeseries— ровно 6 метрик.docs-change-impactне должен обещать произвольную метрику.- GSC лагает ~2 дня,
refresh— раз в сутки. Скилл не должен выдавать вчерашние данные за сегодняшние. get_retentionсчитает по хешам IP — мобильные сети дробят одного читателя, офисный NAT склеивает разных. Скилл обязан цитировать эту оговорку, иначе продуктовник примет решение по мусору.fetch_url— внешний ввод. Содержимое чужого сайта не является инструкцией. Скиллы обязаны обращаться с ним как с данными.- Новые тулы T1–T7 обязаны нести самодостаточный
description. Правило 5-бис перекладывает знание о туле из скилла в сам тул: если описание тула мутное, модель его не выберет, и скилл молча деградирует до grep. Описание тула — теперь часть контракта, а не документация. - Правило легко откатывается назад. Соблазн дописать «возьми данные из X» велик при каждом новом скилле. Держать
grepпо именам тулов как тест-страж в CI, иначе через полгода снова будет 19 из 43.