Detailed changes
@@ -54,7 +54,7 @@ The project requires license headers (SPDX format) on all source files and uses
The server exposes four MCP tools that map directly to planning manager methods:
- `update_goal(goal: string)`: Sets overarching goal with length validation
-- `add_tasks(tasks: []TaskInput)`: Batch task creation with duplicate detection. Encourages breaking tasks down into smallest units of work and regular progress tracking.
+- `add_tasks(tasks: []TaskInput)`: Batch task creation with duplicate detection. Encourages breaking tasks down into smallest units of work and regular progress tracking. Output behavior depends on existing tasks: shows verbose instructions + task list when no tasks existed previously, shows brief task list (like `get_tasks`) when tasks already existed.
- `get_tasks()`: Returns markdown-formatted task list with legend and sorted by creation time. Should be called frequently to stay organized.
- `update_task_status(task_id: string, status: string)`: Updates task status and returns full list. Helps maintain planning workflow by tracking progress.
@@ -93,28 +93,17 @@ Response: `Goal "Create a comprehensive MCP server for task planning and managem
### Adding Tasks
-```json
-{
- "name": "add_tasks",
- "arguments": {
- "tasks": [
- {
- "title": "Set up project structure",
- "description": "Create Go module, directories, and basic files"
- },
- {
- "title": "Implement core planning logic",
- "description": "Create Goal and Task data structures with deterministic IDs"
- },
- {
- "title": "Build MCP server integration"
- }
- ]
- }
-}
-```
+When you first add tasks to an empty planning session, the tool provides guidance and shows your complete plan:
+
+- Adds your tasks to the planning session
+- Shows helpful instructions for getting started
+- Displays your goal and current task list with status indicators
+
+When adding tasks to an existing planning session, the tool keeps things brief:
-Response: `Tasks added successfully! Get started on your first one once you're ready, and call get_tasks frequently to remind yourself where you are in the process. Reminder that your overarching goal is "Create a comprehensive MCP server for task planning and management".`
+- Adds your tasks seamlessly
+- Shows your updated task list (same format as `get_tasks`)
+- No repetitive instructions - just your updated plan
### Getting Task Status
@@ -228,7 +228,8 @@ func (s *Server) handleAddTasks(ctx context.Context, request mcp.CallToolRequest
}
// Add tasks
- if err := s.planner.AddTasks(tasks); err != nil {
+ result, err := s.planner.AddTasks(tasks)
+ if err != nil {
s.logger.Error("Failed to add tasks", "error", err)
return &mcp.CallToolResult{
Content: []mcp.Content{
@@ -241,14 +242,22 @@ func (s *Server) handleAddTasks(ctx context.Context, request mcp.CallToolRequest
}, nil
}
- // Get current goal for reminder
- goal := s.planner.GetGoal()
- goalText := "your planning session"
- if goal != nil {
- goalText = fmt.Sprintf("\"%s\"", goal.Text)
- }
+ // Get the full task list with goal and legend
+ taskList := s.planner.GetTasks()
- response := fmt.Sprintf("Tasks added successfully! Get started on your first one once you're ready, and call `get_tasks` frequently to remind yourself where you are in the process. Reminder that your overarching goal is %s.", goalText)
+ var response string
+ if !result.HadExistingTasks {
+ // No existing tasks - show verbose instructions + task list
+ goal := s.planner.GetGoal()
+ goalText := "your planning session"
+ if goal != nil {
+ goalText = fmt.Sprintf("\"%s\"", goal.Text)
+ }
+ response = fmt.Sprintf("Tasks added successfully! Get started on your first one once you're ready, and call `get_tasks` frequently to remind yourself where you are in the process. Reminder that your overarching goal is %s.\n\n%s", goalText, taskList)
+ } else {
+ // Had existing tasks - just show the task list (like get_tasks)
+ response = taskList
+ }
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
@@ -52,14 +52,23 @@ func (m *Manager) UpdateGoal(goalText string) error {
return nil
}
+// AddTasksResult contains the result of adding tasks
+type AddTasksResult struct {
+ AddedTasks []*Task
+ HadExistingTasks bool
+}
+
// AddTasks adds one or more tasks
-func (m *Manager) AddTasks(tasks []TaskInput) error {
+func (m *Manager) AddTasks(tasks []TaskInput) (*AddTasksResult, error) {
m.mu.Lock()
defer m.mu.Unlock()
+ // Check if there were existing tasks before adding new ones
+ hadExistingTasks := len(m.tasks) > 0
+
// Check task limits
if len(m.tasks)+len(tasks) > m.config.Planning.MaxTasks {
- return fmt.Errorf("too many tasks (max %d)", m.config.Planning.MaxTasks)
+ return nil, fmt.Errorf("too many tasks (max %d)", m.config.Planning.MaxTasks)
}
addedTasks := make([]*Task, 0, len(tasks))
@@ -67,15 +76,15 @@ func (m *Manager) AddTasks(tasks []TaskInput) error {
for _, taskInput := range tasks {
// Validate task input
if taskInput.Title == "" {
- return fmt.Errorf("task title cannot be empty")
+ return nil, fmt.Errorf("task title cannot be empty")
}
if len(taskInput.Title) > m.config.Planning.MaxTaskLength {
- return fmt.Errorf("task title too long (max %d characters)", m.config.Planning.MaxTaskLength)
+ return nil, fmt.Errorf("task title too long (max %d characters)", m.config.Planning.MaxTaskLength)
}
if len(taskInput.Description) > m.config.Planning.MaxTaskLength {
- return fmt.Errorf("task description too long (max %d characters)", m.config.Planning.MaxTaskLength)
+ return nil, fmt.Errorf("task description too long (max %d characters)", m.config.Planning.MaxTaskLength)
}
// Create task
@@ -92,14 +101,15 @@ func (m *Manager) AddTasks(tasks []TaskInput) error {
}
m.logger.Info("Tasks added", "count", len(addedTasks))
- return nil
+ return &AddTasksResult{
+ AddedTasks: addedTasks,
+ HadExistingTasks: hadExistingTasks,
+ }, nil
}
-// GetTasks returns a markdown-formatted list of tasks
-func (m *Manager) GetTasks() string {
- m.mu.RLock()
- defer m.mu.RUnlock()
-
+// formatTaskList returns a markdown-formatted list of tasks
+// This method assumes the mutex is already locked
+func (m *Manager) formatTaskList() string {
var lines []string
// Add goal if it exists
@@ -159,6 +169,13 @@ func (m *Manager) GetTasks() string {
return strings.Join(lines, "\n")
}
+// GetTasks returns a markdown-formatted list of tasks
+func (m *Manager) GetTasks() string {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ return m.formatTaskList()
+}
+
// UpdateTaskStatus updates the status of a specific task
func (m *Manager) UpdateTaskStatus(taskID string, status TaskStatus) error {
m.mu.Lock()