#region Doc.McpClientSetup [C:2] [TYPE ADR] [SEMANTICS mcp,onboarding,runbook,oauth,rbac,scenario,gitea] @BRIEF Onboarding-гайд для BI-аналитика: как подключить внешнего MCP-агента (kilocode / opencode / openwebui / другой MCP-клиент) к ss-tools, какие scopes нужны по фазам работы, типовые цепочки инструментов, PROD-гейт, typed errors и два канала Gitea-конфигурации. @RELATION DEPENDS_ON -> [McpServer.RbacLayer] @RELATION DEPENDS_ON -> [ScenarioExecution.PublishedCatalogSource] @RELATION DEPENDS_ON -> [Api.ScenarioLiveBindings] @RATIONALE UX-3 (docs/reports/ux-flow-improvement-plan-2026-09-11.md): единственный путь создания сценария тестирования дашборда — внешний MCP-агент, но документа по подключению не было; сюда же влез остаток UX-2 (runbook двух каналов Gitea). # Подключение MCP-агента к ss-tools (гайд для BI-аналитика) Документ описывает, как подключить внешнего MCP-клиента (агента) к ss-tools и довести работу до первого smoke-check. Путь «создать сценарий тестирования дашборда» существует **только** через внешнего агента — в продуктовом UI агентских контролов нет (frontend boundary 2026-09-08). ## 1. Подключение клиента **Endpoint:** `{ss-tools}/mcp` (например, `http://127.0.0.1:8000/mcp` на локальном стенде). **Транспорт:** Streamable HTTP. **OAuth-флоу** (проверено live 2026-09-11, см. `docs/2026-09-11-sales-prod-mcp-replay.md` §Execution facts): REST login + OAuth discovery → **Dynamic Client Registration** (public client) → **PKCE** consent в браузере → access token. Клиент регистрируется сам при первом подключении; заранее создавать client_id не нужно. Логинитесь своей учётной записью ss-tools — права агента равны правам вашего пользователя (см. §2). ### Конфиги клиентов (типовые; точные поля зависят от версии клиента) **kilocode** — в настройках MCP (`mcp_settings.json`): ```json { "mcpServers": { "ss-tools": { "url": "http://127.0.0.1:8000/mcp", "transport": "streamable-http" } } } ``` **opencode** — в `opencode.json`: ```json { "mcp": { "ss-tools": { "type": "remote", "url": "http://127.0.0.1:8000/mcp", "oauth": true } } } ``` **openwebui** — Admin Panel → Settings → External Tools: добавить MCP-сервер с URL `http://127.0.0.1:8000/mcp`, тип подключения Streamable HTTP / OAuth. Версии openwebui отличаются по названиям полей; ищите «MCP» / «Streamable HTTP» в настройках инструментов. При первом вызове инструмента клиент откроет браузер для PKCE-consent: подтвердите доступ под своим пользователем. ### Smoke-check | Шаг | Ожидание | |---|---| | `tools/list` | до 62 tools — каталог фильтруется по вашим RBAC-правам, поэтому реальное число зависит от роли (исторический ориентир: live-replay 2026-09-11 фиксировал 61 tool; каталог растёт только аддитивно) | | `search_dashboards(environment_id="ss-prod", query="sales")` | находит dashboard ID 11 («Sales Dashboard») — так на live-стенде 2026-09-11 | Если `tools/list` пуст — у пользователя нет ни одного нужного permission (см. §2). ## 2. Scopes по фазам работы Права проверяются по каталогу `backend/src/mcp_server/rbac_server.py` (permission объявлен рядом с каждым tool). Нужные grants выдаёт администратор в RBAC ss-tools. | Фаза | Permission | Инструменты | |---|---|---| | Inspect / search | без спец-scopes (достаточно аутентификации) | `search_dashboards`, `inspect_dashboard_context`, `inspect_scenario`, `validate_scenario`, `generate_draft_pack`, `bootstrap_authoring_scenario` | | Authoring | `dashboard:testing EXECUTE` + `dashboard:testing WRITE` | `create_agent_run` (EXECUTE), `register_draft_pack` (WRITE) | | Run (не-PROD) | `scenario RUN` | `start_scenario_run`, `list_checkpoints`, `decide_checkpoint` | | Approve (PROD) | `scenario RUN_PROD` | `start_scenario_run` на PROD-среде, `list_pending_approvals`, `decide_approval`, `publish_baseline_catalog` | | Automation | `scenario:automation MANAGE` | `upsert_scenario_schedule`, `delete_scenario_schedule`, `upsert_scenario_trigger_rule`, `upsert_scenario_automation_policy` (list/get — без спец-scopes) | Для `start_scenario_run` сервер сам выбирает `RUN` vs `RUN_PROD` по environment policy: неизвестная среда — fail-closed отказ, PROD-среда — требуется `RUN_PROD`. **Human-only правило.** 47 из 62 инструментов каталога помечены `service_allowed=False` — service principal (`SERVICE_JWT`-интеграции) не подойдёт: вся сценарная работа, approvals, checkpoints и publish доступны только живому пользователю с OAuth-токеном. Не пытайтесь подключить агента под service account. ## 3. Типовые фразы агенту и что он сделает Цепочки подтверждены live-прогоном `docs/2026-09-11-sales-prod-mcp-replay.md` (E2E-EXT-002, стенд, dashboard 11 на ss-prod). | Фраза | Цепочка инструментов под капотом | Результат | |---|---|---| | «Создай сценарий для sales на ss-prod» | `search_dashboards` → `inspect_dashboard_context` → `create_agent_run` → `inspect_scenario` → `validate_scenario` → `generate_draft_pack` → `register_draft_pack` → `bootstrap_authoring_scenario` | Сценарий + current revision в реестре; `context_authority=verified` | | «Запусти» (на ss-prod) | `start_scenario_run` | `pending_approval` — создан durable PROD-гейт; повторный вызов идемпотентен (тот же гейт, не новый ран) | | «Запусти» (на DEV/PREPROD) | `start_scenario_run` | ран уходит в исполнение сразу, без гейта | | «Есть ли раны, ждущие моего решения?» | `list_pending_approvals` | список pending PROD-гейтов | | «Подтверди ран» | `decide_approval` | гейт approved → scheduler исполняет ран | | «Поставь на cron» | `upsert_scenario_schedule` | расписание; PROD — гейт на каждый due-ран (см. §4) | Compile/validate детерминированы (без LLM внутри): агент передаёт в `inspect_scenario` capabilities, возвращённые `inspect_dashboard_context` (`derived_capabilities`), плюс caller-заявки вроде `screenshot: true`; сервер не даёт caller-заявке понизить верифицируемый факт. ## 4. PROD-гейт и human checkpoint - **Approve — только человек.** Решение принимается в UI (панель рана в мониторе) или через MCP `list_pending_approvals` / `decide_approval` (permission `scenario RUN_PROD`, human-only). Автоматического approve не существует by design. - **Scheduled PROD = гейт на КАЖДЫЙ due-ран** (by design): cron создаёт ран и durable гейт; без вашего решения ран не исполняется. Это подтверждено live-цепочкой cron → PROD gate → approval → execution (`specs/044-dashboard-scenario-execution/prototype/live_scheduled_run.py`). - **Автономные расписания — только не-PROD** (см. §8). - **HumanCheckpoint** (шаг с `tool=human`) переводит ран в `waiting_human`; disposition — через UI или MCP `list_checkpoints` / `decide_checkpoint`. Иммутабельный маппинг исходов (044): | Кнопка / disposition | Персистентный исход | |---|---| | «Подтвердить соответствие» (`confirm`) | `passed` — проверка пройдена, **не** подтверждение дефекта | | «Проблема не подтверждена» (`false_positive`) | `inconclusive` | | «Недостаточно данных» (`inconclusive`) | `inconclusive` | - Сценарий с human-шагом **не может** стоять на расписании: scheduler завершит такой due-ран typed `AUTOMATION_INELIGIBLE_HUMAN_STEP`. ## 5. Параметры фильтров `native_filters_key` из URL Superset **эфемерен и не импортируется** (by design): ссылка на дашборд с применённым фильтром не переносится в сценарий. Фильтры задаются typed-параметром графа `filter_values` (объявляется в `inspect_scenario` → `parameters`, образец — `_author_b01` в `specs/044-dashboard-scenario-execution/prototype/live_scheduled_run.py`: ```json "parameters": { "filter_values": {"type": "string_list", "required": true, "default": []} } ``` Просите агента явно: «добавь параметр `filter_values` и проверь с region=South». С 2026-09-12 (plan UX-1/UX-6) значение параметра доезжает до исполнения: `apply_native_filter` — read-only browser-действие (filter bar, чекпоинты `filter_applied`/`charts_settled`), и `filter_values` из параметров запуска биндится в его input при derivation RunnerPlan (`Ref(name="param.filter_values")` из графа резолвится сервером). Семантика fail-closed: - параметр задан → фильтр применяется к заданным значениям; - параметр не задан / пустая строка → шаг работает в режиме наблюдения текущего состояния (`current_state`); - пустой список `[]` → тоже режим наблюдения; невалидный тип параметра → typed reject рана до создания строк (никакой «тихой» подмены фильтра). UI (RunConfigurationPanel) сегодня передаёт string-строку параметра = одно значение; список значений — через MCP `start_scenario_run` c `params.filter_values` списком. ## 6. Troubleshooting: typed errors Fail-closed: неподдержанное/недоступное — всегда typed non-pass, PASS не синтезируется. | Код | Трактовка | Что делать | |---|---|---| | `AUTOMATION_INELIGIBLE_HUMAN_STEP` | У текущей revision есть human-шаг — автоматический (scheduled/triggered) запуск запрещён by design | Запускайте вручную (`trigger=manual`) или пересоберите граф без HumanCheckpoint | | `*_BINDING_MISSING` (`SUPERSET_/BROWSER_/SCREENSHOT_BINDING_MISSING`) | Для dashboard/environment нет live-binding — ран стартовал без авторизованного слепка | Зарегистрируйте binding через admin API (§8) | | `*_BINDING_UNAVAILABLE` | Binding есть, но `enabled=false` | Включите: `PATCH /api/scenario-live-bindings/{ref}` (`{"enabled": true}`) | | `*_BINDING_INVALID` | Слепок binding не прошёл валидацию формы | Перерегистрируйте binding корректным snapshot | | `*_BINDING_MISMATCH` | Идентичность шага/релиза не совпала с запиненной в binding (dashboard/release/principal drift) | Перерегистрируйте binding под актуальный релиз и actor | | `DRAFT_PACK_ACCESS_DENIED` | `register_draft_pack` под чужим/несуществующим AgentRun — владение проверяется по principal | Создайте AgentRun сами (`create_agent_run`) и используйте его id; side effects при отказе — ноль | | `PUBLISH_HEAD_MOVED` | HEAD целевой ветки Gitea сдвинулся с момента approve (CAS publish) | Запросите новый approve и повторите publish | | `PUBLISH_SOURCE_UNCONFIGURED` | Не заданы `PUBLISHED_CATALOG_GITEA_*` env (§7) | Настройте process env и перезапустите backend | | `BROWSER_ACTION_NOT_SUPPORTED` | Изолированный browser-транспорт допускает только read-only каталог (`open_dashboard`, `wait_for_state`, `refresh`, `apply_native_filter`) + контрактные SQL-Lab мутации; запрос не из каталога | Действие не входит в versioned ActionRegistry — проверьте `inspect_scenario`/дескриптор шага; терминал рана честный `inconclusive` | | `BROWSER_SELECTOR_NOT_FOUND` | Filter-bar контрол не найден в текущей версии Superset UI | Fail-closed по дизайну (план UX-1): без retry; уточните `selector_hint` шага или обновите дескриптор | ## 7. Runbook: два канала Gitea-конфигурации Gitea используется в двух независимых местах — настройка одного **не** включает другой: | Канал | Где настраивается | Для чего | |---|---|---| | Settings → Git (UI, ConfigManager) | Веб-интерфейс ss-tools | Git-плагин: ветки, коммиты, deploy дашбордов | | `PUBLISHED_CATALOG_GITEA_URL` / `PUBLISHED_CATALOG_GITEA_TOKEN` / `PUBLISHED_CATALOG_REPO` / `PUBLISHED_CATALOG_REF` (+ опционально `PUBLISHED_CATALOG_PATH_TEMPLATE`) | **Только process env** backend-процесса (`backend/src/services/dashboard_testing/execution/published_catalog_source.py:38-43`) | Baseline catalog: чтение published-слепков при старте рана и publish worker | `@INVARIANT`: токен читается только из process environment, никогда не из БД и не логируется. На чистом стенде без `PUBLISHED_CATALOG_*` publish завершится typed `PUBLISH_SOURCE_UNCONFIGURED` (HTTP 503), а baseline-backed старт — fail-closed `BASELINE_NOT_PUBLISHED`. **Как проверить:** `GET /api/catalog-publications` — список публикаций; smoke publish на DEV-ветку через `publish_baseline_catalog` (требует `scenario RUN_PROD` + approval). Отсутствие env сразу даёт typed `PUBLISH_SOURCE_UNCONFIGURED` — это ожидаемый сигнал, а не сбой. ## 8. DEV/PREPROD: автономные расписания («поставил и забыл») Без человеческого гейта расписание работает только вне PROD: 1. Администратор регистрирует live-binding на DEV/PREPROD-среду через admin API `PUT /api/scenario-live-bindings/{binding_ref}` (`backend/src/api/routes/dashboard_testing/scenario_live_bindings.py`, permission `admin:settings WRITE`). Snapshot содержит только identity/fingerprints — никаких секретов; `execution_principal_fingerprint` = `sha256(actor)` — имя пользователя, чьим OAuth-токеном будут стартовать раны (пример: `sha256("admin")`). 2. Аналитик просит агента: «поставь сценарий X на cron …» → `upsert_scenario_schedule` (permission `scenario:automation MANAGE`). 3. Каждый due-ран на DEV/PREPROD исполняется до терминала **без** approve. Условия: revision без human-шагов (иначе `AUTOMATION_INELIGIBLE_HUMAN_STEP`) и валидный enabled binding (иначе typed `*_BINDING_*`). На PROD та же конфигурация даёт гейт на каждый due-ран — автономных PROD-расписаний не бывает by design. ## Источники - Live-доказательства: `docs/2026-09-11-sales-prod-mcp-replay.md` (E2E-EXT-002). - План: `docs/reports/ux-flow-improvement-plan-2026-09-11.md` (UX-2, UX-3, UX-6, UX-7, UX-8). - Каталог tools/permissions: `backend/src/mcp_server/rbac_server.py`. - Binding admin API: `backend/src/api/routes/dashboard_testing/scenario_live_bindings.py`. - Publish source env: `backend/src/services/dashboard_testing/execution/published_catalog_source.py`. - Typed `filter_values`: `specs/044-dashboard-scenario-execution/prototype/live_scheduled_run.py` (`_author_b01`). - Disposition labels: `frontend/src/lib/i18n/locales/ru/dashboard-testing.json`, `specs/044-dashboard-scenario-execution/spec.md` (Field-run Amendment). #endregion Doc.McpClientSetup