UNPKG

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.

246 lines (245 loc) 10.5 kB
import { KnowledgeService } from "../../../services/neo4j/knowledgeService.js"; import { ProjectService } from "../../../services/neo4j/projectService.js"; import { toKnowledgeResource, ResourceTemplates, ResourceURIs, } from "../types.js"; import { logger, requestContextService } from "../../../utils/index.js"; // Import requestContextService import { BaseErrorCode, McpError, ProjectErrorCode, } from "../../../types/errors.js"; /** * Register Knowledge Resources * * This function registers resource endpoints for the Knowledge entity * - GET atlas://knowledge - List all knowledge items * - GET atlas://knowledge/{knowledgeId} - Get specific knowledge item by ID * - GET atlas://projects/{projectId}/knowledge - List knowledge items for a specific project * * @param server The MCP server instance */ export function registerKnowledgeResources(server) { // List all knowledge server.resource("knowledge-list", ResourceURIs.KNOWLEDGE, { name: "All Knowledge", description: "List of all knowledge items in the Atlas platform with pagination and filtering support", mimeType: "application/json", }, async (uri) => { const reqContext = requestContextService.createRequestContext({ operation: "listAllKnowledge", resourceUri: uri.href, }); try { logger.info("Listing all knowledge items", { ...reqContext, uri: uri.href, }); // Parse query parameters const queryParams = new URLSearchParams(uri.search); // Default project ID required by knowledge service const projectId = queryParams.get("projectId") || "*"; const filters = { projectId, }; // Parse domain parameter const domain = queryParams.get("domain"); if (domain) { filters.domain = String(domain); } // Parse tags parameter const tags = queryParams.get("tags"); if (tags) { // Split comma-separated tags filters.tags = String(tags) .split(",") .map((tag) => tag.trim()); } // Parse search parameter const search = queryParams.get("search"); if (search) { filters.search = String(search); } // Parse pagination parameters const page = queryParams.has("page") ? parseInt(queryParams.get("page") || "1", 10) : 1; const limit = queryParams.has("limit") ? parseInt(queryParams.get("limit") || "20", 10) : 20; // Add pagination to filters filters.page = page; filters.limit = limit; // Query the database const result = await KnowledgeService.getKnowledge(filters); // Map Neo4j knowledge items to resource objects const knowledgeResources = result.data.map((item) => toKnowledgeResource(item)); return { contents: [ { uri: uri.href, mimeType: "application/json", text: JSON.stringify({ knowledge: knowledgeResources, pagination: { total: result.total, page: result.page, limit: result.limit, totalPages: result.totalPages, }, }, null, 2), }, ], }; } catch (error) { logger.error("Error listing knowledge items", error, { ...reqContext, // error is now part of the Error object passed to logger uri: uri.href, }); throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to list knowledge items: ${error instanceof Error ? error.message : String(error)}`); } }); // Get knowledge by ID server.resource("knowledge-by-id", ResourceTemplates.KNOWLEDGE, { name: "Knowledge by ID", description: "Retrieves a single knowledge item by its unique identifier", mimeType: "application/json", }, async (uri, params) => { const reqContext = requestContextService.createRequestContext({ operation: "getKnowledgeById", resourceUri: uri.href, knowledgeIdParam: params.knowledgeId, }); try { const knowledgeId = params.knowledgeId; logger.info("Fetching knowledge by ID", { ...reqContext, knowledgeId, // Already in reqContext but can be explicit for clarity uri: uri.href, // Already in reqContext but can be explicit }); if (!knowledgeId) { throw new McpError(BaseErrorCode.VALIDATION_ERROR, "Knowledge ID is required"); } // Query the database const knowledge = await KnowledgeService.getKnowledgeById(knowledgeId); if (!knowledge) { throw new McpError(BaseErrorCode.NOT_FOUND, `Knowledge item with ID ${knowledgeId} not found`, { knowledgeId }); } // Convert to resource object const knowledgeResource = toKnowledgeResource(knowledge); return { contents: [ { uri: uri.href, mimeType: "application/json", text: JSON.stringify(knowledgeResource, null, 2), }, ], }; } catch (error) { // Handle specific error cases if (error instanceof McpError) { throw error; } logger.error("Error fetching knowledge by ID", error, { ...reqContext, // error is now part of the Error object passed to logger parameters: params, }); throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to fetch knowledge: ${error instanceof Error ? error.message : String(error)}`); } }); // List knowledge by project server.resource("knowledge-by-project", ResourceTemplates.KNOWLEDGE_BY_PROJECT, { name: "Knowledge by Project", description: "Retrieves all knowledge items belonging to a specific project", mimeType: "application/json", }, async (uri, params) => { const reqContext = requestContextService.createRequestContext({ operation: "listKnowledgeByProject", resourceUri: uri.href, projectIdParam: params.projectId, }); try { const projectId = params.projectId; logger.info("Listing knowledge for project", { ...reqContext, projectId, // Already in reqContext but can be explicit uri: uri.href, // Already in reqContext }); if (!projectId) { throw new McpError(BaseErrorCode.VALIDATION_ERROR, "Project ID is required"); } // Verify the project exists const project = await ProjectService.getProjectById(projectId); if (!project) { throw new McpError(ProjectErrorCode.PROJECT_NOT_FOUND, `Project with ID ${projectId} not found`, { projectId }); } // Parse query parameters const queryParams = new URLSearchParams(uri.search); const filters = { projectId, }; // Parse domain parameter const domain = queryParams.get("domain"); if (domain) { filters.domain = String(domain); } // Parse tags parameter const tags = queryParams.get("tags"); if (tags) { // Split comma-separated tags filters.tags = String(tags) .split(",") .map((tag) => tag.trim()); } // Parse search parameter const search = queryParams.get("search"); if (search) { filters.search = String(search); } // Parse pagination parameters const page = queryParams.has("page") ? parseInt(queryParams.get("page") || "1", 10) : 1; const limit = queryParams.has("limit") ? parseInt(queryParams.get("limit") || "20", 10) : 20; // Add pagination to filters filters.page = page; filters.limit = limit; // Query the database const result = await KnowledgeService.getKnowledge(filters); // Map Neo4j knowledge items to resource objects const knowledgeResources = result.data.map((item) => toKnowledgeResource(item)); return { contents: [ { uri: uri.href, mimeType: "application/json", text: JSON.stringify({ projectId, projectName: project.name, knowledge: knowledgeResources, pagination: { total: result.total, page: result.page, limit: result.limit, totalPages: result.totalPages, }, }, null, 2), }, ], }; } catch (error) { // Handle specific error cases if (error instanceof McpError) { throw error; } logger.error("Error listing knowledge for project", error, { ...reqContext, // error is now part of the Error object passed to logger parameters: params, }); throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to list knowledge for project: ${error instanceof Error ? error.message : String(error)}`); } }); }