1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: LicenseRef-MutuaL-1.2
4
5package mcp
6
7import (
8 "fmt"
9 "strconv"
10 "strings"
11
12 "git.secluded.site/cooked-mcp/internal/cooked"
13)
14
15func formatRecipes(recipes []cooked.RecipeCard) string {
16 if len(recipes) == 0 {
17 return "No saved recipes found."
18 }
19
20 var builder strings.Builder
21 builder.WriteString("Saved recipes:\n")
22 for _, recipe := range recipes {
23 builder.WriteString("- ")
24 builder.WriteString(recipe.Title)
25 builder.WriteString(" (id: ")
26 builder.WriteString(recipe.ID)
27 builder.WriteString(")\n")
28 }
29
30 return strings.TrimRight(builder.String(), "\n")
31}
32
33func formatRecipeNextPageHint(query string, nextPage, limit int) string {
34 if query == "" {
35 return fmt.Sprintf(
36 "More recipes may be available. Call read with target recipes, page %d, limit %d to continue.",
37 nextPage,
38 limit,
39 )
40 }
41
42 return fmt.Sprintf(
43 "More matching recipes may be available. Call read with target recipes, query %q, page %d, limit %d to continue.",
44 query,
45 nextPage,
46 limit,
47 )
48}
49
50func formatRecipeSearchLimitHint(query string, page, nextPage, limit int) string {
51 return fmt.Sprintf(
52 "More matching recipes may be available. Call read with target recipes, query %q, page %d, limit %d to include more results from this page before trying page %d.",
53 query,
54 page,
55 limit,
56 nextPage,
57 )
58}
59
60func formatRecipe(recipe RecipeDetail) string {
61 title := recipe.Title
62 if title == "" {
63 title = "(untitled recipe)"
64 }
65
66 var builder strings.Builder
67 builder.WriteString("Recipe: ")
68 builder.WriteString(title)
69 builder.WriteString(" (id: ")
70 builder.WriteString(recipe.ID)
71 builder.WriteString(")\n")
72 if recipe.Owner != "" {
73 builder.WriteString("Owner: ")
74 builder.WriteString(recipe.Owner)
75 builder.WriteByte('\n')
76 }
77 fmt.Fprintf(&builder, "Portions: %s\n", formatPortions(recipe.Portions))
78
79 content := strings.TrimSpace(recipe.Content)
80 if content == "" {
81 builder.WriteString("\nNo recipe content returned.")
82 } else {
83 builder.WriteString("\n")
84 builder.WriteString(content)
85 }
86
87 return strings.TrimRight(builder.String(), "\n")
88}
89
90func formatRecipeTextPreview(preview ExtractIntoRecipeOutput) string {
91 title := preview.Title
92 if title == "" {
93 title = "(untitled extraction)"
94 }
95
96 var builder strings.Builder
97 builder.WriteString("Extracted recipe text: ")
98 builder.WriteString(title)
99 builder.WriteByte('\n')
100 fmt.Fprintf(&builder, "Portions: %s\n", formatPortions(preview.Portions))
101
102 markdown := strings.TrimSpace(preview.Markdown)
103 if markdown == "" {
104 builder.WriteString("\nNo preview markdown returned.")
105 } else {
106 builder.WriteString("\n")
107 builder.WriteString(markdown)
108 }
109
110 return strings.TrimRight(builder.String(), "\n")
111}
112
113func formatPortions(portions float64) string { return strconv.FormatFloat(portions, 'f', -1, 64) }
114
115func formatRecipeSave(output SaveRecipeOutput) string {
116 return "Saved recipe (id: " + output.RecipeID + ")."
117}
118
119func formatRecipeOverwrite(output SaveRecipeOutput) string {
120 return "Saved draft into existing recipe (id: " + output.RecipeID + ")."
121}
122
123func formatRecipeURLImportDraft(output SaveRecipeOutput) string {
124 title := output.Title
125 if strings.TrimSpace(title) == "" {
126 title = "(untitled draft)"
127 }
128
129 var builder strings.Builder
130 builder.WriteString("Imported recipe URL as draft: ")
131 builder.WriteString(title)
132 builder.WriteString(" (draft_id: ")
133 builder.WriteString(output.DraftID)
134 builder.WriteString("). Review the markdown and portions before saving.")
135 if output.Portions > 0 {
136 fmt.Fprintf(&builder, "\nPortions: %s", formatPortions(output.Portions))
137 }
138 if strings.TrimSpace(output.Markdown) != "" {
139 builder.WriteString("\n\n")
140 builder.WriteString(output.Markdown)
141 }
142
143 return strings.TrimRight(builder.String(), "\n")
144}
145
146func formatRecipeDelete(output DeleteRecipeOutput) string {
147 return "Deleted recipe (id: " + output.RecipeID + ")."
148}