@grec0/mcp-s2s-asterisk
Version:
MCP server para asistente telefónico conversacional con Asterisk S2S
239 lines (238 loc) • 8.51 kB
JavaScript
/**
* @fileoverview Task operations for the MCP Kanban server
*
* This module provides functions for interacting with tasks in the Planka Kanban board,
* including creating, retrieving, updating, and deleting tasks, as well as batch operations.
*/
import { z } from "zod";
import { plankaRequest } from "../common/utils.js";
import { PlankaTaskSchema } from "../common/types.js";
// Schema definitions
/**
* Schema for creating a new task
* @property {string} cardId - The ID of the card to create the task in
* @property {string} name - The name of the task
* @property {number} [position] - The position of the task in the card (default: 65535)
*/
export const CreateTaskSchema = z.object({
cardId: z.string().describe("Card ID"),
name: z.string().describe("Task name"),
position: z.number().optional().describe("Task position (default: 65535)"),
});
/**
* Schema for batch creating multiple tasks
* @property {Array<CreateTaskSchema>} tasks - Array of tasks to create
*/
export const BatchCreateTasksSchema = z.object({
tasks: z.array(CreateTaskSchema).describe("Array of tasks to create"),
});
/**
* Schema for retrieving tasks from a card
* @property {string} cardId - The ID of the card to get tasks from
*/
export const GetTasksSchema = z.object({
cardId: z.string().describe("Card ID"),
});
/**
* Schema for retrieving a specific task
* @property {string} id - The ID of the task to retrieve
* @property {string} [cardId] - The ID of the card containing the task
*/
export const GetTaskSchema = z.object({
id: z.string().describe("Task ID"),
cardId: z.string().optional().describe("Card ID containing the task"),
});
/**
* Schema for updating a task
* @property {string} id - The ID of the task to update
* @property {string} [name] - The new name for the task
* @property {boolean} [isCompleted] - Whether the task is completed
* @property {number} [position] - The new position for the task
*/
export const UpdateTaskSchema = z.object({
id: z.string().describe("Task ID"),
name: z.string().optional().describe("Task name"),
isCompleted: z.boolean().optional().describe("Whether the task is completed"),
position: z.number().optional().describe("Task position"),
});
/**
* Schema for deleting a task
* @property {string} id - The ID of the task to delete
*/
export const DeleteTaskSchema = z.object({
id: z.string().describe("Task ID"),
});
// Response schemas
const TasksResponseSchema = z.object({
items: z.array(PlankaTaskSchema),
included: z.record(z.any()).optional(),
});
const TaskResponseSchema = z.object({
item: PlankaTaskSchema,
included: z.record(z.any()).optional(),
});
// Map to store task ID to card ID mapping
const taskCardIdMap = {};
// Function implementations
/**
* Creates a new task for a card
*
* @param {object} params - The task creation parameters
* @param {string} params.cardId - The ID of the card to create the task in
* @param {string} params.name - The name of the new task
* @param {number} params.position - The position of the task in the card
* @returns {Promise<object>} The created task
*/
export async function createTask(params) {
try {
const { cardId, name, position = 65535 } = params;
const response = await plankaRequest(`/api/cards/${cardId}/tasks`, {
method: "POST",
body: { name, position },
});
// Store the task ID to card ID mapping for getTask
if (response.item && response.item.id) {
taskCardIdMap[response.item.id] = cardId;
}
return response.item;
}
catch (error) {
console.error("Error creating task:", error);
throw new Error(`Failed to create task: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Creates multiple tasks for cards in a single operation
*
* @param {BatchCreateTasksOptions} options - The batch create tasks options
* @returns {Promise<{results: any[], successes: any[], failures: TaskError[]}>} The results of the batch operation
* @throws {Error} If the batch operation fails completely
*/
export async function batchCreateTasks(options) {
try {
const results = [];
const successes = [];
const failures = [];
// Process each task in sequence
for (let i = 0; i < options.tasks.length; i++) {
const task = options.tasks[i];
// Ensure position is set if not provided
if (!task.position) {
task.position = 65535 * (i + 1);
}
try {
const result = await createTask(task);
results.push({
success: true,
result,
});
successes.push(result);
}
catch (error) {
const errorMessage = error instanceof Error
? error.message
: String(error);
results.push({
success: false,
error: { message: errorMessage },
});
failures.push({
index: i,
task,
error: errorMessage,
});
}
}
return {
results,
successes,
failures,
};
}
catch (error) {
throw new Error(`Failed to batch create tasks: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Retrieves all tasks for a specific card
*
* @param {string} cardId - The ID of the card to get tasks from
* @returns {Promise<Array<object>>} Array of tasks in the card
*/
export async function getTasks(cardId) {
try {
// Instead of using the tasks endpoint which returns HTML,
// we'll get the card details which includes tasks
const response = await plankaRequest(`/api/cards/${cardId}`);
// Extract tasks from the card response
if (response?.included?.tasks && Array.isArray(response.included.tasks)) {
const tasks = response.included.tasks;
return tasks;
}
return [];
}
catch (error) {
console.error(`Error getting tasks for card ${cardId}:`, error);
// If there's an error, return an empty array
return [];
}
}
/**
* Retrieves a specific task by ID
*
* @param {string} id - The ID of the task to retrieve
* @param {string} [cardId] - Optional card ID to help find the task
* @returns {Promise<object>} The requested task
*/
export async function getTask(id, cardId) {
try {
// Tasks in Planka are always part of a card, so we need the card ID
const taskCardId = cardId || taskCardIdMap[id];
if (!taskCardId) {
throw new Error("Card ID is required to get a task. Either provide it directly or create the task first.");
}
// Get the card details which includes tasks
const response = await plankaRequest(`/api/cards/${taskCardId}`);
if (!response?.included?.tasks ||
!Array.isArray(response.included.tasks)) {
throw new Error(`Failed to get tasks for card ${taskCardId}`);
}
// Find the task with the matching ID
const task = response.included.tasks.find((task) => task.id === id);
if (!task) {
throw new Error(`Task with ID ${id} not found in card ${taskCardId}`);
}
return task;
}
catch (error) {
console.error(`Error getting task with ID ${id}:`, error);
throw new Error(`Failed to get task: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Updates a task's properties
*
* @param {string} id - The ID of the task to update
* @param {Partial<Omit<CreateTaskOptions, "cardId">>} options - The properties to update
* @returns {Promise<object>} The updated task
*/
export async function updateTask(id, options) {
const response = await plankaRequest(`/api/tasks/${id}`, {
method: "PATCH",
body: options,
});
const parsedResponse = TaskResponseSchema.parse(response);
return parsedResponse.item;
}
/**
* Deletes a task by ID
*
* @param {string} id - The ID of the task to delete
* @returns {Promise<{success: boolean}>} Success indicator
*/
export async function deleteTask(id) {
await plankaRequest(`/api/tasks/${id}`, {
method: "DELETE",
});
return { success: true };
}