@@ -113,9 +113,7 @@ func (s *Server) callShoppingListReadTool(ctx context.Context) (*sdk.CallToolRes
ShoppingListRecipes: shoppingList.Recipes,
}
- return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: formatShoppingList(shoppingList)}},
- }, output, nil
+ return newToolResult(formatShoppingList(shoppingList), output), output, nil
}
func (s *Server) callRecipeReadTool(ctx context.Context, rawRecipeID string) (*sdk.CallToolResult, ReadOutput, error) {
@@ -139,7 +137,7 @@ func (s *Server) callRecipeReadTool(ctx context.Context, rawRecipeID string) (*s
}
output := ReadOutput{Recipe: &recipe}
- return &sdk.CallToolResult{Content: []sdk.Content{&sdk.TextContent{Text: formatRecipe(recipe)}}}, output, nil
+ return newToolResult(formatRecipe(recipe), output), output, nil
}
func (s *Server) readRecipeParts(
@@ -175,7 +173,7 @@ func (s *Server) callRecipesReadTool(
text += "\n\n" + paginationHint
}
- return &sdk.CallToolResult{Content: []sdk.Content{&sdk.TextContent{Text: text}}}, output, nil
+ return newToolResult(text, output), output, nil
}
func (s *Server) readRecipeCards(
@@ -235,9 +233,7 @@ func (s *Server) callPreviewRecipeTextTool(
Portions: preview.Portions,
}
- return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: formatRecipeTextPreview(output)}},
- }, output, nil
+ return newToolResult(formatRecipeTextPreview(output), output), output, nil
}
func (s *Server) callSaveRecipeTool(
@@ -328,7 +324,7 @@ func (s *Server) callURLSaveRecipeTool(
recipeID := strings.TrimSpace(imported.RecipeID)
if recipeID != "" {
- return newRecipeSaveResult(recipeID), SaveRecipeOutput{RecipeID: recipeID}, nil
+ return newRecipeSaveResult(recipeID)
}
draftID := strings.TrimSpace(imported.DraftID)
@@ -348,9 +344,7 @@ func (s *Server) callURLSaveRecipeTool(
Portions: content.Portions,
}
- return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: formatRecipeURLImportDraft(output)}},
- }, output, nil
+ return newToolResult(formatRecipeURLImportDraft(output), output), output, nil
}
func (s *Server) callDraftSaveRecipeTool(
@@ -373,7 +367,7 @@ func (s *Server) callDraftSaveRecipeTool(
return nil, SaveRecipeOutput{}, fmt.Errorf("save recipe draft response missing recipe ID")
}
- return newRecipeSaveResult(recipeID), SaveRecipeOutput{RecipeID: recipeID}, nil
+ return newRecipeSaveResult(recipeID)
}
func (s *Server) callExistingSaveRecipeTool(
@@ -392,7 +386,7 @@ func (s *Server) callExistingSaveRecipeTool(
return nil, SaveRecipeOutput{}, err
}
- return newRecipeSaveResult(recipeID), SaveRecipeOutput{RecipeID: recipeID}, nil
+ return newRecipeSaveResult(recipeID)
}
func validateSaveRecipeContent(source, markdown string, portions int) error {
@@ -419,15 +413,13 @@ func (s *Server) savePreparedRecipe(
return nil, SaveRecipeOutput{}, fmt.Errorf("save recipe response missing recipe ID")
}
- return newRecipeSaveResult(recipeID), SaveRecipeOutput{RecipeID: recipeID}, nil
+ return newRecipeSaveResult(recipeID)
}
-func newRecipeSaveResult(recipeID string) *sdk.CallToolResult {
+func newRecipeSaveResult(recipeID string) (*sdk.CallToolResult, SaveRecipeOutput, error) {
output := SaveRecipeOutput{RecipeID: recipeID}
- return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: formatRecipeSave(output)}},
- }
+ return newToolResult(formatRecipeSave(output), output), output, nil
}
func (s *Server) callDeleteRecipeTool(
@@ -446,9 +438,7 @@ func (s *Server) callDeleteRecipeTool(
output := DeleteRecipeOutput{RecipeID: recipeID}
- return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: formatRecipeDelete(output)}},
- }, output, nil
+ return newToolResult(formatRecipeDelete(output), output), output, nil
}
func (s *Server) callChangeShoppingListTool(
@@ -500,13 +490,9 @@ func (s *Server) addShoppingListIngredients(
}
output := ChangeShoppingListOutput{AddedCount: result.AddedCount, Ingredients: result.Ingredients}
+ text := fmt.Sprintf("Added %d shopping-list ingredients.", result.AddedCount)
- return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: fmt.Sprintf(
- "Added %d shopping-list ingredients.",
- result.AddedCount,
- )}},
- }, output, nil
+ return newToolResult(text, output), output, nil
}
func (s *Server) clearShoppingList(ctx context.Context) (*sdk.CallToolResult, ChangeShoppingListOutput, error) {
@@ -516,9 +502,7 @@ func (s *Server) clearShoppingList(ctx context.Context) (*sdk.CallToolResult, Ch
output := ChangeShoppingListOutput{Cleared: true}
- return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: "Cleared shopping list."}},
- }, output, nil
+ return newToolResult("Cleared shopping list.", output), output, nil
}
func (s *Server) removeShoppingListProductGroups(
@@ -535,13 +519,9 @@ func (s *Server) removeShoppingListProductGroups(
}
output := ChangeShoppingListOutput{RemovedProductGroupIDs: ids}
+ text := fmt.Sprintf("Removed %d shopping-list product groups.", len(ids))
- return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: fmt.Sprintf(
- "Removed %d shopping-list product groups.",
- len(ids),
- )}},
- }, output, nil
+ return newToolResult(text, output), output, nil
}
func (s *Server) replaceShoppingListSelection(
@@ -611,13 +591,9 @@ func (s *Server) setShoppingListSelection(
}
output := ChangeShoppingListOutput{SelectedProductGroupIDs: ids}
+ text := fmt.Sprintf("Selected %d shopping-list product groups.", len(ids))
- return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: fmt.Sprintf(
- "Selected %d shopping-list product groups.",
- len(ids),
- )}},
- }, output, nil
+ return newToolResult(text, output), output, nil
}
func (s *Server) updateShoppingListProductGroup(
@@ -668,13 +644,16 @@ func (s *Server) updateShoppingListProductGroup(
}
output := ChangeShoppingListOutput{UpdatedProductGroupID: productGroupID}
+ text := fmt.Sprintf("Updated shopping-list product group (id: %s).", productGroupID)
+ return newToolResult(text, output), output, nil
+}
+
+func newToolResult[Output any](text string, output Output) *sdk.CallToolResult {
return &sdk.CallToolResult{
- Content: []sdk.Content{&sdk.TextContent{Text: fmt.Sprintf(
- "Updated shopping-list product group (id: %s).",
- productGroupID,
- )}},
- }, output, nil
+ Content: []sdk.Content{&sdk.TextContent{Text: text}},
+ StructuredContent: output,
+ }
}
func readTool() *sdk.Tool {
@@ -144,6 +144,7 @@ func TestCallToolACIDRecipesRead5ReadsSingleRecipe(t *testing.T) {
requireSingleRecipeBackendCalls(t, backend)
requireSingleRecipeOutput(t, output)
+ requireStructuredContent(t, result, output)
requireNoImageLeak(t, result, output)
}
@@ -204,20 +205,6 @@ func requireNoImageLeak(t *testing.T, result *sdk.CallToolResult, output ReadOut
}
}
-func requireTextContent(t *testing.T, result *sdk.CallToolResult) string {
- t.Helper()
-
- if len(result.Content) != 1 {
- t.Fatalf("text content length = %d, want 1", len(result.Content))
- }
- textContent, ok := result.Content[0].(*sdk.TextContent)
- if !ok {
- t.Fatalf("content type = %T, want *sdk.TextContent", result.Content[0])
- }
-
- return textContent.Text
-}
-
func TestCallToolACIDToolsReadTool3RequiresRecipeIDForRecipeTarget(t *testing.T) {
backend := &fakeBackend{}
server := NewServer(backend, "test")
@@ -240,7 +227,7 @@ func TestCallToolACIDRecipesPreviewText1PreviewsRawText(t *testing.T) {
}}
server := NewServer(backend, "test")
- _, output, err := server.callPreviewRecipeTextTool(
+ result, output, err := server.callPreviewRecipeTextTool(
context.Background(),
nil,
PreviewRecipeTextArguments{Title: " Pasta ", Text: " Boil pasta.\n"},
@@ -262,6 +249,7 @@ func TestCallToolACIDRecipesPreviewText1PreviewsRawText(t *testing.T) {
output.Portions != 2 {
t.Fatalf("preview output = %#v, want title/markdown/portions", output)
}
+ requireStructuredContent(t, result, output)
}
func TestCallToolACIDToolsPreviewRecipeTextTool1RequiresTitle(t *testing.T) {
@@ -302,7 +290,7 @@ func TestCallToolACIDRecipesSave2And9SavesPreparedRecipe(t *testing.T) {
backend := &fakeBackend{saveRecipeID: "recipe-1"}
server := NewServer(backend, "test")
- _, output, err := server.callSaveRecipeTool(
+ result, output, err := server.callSaveRecipeTool(
context.Background(),
nil,
SaveRecipeArguments{
@@ -331,6 +319,7 @@ func TestCallToolACIDRecipesSave2And9SavesPreparedRecipe(t *testing.T) {
if output.RecipeID != "recipe-1" {
t.Fatalf("save output recipe ID = %q, want recipe-1", output.RecipeID)
}
+ requireStructuredContent(t, result, output)
}
func TestCallToolACIDRecipesSave2_1To2_3RequiresPreparedFields(t *testing.T) {
@@ -510,7 +499,7 @@ func TestCallToolACIDRecipesDelete1And2DeletesRecipe(t *testing.T) {
backend := &fakeBackend{}
server := NewServer(backend, "test")
- _, output, err := server.callDeleteRecipeTool(
+ result, output, err := server.callDeleteRecipeTool(
context.Background(),
nil,
DeleteRecipeArguments{RecipeID: " recipe-1 "},
@@ -528,6 +517,7 @@ func TestCallToolACIDRecipesDelete1And2DeletesRecipe(t *testing.T) {
if output.RecipeID != "recipe-1" {
t.Fatalf("delete output recipe ID = %q, want recipe-1", output.RecipeID)
}
+ requireStructuredContent(t, result, output)
}
func TestCallToolACIDToolsDeleteRecipeTool1RequiresRecipeID(t *testing.T) {
@@ -569,7 +559,7 @@ func TestCallToolACIDShoppingListClear1And2ClearsShoppingList(t *testing.T) {
backend := &fakeBackend{}
server := NewServer(backend, "test")
- _, output, err := server.callChangeShoppingListTool(
+ result, output, err := server.callChangeShoppingListTool(
context.Background(),
nil,
ChangeShoppingListArguments{Action: " clear "},
@@ -584,6 +574,7 @@ func TestCallToolACIDShoppingListClear1And2ClearsShoppingList(t *testing.T) {
if !output.Cleared {
t.Fatalf("clear output cleared = %v, want true", output.Cleared)
}
+ requireStructuredContent(t, result, output)
}
func TestCallToolACIDShoppingListActions2RejectsUnknownActionBeforeCallingCooked(t *testing.T) {