package tools import ( "context" "strings" "time" "github.com/google/uuid" "github.com/modelcontextprotocol/go-sdk/mcp" "git.warky.dev/wdevs/amcs/internal/store" ext "git.warky.dev/wdevs/amcs/internal/types" ) type MaintenanceTool struct { store *store.DB } func NewMaintenanceTool(db *store.DB) *MaintenanceTool { return &MaintenanceTool{store: db} } // add_maintenance_task type AddMaintenanceTaskInput struct { Name string `json:"name" jsonschema:"task name"` Category string `json:"category,omitempty" jsonschema:"e.g. hvac, plumbing, exterior, appliance, landscaping"` FrequencyDays *int `json:"frequency_days,omitempty" jsonschema:"recurrence interval in days; omit for one-time tasks"` NextDue *time.Time `json:"next_due,omitempty" jsonschema:"when the task is next due"` Priority string `json:"priority,omitempty" jsonschema:"low, medium, high, or urgent (default: medium)"` Notes string `json:"notes,omitempty"` } type AddMaintenanceTaskOutput struct { Task ext.MaintenanceTask `json:"task"` } func (t *MaintenanceTool) AddTask(ctx context.Context, _ *mcp.CallToolRequest, in AddMaintenanceTaskInput) (*mcp.CallToolResult, AddMaintenanceTaskOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, AddMaintenanceTaskOutput{}, errInvalidInput("name is required") } priority := strings.TrimSpace(in.Priority) if priority == "" { priority = "medium" } task, err := t.store.AddMaintenanceTask(ctx, ext.MaintenanceTask{ Name: strings.TrimSpace(in.Name), Category: strings.TrimSpace(in.Category), FrequencyDays: in.FrequencyDays, NextDue: in.NextDue, Priority: priority, Notes: strings.TrimSpace(in.Notes), }) if err != nil { return nil, AddMaintenanceTaskOutput{}, err } return nil, AddMaintenanceTaskOutput{Task: task}, nil } // log_maintenance type LogMaintenanceInput struct { TaskID uuid.UUID `json:"task_id" jsonschema:"id of the maintenance task"` CompletedAt *time.Time `json:"completed_at,omitempty" jsonschema:"when the work was done (defaults to now)"` PerformedBy string `json:"performed_by,omitempty" jsonschema:"who did the work (self, vendor name, etc.)"` Cost *float64 `json:"cost,omitempty" jsonschema:"cost of the work"` Notes string `json:"notes,omitempty"` NextAction string `json:"next_action,omitempty" jsonschema:"recommended follow-up"` } type LogMaintenanceOutput struct { Log ext.MaintenanceLog `json:"log"` } func (t *MaintenanceTool) LogWork(ctx context.Context, _ *mcp.CallToolRequest, in LogMaintenanceInput) (*mcp.CallToolResult, LogMaintenanceOutput, error) { completedAt := time.Now() if in.CompletedAt != nil { completedAt = *in.CompletedAt } log, err := t.store.LogMaintenance(ctx, ext.MaintenanceLog{ TaskID: in.TaskID, CompletedAt: completedAt, PerformedBy: strings.TrimSpace(in.PerformedBy), Cost: in.Cost, Notes: strings.TrimSpace(in.Notes), NextAction: strings.TrimSpace(in.NextAction), }) if err != nil { return nil, LogMaintenanceOutput{}, err } return nil, LogMaintenanceOutput{Log: log}, nil } // get_upcoming_maintenance type GetUpcomingMaintenanceInput struct { DaysAhead int `json:"days_ahead,omitempty" jsonschema:"how many days to look ahead (default: 30)"` } type GetUpcomingMaintenanceOutput struct { Tasks []ext.MaintenanceTask `json:"tasks"` } func (t *MaintenanceTool) GetUpcoming(ctx context.Context, _ *mcp.CallToolRequest, in GetUpcomingMaintenanceInput) (*mcp.CallToolResult, GetUpcomingMaintenanceOutput, error) { tasks, err := t.store.GetUpcomingMaintenance(ctx, in.DaysAhead) if err != nil { return nil, GetUpcomingMaintenanceOutput{}, err } if tasks == nil { tasks = []ext.MaintenanceTask{} } return nil, GetUpcomingMaintenanceOutput{Tasks: tasks}, nil } // search_maintenance_history type SearchMaintenanceHistoryInput struct { Query string `json:"query,omitempty" jsonschema:"search text matching task name or notes"` Category string `json:"category,omitempty" jsonschema:"filter by task category"` Start *time.Time `json:"start,omitempty" jsonschema:"filter logs completed on or after this date"` End *time.Time `json:"end,omitempty" jsonschema:"filter logs completed on or before this date"` } type SearchMaintenanceHistoryOutput struct { Logs []ext.MaintenanceLogWithTask `json:"logs"` } func (t *MaintenanceTool) SearchHistory(ctx context.Context, _ *mcp.CallToolRequest, in SearchMaintenanceHistoryInput) (*mcp.CallToolResult, SearchMaintenanceHistoryOutput, error) { logs, err := t.store.SearchMaintenanceHistory(ctx, in.Query, in.Category, in.Start, in.End) if err != nil { return nil, SearchMaintenanceHistoryOutput{}, err } if logs == nil { logs = []ext.MaintenanceLogWithTask{} } return nil, SearchMaintenanceHistoryOutput{Logs: logs}, nil }