Рецепты MCP: разобранные сценарии
Зачем это нужно
Точные последовательности вызовов за тем, что люди действительно просят агента сделать в DADA Cloud: выкатить образ, подключить репозиторий, получить песочницу с базой внутри, разобраться с падающим приложением. В каждом рецепте есть фраза, которую можно сказать ассистенту, и вызовы, которые он должен сделать, — чтобы вы видели, делает он правильное или импровизирует.
Сначала настройка: управление DADA Cloud из AI-агента (MCP). Полные списки аргументов: справочник инструментов MCP.
Два вызова, с которых начинается всё
Почти каждый рецепт ниже открывается одинаково, потому что projectId и envId — это UUID, и больше их взять негде:
listProjects → выбрать проект, взять его id
getProject(projectId) → взять id окружения
Агент, который это пропустит и передаст slug проекта, увиденный в URL, получит 404 на каждом следующем вызове. Если ваш так делает, скажите ему: «id проектов — это UUID, сперва вызови listProjects».
Рецепт: выкатить контейнерный образ
«Выкати
ghcr.io/acme/api:1.4.0в мой проект какapiна порт 8080, сLOG_LEVEL=infoи секретнымDATABASE_URL.»
listProjects
getProject(projectId)
createApp(projectId, envId, name="api",
image="ghcr.io/acme/api:1.4.0", port=8080) → id операции
getOperation(projectId, operationId) → опрашивать до Committed
setEnvVar(projectId, envId, appName="api",
key="LOG_LEVEL", value="info")
setEnvVar(projectId, envId, appName="api",
key="DATABASE_URL", value="…", is_secret=true)
listApps(projectId, envId) → опрашивать до фазы Healthy
Здесь на практике ломаются две вещи. Committed — это не Healthy: агент, остановившийся на операции, доложит об успешном деплое, пока контейнер крутится в crash-loop. И инструмента перезапуска нет, потому что перезапускать нечего: изменения переменных и образа примиряются сами.
Если фаза так и не становится Healthy, идите в рецепт диагностики.
Рецепт: выкатить новую версию того, что уже работает
«Переведи
apiна1.4.1.»
listProjects
getProject(projectId)
updateAppImage(projectId, envId, appName="api",
image="ghcr.io/acme/api:1.4.1") → id операции
getOperation(projectId, operationId) → опрашивать до Committed
listApps(projectId, envId) → опрашивать до фазы Healthy
Рецепт: деплой из репозитория GitHub
«Подключи
acme/apiи собери его.»
listProjects
getProject(projectId)
listGitInstallations(projectId)
Если список пуст, агент не сможет это починить: установка GitHub App — человеческое действие на экране согласия самого GitHub. Он должен дать вам ссылку:
getGitInstallUrl(projectId, provider="github") → откройте сами, потом продолжайте
Когда установка на месте:
listInstallationRepos(projectId, installationId)
connectGitRepo(projectId, envId, installation_id=…,
repo_full_name="acme/api",
production_branch="main", auto_deploy=true,
port=8080)
triggerBuild(projectId, envId, appName="api") → возвращает сборку, а не операцию
getBuild(projectId, buildId) → опрашивать именно её
listApps(projectId, envId) → опрашивать до фазы Healthy
Обратите внимание на асимметрию: triggerBuild императивен и отдаёт id сборки, поэтому getOperation для наблюдения за ней не подходит. Всё остальное в этом документе наблюдается через getOperation.
Подробнее: деплой приложения из GitHub.
Рецепт: добавить управляемый Postgres
«Дай
apiбазу и подключи его к ней.»
listProjects
getProject(projectId)
createDatabase(projectId, envId, name="api-db", app_ref="api",
backup_enabled=true) → id операции
getOperation(projectId, operationId) → опрашивать до Committed
listDatabases(projectId, envId) → опрашивать, пока фаза не станет готовой
getDatabaseCredentials(projectId, envId,
name="api-db", reveal=true)
setEnvVar(projectId, envId, appName="api",
key="DATABASE_URL", value="…", is_secret=true)
getDatabaseCredentials отдаёт 404, пока база ещё создаётся — это «ещё нет», а не «не найдено». Агент, для которого 404 фатален, сдастся на один опрос раньше времени; скажите ему сначала опрашивать listDatabases.
Каждое раскрытие пишется в аудит, так что ждите эти вызовы в своём аудит-логе с личностью агента — то есть вашей.
Подробнее: управляемые базы Postgres.
Рецепт: получить песочницу для работы
Этот поток агент должен выбирать, когда ему нужно место, где что-то запускать, а не куда деплоить.
«Дай мне box с Postgres и открой порт 3000.»
getBoxCatalog → настоящие имена образов и профилей
listProjects
boxUp(projectId, name="scratch", image=…, profile=…,
ttl_seconds=…, spend_cap_rub=…,
ssh_public_key="ssh-ed25519 AAAA…")
boxUp синхронный и возвращается только после того, как внутри box действительно выполнилась команда. В ответе — координаты подключения, одноразовый токен dadabox_ и готовый к вставке сниппет mcpServers.
Добавьте этот сниппет в свой клиент вторым MCP-сервером. В этом весь замысел: команды выполняет собственный эндпоинт box, поэтому ни ваш код, ни ваши модельные ключи не идут через наш control plane. Инструмента «выполни команду» на этом сервере нет и не будет.
Дальше:
attachBoxDatabase(projectId, boxName="scratch", name="db")
listBoxAttachments(projectId, boxName="scratch") → какие КЛЮЧИ окружения проброшены
exposeBox(projectId, boxName="scratch", port=3000) → выданное платформой имя хоста
База создаётся снаружи box и пробрасывается в его env-файл, поэтому удаление box никогда не уничтожает ваши данные. Выбрать имя открытого хоста нельзя — это возможность кристаллизации, а не эфемерной песочницы.
Уборка, которую агент должен делать без напоминаний:
getBoxUsage(projectId, boxName="scratch") → во что это уже обошлось
suspendBox(projectId, boxName="scratch") → остановить оплату вычислений, оставить диск
resumeBox(projectId, boxName="scratch") → тот же box, тот же диск, те же учётки
deleteBox(projectId, boxName="scratch") → разрушающая операция, освобождает мощности
ttl_seconds усыпляет box, а не уничтожает его. Достигнутый spend_cap_rub тоже приостанавливает его — по той же причине: побег должен стоить вам денег, но никогда не данных.
Рецепт: превратить песочницу в настоящую VM
«Это работает — сделай постоянным на
api.acme.com.»
crystallizeBox(projectId, boxName="scratch",
app_server_name="api-prod",
domain="api.acme.com", probe_path="/healthz",
ack_monthly_charge=true)
listBoxCrystallizations(projectId, boxName="scratch") → отчёт о проверке
listAppServers(projectId)
ack_monthly_charge — это согласие, а не флажок: без него вызов вернёт 409. Повышение превращает поминутную оплату в месячный счёт за VM, поэтому агент не может подписать вас на это случайно.
Читайте verified в отчёте отдельно от status. Попытка может завершиться и всё равно не пройти проверку: должны сойтись манифест файлов, набор слушающих сокетов, сравнение хешей переменных по ключам и сквозная HTTP-проба.
Рецепт: диагностика падающего приложения
«
apiнездоров, что не так?»
listProjects
getProject(projectId)
listApps(projectId, envId) → прочитать фазу
searchLogs(projectId, app="api", since="1h", size=200)
searchLogs(projectId, app="api", q="error", since="6h")
getBuild(projectId, buildId) → если похоже на проблему сборки
Для приложения на VM или Compose передавайте vm="<имя сервера>" вместо app=. Одно из двух обязательно — именно эта проверка не даёт одному арендатору залезть в чужие логи.
Встроенный prompt diagnose-app делает ровно эту последовательность и имеет прямой запрет что-либо менять, поэтому его безопасно давать агенту, который вот-вот начнёт гадать.
Подробнее: мониторинг: метрики, логи и алерты.
Рецепт: подключить свой домен
addDomainAuthorization(projectId, apex_domain="acme.com") → TXT-челлендж
# TXT-запись у своего DNS-провайдера публикуете вы сами
verifyDomainAuthorization(projectId, id=…)
Авторизация апекса покрывает его поддомены. Привязка проверенного хоста к конкретному приложению на поверхности агента отсутствует — этот шаг доделайте в консоли.
Подробнее: свой домен и HTTPS.
Когда агент застрял
Четыре режима отказа покрывают почти всё:
- Передал slug там, где ждали UUID. Симптом: 404 на каждый вызов. Лечение:
listProjects, потомgetProject. - Остановился на Committed. Симптом: «успешно выкачено» для приложения в crash-loop. Лечение: заставить опрашивать фазу через
listApps. - Ищет инструмент, которого нет. Симптом: предлагает перезапустить, удалить или выполнить команду в шелле. Этого нет намеренно; отправьте его в справочник инструментов.
- Сдался на 404, который значил «ещё нет». Симптом: бросает базу сразу после создания. Лечение: опрашивать list-инструмент, пока фаза не станет готовой.