/**
* List Tasks Tool
*
* Lists all tasks within a project by slug from the CodeRide API
*/
import { z } from 'zod';
import { BaseTool, MCPToolDefinition, ToolAnnotations, AgentInstructions } from '../utils/base-tool.js';
import { SecureApiClient, TaskListApiResponse } from '../utils/secure-api-client.js';
import { logger } from '../utils/logger.js';
/**
* Schema for the list-tasks tool input
*/
const ListTasksSchema = z.object({
// Project slug (URL-friendly identifier)
slug: z.string({
required_error: "Project slug is required"
})
.regex(/^[A-Za-z]{3}$/, { message: "Project slug must be three letters (e.g., CDB or cdb). Case insensitive." }),
}).strict();
/**
* Type for the list-tasks tool input
*/
type ListTasksInput = z.infer<typeof ListTasksSchema>;
/**
* List Tasks Tool Implementation
*/
export class ListTasksTool extends BaseTool<typeof ListTasksSchema> {
readonly name = 'list_tasks';
readonly description = "Lists all tasks within a project using the project slug (e.g., 'CDB'). Returns tasks organized by status columns with their order and current status.";
readonly zodSchema = ListTasksSchema;
readonly annotations: ToolAnnotations = {
title: "List Tasks",
readOnlyHint: true,
openWorldHint: true, // Interacts with an external API
};
/**
* Constructor with dependency injection
*/
constructor(apiClient?: SecureApiClient) {
super(apiClient);
}
/**
* Returns the full tool definition conforming to MCP.
*/
getMCPToolDefinition(): MCPToolDefinition {
return {
name: this.name,
description: this.description,
annotations: this.annotations,
inputSchema: {
type: "object",
properties: {
slug: {
type: "string",
pattern: "^[A-Za-z]{3}$",
description: "The unique three-letter identifier for the project (e.g., 'CDB' or 'cdb'). Case insensitive - will be converted to uppercase."
}
},
required: ["slug"],
additionalProperties: false
}
};
}
/**
* Generate agent instructions for list_tasks tool
*/
protected generateAgentInstructions(input: ListTasksInput, result: any): AgentInstructions {
return {
immediateActions: [
"Review available tasks and their current status",
"Help user select appropriate task to work on",
"Consider task status (to-do, in-progress, completed) for workflow planning"
],
nextRecommendedTools: ["get_project", "get_task"],
workflowPhase: 'discovery',
criticalReminders: [
"Always establish project context before starting selected task",
"Follow optimal workflow: get_project → get_task → get_prompt"
],
automationHints: {
taskSelection: "Guide user to select tasks based on priority and dependencies",
workflowGuidance: "Ensure project context is established before task work begins"
}
};
}
/**
* Execute the list-tasks tool
*/
async execute(input: ListTasksInput): Promise<unknown> {
logger.info('Executing list-tasks tool', input);
try {
// Use the injected API client to get task list
if (!this.apiClient) {
throw new Error('API client not available - tool not properly initialized');
}
const url = `/task/project/slug/${input.slug.toUpperCase()}`;
logger.debug(`Making GET request to: ${url}`);
const responseData = await this.apiClient.get<TaskListApiResponse>(url) as unknown as TaskListApiResponse;
if (!responseData) {
logger.warn(`No project found or invalid response format from ${url}`);
return {
isError: true,
content: [{ type: "text", text: `Project with slug '${input.slug}' not found` }]
};
}
// Calculate total task count across all columns
const totalTasks = responseData.columns?.reduce((total, column) => total + (column.tasks?.length || 0), 0) || 0;
// Return formatted task list with project info and organized tasks
return {
project: {
id: responseData.id,
name: responseData.name,
slug: responseData.slug,
status: responseData.status
},
taskSummary: {
totalTasks,
statusBreakdown: responseData.columns?.map(column => ({
status: column.id,
name: column.name,
count: column.tasks?.length || 0
})) || []
},
tasksByStatus: responseData.columns?.map(column => ({
status: column.id,
name: column.name,
tasks: column.tasks?.map(task => ({
number: task.number,
title: task.title,
description: task.description,
status: task.status,
priority: task.priority,
position: task.position,
hasContext: !!task.context,
hasInstructions: !!task.instructions
})).sort((a, b) => {
// Sort tasks by their number (e.g., CDB-1, CDB-2, CDB-3, etc.)
const getTaskNumber = (taskNumber: string) => {
const match = taskNumber.match(/^[A-Z]{3}-(\d+)$/);
return match ? parseInt(match[1], 10) : 0;
};
return getTaskNumber(a.number) - getTaskNumber(b.number);
}) || []
})) || []
};
} catch (error) {
const errorMessage = (error instanceof Error) ? error.message : 'An unknown error occurred';
logger.error(`Error in task-list tool: ${errorMessage}`, error instanceof Error ? error : undefined);
return {
isError: true,
content: [{ type: "text", text: errorMessage }]
};
}
}
}