atlas-mcp-server
Version:
ATLAS (Adaptive Task & Logic Automation System): An MCP server enabling LLM agents to manage projects, tasks, and knowledge via a Neo4j-backed, three-tier architecture. Facilitates complex workflow automation and project management through LLM Agents.
184 lines (183 loc) • 8.52 kB
JavaScript
import { KnowledgeService, ProjectService, TaskService, } from "../../../services/neo4j/index.js";
import { BaseErrorCode, McpError } from "../../../types/errors.js";
import { logger, requestContextService } from "../../../utils/index.js"; // Import requestContextService
/**
* Retrieve and filter project entities based on specified criteria
* Provides two query modes: detailed entity retrieval or paginated collection listing
*
* @param request The project query parameters including filters and pagination controls
* @returns Promise resolving to structured project entities with optional related resources
*/
export async function listProjects(request) {
const reqContext = requestContextService.createRequestContext({
toolName: "listProjects",
requestMode: request.mode,
});
try {
const { mode = "all", id, page = 1, limit = 20, includeKnowledge = false, includeTasks = false, taskType, status, } = request;
// Parameter validation
if (mode === "details" && !id) {
throw new McpError(BaseErrorCode.VALIDATION_ERROR, 'Project identifier is required when using mode="details"');
}
// Sanitize pagination parameters
const validatedPage = Math.max(1, page);
const validatedLimit = Math.min(Math.max(1, limit), 100);
let projects = [];
let total = 0;
let totalPages = 0;
if (mode === "details") {
// Retrieve specific project entity by identifier
const projectResult = await ProjectService.getProjectById(id);
if (!projectResult) {
throw new McpError(BaseErrorCode.NOT_FOUND, `Project with identifier ${id} not found`);
}
// Cast to the tool's Project type
projects = [projectResult];
total = 1;
totalPages = 1;
}
else {
// Get paginated list of projects with filters
const projectsResult = await ProjectService.getProjects({
status,
taskType,
page: validatedPage,
limit: validatedLimit,
});
// Cast each project to the tool's Project type
projects = projectsResult.data.map((p) => p);
total = projectsResult.total;
totalPages = projectsResult.totalPages;
}
// Process knowledge resource associations if requested
if (includeKnowledge && projects.length > 0) {
for (const project of projects) {
if (mode === "details") {
// For detailed view, retrieve comprehensive knowledge resources
const knowledgeResult = await KnowledgeService.getKnowledge({
projectId: project.id, // Access directly
page: 1,
limit: 100, // Reasonable threshold for associated resources
});
// Add debug logging
logger.info("Knowledge items retrieved", {
...reqContext,
projectId: project.id, // Access directly
count: knowledgeResult.data.length,
firstItem: knowledgeResult.data[0]
? JSON.stringify(knowledgeResult.data[0])
: "none",
});
// Map directly, assuming KnowledgeService returns Neo4jKnowledge objects
project.knowledge = knowledgeResult.data.map((item) => {
// More explicit mapping with debug info
logger.debug("Processing knowledge item", {
...reqContext,
id: item.id,
domain: item.domain,
textLength: item.text ? item.text.length : 0,
});
// Cast to the tool's Knowledge type
return item;
});
}
else {
// For list mode, get abbreviated knowledge items
const knowledgeResult = await KnowledgeService.getKnowledge({
projectId: project.id, // Access directly
page: 1,
limit: 5, // Just a few for summary view
});
// Map directly, assuming KnowledgeService returns Neo4jKnowledge objects
project.knowledge = knowledgeResult.data.map((item) => {
// Cast to the tool's Knowledge type, potentially truncating text
const knowledgeItem = item;
return {
...knowledgeItem,
// Show a preview of the text - increased to 200 characters
text: item.text && item.text.length > 200
? item.text.substring(0, 200) + "... (truncated)"
: item.text,
};
});
}
}
}
// Process task entity associations if requested
if (includeTasks && projects.length > 0) {
for (const project of projects) {
if (mode === "details") {
// For detailed view, retrieve prioritized task entities
const tasksResult = await TaskService.getTasks({
projectId: project.id, // Access directly
page: 1,
limit: 100, // Reasonable threshold for associated entities
sortBy: "priority",
sortDirection: "desc",
});
// Add debug logging
logger.info("Tasks retrieved for project", {
...reqContext,
projectId: project.id, // Access directly
count: tasksResult.data.length,
firstItem: tasksResult.data[0]
? JSON.stringify(tasksResult.data[0])
: "none",
});
// Map directly, assuming TaskService returns Neo4jTask objects
project.tasks = tasksResult.data.map((item) => {
// Debug info
logger.debug("Processing task item", {
...reqContext,
id: item.id,
title: item.title,
status: item.status,
priority: item.priority,
});
// Cast to the tool's Task type
return item;
});
}
else {
// For list mode, get abbreviated task items
const tasksResult = await TaskService.getTasks({
projectId: project.id, // Access directly
page: 1,
limit: 5, // Just a few for summary view
sortBy: "priority",
sortDirection: "desc",
});
// Map directly, assuming TaskService returns Neo4jTask objects
project.tasks = tasksResult.data.map((item) => {
// Cast to the tool's Task type
return item;
});
}
}
}
// Construct the response
const response = {
projects,
total,
page: validatedPage,
limit: validatedLimit,
totalPages,
};
logger.info("Project query executed successfully", {
...reqContext,
mode,
count: projects.length,
total,
includeKnowledge,
includeTasks,
});
return response;
}
catch (error) {
logger.error("Project query execution failed", error, reqContext);
if (error instanceof McpError) {
throw error;
}
throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to retrieve project entities: ${error instanceof Error ? error.message : String(error)}`);
}
}