package tools import ( "context" "fmt" "github.com/modelcontextprotocol/go-sdk/mcp" ) func registerMonitoringTools(s *mcp.Server) { // usage s.AddTool(&mcp.Tool{ Name: "usage", Description: "Get CPU/RAM/Disk usage summary", InputSchema: schema(map[string]any{ "target": strProps("Connection alias (default: primary)", false), }, nil), }, usageHandler) // ps s.AddTool(&mcp.Tool{ Name: "ps", Description: "List top processes sorted by CPU or memory", InputSchema: schema(map[string]any{ "sort_by": strProps("Sort field (default: cpu)", false, "cpu", "mem"), "limit": intProps("Number of processes to show (default: 10)", false), "target": strProps("Connection alias (default: primary)", false), }, nil), }, psHandler) // logs s.AddTool(&mcp.Tool{ Name: "logs", Description: "Read the tail of a log file", InputSchema: schema(map[string]any{ "path": strProps("Path to log file", true), "lines": intProps("Number of lines to read (default: 50, max: 500)", false), "grep": strProps("Optional filter pattern", false), "target": strProps("Connection alias (default: primary)", false), }, []string{"path"}), }, logsHandler) // journal_read s.AddTool(&mcp.Tool{ Name: "journal_read", Description: "Read system logs (journalctl/syslog)", InputSchema: schema(map[string]any{ "service": strProps("Service name to filter (e.g., nginx, sshd)", false), "since": strProps("Time filter (e.g., '1 hour ago')", false), "lines": intProps("Number of lines (default: 100, max: 500)", false), "priority": strProps("Minimum log priority level", false, "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"), "target": strProps("Connection alias (default: primary)", false), }, nil), }, journalReadHandler) // dmesg_read s.AddTool(&mcp.Tool{ Name: "dmesg_read", Description: "Read kernel ring buffer (dmesg)", InputSchema: schema(map[string]any{ "grep": strProps("Optional pattern to filter messages", false), "lines": intProps("Number of lines (default: 100)", false), "target": strProps("Connection alias (default: primary)", false), }, nil), }, dmesgReadHandler) // diagnose_system s.AddTool(&mcp.Tool{ Name: "diagnose_system", Description: "One-click SRE health check: load, OOM, disk, failed services", InputSchema: schema(map[string]any{ "target": strProps("Connection alias (default: primary)", false), }, nil), }, diagnoseHandler) // list_services s.AddTool(&mcp.Tool{ Name: "list_services", Description: "List system services (systemd/OpenRC)", InputSchema: schema(map[string]any{ "failed_only": boolProps("Show only failed services", false), "target": strProps("Connection alias (default: primary)", false), }, nil), }, listServicesHandler) } func usageHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { m := manager() if m == nil { return errorResult("no SSH manager initialized"), nil } target := getString(requestArgs(req), "target", "primary") cmd := ` echo "=== LOAD AVERAGE ===" uptime 2>/dev/null echo "" echo "=== MEMORY ===" free -h 2>/dev/null || top -l 1 -s 0 2>/dev/null | grep -i phys || cat /proc/meminfo 2>/dev/null | head -5 echo "" echo "=== DISK (all mounts) ===" df -hP 2>/dev/null ` output, err := m.Execute(ctx, cmd, target) if err != nil { return errorResult(err.Error()), nil } return textResult(output), nil } func psHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { m := manager() if m == nil { return errorResult("no SSH manager initialized"), nil } args := requestArgs(req) sortBy := getString(args, "sort_by", "cpu") limit := getInt(args, "limit", 10) target := getString(args, "target", "primary") if limit > 50 { limit = 50 } sortCol := "3" if sortBy == "mem" { sortCol = "4" } cmd := fmt.Sprintf("ps -eo pid,user,%%cpu,%%mem,comm | awk 'NR==1{print} NR>1{print | \"sort -k%s -rn\"}' | head -n %d", sortCol, limit+1) output, err := m.Execute(ctx, cmd, target) if err != nil { return errorResult(err.Error()), nil } return textResult(output), nil } func logsHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { m := manager() if m == nil { return errorResult("no SSH manager initialized"), nil } args := requestArgs(req) path, _ := requireString(args, "path") lines := getInt(args, "lines", 50) grep := getString(args, "grep", "") target := getString(args, "target", "primary") if lines > 500 { lines = 500 } cmd := fmt.Sprintf("tail -n %d %s", lines, shellQuote(path)) if grep != "" { cmd += fmt.Sprintf(" | grep %s", shellQuote(grep)) } output, err := m.Execute(ctx, cmd, target) if err != nil { return errorResult(err.Error()), nil } return textResult(output), nil } func journalReadHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { m := manager() if m == nil { return errorResult("no SSH manager initialized"), nil } args := requestArgs(req) service := getString(args, "service", "") since := getString(args, "since", "") lines := getInt(args, "lines", 100) priority := getString(args, "priority", "") target := getString(args, "target", "primary") if lines > 500 { lines = 500 } checkCmd := "command -v journalctl >/dev/null 2>&1 && echo 'systemd' || echo 'syslog'" checkOutput, err := m.Execute(ctx, checkCmd, target) if err != nil { return errorResult(err.Error()), nil } var cmd string if containsString(checkOutput, "systemd") { cmd = "journalctl --no-pager" if service != "" { cmd += fmt.Sprintf(" -u %s", shellQuote(service)) } if since != "" { cmd += fmt.Sprintf(" --since %s", shellQuote(since)) } if priority != "" { cmd += fmt.Sprintf(" -p %s", shellQuote(priority)) } cmd += fmt.Sprintf(" -n %d 2>/dev/null", lines) } else { cmd = fmt.Sprintf("cat /var/log/syslog /var/log/messages /var/log/system.log 2>/dev/null | tail -n %d", lines) if service != "" { cmd += fmt.Sprintf(" | grep -i %s", shellQuote(service)) } } output, err := m.Execute(ctx, cmd, target) if err != nil { return errorResult(err.Error()), nil } return textResult(output), nil } func dmesgReadHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { m := manager() if m == nil { return errorResult("no SSH manager initialized"), nil } args := requestArgs(req) grep := getString(args, "grep", "") lines := getInt(args, "lines", 100) target := getString(args, "target", "primary") if lines > 500 { lines = 500 } cmd := "dmesg --time-format iso 2>/dev/null || dmesg 2>/dev/null" if grep != "" { cmd += fmt.Sprintf(" | grep -i %s", shellQuote(grep)) } cmd += fmt.Sprintf(" | tail -n %d", lines) output, err := m.Execute(ctx, cmd, target) if err != nil { return errorResult(err.Error()), nil } return textResult(output), nil } func diagnoseHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { m := manager() if m == nil { return errorResult("no SSH manager initialized"), nil } target := getString(requestArgs(req), "target", "primary") cmd := ` echo "=== SYSTEM HEALTH DIAGNOSTIC ===" echo "" echo "--- LOAD AVERAGE ---" LOAD=$(uptime 2>/dev/null | awk -F'load average[s]?: ' '{print $2}' | awk -F'[, ]' '{print $1}') CPUS=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1) echo "Load (1min): $LOAD (CPUs: $CPUS)" if echo "$LOAD $CPUS" | awk '{if ($1 > $2 * 2) exit 0; else exit 1}'; then echo "WARNING: High load detected!" fi echo "" echo "--- TOP CPU CONSUMERS ---" ps -eo pid,user,%cpu,%mem,comm | awk 'NR==1{print} NR>1{print | "sort -k3 -rn"}' | head -n 6 echo "" echo "--- OOM EVENTS ---" OOM=$(dmesg 2>/dev/null | grep -i 'out of memory' | tail -n 3) if [ -n "$OOM" ]; then echo "$OOM" echo "WARNING: OOM events found!" else echo "No OOM events in dmesg" fi echo "" echo "--- DISK PRESSURE (>=80%) ---" df -hP 2>/dev/null | awk 'NR==1{print; next} {c=$5; sub(/%/,"",c); m=(c+0>=80 ? " </dev/null 2>&1; then FAILED=$(systemctl --failed --no-legend --no-pager 2>/dev/null | head -n 5) if [ -n "$FAILED" ]; then echo "$FAILED" else echo "No failed services" fi elif command -v rc-status >/dev/null 2>&1; then rc-status --crashed 2>/dev/null | head -n 5 || echo "No crashed services" else echo "Init system not detected" fi echo "" echo "=== END DIAGNOSTIC ===" ` output, err := m.Execute(ctx, cmd, target) if err != nil { return errorResult(err.Error()), nil } return textResult(output), nil } func listServicesHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { m := manager() if m == nil { return errorResult("no SSH manager initialized"), nil } args := requestArgs(req) failedOnly := getBool(args, "failed_only", false) target := getString(args, "target", "primary") var cmd string if failedOnly { cmd = `if command -v systemctl >/dev/null 2>&1; then systemctl --failed --no-pager 2>/dev/null; elif command -v rc-status >/dev/null 2>&1; then rc-status --crashed 2>/dev/null; elif command -v launchctl >/dev/null 2>&1; then launchctl list 2>/dev/null | head -50; else echo "No supported init system detected"; fi` } else { cmd = `if command -v systemctl >/dev/null 2>&1; then systemctl list-units --type=service --no-pager 2>/dev/null | head -50; elif command -v rc-status >/dev/null 2>&1; then rc-status 2>/dev/null; elif command -v launchctl >/dev/null 2>&1; then launchctl list 2>/dev/null | head -50; elif [ -x /usr/sbin/service ]; then service -e 2>/dev/null | head -50; else echo "No supported init system detected"; fi` } output, err := m.Execute(ctx, cmd, target) if err != nil { return errorResult(err.Error()), nil } return textResult(output), nil }