package tools import ( "context" "strings" "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 HouseholdTool struct { store *store.DB } func NewHouseholdTool(db *store.DB) *HouseholdTool { return &HouseholdTool{store: db} } // add_household_item type AddHouseholdItemInput struct { Name string `json:"name" jsonschema:"name of the item"` Category string `json:"category,omitempty" jsonschema:"category (e.g. paint, appliance, measurement, document)"` Location string `json:"location,omitempty" jsonschema:"where in the home this item is"` Details map[string]any `json:"details,omitempty" jsonschema:"flexible metadata (model numbers, colors, specs, etc.)"` Notes string `json:"notes,omitempty"` } type AddHouseholdItemOutput struct { Item ext.HouseholdItem `json:"item"` } func (t *HouseholdTool) AddItem(ctx context.Context, _ *mcp.CallToolRequest, in AddHouseholdItemInput) (*mcp.CallToolResult, AddHouseholdItemOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, AddHouseholdItemOutput{}, errRequiredField("name") } if in.Details == nil { in.Details = map[string]any{} } item, err := t.store.AddHouseholdItem(ctx, ext.HouseholdItem{ Name: strings.TrimSpace(in.Name), Category: strings.TrimSpace(in.Category), Location: strings.TrimSpace(in.Location), Details: in.Details, Notes: strings.TrimSpace(in.Notes), }) if err != nil { return nil, AddHouseholdItemOutput{}, err } return nil, AddHouseholdItemOutput{Item: item}, nil } // search_household_items type SearchHouseholdItemsInput struct { Query string `json:"query,omitempty" jsonschema:"search text matching name or notes"` Category string `json:"category,omitempty" jsonschema:"filter by category"` Location string `json:"location,omitempty" jsonschema:"filter by location"` } type SearchHouseholdItemsOutput struct { Items []ext.HouseholdItem `json:"items"` } func (t *HouseholdTool) SearchItems(ctx context.Context, _ *mcp.CallToolRequest, in SearchHouseholdItemsInput) (*mcp.CallToolResult, SearchHouseholdItemsOutput, error) { items, err := t.store.SearchHouseholdItems(ctx, in.Query, in.Category, in.Location) if err != nil { return nil, SearchHouseholdItemsOutput{}, err } if items == nil { items = []ext.HouseholdItem{} } return nil, SearchHouseholdItemsOutput{Items: items}, nil } // get_household_item type GetHouseholdItemInput struct { ID uuid.UUID `json:"id" jsonschema:"item id"` } type GetHouseholdItemOutput struct { Item ext.HouseholdItem `json:"item"` } func (t *HouseholdTool) GetItem(ctx context.Context, _ *mcp.CallToolRequest, in GetHouseholdItemInput) (*mcp.CallToolResult, GetHouseholdItemOutput, error) { item, err := t.store.GetHouseholdItem(ctx, in.ID) if err != nil { return nil, GetHouseholdItemOutput{}, err } return nil, GetHouseholdItemOutput{Item: item}, nil } // add_vendor type AddVendorInput struct { Name string `json:"name" jsonschema:"vendor name"` ServiceType string `json:"service_type,omitempty" jsonschema:"type of service (e.g. plumber, electrician, landscaper)"` Phone string `json:"phone,omitempty"` Email string `json:"email,omitempty"` Website string `json:"website,omitempty"` Notes string `json:"notes,omitempty"` Rating *int `json:"rating,omitempty" jsonschema:"1-5 rating"` } type AddVendorOutput struct { Vendor ext.HouseholdVendor `json:"vendor"` } func (t *HouseholdTool) AddVendor(ctx context.Context, _ *mcp.CallToolRequest, in AddVendorInput) (*mcp.CallToolResult, AddVendorOutput, error) { if strings.TrimSpace(in.Name) == "" { return nil, AddVendorOutput{}, errRequiredField("name") } vendor, err := t.store.AddVendor(ctx, ext.HouseholdVendor{ Name: strings.TrimSpace(in.Name), ServiceType: strings.TrimSpace(in.ServiceType), Phone: strings.TrimSpace(in.Phone), Email: strings.TrimSpace(in.Email), Website: strings.TrimSpace(in.Website), Notes: strings.TrimSpace(in.Notes), Rating: in.Rating, }) if err != nil { return nil, AddVendorOutput{}, err } return nil, AddVendorOutput{Vendor: vendor}, nil } // list_vendors type ListVendorsInput struct { ServiceType string `json:"service_type,omitempty" jsonschema:"filter by service type"` } type ListVendorsOutput struct { Vendors []ext.HouseholdVendor `json:"vendors"` } func (t *HouseholdTool) ListVendors(ctx context.Context, _ *mcp.CallToolRequest, in ListVendorsInput) (*mcp.CallToolResult, ListVendorsOutput, error) { vendors, err := t.store.ListVendors(ctx, in.ServiceType) if err != nil { return nil, ListVendorsOutput{}, err } if vendors == nil { vendors = []ext.HouseholdVendor{} } return nil, ListVendorsOutput{Vendors: vendors}, nil }