import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { api } from "../api/client.js";
import { formatResponse, formatText, validateDeleteOperation, createDeleteSafetyError } from "../utils/index.js";
export function registerTaskTools(server: McpServer) {
// List tasks in a board
server.tool(
"list_tasks",
"List tasks in a board",
{
board_id: z.number().int().positive().describe("Board ID"),
},
async (args) => {
const { board_id } = args;
const response = await api.get(`/projects/${board_id}/tasks`);
return formatResponse(response.data);
}
);
// Get task details
server.tool(
"get_task",
"Get details of a specific task including comments and attachments",
{
board_id: z.number().int().positive().describe("Board ID"),
task_id: z.number().int().positive().describe("Task ID"),
},
async (args) => {
const { board_id, task_id } = args;
const response = await api.get(`/projects/${board_id}/tasks/${task_id}`);
return formatResponse(response.data);
}
);
// Create a new task
server.tool(
"create_task",
"Create a new task",
{
board_id: z.number().int().positive().describe("Board ID"),
title: z.string().min(1).describe("Task title"),
description: z.string().optional().describe("Task description"),
stage_id: z
.number()
.int()
.positive()
.optional()
.describe("Stage ID (optional)"),
},
async (args) => {
const { board_id, title, description, stage_id } = args;
const taskData: any = {
title,
board_id,
};
if (description) {
taskData.description = formatText(description);
}
if (stage_id) {
taskData.stage_id = stage_id;
}
const response = await api.post(`/projects/${board_id}/tasks`, { task: taskData });
return formatResponse(response.data);
}
);
// Update an existing task
server.tool(
"update_task",
"Update an existing task",
{
board_id: z.number().int().positive().describe("Board ID"),
task_id: z.number().int().positive().describe("Task ID"),
title: z.string().optional().describe("New task title"),
description: z.string().optional().describe("New task description"),
stage_id: z.number().int().positive().optional().describe("New stage ID"),
},
async (args) => {
const { board_id, task_id, title, description, stage_id } = args;
let response;
// Update title if provided
if (title) {
response = await api.put(
`/projects/${board_id}/tasks/${task_id}`,
{ property: 'title', value: title }
);
}
// Update description if provided
if (description) {
response = await api.put(
`/projects/${board_id}/tasks/${task_id}`,
{ property: 'description', value: formatText(description) }
);
}
// Update stage if provided
if (stage_id) {
response = await api.put(
`/projects/${board_id}/tasks/${task_id}`,
{ property: 'stage_id', value: stage_id }
);
}
if (!response) {
throw new Error('No properties provided for update');
}
return formatResponse(response.data);
}
);
// Change task status/stage
server.tool(
"change_task_status",
"Change the status/stage of a task",
{
board_id: z.number().int().positive().describe("Board ID"),
task_id: z.number().int().positive().describe("Task ID"),
stage_id: z.number().int().positive().describe("New stage ID"),
},
async (args) => {
const { board_id, task_id, stage_id } = args;
const response = await api.put(`/projects/${board_id}/tasks/${task_id}`, {
property: 'stage_id',
value: stage_id,
});
return formatResponse(response.data);
}
);
// Delete a task
server.tool(
"delete_task",
"Delete a task permanently",
{
board_id: z.number().int().positive().describe("Board ID"),
task_id: z.number().int().positive().describe("Task ID"),
confirm_delete: z.union([z.boolean(), z.string()]).optional().describe("Confirmation required: set to true, 'yes', or 'confirm' to proceed"),
},
async (args) => {
const { board_id, task_id, confirm_delete } = args;
// Validate delete operation safety
const safetyCheck = validateDeleteOperation("task", confirm_delete);
if (!safetyCheck.allowed) {
return formatResponse(createDeleteSafetyError(safetyCheck));
}
const response = await api.delete(`/projects/${board_id}/tasks/${task_id}`);
return formatResponse(response.data);
}
);
}