From cdfec83fe5fe126ba61832c438bea0c6b01db2f6 Mon Sep 17 00:00:00 2001 From: Amolith Date: Sun, 31 Aug 2025 11:26:23 -0600 Subject: [PATCH] feat: add Eisenhower Matrix field to task creation and updates Add support for Eisenhower Matrix quadrant classification in both create_task and update_task operations. The field accepts human-readable values that are translated to API integers: - uncategorised (0) - clears the field - both urgent and important (1) - urgent, but not important (2) - important, but not urgent (3) - neither urgent nor important (4) Includes proper validation and helpful error messages following the existing priority field pattern. --- cmd/lunatask-mcp-server.go | 8 ++++++++ lunatask/tasks.go | 1 + tools/tasks.go | 39 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/cmd/lunatask-mcp-server.go b/cmd/lunatask-mcp-server.go index 942a203469d259b60d5d5b4a0389aebeda696caf..ee981b5b8417c2b80eaa81ef341b0f4133ecff37 100644 --- a/cmd/lunatask-mcp-server.go +++ b/cmd/lunatask-mcp-server.go @@ -241,6 +241,10 @@ func NewMCPServer(appConfig *Config) *server.MCPServer { mcp.Description("Level of importance for the task. Valid values: 'must' (critical/required), 'should' (important), 'want' (nice-to-have). Only include if the user's language suggests strong obligation ('I need to', 'I have to') vs preference ('I'd like to', 'I want to')."), mcp.Enum("must", "should", "want"), ), + mcp.WithString("eisenhower", + mcp.Description("Eisenhower Matrix quadrant for task prioritization. Valid values: 'both urgent and important', 'urgent, but not important', 'important, but not urgent', 'neither urgent nor important', 'uncategorised'. Only include for areas which the user has indicated follow the Eisenhower workflow."), + mcp.Enum("both urgent and important", "urgent, but not important", "important, but not urgent", "neither urgent nor important", "uncategorised"), + ), mcp.WithString("status", mcp.Description("Initial task status. Valid values: 'later' (someday/backlog), 'next' (upcoming/soon), 'started' (in progress), 'waiting' (blocked), 'completed' (finished). Infer from context: 'working on' = 'started', 'soon'/'upcoming' = 'next', 'blocked'/'waiting for' = 'waiting'. Omit for normal new tasks (defaults to appropriate status)."), mcp.Enum("later", "next", "started", "waiting", "completed"), @@ -282,6 +286,10 @@ func NewMCPServer(appConfig *Config) *server.MCPServer { mcp.Description("New level of importance for the task. Valid values: 'must' (critical/required), 'should' (important), 'want' (nice-to-have), or empty string to clear. Only include if changing the motivation level."), mcp.Enum("must", "should", "want", ""), ), + mcp.WithString("eisenhower", + mcp.Description("New Eisenhower Matrix quadrant for task prioritization. Valid values: 'both urgent and important', 'urgent, but not important', 'important, but not urgent', 'neither urgent nor important', 'uncategorised' (clears the field). Only include for areas which the user has indicated follow the Eisenhower workflow."), + mcp.Enum("both urgent and important", "urgent, but not important", "important, but not urgent", "neither urgent nor important", "uncategorised"), + ), mcp.WithString("status", mcp.Description("New task status. Valid values: 'later' (someday/backlog), 'next' (upcoming/soon), 'started' (in progress), 'waiting' (blocked), 'completed' (finished), or empty string to clear. Only include if changing the task status."), mcp.Enum("later", "next", "started", "waiting", "completed", ""), diff --git a/lunatask/tasks.go b/lunatask/tasks.go index c87b077fbc7ee13795ee76da41241304d7ffb431..9f29fea860d4305d053933690609e00b1433b04f 100644 --- a/lunatask/tasks.go +++ b/lunatask/tasks.go @@ -52,6 +52,7 @@ type CreateTaskRequest struct { Motivation string `json:"motivation,omitempty" validate:"omitempty,oneof=must should want unknown"` Estimate int `json:"estimate,omitempty" validate:"omitempty,min=0,max=720"` Priority int `json:"priority,omitempty" validate:"omitempty,min=-2,max=2"` + Eisenhower int `json:"eisenhower,omitempty" validate:"omitempty,min=0,max=4"` ScheduledOn string `json:"scheduled_on,omitempty" validate:"omitempty"` CompletedAt string `json:"completed_at,omitempty" validate:"omitempty"` Source string `json:"source,omitempty" validate:"omitempty"` diff --git a/tools/tasks.go b/tools/tasks.go index 043c56d7da168e604c04709731770f12eba18092..edab3872d6ffb40f5b3dca78593418b915418d06 100644 --- a/tools/tasks.go +++ b/tools/tasks.go @@ -72,6 +72,26 @@ func (h *Handlers) HandleCreateTask(ctx context.Context, request mcp.CallToolReq arguments["priority"] = translatedPriority } + eisenhowerMap := map[string]int{ + "uncategorised": 0, + "both urgent and important": 1, + "urgent, but not important": 2, + "important, but not urgent": 3, + "neither urgent nor important": 4, + } + + if eisenhowerArg, exists := arguments["eisenhower"]; exists && eisenhowerArg != nil { + eisenhowerStr, ok := eisenhowerArg.(string) + if !ok { + return reportMCPError("Invalid type for 'eisenhower' argument: expected string.") + } + translatedEisenhower, isValid := eisenhowerMap[strings.ToLower(eisenhowerStr)] + if !isValid { + return reportMCPError(fmt.Sprintf("Invalid 'eisenhower' value: '%s'. Must be one of 'uncategorised', 'both urgent and important', 'urgent, but not important', 'important, but not urgent', 'neither urgent nor important'.", eisenhowerStr)) + } + arguments["eisenhower"] = translatedEisenhower + } + 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} @@ -246,6 +266,25 @@ func (h *Handlers) HandleUpdateTask(ctx context.Context, request mcp.CallToolReq updatePayload.Priority = translatedPriority } + if eisenhowerArg, exists := arguments["eisenhower"]; exists && eisenhowerArg != nil { + eisenhowerStr, ok := eisenhowerArg.(string) + if !ok { + return reportMCPError("Invalid type for 'eisenhower' argument: expected string.") + } + eisenhowerMap := map[string]int{ + "uncategorised": 0, + "both urgent and important": 1, + "urgent, but not important": 2, + "important, but not urgent": 3, + "neither urgent nor important": 4, + } + translatedEisenhower, isValid := eisenhowerMap[strings.ToLower(eisenhowerStr)] + if !isValid { + return reportMCPError(fmt.Sprintf("Invalid 'eisenhower' value: '%s'. Must be one of 'uncategorised', 'both urgent and important', 'urgent, but not important', 'important, but not urgent', 'neither urgent nor important'.", eisenhowerStr)) + } + updatePayload.Eisenhower = translatedEisenhower + } + if motivationArg, exists := arguments["motivation"]; exists { if motivationStr, ok := motivationArg.(string); ok { if motivationStr != "" {