package econetmcp import ( "context" "fmt" "strings" "github.com/modelcontextprotocol/go-sdk/mcp" "k8s.io/utils/ptr" pb "gitea.libretechconsulting.com/rmcguire/econet-exporter/api/econet/v1alpha1" ) var ListDevicesTool = &mcp.Tool{ Name: "list_water_heaters", Title: "List EcoNet Water Heaters", Description: "Lists Rheem EcoNet devices and their current state (mode, setpoint, " + "hot water availability, connectivity). Optionally filter to one serial number.", Annotations: &mcp.ToolAnnotations{ ReadOnlyHint: true, OpenWorldHint: ptr.To(true), }, } type ListDevicesParams struct { SerialNumber string `json:"serial_number,omitempty" jsonschema:"Optional device serial number to return a single device"` } func (m *EconetMCPServer) addListDevicesTool() { mcp.AddTool(m.server, ListDevicesTool, m.listDevicesHandler) } func (m *EconetMCPServer) listDevicesHandler(ctx context.Context, _ *mcp.CallToolRequest, args *ListDevicesParams) ( *mcp.CallToolResult, any, error, ) { m.log.Debug().Str("serial", args.SerialNumber).Msg("list water heaters tool called") devices, err := m.lookup(ctx, args.SerialNumber) if err != nil { return &mcp.CallToolResult{IsError: true, Meta: mcp.Meta{"error": err.Error()}}, nil, err } return &mcp.CallToolResult{ Content: []mcp.Content{&mcp.TextContent{Text: summarizeDevices(devices)}}, }, nil, nil } func (m *EconetMCPServer) lookup(ctx context.Context, serial string) ([]*pb.Device, error) { if serial != "" { resp, err := m.econetGRPC.GetDevice(ctx, &pb.GetDeviceRequest{SerialNumber: serial}) if err != nil { return nil, err } return []*pb.Device{resp.GetDevice()}, nil } resp, err := m.econetGRPC.ListDevices(ctx, &pb.ListDevicesRequest{}) if err != nil { return nil, err } return resp.GetDevices(), nil } func summarizeDevices(devices []*pb.Device) string { if len(devices) == 0 { return "No EcoNet devices found." } var b strings.Builder for _, d := range devices { fmt.Fprintf(&b, "%s (%s, serial %s): mode=%s setpoint=%d°F running=%t connected=%t hotWater=%d%% alerts=%d\n", d.GetFriendlyName(), d.GetGenericType(), d.GetSerialNumber(), d.GetMode(), d.GetSetpoint(), d.GetRunning(), d.GetConnected(), d.GetHotWaterAvailability(), d.GetAlertCount()) } return strings.TrimRight(b.String(), "\n") }