Initial commit: omnichannel-mcp — MCP-сервер управления платформой Omnichannel
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user