package mcp import ( "context" "fmt" "strings" "github.com/dtoro/oikos/internal/checkdefaults" "github.com/dtoro/oikos/internal/db" "github.com/google/uuid" "github.com/modelcontextprotocol/go-sdk/mcp" ) type toolReg struct { tool *mcp.Tool handler toolHandler } func allTools(pool *db.Pool, agentID uuid.UUID, sec secretBackend) []toolReg { return append(append(append(append( []toolReg{}, EntityTools(pool, agentID, sec)...), OpsTools(pool, agentID, sec)...), KnowledgeTools(pool, agentID, sec)...), AnalysisTools(pool, agentID, sec)...) } func formatCheckResult(res checkdefaults.Result) string { var b strings.Builder if res.Created > 0 { fmt.Fprintf(&b, " Derived %d check(s).", res.Created) } if res.Undeclared { b.WriteString(" Type declares no monitoring — no checks derived (set the entity's `monitoring` attribute and call update_entity_attributes to regenerate).") } for _, s := range res.Skipped { fmt.Fprintf(&b, " Skipped %s (%s).", s.Kind, s.Reason) } return b.String() } func formatCreateResult(slug, entityType string, res checkdefaults.Result) string { return fmt.Sprintf("Created %s (%s).%s", slug, entityType, formatCheckResult(res)) } func rowsToMap(ctx context.Context, pool *db.Pool, query string, args ...any) map[string]any { m := map[string]any{} rows, err := pool.Query(ctx, query, args...) if err != nil { return m } defer rows.Close() for rows.Next() { var key string var val int if rows.Scan(&key, &val) == nil { m[key] = val } } return m } func queryRowsJSONSingle(ctx context.Context, pool *db.Pool, query string, args ...any) []map[string]any { rows, err := pool.Query(ctx, query, args...) if err != nil { return nil } defer rows.Close() cols := rows.FieldDescriptions() var items []map[string]any for rows.Next() { vals, err := rows.Values() if err != nil { continue } m := make(map[string]any) for i, col := range cols { m[string(col.Name)] = fmt.Sprintf("%v", vals[i]) } items = append(items, m) } return items }