110 lines
3.6 KiB
Go
110 lines
3.6 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
|
|
"omnichannel-configserver-mcp/internal/configserver"
|
|
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
// overview.go — сводные read-only инструменты для эксплуатации. Они не
|
|
// изменяют состояние, а собирают картину несколькими GET-вызовами (best-effort:
|
|
// сбой одного источника не рушит остальные, а попадает в поле errors).
|
|
|
|
func registerOverviewTools(s *mcp.Server) {
|
|
addTool(s, &mcp.Tool{
|
|
Name: "overview",
|
|
Description: "Сводка состояния стенда одним вызовом: health, статистика, карта сервисов, приложения не в OK, последние failed-задачи. Read-only.",
|
|
InputSchema: schema(map[string]any{"server": serverProp()}, nil),
|
|
}, handleOverview)
|
|
|
|
addTool(s, &mcp.Tool{
|
|
Name: "release_status",
|
|
Description: "Состояние развёртывания релиза: release_job, release-задачи, приложения, сопоставление IP. Read-only.",
|
|
InputSchema: schema(map[string]any{"server": serverProp()}, nil),
|
|
}, handleReleaseStatus)
|
|
}
|
|
|
|
func handleOverview(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
return observeCall(ctx, req, func(api *configserver.API) (any, error) {
|
|
out := map[string]any{"server": api.Alias(), "read_only": api.Config.ReadOnly}
|
|
var errs []string
|
|
|
|
if status, body, err := api.Health(ctx); err != nil {
|
|
errs = append(errs, "health: "+err.Error())
|
|
} else {
|
|
out["health"] = map[string]any{"http_status": status, "body": body}
|
|
}
|
|
if st, err := api.Stats(ctx); err != nil {
|
|
errs = append(errs, "stats: "+err.Error())
|
|
} else {
|
|
out["stats"] = st
|
|
}
|
|
if sm, err := api.ServiceMap(ctx); err != nil {
|
|
errs = append(errs, "service_map: "+err.Error())
|
|
} else {
|
|
out["service_map"] = sm
|
|
}
|
|
if apps, err := api.ListApplications(ctx, false); err != nil {
|
|
errs = append(errs, "applications: "+err.Error())
|
|
} else {
|
|
out["unhealthy_applications"] = unhealthy(apps)
|
|
}
|
|
if tasks, err := api.ListTasks(ctx, configserver.TaskFilter{Status: "failed", Limit: 10}); err != nil {
|
|
errs = append(errs, "failed_tasks: "+err.Error())
|
|
} else {
|
|
out["recent_failed_tasks"] = tasks
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
out["errors"] = errs
|
|
}
|
|
return out, nil
|
|
})
|
|
}
|
|
|
|
func handleReleaseStatus(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
return observeCall(ctx, req, func(api *configserver.API) (any, error) {
|
|
out := map[string]any{"server": api.Alias()}
|
|
var errs []string
|
|
|
|
if job, err := api.ReleaseJob(ctx); err != nil {
|
|
errs = append(errs, "release_job: "+err.Error())
|
|
} else {
|
|
out["release_job"] = job
|
|
}
|
|
if tasks, err := api.DeploymentTasks(ctx); err != nil {
|
|
errs = append(errs, "deployment_tasks: "+err.Error())
|
|
} else {
|
|
out["deployment_tasks"] = tasks
|
|
}
|
|
if apps, err := api.ListApplications(ctx, false); err != nil {
|
|
errs = append(errs, "applications: "+err.Error())
|
|
} else {
|
|
out["applications"] = apps
|
|
}
|
|
if ipm, err := api.IPMatch(ctx); err != nil {
|
|
errs = append(errs, "ip_match: "+err.Error())
|
|
} else {
|
|
out["ip_match"] = ipm
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
out["errors"] = errs
|
|
}
|
|
return out, nil
|
|
})
|
|
}
|
|
|
|
// unhealthy возвращает приложения со статусом, отличным от OK.
|
|
func unhealthy(apps []configserver.Application) []configserver.Application {
|
|
out := []configserver.Application{}
|
|
for _, app := range apps {
|
|
if app.Status != "OK" {
|
|
out = append(out, app)
|
|
}
|
|
}
|
|
return out
|
|
}
|