diff --git a/main.go b/main.go index f8a6e5a9b72dcad39ff0174eb12728039a6caf5c..432a307b85662df031aa42cf6dee0901025c0f6b 100644 --- a/main.go +++ b/main.go @@ -167,6 +167,7 @@ func NewMCPServer(config *Config) *server.MCPServer { "Lunatask MCP Server", "0.1.0", server.WithHooks(hooks), + server.WithToolCapabilities(true), ) mcpServer.AddTool(mcp.NewTool("get_task_timestamp", @@ -225,11 +226,11 @@ func NewMCPServer(config *Config) *server.MCPServer { mcpServer.AddTool(mcp.NewTool("create_task", mcp.WithDescription("Creates a new task"), mcp.WithString("area_id", - mcp.Description("ID of the area in which to create the task"), + mcp.Description("Area ID in which to create the task"), mcp.Required(), ), mcp.WithString("goal_id", - mcp.Description("ID of the goal, which must belong to the specified area, that the task should be associated with."), + mcp.Description("Goal ID, which must belong to the provided area, to associate the task with."), ), mcp.WithString("name", mcp.Description("Plain text task name using sentence case."), @@ -239,10 +240,25 @@ func NewMCPServer(config *Config) *server.MCPServer { mcp.Description("Note attached to the task, optionally Markdown-formatted"), ), mcp.WithNumber("estimate", - mcp.Description("Estimated time to completion in minutes"), + mcp.Description("Estimated time completion time in minutes"), + mcp.Min(0), + mcp.Max(1440), + ), + mcp.WithNumber("priority", + mcp.Description("Task priority, -2 being lowest, 0 being normal, and 2 being highest"), + mcp.Min(-2), + mcp.Max(2), + ), + mcp.WithString("motivation", + mcp.Description("Motivation driving task creation"), + mcp.Enum("must", "should", "want"), + ), + mcp.WithString("status", + mcp.Description("Task state, such as in progress, provided as 'started', already started, provided as 'started', soon, provided as 'next', blocked as waiting, omit unspecified, and so on. Intuit the task's status."), + mcp.Enum("later", "next", "started", "waiting", "completed"), ), mcp.WithString("scheduled_on", - mcp.Description("Formatted timestamp"), + mcp.Description("Formatted timestamp from get_task_timestamp tool"), ), ), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { return handleCreateTask(ctx, request, config) @@ -322,6 +338,42 @@ func handleCreateTask( } } + if priorityVal, exists := arguments["priority"]; exists && priorityVal != nil { + if priority, ok := priorityVal.(float64); ok { + if priority < -2 || priority > 2 { + return reportMCPError("'priority' must be between -2 and 2 (inclusive)") + } + } else { + return reportMCPError("'priority' must be a number") + } + } + + if motivationVal, exists := arguments["motivation"]; exists && motivationVal != nil { + if motivation, ok := motivationVal.(string); ok && motivation != "" { + validMotivations := map[string]bool{"must": true, "should": true, "want": true} + if !validMotivations[motivation] { + return reportMCPError("'motivation' must be one of 'must', 'should', or 'want'") + } + } else if ok { + // empty string is allowed + } else { + return reportMCPError("'motivation' must be a string") + } + } + + if statusVal, exists := arguments["status"]; exists && statusVal != nil { + if status, ok := statusVal.(string); ok && status != "" { + validStatus := map[string]bool{"later": true, "next": true, "started": true, "waiting": true, "completed": true} + if !validStatus[status] { + return reportMCPError("'status' must be one of 'later', 'next', 'started', 'waiting', or 'completed'") + } + } else if ok { + // empty string is allowed + } else { + return reportMCPError("'status' must be a string") + } + } + // Validate scheduled_on format if provided if scheduledOnArg, exists := arguments["scheduled_on"]; exists { if scheduledOnStr, ok := scheduledOnArg.(string); ok && scheduledOnStr != "" {