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.

160 lines (137 loc) 6.74 kB
import { ResponseFormatter, createFormattedResponse } from "../../../utils/responseFormatter.js"; import { Project, ProjectListResponse } from "./types.js"; /** * Formatter for structured project query responses */ export class ProjectListFormatter implements ResponseFormatter<ProjectListResponse> { /** * Get an emoji indicator for the task status */ private getStatusEmoji(status: string): string { switch (status) { case 'backlog': return '📋'; case 'todo': return '📝'; case 'in_progress': return '🔄'; case 'completed': return '✅'; default: return '❓'; } } /** * Get a visual indicator for the priority level */ private getPriorityIndicator(priority: string): string { switch (priority) { case 'critical': return '[!!!]'; case 'high': return '[!!]'; case 'medium': return '[!]'; case 'low': return '[-]'; default: return '[?]'; } } format(data: ProjectListResponse): string { const { projects, total, page, limit, totalPages } = data; // Generate result summary section const summary = `Project Portfolio\n\n` + `Total Entities: ${total}\n` + `Page: ${page} of ${totalPages}\n` + `Displaying: ${Math.min(limit, projects.length)} project(s) per page\n`; if (projects.length === 0) { return `${summary}\n\nNo project entities matched the specified criteria`; } // Format each project const projectsSections = projects.map((project, index) => { // Access properties directly from the project object const { name, id, status, taskType, createdAt } = project; let projectSection = `${index + 1}. ${name || 'Unnamed Project'}\n\n` + `ID: ${id || 'Unknown ID'}\n` + `Status: ${status || 'Unknown Status'}\n` + `Type: ${taskType || 'Unknown Type'}\n` + `Created: ${createdAt ? new Date(createdAt).toLocaleString() : 'Unknown Date'}\n`; // Add project details in plain text format projectSection += `\nProject Details\n\n`; // Add each property with proper formatting, accessing directly from 'project' if (project.id) projectSection += `ID: ${project.id}\n`; if (project.name) projectSection += `Name: ${project.name}\n`; if (project.description) projectSection += `Description: ${project.description}\n`; if (project.status) projectSection += `Status: ${project.status}\n`; // Format URLs array if (project.urls) { const urlsValue = Array.isArray(project.urls) && project.urls.length > 0 ? JSON.stringify(project.urls) : "None"; projectSection += `URLs: ${urlsValue}\n`; } if (project.completionRequirements) projectSection += `Completion Requirements: ${project.completionRequirements}\n`; if (project.outputFormat) projectSection += `Output Format: ${project.outputFormat}\n`; if (project.taskType) projectSection += `Task Type: ${project.taskType}\n`; // Format dates if (project.createdAt) { const createdDate = typeof project.createdAt === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(project.createdAt) ? new Date(project.createdAt).toLocaleString() : project.createdAt; projectSection += `Created At: ${createdDate}\n`; } if (project.updatedAt) { const updatedDate = typeof project.updatedAt === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(project.updatedAt) ? new Date(project.updatedAt).toLocaleString() : project.updatedAt; projectSection += `Updated At: ${updatedDate}\n`; } // Add tasks if included if (project.tasks && project.tasks.length > 0) { projectSection += `\nTasks (${project.tasks.length})\n\n`; projectSection += project.tasks.map((task, taskIndex) => { // Ensure we're accessing the task properties correctly const taskTitle = task.title || 'Unnamed Task'; const taskId = task.id || 'Unknown ID'; const taskStatus = task.status || 'Unknown Status'; const taskPriority = task.priority || 'Unknown Priority'; const taskCreatedAt = task.createdAt ? new Date(task.createdAt).toLocaleString() : 'Unknown Date'; // Get status emoji for the task const statusEmoji = this.getStatusEmoji(taskStatus); // Get priority indicator for the task const priorityIndicator = this.getPriorityIndicator(taskPriority); return `Task ${taskIndex + 1}. ${statusEmoji} ${priorityIndicator} ${taskTitle}\n\n` + `ID: ${taskId}\n` + `Status: ${taskStatus}\n` + `Priority: ${taskPriority}\n` + `Created: ${taskCreatedAt}\n`; }).join("\n\n"); } // Add knowledge if included if (project.knowledge && project.knowledge.length > 0) { projectSection += `\nKnowledge Items (${project.knowledge.length})\n\n`; projectSection += project.knowledge.map((item, itemIndex) => { // Access item fields directly since they're already extracted in listProjects.ts return `Knowledge ${itemIndex + 1}. ${item.domain || 'Uncategorized'} Knowledge\n\n` + `ID: ${item.id || 'Unknown ID'}\n` + `Domain: ${item.domain || 'Uncategorized'}\n` + `${item.tags && item.tags.length > 0 ? `Tags: ${item.tags.join(", ")}\n` : ""}` + `Created: ${item.createdAt ? new Date(item.createdAt).toLocaleString() : 'Unknown Date'}\n\n` + "Content:\n" + (item.text || 'No content available'); }).join("\n\n"); } return projectSection; }).join("\n\n----------\n\n"); // Append pagination metadata for multi-page results let paginationInfo = ""; if (totalPages > 1) { paginationInfo = `\n\nPagination Controls\n\n` + `Viewing page ${page} of ${totalPages}.\n` + `${page < totalPages ? "Use 'page' parameter to navigate to additional results." : ""}`; } return `${summary}\n\n${projectsSections}${paginationInfo}`; } } /** * Create a human-readable formatted response for the atlas_project_list tool * * @param data The structured project query response data * @param isError Whether this response represents an error condition * @returns Formatted MCP tool response with appropriate structure */ export function formatProjectListResponse(data: any, isError = false): any { return createFormattedResponse(data, new ProjectListFormatter(), isError); }