Skip to main content
Glama

mcp-chain-of-thought

taskTools.ts34.5 kB
import { z } from "zod"; import path from "path"; import { fileURLToPath } from "url"; import { getAllTasks, getTaskById, updateTaskStatus, canExecuteTask, batchCreateOrUpdateTasks, deleteTask as modelDeleteTask, updateTaskSummary, assessTaskComplexity, clearAllTasks as modelClearAllTasks, updateTaskContent as modelUpdateTaskContent, updateTaskRelatedFiles as modelUpdateTaskRelatedFiles, searchTasksWithCommand, } from "../models/taskModel.js"; import { TaskStatus, TaskComplexityLevel, RelatedFileType, RelatedFile, Task, TaskDependency, } from "../types/index.js"; import { extractSummary, generateTaskSummary, } from "../utils/summaryExtractor.js"; import { loadTaskRelatedFiles } from "../utils/fileLoader.js"; // Import prompt generators import { getPlanTaskPrompt, getAnalyzeTaskPrompt, getReflectTaskPrompt, getSplitTasksPrompt, getExecuteTaskPrompt, getVerifyTaskPrompt, getCompleteTaskPrompt, getListTasksPrompt, getQueryTaskPrompt, getTaskDetailPrompt, getDeleteTaskPrompt, getClearAllTasksPrompt, getUpdateTaskContentPrompt, } from "../prompts/index.js"; // Task planning tool export const planTaskSchema = z.object({ description: z .string() .min(10, { message: "Task description cannot be less than 10 characters, please provide a more detailed description to ensure clear task objectives", }) .describe("Complete detailed task problem description, should include task objectives, background and expected outcomes"), requirements: z .string() .optional() .describe("Specific technical requirements, business constraints or quality standards for the task (optional)"), existingTasksReference: z .boolean() .optional() .default(false) .describe("Whether to reference existing tasks as a planning basis, used for task adjustment and continuity planning"), }); export async function planTask({ description, requirements, existingTasksReference = false, }: z.infer<typeof planTaskSchema>) { // Get base directory path const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const PROJECT_ROOT = path.resolve(__dirname, "../.."); const DATA_DIR = process.env.DATA_DIR || path.join(PROJECT_ROOT, "data"); const MEMORY_DIR = path.join(DATA_DIR, "memory"); // Prepare required parameters let completedTasks: Task[] = []; let pendingTasks: Task[] = []; // When existingTasksReference is true, load all tasks from the database as reference if (existingTasksReference) { try { const allTasks = await getAllTasks(); // Split tasks into completed and pending categories completedTasks = allTasks.filter( (task) => task.status === TaskStatus.COMPLETED ); pendingTasks = allTasks.filter( (task) => task.status !== TaskStatus.COMPLETED ); } catch (error) {} } // Use prompt generator to get the final prompt const prompt = getPlanTaskPrompt({ description, requirements, existingTasksReference, completedTasks, pendingTasks, memoryDir: MEMORY_DIR, }); return { content: [ { type: "text" as const, text: prompt, }, ], }; } // Task analysis tool export const analyzeTaskSchema = z.object({ summary: z .string() .min(10, { message: "Task summary cannot be less than 10 characters, please provide a more detailed description to ensure clear task objectives", }) .describe( "Structured task summary including task objectives, scope and key technical challenges, minimum 10 characters" ), initialConcept: z .string() .min(50, { message: "Initial solution concept cannot be less than 50 characters, please provide more detailed content to ensure the technical solution is clear", }) .describe( "At least 50 characters of initial solution concept, including technical solution, architectural design and implementation strategy, if code is needed use pseudocode format and only provide high-level logic flow and key steps avoiding complete code" ), previousAnalysis: z .string() .optional() .describe("Analysis results from previous iterations, used for continuous improvement of solutions (only required for reanalysis)"), }); export async function analyzeTask({ summary, initialConcept, previousAnalysis, }: z.infer<typeof analyzeTaskSchema>) { // Use prompt generator to get the final prompt const prompt = getAnalyzeTaskPrompt({ summary, initialConcept, previousAnalysis, }); return { content: [ { type: "text" as const, text: prompt, }, ], }; } // Reflection tool export const reflectTaskSchema = z.object({ summary: z .string() .min(10, { message: "Task summary cannot be less than 10 characters, please provide a more detailed description to ensure clear task objectives", }) .describe("Structured task summary, keeping consistent with the analysis phase to ensure continuity"), analysis: z .string() .min(100, { message: "Technical analysis content is not detailed enough, please provide complete technical analysis and implementation plan", }) .describe( "Comprehensive technical analysis results, including all technical details, dependent components and implementation plans, if code is needed use pseudocode format and only provide high-level logic flow and key steps avoiding complete code" ), }); export async function reflectTask({ summary, analysis, }: z.infer<typeof reflectTaskSchema>) { // Use prompt generator to get the final prompt const prompt = getReflectTaskPrompt({ summary, analysis, }); return { content: [ { type: "text" as const, text: prompt, }, ], }; } // Task splitting tool export const splitTasksSchema = z.object({ updateMode: z .enum(["append", "overwrite", "selective", "clearAllTasks"]) .describe( "Task update mode selection: 'append' (preserve all existing tasks and add new tasks), 'overwrite' (clear all unfinished tasks and completely replace, preserve completed tasks), 'selective' (intelligent update: match and update existing tasks by name, preserve tasks not in the list, recommended for minor task adjustments), 'clearAllTasks' (clear all tasks and create a backup).\nDefault is 'clearAllTasks' mode, only use other modes when the user requests changes or modifications to the plan content" ), tasks: z .array( z.object({ name: z .string() .max(100, { message: "Task name too long, please limit to 100 characters", }) .describe("Brief and clear task name, should be able to express the purpose of the task"), description: z .string() .min(10, { message: "Task description too short, please provide more detailed content to ensure understanding", }) .describe("Detailed task description, including implementation points, technical details and acceptance standards"), implementationGuide: z .string() .describe( "Specific implementation method and steps for this task, please refer to previous analysis results and provide simplified pseudocode" ), dependencies: z .array(z.string()) .optional() .describe( "List of previous task IDs or task names this task depends on, supports two referencing methods, name referencing is more intuitive, and is a string array" ), notes: z .string() .optional() .describe("Supplementary notes, special processing requirements or implementation suggestions (optional)"), relatedFiles: z .array( z.object({ path: z .string() .min(1, { message: "File path cannot be empty", }) .describe("File path, can be a path relative to the project root directory or an absolute path"), type: z .nativeEnum(RelatedFileType) .describe( "File type (TO_MODIFY: to be modified, REFERENCE: reference material, CREATE: to be created, DEPENDENCY: dependency file, OTHER: other)" ), description: z .string() .min(1, { message: "File description cannot be empty", }) .describe("File description, used to explain the purpose and content of the file"), lineStart: z .number() .int() .positive() .optional() .describe("Starting line of the relevant code block (optional)"), lineEnd: z .number() .int() .positive() .optional() .describe("Ending line of the relevant code block (optional)"), }) ) .optional() .describe( "List of files related to the task, used to record code files, reference materials, files to be created, etc. related to the task (optional)" ), verificationCriteria: z .string() .optional() .describe("Verification criteria and inspection methods for this specific task"), }) ) .min(1, { message: "Please provide at least one task", }) .describe( "Structured task list, each task should be atomic and have a clear completion standard, avoid overly simple tasks, simple modifications can be integrated with other tasks, avoid too many tasks" ), globalAnalysisResult: z .string() .optional() .describe("Global analysis result: complete analysis result from reflect_task, applicable to the common parts of all tasks"), }); export async function splitTasks({ updateMode, tasks, globalAnalysisResult, }: z.infer<typeof splitTasksSchema>) { try { // Check if there are duplicate names in the tasks const nameSet = new Set(); for (const task of tasks) { if (nameSet.has(task.name)) { return { content: [ { type: "text" as const, text: "Duplicate task names exist in the tasks parameter, please ensure that each task name is unique", }, ], }; } nameSet.add(task.name); } // Process tasks based on different update modes let message = ""; let actionSuccess = true; let backupFile = null; let createdTasks: Task[] = []; let allTasks: Task[] = []; // Convert task data to the format required for batchCreateOrUpdateTasks const convertedTasks = tasks.map((task) => ({ name: task.name, description: task.description, notes: task.notes, dependencies: task.dependencies, implementationGuide: task.implementationGuide, verificationCriteria: task.verificationCriteria, relatedFiles: task.relatedFiles?.map((file) => ({ path: file.path, type: file.type as RelatedFileType, description: file.description, lineStart: file.lineStart, lineEnd: file.lineEnd, })), })); // Process clearAllTasks mode if (updateMode === "clearAllTasks") { const clearResult = await modelClearAllTasks(); if (clearResult.success) { message = clearResult.message; backupFile = clearResult.backupFile; try { // Clear tasks and then create new tasks createdTasks = await batchCreateOrUpdateTasks( convertedTasks, "append", globalAnalysisResult ); message += `\nSuccessfully created ${createdTasks.length} new tasks.`; } catch (error) { actionSuccess = false; message += `\nError occurred when creating new tasks: ${ error instanceof Error ? error.message : String(error) }`; } } else { actionSuccess = false; message = clearResult.message; } } else { // For other modes, directly use batchCreateOrUpdateTasks try { createdTasks = await batchCreateOrUpdateTasks( convertedTasks, updateMode, globalAnalysisResult ); // Generate messages based on different update modes switch (updateMode) { case "append": message = `Successfully appended ${createdTasks.length} new tasks.`; break; case "overwrite": message = `Successfully cleared unfinished tasks and created ${createdTasks.length} new tasks.`; break; case "selective": message = `Successfully selectively updated/created ${createdTasks.length} tasks.`; break; } } catch (error) { actionSuccess = false; message = `Task creation failed: ${ error instanceof Error ? error.message : String(error) }`; } } // Get all tasks for displaying dependency relationships try { allTasks = await getAllTasks(); } catch (error) { allTasks = [...createdTasks]; // If retrieval fails, use just created tasks } // Use prompt generator to get the final prompt const prompt = getSplitTasksPrompt({ updateMode, createdTasks, allTasks, }); return { content: [ { type: "text" as const, text: prompt, }, ], ephemeral: { taskCreationResult: { success: actionSuccess, message, backupFilePath: backupFile, }, }, }; } catch (error) { return { content: [ { type: "text" as const, text: "Error occurred when executing task splitting: " + (error instanceof Error ? error.message : String(error)), }, ], }; } } export const listTasksSchema = z.object({ status: z .enum(["all", "pending", "in_progress", "completed"]) .describe("Task status to list, can choose 'all' to list all tasks, or specify specific status"), }); // List tasks tool export async function listTasks({ status }: z.infer<typeof listTasksSchema>) { const tasks = await getAllTasks(); let filteredTasks = tasks; switch (status) { case "all": break; case "pending": filteredTasks = tasks.filter( (task) => task.status === TaskStatus.PENDING ); break; case "in_progress": filteredTasks = tasks.filter( (task) => task.status === TaskStatus.IN_PROGRESS ); break; case "completed": filteredTasks = tasks.filter( (task) => task.status === TaskStatus.COMPLETED ); break; } if (filteredTasks.length === 0) { return { content: [ { type: "text" as const, text: `## System Notification\n\nCurrently, there are no ${ status === "all" ? "any" : `any ${status} ` }tasks in the system. Please query other status tasks or first use the "split_tasks" tool to create task structure, then proceed with subsequent operations.`, }, ], }; } const tasksByStatus = tasks.reduce((acc, task) => { if (!acc[task.status]) { acc[task.status] = []; } acc[task.status].push(task); return acc; }, {} as Record<string, typeof tasks>); // Use prompt generator to get the final prompt const prompt = getListTasksPrompt({ status, tasks: tasksByStatus, allTasks: filteredTasks, }); return { content: [ { type: "text" as const, text: prompt, }, ], }; } // Execute task tool export const executeTaskSchema = z.object({ taskId: z .string() .uuid({ message: "Task ID must be a valid UUID format", }) .describe("Unique identifier of the task to execute, must be an existing valid task ID in the system"), }); export async function executeTask({ taskId, }: z.infer<typeof executeTaskSchema>) { try { // Check if the task exists const task = await getTaskById(taskId); if (!task) { return { content: [ { type: "text" as const, text: `Task with ID \`${taskId}\` not found. Please confirm if the ID is correct.`, }, ], }; } // Check if the task can be executed (all dependencies are completed) const executionCheck = await canExecuteTask(taskId); if (!executionCheck.canExecute) { const blockedByTasksText = executionCheck.blockedBy && executionCheck.blockedBy.length > 0 ? `Blocked by the following unfinished dependency tasks: ${executionCheck.blockedBy.join(", ")}` : "Unable to determine blocking reason"; return { content: [ { type: "text" as const, text: `Task "${task.name}" (ID: \`${taskId}\`) cannot be executed at this time. ${blockedByTasksText}`, }, ], }; } // If the task is already marked as "in progress", prompt the user if (task.status === TaskStatus.IN_PROGRESS) { return { content: [ { type: "text" as const, text: `Task "${task.name}" (ID: \`${taskId}\`) is already in progress.`, }, ], }; } // If the task is already marked as "completed", prompt the user if (task.status === TaskStatus.COMPLETED) { return { content: [ { type: "text" as const, text: `Task "${task.name}" (ID: \`${taskId}\`) has been marked as completed. If you need to execute it again, please delete the task and recreate it first.`, }, ], }; } // Update task status to "in progress" await updateTaskStatus(taskId, TaskStatus.IN_PROGRESS); // Assess task complexity const complexityResult = await assessTaskComplexity(taskId); // Convert complexity result to appropriate format const complexityAssessment = complexityResult ? { level: complexityResult.level, metrics: { descriptionLength: complexityResult.metrics.descriptionLength, dependenciesCount: complexityResult.metrics.dependenciesCount, }, recommendations: complexityResult.recommendations, } : undefined; // Get dependency tasks, for displaying completion summary const dependencyTasks: Task[] = []; if (task.dependencies && task.dependencies.length > 0) { for (const dep of task.dependencies) { const depTask = await getTaskById(dep.taskId); if (depTask) { dependencyTasks.push(depTask); } } } // Load task-related file content let relatedFilesSummary = ""; if (task.relatedFiles && task.relatedFiles.length > 0) { try { const relatedFilesResult = await loadTaskRelatedFiles( task.relatedFiles ); relatedFilesSummary = typeof relatedFilesResult === "string" ? relatedFilesResult : relatedFilesResult.summary || ""; } catch (error) { relatedFilesSummary = "Error loading related files, please check the files manually."; } } // Use prompt generator to get the final prompt const prompt = getExecuteTaskPrompt({ task, complexityAssessment, relatedFilesSummary, dependencyTasks, }); return { content: [ { type: "text" as const, text: prompt, }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error occurred when executing task: ${ error instanceof Error ? error.message : String(error) }`, }, ], isError: true, }; } } // Verify task tool export const verifyTaskSchema = z.object({ taskId: z .string() .uuid({ message: "Invalid task ID format, please provide a valid UUID format" }) .describe("Unique identifier of the task to verify, must be an existing valid task ID in the system"), }); export async function verifyTask({ taskId }: z.infer<typeof verifyTaskSchema>) { const task = await getTaskById(taskId); if (!task) { return { content: [ { type: "text" as const, text: `## System Error\n\nTask with ID \`${taskId}\` not found. Please use the "list_tasks" tool to confirm a valid task ID before trying again.`, }, ], isError: true, }; } if (task.status !== TaskStatus.IN_PROGRESS) { return { content: [ { type: "text" as const, text: `## Status Error\n\nTask "${task.name}" (ID: \`${task.id}\`) current status is "${task.status}", not in progress state, cannot be verified.\n\nOnly tasks in "in progress" state can be verified. Please use the "execute_task" tool to start task execution first.`, }, ], isError: true, }; } // Use prompt generator to get the final prompt const prompt = getVerifyTaskPrompt({ task }); return { content: [ { type: "text" as const, text: prompt, }, ], }; } // Complete task tool export const completeTaskSchema = z.object({ taskId: z .string() .uuid({ message: "Invalid task ID format, please provide a valid UUID format" }) .describe( "Unique identifier of the task to mark as completed, must be a valid unfinished task ID in the status of \"in progress\"" ), summary: z .string() .min(30, { message: "Task summary too short, please provide a more detailed completion report, including implementation results and main decisions", }) .optional() .describe( "Task completion summary, concise description of implementation results and important decisions (optional, will be automatically generated if not provided)" ), }); export async function completeTask({ taskId, summary, }: z.infer<typeof completeTaskSchema>) { const task = await getTaskById(taskId); if (!task) { return { content: [ { type: "text" as const, text: `## System Error\n\nTask with ID \`${taskId}\` not found. Please use the "list_tasks" tool to confirm a valid task ID before trying again.`, }, ], isError: true, }; } if (task.status !== TaskStatus.IN_PROGRESS) { return { content: [ { type: "text" as const, text: `## Status Error\n\nTask "${task.name}" (ID: \`${task.id}\`) current status is "${task.status}", not in progress state, cannot mark as completed.\n\nOnly tasks in "in progress" state can be marked as completed. Please use the "execute_task" tool to start task execution first.`, }, ], isError: true, }; } // Process summary information let taskSummary = summary; if (!taskSummary) { // Automatically generate summary taskSummary = generateTaskSummary(task.name, task.description); } // Update task status to completed and add summary await updateTaskStatus(taskId, TaskStatus.COMPLETED); await updateTaskSummary(taskId, taskSummary); // Use prompt generator to get the final prompt const prompt = getCompleteTaskPrompt({ task, completionTime: new Date().toISOString(), }); return { content: [ { type: "text" as const, text: prompt, }, ], }; } // Delete task tool export const deleteTaskSchema = z.object({ taskId: z .string() .uuid({ message: "Invalid task ID format, please provide a valid UUID format" }) .describe("Unique identifier of the task to delete, must be an existing unfinished task ID in the system"), }); export async function deleteTask({ taskId }: z.infer<typeof deleteTaskSchema>) { const task = await getTaskById(taskId); if (!task) { return { content: [ { type: "text" as const, text: getDeleteTaskPrompt({ taskId }), }, ], isError: true, }; } if (task.status === TaskStatus.COMPLETED) { return { content: [ { type: "text" as const, text: getDeleteTaskPrompt({ taskId, task, isTaskCompleted: true }), }, ], isError: true, }; } const result = await modelDeleteTask(taskId); return { content: [ { type: "text" as const, text: getDeleteTaskPrompt({ taskId, task, success: result.success, message: result.message, }), }, ], isError: !result.success, }; } // Clear all tasks tool export const clearAllTasksSchema = z.object({ confirm: z .boolean() .refine((val) => val === true, { message: "Must clearly confirm the clear operation, please set the confirm parameter to true to confirm this dangerous operation", }) .describe("Confirm to delete all unfinished tasks (this operation is irreversible)"), }); export async function clearAllTasks({ confirm, }: z.infer<typeof clearAllTasksSchema>) { // Security check: If not confirmed, refuse operation if (!confirm) { return { content: [ { type: "text" as const, text: getClearAllTasksPrompt({ confirm: false }), }, ], }; } // Check if there are really tasks to clear const allTasks = await getAllTasks(); if (allTasks.length === 0) { return { content: [ { type: "text" as const, text: getClearAllTasksPrompt({ isEmpty: true }), }, ], }; } // Execute clear operation const result = await modelClearAllTasks(); return { content: [ { type: "text" as const, text: getClearAllTasksPrompt({ success: result.success, message: result.message, backupFile: result.backupFile, }), }, ], isError: !result.success, }; } // Update task content tool export const updateTaskContentSchema = z.object({ taskId: z .string() .uuid({ message: "Invalid task ID format, please provide a valid UUID format" }) .describe("Unique identifier of the task to update, must be an existing and unfinished task ID in the system"), name: z.string().optional().describe("New name for the task (optional)"), description: z.string().optional().describe("New description content for the task (optional)"), notes: z.string().optional().describe("New supplementary notes for the task (optional)"), dependencies: z .array(z.string()) .optional() .describe("New dependency relationships for the task (optional)"), relatedFiles: z .array( z.object({ path: z .string() .min(1, { message: "File path cannot be empty, please provide a valid file path" }) .describe("File path, can be a path relative to the project root directory or an absolute path"), type: z .nativeEnum(RelatedFileType) .describe( "Relationship type between the file and task (TO_MODIFY, REFERENCE, CREATE, DEPENDENCY, OTHER)" ), description: z.string().optional().describe("Supplementary description of the file (optional)"), lineStart: z .number() .int() .positive() .optional() .describe("Starting line of the relevant code block (optional)"), lineEnd: z .number() .int() .positive() .optional() .describe("Ending line of the relevant code block (optional)"), }) ) .optional() .describe( "List of files related to the task, used to record code files, reference materials, files to be created, etc. related to the task (optional)" ), implementationGuide: z .string() .optional() .describe("New implementation guide for the task (optional)"), verificationCriteria: z .string() .optional() .describe("New verification criteria for the task (optional)"), }); export async function updateTaskContent({ taskId, name, description, notes, relatedFiles, dependencies, implementationGuide, verificationCriteria, }: z.infer<typeof updateTaskContentSchema>) { if (relatedFiles) { for (const file of relatedFiles) { if ( (file.lineStart && !file.lineEnd) || (!file.lineStart && file.lineEnd) || (file.lineStart && file.lineEnd && file.lineStart > file.lineEnd) ) { return { content: [ { type: "text" as const, text: getUpdateTaskContentPrompt({ taskId, validationError: "Invalid line number settings: must set both start and end lines, and the start line must be less than the end line", }), }, ], }; } } } if ( !( name || description || notes || dependencies || implementationGuide || verificationCriteria || relatedFiles ) ) { return { content: [ { type: "text" as const, text: getUpdateTaskContentPrompt({ taskId, emptyUpdate: true, }), }, ], }; } // Get the task to check if it exists const task = await getTaskById(taskId); if (!task) { return { content: [ { type: "text" as const, text: getUpdateTaskContentPrompt({ taskId, }), }, ], isError: true, }; } // Record the task and content to be updated let updateSummary = `Preparing to update task: ${task.name} (ID: ${task.id})`; if (name) updateSummary += `, new name: ${name}`; if (description) updateSummary += `, update description`; if (notes) updateSummary += `, update notes`; if (relatedFiles) updateSummary += `, update related files (${relatedFiles.length})`; if (dependencies) updateSummary += `, update dependencies (${dependencies.length})`; if (implementationGuide) updateSummary += `, update implementation guide`; if (verificationCriteria) updateSummary += `, update verification criteria`; // Execute the update operation const result = await modelUpdateTaskContent(taskId, { name, description, notes, relatedFiles, dependencies, implementationGuide, verificationCriteria, }); return { content: [ { type: "text" as const, text: getUpdateTaskContentPrompt({ taskId, task, success: result.success, message: result.message, updatedTask: result.task, }), }, ], isError: !result.success, }; } // Query task tool export const queryTaskSchema = z.object({ query: z .string() .min(1, { message: "Query content cannot be empty, please provide task ID or search keywords", }) .describe("Search query text, can be task ID or multiple keywords (space separated)"), isId: z .boolean() .optional() .default(false) .describe("Specify whether it's ID query mode, default is no (keyword mode)"), page: z .number() .int() .positive() .optional() .default(1) .describe("Page number, default is page 1"), pageSize: z .number() .int() .positive() .min(1) .max(20) .optional() .default(5) .describe("Number of tasks to display per page, default is 5, maximum 20"), }); export async function queryTask({ query, isId = false, page = 1, pageSize = 3, }: z.infer<typeof queryTaskSchema>) { try { // Use system command search function const results = await searchTasksWithCommand(query, isId, page, pageSize); // Use prompt generator to get the final prompt const prompt = getQueryTaskPrompt({ query, isId, tasks: results.tasks, totalTasks: results.pagination.totalResults, page: results.pagination.currentPage, pageSize, totalPages: results.pagination.totalPages, }); return { content: [ { type: "text" as const, text: prompt, }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `## System Error\n\nError occurred when querying tasks: ${ error instanceof Error ? error.message : String(error) }`, }, ], isError: true, }; } } // Get complete task detail parameter export const getTaskDetailSchema = z.object({ taskId: z .string() .min(1, { message: "Task ID cannot be empty, please provide a valid task ID", }) .describe("Task ID to view details"), }); // Get complete task detail export async function getTaskDetail({ taskId, }: z.infer<typeof getTaskDetailSchema>) { try { // Use searchTasksWithCommand instead of getTaskById to implement memory area task search // Set isId to true to search by ID; page number is 1, page size is 1 const result = await searchTasksWithCommand(taskId, true, 1, 1); // Check if the task is found if (result.tasks.length === 0) { return { content: [ { type: "text" as const, text: `## Error\n\nTask with ID \`${taskId}\` not found. Please confirm if the task ID is correct.`, }, ], isError: true, }; } // Get the found task (the first and only one) const task = result.tasks[0]; // Use prompt generator to get the final prompt const prompt = getTaskDetailPrompt({ taskId, task, }); return { content: [ { type: "text" as const, text: prompt, }, ], }; } catch (error) { // Use prompt generator to get error message const errorPrompt = getTaskDetailPrompt({ taskId, error: error instanceof Error ? error.message : String(error), }); return { content: [ { type: "text" as const, text: errorPrompt, }, ], isError: true, }; } }

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/liorfranko/mcp-chain-of-thought'

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