Skip to main content
Glama

task-orchestrator-mcp

by 108yen
validation.ts7.49 kB
import type { Task } from "./storage.js" export interface TaskInput { completion_criteria?: string[] constraints?: string[] description?: string name: string tasks?: TaskInput[] } /** * Recursively find a task by ID in nested task structure * @param tasks Array of tasks to search in * @param id Task ID to find * @returns Found task or undefined */ export function findTaskById(tasks: Task[], id: string): Task | undefined { for (const task of tasks) { if (task.id === id) { return task } const found = findTaskById(task.tasks, id) if (found) { return found } } return undefined } /** * Validate basic task creation parameters * @param params Task creation parameters */ export function validateCreateTaskBasicParams(params: { completion_criteria?: string[] constraints?: string[] insertIndex?: number name: string }): void { const { completion_criteria, constraints, insertIndex, name } = params if (!name || typeof name !== "string" || name.trim() === "") { throw new Error("Task name is required and must be a non-empty string") } // Validate completion_criteria if provided if (completion_criteria !== undefined) { if (!Array.isArray(completion_criteria)) { throw new Error("Completion criteria must be an array") } if (completion_criteria.some((criteria) => typeof criteria !== "string")) { throw new Error("All completion criteria must be strings") } } // Validate constraints if provided if (constraints !== undefined) { if (!Array.isArray(constraints)) { throw new Error("Constraints must be an array") } if (constraints.some((constraint) => typeof constraint !== "string")) { throw new Error("All constraints must be strings") } } // Validate insertIndex if provided if (insertIndex !== undefined && typeof insertIndex !== "number") { throw new Error("Insert index must be a number") } } /** * Validate a single TaskInput object recursively * @param taskInput Task input to validate * @param path Path for error reporting * @param depth Current nesting depth */ export function validateTaskInput( taskInput: unknown, path: string, depth = 0, ): void { // Prevent excessive nesting if (depth > 10) { throw new Error(`Task hierarchy too deep`) } if ( taskInput === null || typeof taskInput !== "object" || Array.isArray(taskInput) ) { throw new Error(`Task at ${path} must be an object`) } const task = taskInput as TaskInput if (!task.name || typeof task.name !== "string" || task.name.trim() === "") { throw new Error(`Task at ${path} must have a non-empty name`) } // Validate description if provided if (task.description !== undefined && typeof task.description !== "string") { throw new Error(`Task description at ${path} must be a string`) } // Validate completion_criteria if provided if (task.completion_criteria !== undefined) { if (!Array.isArray(task.completion_criteria)) { throw new Error(`Completion criteria at ${path} must be an array`) } if ( task.completion_criteria.some((criteria) => typeof criteria !== "string") ) { throw new Error(`All completion criteria at ${path} must be strings`) } } // Validate constraints if provided if (task.constraints !== undefined) { if (!Array.isArray(task.constraints)) { throw new Error(`Constraints at ${path} must be an array`) } if (task.constraints.some((constraint) => typeof constraint !== "string")) { throw new Error(`All constraints at ${path} must be strings`) } } // Validate subtasks recursively if (task.tasks) { if (!Array.isArray(task.tasks)) { throw new Error(`Tasks property at ${path} must be an array`) } for (let i = 0; i < task.tasks.length; i++) { const childTask = task.tasks[i] validateTaskInput(childTask, `${path}.tasks[${i}]`, depth + 1) } } } /** * Validate subtasks array if provided * @param subtasks Subtasks to validate */ export function validateSubtasks(subtasks?: TaskInput[]): void { // Validate subtasks if provided if (subtasks && !Array.isArray(subtasks)) { throw new Error("Tasks parameter must be an array") } // Validate each subtask's required fields recursively if (subtasks) { for (let i = 0; i < subtasks.length; i++) { const subtask = subtasks[i] validateTaskInput(subtask, `tasks[${i}]`, 1) } } } /** * Validate and find parent task if parentId is provided * @param parentId Parent task ID * @param allTasks All tasks to search in * @returns Parent task if found, undefined otherwise */ export function validateAndFindParentTask( parentId: string | undefined, allTasks: Task[], ): Task | undefined { if (!parentId) { return undefined } // Validate parentId format (should be UUID) if (typeof parentId !== "string" || parentId.trim() === "") { throw new Error("Parent ID must be a non-empty string") } const parentTask = findTaskById(allTasks, parentId) if (!parentTask) { throw new Error(`Parent task with id '${parentId}' not found`) } return parentTask } /** * Validate parameters for starting a task * @param id Task ID * @param tasks All tasks * @returns The task to start */ export function validateStartTaskParams(id: string, tasks: Task[]): Task { if (!id || typeof id !== "string") { throw new Error("Task ID is required and must be a string") } const task = findTaskById(tasks, id) if (!task) { throw new Error(`Task with id '${id}' not found`) } if (task.status === "done") { throw new Error(`Task '${id}' is already completed`) } if (task.status === "in_progress") { throw new Error(`Task '${id}' is already in progress`) } return task } /** * Validate parameters for completing a task * @param id Task ID * @param resolution Task resolution */ export function validateCompleteTaskParams( id: string, resolution: string, ): void { if (!id || typeof id !== "string") { throw new Error("Task ID is required and must be a string") } if ( !resolution || typeof resolution !== "string" || resolution.trim() === "" ) { throw new Error("Resolution is required and must be a non-empty string") } } /** * Find and validate task to complete * @param id Task ID * @param tasks All tasks * @returns Task to complete */ export function findAndValidateTaskToComplete(id: string, tasks: Task[]): Task { // Find the task using the recursive helper function const taskToComplete = findTaskById(tasks, id) if (!taskToComplete) { throw new Error(`Task with id '${id}' not found`) } if (taskToComplete.status === "done") { throw new Error(`Task '${id}' is already completed`) } return taskToComplete } /** * Validate that task has no incomplete subtasks * @param task Task to validate */ export function validateNoIncompleteSubtasks(task: Task): void { // Check if the task has incomplete subtasks if (task.tasks.length > 0) { const incompleteSubtasks = task.tasks.filter((t) => t.status !== "done") if (incompleteSubtasks.length > 0) { const incompleteNames = incompleteSubtasks .map((t) => `'${t.name}'`) .join(", ") throw new Error( `Cannot complete task '${task.name}' because it has incomplete subtasks: ${incompleteNames}. Please complete all subtasks first.`, ) } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/108yen/task-orchestrator-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server