1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5// Package shared provides common interfaces, types, and helpers for MCP tools.
6package shared
7
8import (
9 "errors"
10 "fmt"
11 "time"
12
13 "github.com/mark3labs/mcp-go/mcp"
14)
15
16// ErrTimezoneNotConfigured is returned when the timezone config value is empty.
17var ErrTimezoneNotConfigured = errors.New(
18 "timezone is not configured; please set the 'timezone' value in your " +
19 "config file (e.g. 'UTC' or 'America/New_York')",
20)
21
22// AreaProvider defines the interface for accessing area data.
23type AreaProvider interface {
24 GetName() string
25 GetID() string
26 GetGoals() []GoalProvider
27}
28
29// GoalProvider defines the interface for accessing goal data.
30//
31//nolint:iface // semantically distinct from HabitProvider
32type GoalProvider interface {
33 GetName() string
34 GetID() string
35}
36
37// HabitProvider defines the interface for accessing habit data.
38//
39//nolint:iface // semantically distinct from GoalProvider
40type HabitProvider interface {
41 GetName() string
42 GetID() string
43}
44
45// Config holds the necessary configuration for tool handlers.
46type Config struct {
47 AccessToken string
48 Timezone string
49 Areas []AreaProvider
50 Habits []HabitProvider
51}
52
53// ReportError creates an MCP error result.
54func ReportError(msg string) (*mcp.CallToolResult, error) {
55 return &mcp.CallToolResult{
56 IsError: true,
57 Content: []mcp.Content{mcp.TextContent{Type: "text", Text: msg}},
58 }, nil
59}
60
61// LoadLocation loads a timezone location string, returning a *time.Location or error.
62func LoadLocation(timezone string) (*time.Location, error) {
63 if timezone == "" {
64 return nil, ErrTimezoneNotConfigured
65 }
66
67 loc, err := time.LoadLocation(timezone)
68 if err != nil {
69 return nil, fmt.Errorf("could not load timezone '%s': %w", timezone, err)
70 }
71
72 return loc, nil
73}