227 lines
18 KiB
Markdown
227 lines
18 KiB
Markdown
#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
|