- Implement maintenance tool for adding, logging, and retrieving tasks - Create meals tool for managing recipes, meal plans, and shopping lists - Introduce reparse metadata tool for updating thought metadata - Add household knowledge, home maintenance, family calendar, meal planning, and professional CRM database migrations - Grant necessary permissions for new database tables
211 lines
6.8 KiB
Go
211 lines
6.8 KiB
Go
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 MealsTool struct {
|
|
store *store.DB
|
|
}
|
|
|
|
func NewMealsTool(db *store.DB) *MealsTool {
|
|
return &MealsTool{store: db}
|
|
}
|
|
|
|
// add_recipe
|
|
|
|
type AddRecipeInput struct {
|
|
Name string `json:"name" jsonschema:"recipe name"`
|
|
Cuisine string `json:"cuisine,omitempty"`
|
|
PrepTimeMinutes *int `json:"prep_time_minutes,omitempty"`
|
|
CookTimeMinutes *int `json:"cook_time_minutes,omitempty"`
|
|
Servings *int `json:"servings,omitempty"`
|
|
Ingredients []ext.Ingredient `json:"ingredients,omitempty" jsonschema:"list of ingredients with name, quantity, unit"`
|
|
Instructions []string `json:"instructions,omitempty" jsonschema:"ordered list of steps"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
Rating *int `json:"rating,omitempty" jsonschema:"1-5 rating"`
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
type AddRecipeOutput struct {
|
|
Recipe ext.Recipe `json:"recipe"`
|
|
}
|
|
|
|
func (t *MealsTool) AddRecipe(ctx context.Context, _ *mcp.CallToolRequest, in AddRecipeInput) (*mcp.CallToolResult, AddRecipeOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, AddRecipeOutput{}, errInvalidInput("name is required")
|
|
}
|
|
if in.Ingredients == nil {
|
|
in.Ingredients = []ext.Ingredient{}
|
|
}
|
|
if in.Instructions == nil {
|
|
in.Instructions = []string{}
|
|
}
|
|
if in.Tags == nil {
|
|
in.Tags = []string{}
|
|
}
|
|
recipe, err := t.store.AddRecipe(ctx, ext.Recipe{
|
|
Name: strings.TrimSpace(in.Name),
|
|
Cuisine: strings.TrimSpace(in.Cuisine),
|
|
PrepTimeMinutes: in.PrepTimeMinutes,
|
|
CookTimeMinutes: in.CookTimeMinutes,
|
|
Servings: in.Servings,
|
|
Ingredients: in.Ingredients,
|
|
Instructions: in.Instructions,
|
|
Tags: in.Tags,
|
|
Rating: in.Rating,
|
|
Notes: strings.TrimSpace(in.Notes),
|
|
})
|
|
if err != nil {
|
|
return nil, AddRecipeOutput{}, err
|
|
}
|
|
return nil, AddRecipeOutput{Recipe: recipe}, nil
|
|
}
|
|
|
|
// search_recipes
|
|
|
|
type SearchRecipesInput struct {
|
|
Query string `json:"query,omitempty" jsonschema:"search by recipe name"`
|
|
Cuisine string `json:"cuisine,omitempty"`
|
|
Tags []string `json:"tags,omitempty" jsonschema:"filter by tags (all must match)"`
|
|
Ingredient string `json:"ingredient,omitempty" jsonschema:"filter by ingredient name"`
|
|
}
|
|
|
|
type SearchRecipesOutput struct {
|
|
Recipes []ext.Recipe `json:"recipes"`
|
|
}
|
|
|
|
func (t *MealsTool) SearchRecipes(ctx context.Context, _ *mcp.CallToolRequest, in SearchRecipesInput) (*mcp.CallToolResult, SearchRecipesOutput, error) {
|
|
recipes, err := t.store.SearchRecipes(ctx, in.Query, in.Cuisine, in.Tags, in.Ingredient)
|
|
if err != nil {
|
|
return nil, SearchRecipesOutput{}, err
|
|
}
|
|
if recipes == nil {
|
|
recipes = []ext.Recipe{}
|
|
}
|
|
return nil, SearchRecipesOutput{Recipes: recipes}, nil
|
|
}
|
|
|
|
// update_recipe
|
|
|
|
type UpdateRecipeInput struct {
|
|
ID uuid.UUID `json:"id" jsonschema:"recipe id to update"`
|
|
Name string `json:"name" jsonschema:"recipe name"`
|
|
Cuisine string `json:"cuisine,omitempty"`
|
|
PrepTimeMinutes *int `json:"prep_time_minutes,omitempty"`
|
|
CookTimeMinutes *int `json:"cook_time_minutes,omitempty"`
|
|
Servings *int `json:"servings,omitempty"`
|
|
Ingredients []ext.Ingredient `json:"ingredients,omitempty"`
|
|
Instructions []string `json:"instructions,omitempty"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
Rating *int `json:"rating,omitempty"`
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
type UpdateRecipeOutput struct {
|
|
Recipe ext.Recipe `json:"recipe"`
|
|
}
|
|
|
|
func (t *MealsTool) UpdateRecipe(ctx context.Context, _ *mcp.CallToolRequest, in UpdateRecipeInput) (*mcp.CallToolResult, UpdateRecipeOutput, error) {
|
|
if strings.TrimSpace(in.Name) == "" {
|
|
return nil, UpdateRecipeOutput{}, errInvalidInput("name is required")
|
|
}
|
|
if in.Ingredients == nil {
|
|
in.Ingredients = []ext.Ingredient{}
|
|
}
|
|
if in.Instructions == nil {
|
|
in.Instructions = []string{}
|
|
}
|
|
if in.Tags == nil {
|
|
in.Tags = []string{}
|
|
}
|
|
recipe, err := t.store.UpdateRecipe(ctx, in.ID, ext.Recipe{
|
|
Name: strings.TrimSpace(in.Name),
|
|
Cuisine: strings.TrimSpace(in.Cuisine),
|
|
PrepTimeMinutes: in.PrepTimeMinutes,
|
|
CookTimeMinutes: in.CookTimeMinutes,
|
|
Servings: in.Servings,
|
|
Ingredients: in.Ingredients,
|
|
Instructions: in.Instructions,
|
|
Tags: in.Tags,
|
|
Rating: in.Rating,
|
|
Notes: strings.TrimSpace(in.Notes),
|
|
})
|
|
if err != nil {
|
|
return nil, UpdateRecipeOutput{}, err
|
|
}
|
|
return nil, UpdateRecipeOutput{Recipe: recipe}, nil
|
|
}
|
|
|
|
// create_meal_plan
|
|
|
|
type CreateMealPlanInput struct {
|
|
WeekStart time.Time `json:"week_start" jsonschema:"Monday of the week to plan"`
|
|
Meals []ext.MealPlanInput `json:"meals" jsonschema:"list of meal entries for the week"`
|
|
}
|
|
|
|
type CreateMealPlanOutput struct {
|
|
Entries []ext.MealPlanEntry `json:"entries"`
|
|
}
|
|
|
|
func (t *MealsTool) CreateMealPlan(ctx context.Context, _ *mcp.CallToolRequest, in CreateMealPlanInput) (*mcp.CallToolResult, CreateMealPlanOutput, error) {
|
|
if len(in.Meals) == 0 {
|
|
return nil, CreateMealPlanOutput{}, errInvalidInput("meals are required")
|
|
}
|
|
entries, err := t.store.CreateMealPlan(ctx, in.WeekStart, in.Meals)
|
|
if err != nil {
|
|
return nil, CreateMealPlanOutput{}, err
|
|
}
|
|
if entries == nil {
|
|
entries = []ext.MealPlanEntry{}
|
|
}
|
|
return nil, CreateMealPlanOutput{Entries: entries}, nil
|
|
}
|
|
|
|
// get_meal_plan
|
|
|
|
type GetMealPlanInput struct {
|
|
WeekStart time.Time `json:"week_start" jsonschema:"Monday of the week to retrieve"`
|
|
}
|
|
|
|
type GetMealPlanOutput struct {
|
|
Entries []ext.MealPlanEntry `json:"entries"`
|
|
}
|
|
|
|
func (t *MealsTool) GetMealPlan(ctx context.Context, _ *mcp.CallToolRequest, in GetMealPlanInput) (*mcp.CallToolResult, GetMealPlanOutput, error) {
|
|
entries, err := t.store.GetMealPlan(ctx, in.WeekStart)
|
|
if err != nil {
|
|
return nil, GetMealPlanOutput{}, err
|
|
}
|
|
if entries == nil {
|
|
entries = []ext.MealPlanEntry{}
|
|
}
|
|
return nil, GetMealPlanOutput{Entries: entries}, nil
|
|
}
|
|
|
|
// generate_shopping_list
|
|
|
|
type GenerateShoppingListInput struct {
|
|
WeekStart time.Time `json:"week_start" jsonschema:"Monday of the week to generate shopping list for"`
|
|
}
|
|
|
|
type GenerateShoppingListOutput struct {
|
|
ShoppingList ext.ShoppingList `json:"shopping_list"`
|
|
}
|
|
|
|
func (t *MealsTool) GenerateShoppingList(ctx context.Context, _ *mcp.CallToolRequest, in GenerateShoppingListInput) (*mcp.CallToolResult, GenerateShoppingListOutput, error) {
|
|
list, err := t.store.GenerateShoppingList(ctx, in.WeekStart)
|
|
if err != nil {
|
|
return nil, GenerateShoppingListOutput{}, err
|
|
}
|
|
return nil, GenerateShoppingListOutput{ShoppingList: list}, nil
|
|
}
|