feat: add task priority, motivation, and status fields

Amolith created

Add new fields for task priority, motivation, and status to the MCP server
configuration. Include validation for each field to ensure correct values are
provided.

- Priority: integer between -2 and 2 (inclusive).
- Motivation: string, must be one of 'must', 'should', or 'want'. Unknown is
  allowed, but I don't see a reason to include that yet.
- Status: string, must be one of 'later', 'next', 'started', 'waiting', or
  'completed'.
- Include further validation details for the LLM.

Change summary

main.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 56 insertions(+), 4 deletions(-)

Detailed changes

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 != "" {