141 lines
5.3 KiB
Go
141 lines
5.3 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
|
|
"omnichannel-mcp/internal/configserver"
|
|
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
// operate.go — жизненный цикл сервиса (deploy/restart/down/migrate) и загрузка
|
|
// фронта. Мутации; обычно require_approval в agent.yaml.
|
|
|
|
func registerOperateTools(s *mcp.Server) {
|
|
addPatternTool(s, &mcp.Tool{
|
|
Name: "deploy",
|
|
Description: "Поднять сервис (docker-compose up -d). Создаёт задачу агенту; wait=true — дождаться результата.",
|
|
InputSchema: lifecycleSchema(),
|
|
}, appPatterns, func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
return runLifecycle(ctx, req, "deploy", false)
|
|
})
|
|
|
|
addPatternTool(s, &mcp.Tool{
|
|
Name: "restart",
|
|
Description: "Перезапустить сервис (down + up -d). Создаёт задачу агенту; wait=true — дождаться результата.",
|
|
InputSchema: lifecycleSchema(),
|
|
}, appPatterns, func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
return runLifecycle(ctx, req, "restart", false)
|
|
})
|
|
|
|
addPatternTool(s, &mcp.Tool{
|
|
Name: "migrate",
|
|
Description: "Запустить миграции сервиса (docker-compose run migration). Создаёт задачу агенту.",
|
|
InputSchema: lifecycleSchema(),
|
|
}, appPatterns, func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
return runLifecycle(ctx, req, "migrate", false)
|
|
})
|
|
|
|
addPatternTool(s, &mcp.Tool{
|
|
Name: "down",
|
|
Description: "Остановить сервис (docker-compose down). Разрушительно: требует confirm=\"true\".",
|
|
InputSchema: schema(map[string]any{
|
|
"server": serverProp(),
|
|
"app_id": intProps("Идентификатор сервиса", true),
|
|
"wait": boolProps("Дождаться завершения (по умолчанию false)", false),
|
|
"confirm": boolProps("Подтверждение разрушительной операции (обязательно true)", true),
|
|
}, []string{"app_id", "confirm"}),
|
|
}, appPatterns, func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
return runLifecycle(ctx, req, "down", true)
|
|
})
|
|
|
|
addPatternTool(s, &mcp.Tool{
|
|
Name: "update_front",
|
|
Description: "Загрузить локальную сборку фронта на хост сервиса (полная замена build_folder). Разрушительно: требует confirm=\"true\". Каталог — внутри front_roots.",
|
|
InputSchema: schema(map[string]any{
|
|
"server": serverProp(),
|
|
"app_id": intProps("Идентификатор фронт-сервиса", true),
|
|
"build_dir": strProps("Локальный каталог сборки (абсолютный путь внутри front_roots)", true),
|
|
"confirm": boolProps("Подтверждение разрушительной операции (обязательно true)", true),
|
|
}, []string{"app_id", "build_dir", "confirm"}),
|
|
}, appPatterns, func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
if res := confirmGate(args, "update_front"); res != nil {
|
|
return res, nil
|
|
}
|
|
id, res := appIDArg(args)
|
|
if res != nil {
|
|
return res, nil
|
|
}
|
|
buildDir, err := requireString(args, "build_dir")
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
api, res := openAPI(ctx, args, true)
|
|
if res != nil {
|
|
return res, nil
|
|
}
|
|
defer api.Close()
|
|
return result(api.UpdateFront(ctx, id, buildDir))
|
|
})
|
|
}
|
|
|
|
// lifecycleSchema — общая схема deploy/restart/migrate/down.
|
|
func lifecycleSchema() map[string]any {
|
|
return schema(map[string]any{
|
|
"server": serverProp(),
|
|
"app_id": intProps("Идентификатор сервиса", true),
|
|
"wait": boolProps("Дождаться завершения задачи (по умолчанию false)", false),
|
|
}, []string{"app_id"})
|
|
}
|
|
|
|
// runLifecycle — общий путь операций жизненного цикла: вызвать действие, при
|
|
// wait=true ограниченно дождаться задачи агента.
|
|
func runLifecycle(ctx context.Context, req *mcp.CallToolRequest, action string, needConfirm bool) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
if needConfirm {
|
|
if res := confirmGate(args, action); res != nil {
|
|
return res, nil
|
|
}
|
|
}
|
|
id, res := appIDArg(args)
|
|
if res != nil {
|
|
return res, nil
|
|
}
|
|
wait := getBool(args, "wait", false)
|
|
|
|
api, res := openAPI(ctx, args, true)
|
|
if res != nil {
|
|
return res, nil
|
|
}
|
|
defer api.Close()
|
|
|
|
var (
|
|
ack *configserver.TaskAck
|
|
err error
|
|
)
|
|
switch action {
|
|
case "deploy":
|
|
ack, err = api.Deploy(ctx, id)
|
|
case "restart":
|
|
ack, err = api.Restart(ctx, id)
|
|
case "down":
|
|
ack, err = api.Down(ctx, id)
|
|
case "migrate":
|
|
ack, err = api.Migrate(ctx, id)
|
|
}
|
|
if err != nil {
|
|
return result(nil, err)
|
|
}
|
|
|
|
// Без wait отдаём task_id, чтобы не блокировать последовательный loop агента.
|
|
if !wait {
|
|
return result(ack, nil)
|
|
}
|
|
task, err := api.WaitTask(ctx, ack.TaskID)
|
|
if err != nil {
|
|
return result(nil, err)
|
|
}
|
|
return result(map[string]any{"ack": ack, "task": task}, nil)
|
|
}
|