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.

172 lines (171 loc) 7.86 kB
#!/usr/bin/env node // Imports MUST be at the top level import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import http from "http"; import { config, environment } from "./config/index.js"; // This loads .env via dotenv.config() import { initializeAndStartServer } from "./mcp/server.js"; import { closeNeo4jConnection } from "./services/neo4j/index.js"; import { logger, requestContextService } from "./utils/index.js"; /** * The main MCP server instance, stored if transport is stdio for shutdown. * Or the HTTP server instance if transport is http. * @type {McpServer | http.Server | undefined} */ let serverInstance; /** * Gracefully shuts down the main MCP server and related services. * Handles process termination signals (SIGTERM, SIGINT) and critical errors. * * @param signal - The signal or event name that triggered the shutdown (e.g., "SIGTERM", "uncaughtException"). */ const shutdown = async (signal) => { // Create a proper RequestContext for shutdown operations const shutdownContext = requestContextService.createRequestContext({ operation: "Shutdown", signal, appName: config.mcpServerName, }); logger.info(`Received ${signal}. Starting graceful shutdown for ${config.mcpServerName}...`, shutdownContext); try { if (serverInstance) { if (serverInstance instanceof McpServer) { logger.info("Closing main MCP server (stdio) instance...", shutdownContext); await serverInstance.close(); logger.info("Main MCP server (stdio) instance closed successfully.", shutdownContext); } else if (serverInstance instanceof http.Server) { logger.info("Closing HTTP server instance...", shutdownContext); const currentHttpServer = serverInstance; await new Promise((resolve, reject) => { currentHttpServer.close((err) => { if (err) { logger.error("Error closing HTTP server", err, shutdownContext); return reject(err); } logger.info("HTTP server instance closed successfully.", shutdownContext); resolve(); }); }); } } else { logger.info("No global MCP server instance to close (expected for HTTP transport or if not yet initialized).", shutdownContext); } logger.info("Closing Neo4j driver connection...", shutdownContext); await closeNeo4jConnection(); logger.info("Neo4j driver connection closed successfully.", shutdownContext); logger.info(`Graceful shutdown for ${config.mcpServerName} completed successfully. Exiting.`, shutdownContext); process.exit(0); } catch (error) { // Pass the error object directly as the second argument to logger.error logger.error("Critical error during shutdown", error, { ...shutdownContext, // error message and stack are now part of the Error object passed to logger }); process.exit(1); } }; /** * Initializes and starts the main MCP server. * Sets up logging, request context, initializes the server instance, starts the transport, * and registers signal handlers for graceful shutdown and error handling. */ const start = async () => { // --- Logger Initialization --- const validMcpLogLevels = [ "debug", "info", "notice", "warning", "error", "crit", "alert", "emerg", ]; const initialLogLevelConfig = config.logLevel; let validatedMcpLogLevel = "info"; // Default if (validMcpLogLevels.includes(initialLogLevelConfig)) { validatedMcpLogLevel = initialLogLevelConfig; } else { // Use console.warn here as logger isn't fully initialized yet, only if TTY if (process.stdout.isTTY) { console.warn(`Invalid MCP_LOG_LEVEL "${initialLogLevelConfig}" provided via config/env. Defaulting to "info".`); } } // Initialize the logger with the validated MCP level and wait for it to complete. await logger.initialize(validatedMcpLogLevel); // The logger.initialize() method itself logs its status, so no redundant log here. // --- End Logger Initialization --- const configLoadContext = requestContextService.createRequestContext({ operation: "ConfigLoad", }); logger.debug("Configuration loaded successfully", { ...configLoadContext, configValues: config, }); // Spread context and add specific data const transportType = config.mcpTransportType; const startupContext = requestContextService.createRequestContext({ operation: `AtlasServerStartup_${transportType}`, appName: config.mcpServerName, appVersion: config.mcpServerVersion, environment: environment, }); logger.info(`Starting ${config.mcpServerName} v${config.mcpServerVersion} (Transport: ${transportType})...`, startupContext); try { logger.debug("Initializing and starting MCP server transport...", startupContext); const potentialServer = await initializeAndStartServer(); if (transportType === "stdio" && potentialServer instanceof McpServer) { serverInstance = potentialServer; logger.debug("Stored McpServer instance for stdio transport.", startupContext); } else if (transportType === "http" && potentialServer instanceof http.Server) { serverInstance = potentialServer; logger.debug("Stored HTTP server instance. MCP sessions are per-request.", startupContext); } else if (transportType === "http" && !potentialServer) { logger.debug("HTTP transport started. Server instance not returned by initializeAndStartServer. MCP sessions are per-request.", startupContext); } logger.info(`${config.mcpServerName} is running with ${transportType} transport.`, { ...startupContext, startTime: new Date().toISOString(), }); // --- Signal and Error Handling Setup --- process.on("SIGTERM", () => shutdown("SIGTERM")); process.on("SIGINT", () => shutdown("SIGINT")); process.on("uncaughtException", async (error) => { const errorContext = { ...startupContext, event: "uncaughtException", error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, }; logger.error("Uncaught exception detected. Initiating shutdown...", errorContext); await shutdown("uncaughtException"); }); process.on("unhandledRejection", async (reason) => { const rejectionContext = { ...startupContext, event: "unhandledRejection", reason: reason instanceof Error ? reason.message : String(reason), stack: reason instanceof Error ? reason.stack : undefined, }; logger.error("Unhandled promise rejection detected. Initiating shutdown...", rejectionContext); await shutdown("unhandledRejection"); }); } catch (error) { logger.error("Critical error during ATLAS MCP Server startup, exiting.", { ...startupContext, finalErrorContext: "Startup Failure", error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, }); process.exit(1); } }; // --- Async IIFE to allow top-level await --- (async () => { await start(); })();