1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package shared
6
7import (
8 "errors"
9 "fmt"
10 "slices"
11 "strings"
12
13 "git.secluded.site/go-lunatask"
14 "github.com/modelcontextprotocol/go-sdk/mcp"
15)
16
17// ErrorResult creates an MCP CallToolResult indicating an error.
18// Use this for user-facing errors (validation failures, API errors, etc.).
19// The Go error return should remain nil per MCP SDK conventions.
20func ErrorResult(msg string) *mcp.CallToolResult {
21 return &mcp.CallToolResult{
22 IsError: true,
23 Content: []mcp.Content{
24 &mcp.TextContent{Text: msg},
25 },
26 }
27}
28
29// Estimate constraints.
30const (
31 MinEstimate = 0
32 MaxEstimate = 720
33)
34
35// ErrInvalidEstimate indicates the estimate is out of range.
36var ErrInvalidEstimate = errors.New("estimate must be between 0 and 720 minutes")
37
38// ErrInvalidStatusForWorkflow indicates the status is not valid for the area's workflow.
39var ErrInvalidStatusForWorkflow = errors.New("invalid status for workflow")
40
41// ValidateEstimate checks that an estimate is within the valid range (0-720 minutes).
42func ValidateEstimate(estimate int) error {
43 if estimate < MinEstimate || estimate > MaxEstimate {
44 return fmt.Errorf("%w: got %d", ErrInvalidEstimate, estimate)
45 }
46
47 return nil
48}
49
50// ValidateStatusForWorkflow parses a status string and validates it's allowed for
51// the given workflow. Returns the parsed status and nil on success, or an error
52// with valid options on failure.
53func ValidateStatusForWorkflow(
54 input string,
55 workflow lunatask.Workflow,
56) (lunatask.TaskStatus, error) {
57 status, err := lunatask.ParseTaskStatus(input)
58 if err != nil {
59 return "", fmt.Errorf(
60 "%w: '%s' for %s; valid: %s",
61 ErrInvalidStatusForWorkflow, input, workflow.Description(), formatValidStatuses(workflow),
62 )
63 }
64
65 if !slices.Contains(workflow.ValidStatuses(), status) {
66 return "", fmt.Errorf(
67 "%w: '%s' not valid for %s; valid: %s",
68 ErrInvalidStatusForWorkflow, input, workflow.Description(), formatValidStatuses(workflow),
69 )
70 }
71
72 return status, nil
73}
74
75func formatValidStatuses(workflow lunatask.Workflow) string {
76 statuses := workflow.ValidStatuses()
77 strs := make([]string, len(statuses))
78
79 for i, s := range statuses {
80 strs[i] = string(s)
81 }
82
83 return strings.Join(strs, ", ")
84}
85
86// FormatAllStatuses returns a comma-separated list of all valid task statuses.
87func FormatAllStatuses() string {
88 statuses := lunatask.AllTaskStatuses()
89 strs := make([]string, len(statuses))
90
91 for i, s := range statuses {
92 strs[i] = string(s)
93 }
94
95 return strings.Join(strs, ", ")
96}