From ac954068a9cd194a09dda5bb764d29872cfea9ea Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Mon, 30 Jun 2025 17:13:12 +0300 Subject: [PATCH] update todos --- internal/builtin/todo.go | 70 +++++++++++++++++++---------------- internal/builtin/todo_test.go | 43 +++++++++++---------- 2 files changed, 61 insertions(+), 52 deletions(-) diff --git a/internal/builtin/todo.go b/internal/builtin/todo.go index 0c01469c..4744d351 100644 --- a/internal/builtin/todo.go +++ b/internal/builtin/todo.go @@ -89,6 +89,36 @@ func (ts *TodoServer) getTodos() []TodoInfo { return todos } +// formatTodos returns a nice readable format for todos +func formatTodos(todos []TodoInfo) string { + if len(todos) == 0 { + return "\n\nNo todos" + } + + var result strings.Builder + result.WriteString("\n\n") + for _, todo := range todos { + var checkbox string + switch todo.Status { + case "completed": + checkbox = "[X]" + case "in_progress": + checkbox = "[~]" + default: // pending + checkbox = "[ ]" + } + result.WriteString(fmt.Sprintf("%s %s\n", checkbox, todo.Content)) + } + + // Remove trailing newline + output := result.String() + if len(output) > 0 { + output = output[:len(output)-1] + } + + return output +} + // setTodos stores todos in memory func (ts *TodoServer) setTodos(todos []TodoInfo) { ts.mutex.Lock() @@ -136,24 +166,12 @@ func (ts *TodoServer) executeTodoWrite(ctx context.Context, request mcp.CallTool // Store todos in memory ts.setTodos(todos) - // Count non-completed todos - activeTodos := 0 - for _, todo := range todos { - if todo.Status != "completed" { - activeTodos++ - } - } + // Format output in readable format + output := formatTodos(todos) - // Format output - output, err := json.MarshalIndent(todos, "", " ") - if err != nil { - return mcp.NewToolResultError("failed to format todos"), nil - } - - // Create result with metadata - result := mcp.NewToolResultText(string(output)) + // Create result with formatted output + result := mcp.NewToolResultText(output) result.Meta = map[string]any{ - "title": fmt.Sprintf("%d todos", activeTodos), "todos": todos, } @@ -165,24 +183,12 @@ func (ts *TodoServer) executeTodoRead(ctx context.Context, request mcp.CallToolR // Get todos from memory todos := ts.getTodos() - // Count non-completed todos - activeTodos := 0 - for _, todo := range todos { - if todo.Status != "completed" { - activeTodos++ - } - } + // Format output in readable format + output := formatTodos(todos) - // Format output - output, err := json.MarshalIndent(todos, "", " ") - if err != nil { - return mcp.NewToolResultError("failed to format todos"), nil - } - - // Create result with metadata - result := mcp.NewToolResultText(string(output)) + // Create result with formatted output + result := mcp.NewToolResultText(output) result.Meta = map[string]any{ - "title": fmt.Sprintf("%d todos", activeTodos), "todos": todos, } diff --git a/internal/builtin/todo_test.go b/internal/builtin/todo_test.go index 4bf76e00..00afa2a1 100644 --- a/internal/builtin/todo_test.go +++ b/internal/builtin/todo_test.go @@ -2,7 +2,6 @@ package builtin import ( "context" - "encoding/json" "testing" "github.com/mark3labs/mcp-go/mcp" @@ -102,13 +101,11 @@ func TestTodoWrite(t *testing.T) { t.Errorf("Expected 2 todos, got %d", len(storedTodos)) } - // Verify the content + // Verify the content is in readable format if textContent, ok := mcp.AsTextContent(result.Content[0]); ok { - var resultTodos []TodoInfo - if err := json.Unmarshal([]byte(textContent.Text), &resultTodos); err != nil { - t.Errorf("Failed to parse result JSON: %v", err) - } else if len(resultTodos) != 2 { - t.Errorf("Expected 2 todos in result, got %d", len(resultTodos)) + expectedOutput := "\n\n[ ] Test task 1\n[~] Test task 2" + if textContent.Text != expectedOutput { + t.Errorf("Expected formatted output:\n%s\nGot:\n%s", expectedOutput, textContent.Text) } } else { t.Error("Expected text content") @@ -148,15 +145,11 @@ func TestTodoRead(t *testing.T) { t.Fatal("Expected result to have content") } - // Verify the content + // Verify the content is in readable format if textContent, ok := mcp.AsTextContent(result.Content[0]); ok { - var resultTodos []TodoInfo - if err := json.Unmarshal([]byte(textContent.Text), &resultTodos); err != nil { - t.Errorf("Failed to parse result JSON: %v", err) - } else if len(resultTodos) != 1 { - t.Errorf("Expected 1 todo in result, got %d", len(resultTodos)) - } else if resultTodos[0].Content != "Existing task" { - t.Errorf("Expected 'Existing task', got '%s'", resultTodos[0].Content) + expectedOutput := "\n\n[ ] Existing task" + if textContent.Text != expectedOutput { + t.Errorf("Expected formatted output:\n%s\nGot:\n%s", expectedOutput, textContent.Text) } } else { t.Error("Expected text content") @@ -282,17 +275,27 @@ func TestTodoActiveCounting(t *testing.T) { t.Fatalf("Failed to execute todowrite: %v", err) } - // Check metadata for correct active count (should be 3: 2 pending + 1 in_progress) + // Check that metadata contains todos if result.Meta == nil { t.Fatal("Expected metadata to be non-nil") } - title, ok := result.Meta["title"].(string) + metaTodos, ok := result.Meta["todos"].([]TodoInfo) if !ok { - t.Fatal("Expected title in metadata") + t.Fatal("Expected todos in metadata") } - if title != "3 todos" { - t.Errorf("Expected '3 todos', got '%s'", title) + if len(metaTodos) != 4 { + t.Errorf("Expected 4 todos in metadata, got %d", len(metaTodos)) + } + + // Verify the content is in readable format + if textContent, ok := mcp.AsTextContent(result.Content[0]); ok { + expectedOutput := "\n\n[ ] Task 1\n[~] Task 2\n[X] Task 3\n[ ] Task 4" + if textContent.Text != expectedOutput { + t.Errorf("Expected formatted output:\n%s\nGot:\n%s", expectedOutput, textContent.Text) + } + } else { + t.Error("Expected text content") } }