Files
ss-tools/docs/mcp-client-setup.md

227 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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