UNPKG

@mseep/atlas-mcp-server

Version:

A Model Context Protocol (MCP) server for ATLAS, a Neo4j-powered task management system for LLM Agents - implementing a three-tier architecture (Projects, Tasks, Knowledge) to manage complex workflows.

173 lines (151 loc) 6.53 kB
import { TaskResponse } from "../../../types/mcp.js"; import { ResponseFormatter, createFormattedResponse } from "../../../utils/responseFormatter.js"; /** * Extends the TaskResponse to include Neo4j properties structure */ interface SingleTaskResponse extends TaskResponse { properties?: any; identity?: number; labels?: string[]; elementId?: string; } /** * Interface for bulk task update response */ interface BulkTaskResponse { success: boolean; message: string; updated: (TaskResponse & { properties?: any; identity?: number; labels?: string[]; elementId?: string; })[]; errors: { index: number; task: { id: string; updates: any; }; error: { code: string; message: string; details?: any; }; }[]; } /** * Formatter for individual task update responses */ export class SingleTaskUpdateFormatter implements ResponseFormatter<SingleTaskResponse> { format(data: SingleTaskResponse): string { // Extract task properties from Neo4j structure const taskData = data.properties || data; const { title, id, projectId, status, priority, taskType, updatedAt } = taskData; // Create a structured summary section const summary = `Task Updated Successfully\n\n` + `Task: ${title || 'Unnamed Task'}\n` + `ID: ${id || 'Unknown ID'}\n` + `Project ID: ${projectId || 'Unknown Project'}\n` + `Status: ${status || 'Unknown Status'}\n` + `Priority: ${priority || 'Unknown Priority'}\n` + `Type: ${taskType || 'Unknown Type'}\n` + `Updated: ${updatedAt ? new Date(updatedAt).toLocaleString() : 'Unknown Date'}\n`; // Create a comprehensive details section with all task attributes let details = `Task Details\n\n`; // Add each property with proper formatting if (taskData.id) details += `ID: ${taskData.id}\n`; if (taskData.projectId) details += `Project ID: ${taskData.projectId}\n`; if (taskData.title) details += `Title: ${taskData.title}\n`; if (taskData.description) details += `Description: ${taskData.description}\n`; if (taskData.priority) details += `Priority: ${taskData.priority}\n`; if (taskData.status) details += `Status: ${taskData.status}\n`; if (taskData.assignedTo) details += `Assigned To: ${taskData.assignedTo}\n`; // Format URLs array if (taskData.urls) { const urlsValue = Array.isArray(taskData.urls) && taskData.urls.length > 0 ? JSON.stringify(taskData.urls) : "None"; details += `URLs: ${urlsValue}\n`; } // Format tags array if (taskData.tags) { const tagsValue = Array.isArray(taskData.tags) && taskData.tags.length > 0 ? taskData.tags.join(", ") : "None"; details += `Tags: ${tagsValue}\n`; } if (taskData.completionRequirements) details += `Completion Requirements: ${taskData.completionRequirements}\n`; if (taskData.outputFormat) details += `Output Format: ${taskData.outputFormat}\n`; if (taskData.taskType) details += `Task Type: ${taskData.taskType}\n`; // Format dates if (taskData.createdAt) { const createdDate = typeof taskData.createdAt === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(taskData.createdAt) ? new Date(taskData.createdAt).toLocaleString() : taskData.createdAt; details += `Created At: ${createdDate}\n`; } if (taskData.updatedAt) { const updatedDate = typeof taskData.updatedAt === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(taskData.updatedAt) ? new Date(taskData.updatedAt).toLocaleString() : taskData.updatedAt; details += `Updated At: ${updatedDate}\n`; } return `${summary}\n\n${details}`; } } /** * Formatter for bulk task update responses */ export class BulkTaskUpdateFormatter implements ResponseFormatter<BulkTaskResponse> { format(data: BulkTaskResponse): string { const { success, message, updated, errors } = data; // Create a summary section const summary = `${success ? "Tasks Updated Successfully" : "Task Updates Completed with Errors"}\n\n` + `Status: ${success ? "✅ Success" : "⚠️ Partial Success"}\n` + `Summary: ${message}\n` + `Updated: ${updated.length} task(s)\n` + `Errors: ${errors.length} error(s)\n`; // List all successfully modified tasks let updatedSection = ""; if (updated.length > 0) { updatedSection = `Updated Tasks\n\n`; updatedSection += updated.map((task, index) => { // Extract task properties from Neo4j structure const taskData = task.properties || task; return `${index + 1}. ${taskData.title || 'Unnamed Task'}\n\n` + `ID: ${taskData.id || 'Unknown ID'}\n` + `Project ID: ${taskData.projectId || 'Unknown Project'}\n` + `Type: ${taskData.taskType || 'Unknown Type'}\n` + `Status: ${taskData.status || 'Unknown Status'}\n` + `Priority: ${taskData.priority || 'Unknown Priority'}\n` + `Updated: ${taskData.updatedAt ? new Date(taskData.updatedAt).toLocaleString() : 'Unknown Date'}\n`; }).join("\n\n"); } // List any errors that occurred let errorsSection = ""; if (errors.length > 0) { errorsSection = `Errors\n\n`; errorsSection += errors.map((error, index) => { return `${index + 1}. Error updating Task ID: "${error.task.id}"\n\n` + `Error Code: ${error.error.code}\n` + `Message: ${error.error.message}\n` + (error.error.details ? `Details: ${JSON.stringify(error.error.details)}\n` : ""); }).join("\n\n"); } return `${summary}\n\n${updatedSection}\n\n${errorsSection}`.trim(); } } /** * Create a formatted, human-readable response for the atlas_task_update tool * * @param data The raw task update response * @param isError Whether this response represents an error condition * @returns Formatted MCP tool response with appropriate structure */ export function formatTaskUpdateResponse(data: any, isError = false): any { // Determine if this is a single or bulk task response const isBulkResponse = data.hasOwnProperty("success") && data.hasOwnProperty("updated"); if (isBulkResponse) { return createFormattedResponse(data, new BulkTaskUpdateFormatter(), isError); } else { return createFormattedResponse(data, new SingleTaskUpdateFormatter(), isError); } }