UNPKG

@alcyone-labs/simple-mcp-logger

Version:

Logging solution for MCP servers. Prevents console output from corrupting MCP protocol communication. Drop-in replacement for console, Winston, and Pino with automatic STDOUT suppression in MCP mode.

1 lines 22.9 kB
{"version":3,"file":"SimpleMcpLogger-D6nWOVH5.cjs","sources":["../src/SimpleMcpLogger.ts"],"sourcesContent":["/**\n * Lightweight centralized logger for ArgParser\n * Provides MCP-compliant logging that can be disabled in MCP mode\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n\n/**\n * Configuration options for SimpleMcpLogger\n */\nexport interface LoggerConfig {\n /** Minimum log level to output */\n level: LogLevel;\n /** When true, suppresses console output to prevent MCP protocol corruption */\n mcpMode: boolean;\n /** Optional prefix to prepend to all log messages */\n prefix?: string;\n /** Optional file path for persistent logging. Creates directory if it doesn't exist. */\n logToFile?: string;\n}\n\n/**\n * Configuration options for MCP Logger\n * @since 1.2.0\n */\nexport interface McpLoggerOptions {\n /** Minimum log level to output. Defaults to 'error' for backward compatibility */\n level?: LogLevel;\n /** When true, suppresses console output to prevent MCP protocol corruption. Defaults to true */\n mcpMode?: boolean;\n /** Optional prefix to prepend to all log messages */\n prefix?: string;\n /** Optional file path for persistent logging. Creates directory if it doesn't exist */\n logToFile?: string;\n}\n\nexport class Logger {\n private config: LoggerConfig;\n private fileStream?: fs.WriteStream;\n\n constructor(config: Partial<LoggerConfig> = {}) {\n this.config = {\n level: \"info\",\n mcpMode: false,\n prefix: \"\",\n ...config,\n };\n\n // Initialize file logging if specified\n if (this.config.logToFile) {\n this.initFileLogging(this.config.logToFile);\n }\n }\n\n /**\n * Initialize file logging\n */\n private initFileLogging(filePath: string): void {\n try {\n // Ensure directory exists\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n\n this.fileStream = fs.createWriteStream(filePath, { flags: \"a\" });\n\n this.fileStream.on(\"error\", (error) => {\n console.error(\n `SimpleMcpLogger: File stream error for '${filePath}': ${error.message}`,\n );\n console.error(\n `SimpleMcpLogger: File logging will be disabled. Check file permissions and disk space.`,\n );\n });\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n console.error(\n `SimpleMcpLogger: Failed to initialize file logging for '${filePath}': ${errorMessage}`,\n );\n console.error(\n `SimpleMcpLogger: Possible causes: invalid path, insufficient permissions, or disk full.`,\n );\n this.fileStream = undefined;\n }\n }\n\n /**\n * Write to file if file logging is enabled\n */\n private writeToFile(level: string, message: string, ...args: any[]): void {\n if (this.fileStream && !this.fileStream.destroyed) {\n const timestamp = new Date().toISOString();\n const formattedArgs =\n args.length > 0\n ? \" \" +\n args\n .map((arg) =>\n typeof arg === \"object\" ? JSON.stringify(arg) : String(arg),\n )\n .join(\" \")\n : \"\";\n\n const logLine = `[${timestamp}] ${level.toUpperCase()}: ${message}${formattedArgs}\\n`;\n this.fileStream.write(logLine);\n }\n }\n\n /**\n * Set MCP mode - when true, all console output is suppressed\n */\n public setMcpMode(enabled: boolean): void {\n this.config.mcpMode = enabled;\n }\n\n /**\n * Set log level\n */\n public setLevel(level: LogLevel): void {\n this.config.level = level;\n }\n\n /**\n * Set prefix for all log messages\n */\n public setPrefix(prefix: string): void {\n this.config.prefix = prefix;\n }\n\n /**\n * Set or change the log file path\n *\n * @param filePath Path to the log file. Directory will be created if it doesn't exist.\n *\n * @example\n * ```typescript\n * await logger.setLogFile('./logs/app.log');\n * logger.info('This goes to the new file');\n * ```\n */\n public async setLogFile(filePath: string): Promise<void> {\n // Close existing stream\n await this.close();\n\n this.config.logToFile = filePath;\n this.initFileLogging(filePath);\n }\n\n /**\n * Close file stream\n */\n public close(): Promise<void> {\n return new Promise((resolve) => {\n if (this.fileStream && !this.fileStream.destroyed) {\n // Ensure all data is written before closing\n this.fileStream.end(() => {\n this.fileStream = undefined;\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Check if logging is enabled for a given level\n */\n private shouldLog(level: LogLevel): boolean {\n if (this.config.mcpMode && !this.config.logToFile) {\n return false; // No console output in MCP mode unless file logging\n }\n\n if (this.config.level === \"silent\") {\n return false;\n }\n\n const levels: LogLevel[] = [\"debug\", \"info\", \"warn\", \"error\"];\n const currentLevelIndex = levels.indexOf(this.config.level);\n const messageLevelIndex = levels.indexOf(level);\n\n return messageLevelIndex >= currentLevelIndex;\n }\n\n /**\n * Check if console logging should be used (not in MCP mode or file logging disabled)\n */\n private shouldLogToConsole(level: LogLevel): boolean {\n return (\n this.shouldLog(level) && (!this.config.mcpMode || !this.config.logToFile)\n );\n }\n\n /**\n * Format message with prefix\n */\n private formatMessage(message: string): string {\n return this.config.prefix ? `${this.config.prefix} ${message}` : message;\n }\n\n /**\n * Debug logging\n */\n public debug(message: string, ...args: any[]): void {\n if (this.shouldLog(\"debug\")) {\n if (this.config.logToFile) {\n this.writeToFile(\"debug\", this.formatMessage(message), ...args);\n }\n if (this.shouldLogToConsole(\"debug\")) {\n console.debug(this.formatMessage(message), ...args);\n }\n }\n }\n\n /**\n * Environment-aware debug logging - only outputs if DEBUG environment variable is truthy\n * This method respects the DEBUG environment variable and will only log when the DEBUG env var is truthy.\n * It works with all configured transports (console, file, etc.) and respects MCP mode settings.\n * Treats \"false\", \"0\", and empty string as falsy values.\n */\n public envDebug(message: string, ...args: any[]): void {\n const debugEnv = process.env.DEBUG;\n if (!debugEnv || debugEnv === \"false\" || debugEnv === \"0\") {\n return;\n }\n\n if (this.shouldLog(\"debug\")) {\n if (this.config.logToFile) {\n this.writeToFile(\n \"debug\",\n this.formatMessage(`[ENV-DEBUG] ${message}`),\n ...args,\n );\n }\n if (this.shouldLogToConsole(\"debug\")) {\n console.debug(this.formatMessage(`[ENV-DEBUG] ${message}`), ...args);\n }\n }\n }\n\n /**\n * Info logging - uses stderr for MCP compliance\n */\n public info(message: string, ...args: any[]): void {\n if (this.shouldLog(\"info\")) {\n if (this.config.logToFile) {\n this.writeToFile(\"info\", this.formatMessage(message), ...args);\n }\n if (this.shouldLogToConsole(\"info\")) {\n console.error(this.formatMessage(message), ...args);\n }\n }\n }\n\n /**\n * Warning logging\n */\n public warn(message: string, ...args: any[]): void {\n if (this.shouldLog(\"warn\")) {\n if (this.config.logToFile) {\n this.writeToFile(\"warn\", this.formatMessage(message), ...args);\n }\n if (this.shouldLogToConsole(\"warn\")) {\n console.warn(this.formatMessage(message), ...args);\n }\n }\n }\n\n /**\n * Error logging - uses stderr which is allowed in MCP mode for debugging\n */\n public error(message: string, ...args: any[]): void {\n if (this.shouldLog(\"error\")) {\n if (this.config.logToFile) {\n this.writeToFile(\"error\", this.formatMessage(message), ...args);\n }\n if (this.shouldLogToConsole(\"error\")) {\n console.error(this.formatMessage(message), ...args);\n }\n }\n }\n\n /**\n * Log method - alias for info to match console.log behavior\n */\n public log(message: string, ...args: any[]): void {\n this.info(message, ...args);\n }\n\n /**\n * Trace logging - uses console.trace for stack traces\n */\n public trace(message?: string, ...args: any[]): void {\n if (this.shouldLog(\"debug\")) {\n if (message) {\n console.trace(this.formatMessage(message), ...args);\n } else {\n console.trace();\n }\n }\n }\n\n /**\n * Table logging - uses console.table for structured data\n */\n public table(data: any, columns?: string[]): void {\n if (this.shouldLog(\"info\")) {\n console.table(data, columns);\n }\n }\n\n /**\n * Group logging - creates a collapsible group\n */\n public group(label?: string): void {\n if (this.shouldLog(\"info\")) {\n if (label) {\n console.group(this.formatMessage(label));\n } else {\n console.group();\n }\n }\n }\n\n /**\n * Collapsed group logging\n */\n public groupCollapsed(label?: string): void {\n if (this.shouldLog(\"info\")) {\n if (label) {\n console.groupCollapsed(this.formatMessage(label));\n } else {\n console.groupCollapsed();\n }\n }\n }\n\n /**\n * End group logging\n */\n public groupEnd(): void {\n if (this.shouldLog(\"info\")) {\n console.groupEnd();\n }\n }\n\n /**\n * Time logging - starts a timer\n */\n public time(label?: string): void {\n if (this.shouldLog(\"debug\")) {\n const timerLabel = label ? this.formatMessage(label) : undefined;\n console.time(timerLabel);\n }\n }\n\n /**\n * Time end logging - ends a timer and logs the duration\n */\n public timeEnd(label?: string): void {\n if (this.shouldLog(\"debug\")) {\n const timerLabel = label ? this.formatMessage(label) : undefined;\n console.timeEnd(timerLabel);\n }\n }\n\n /**\n * Time log - logs current timer value without ending it\n */\n public timeLog(label?: string, ...args: any[]): void {\n if (this.shouldLog(\"debug\")) {\n const timerLabel = label ? this.formatMessage(label) : undefined;\n console.timeLog(timerLabel, ...args);\n }\n }\n\n /**\n * Count logging - maintains a counter for the label\n */\n public count(label?: string): void {\n if (this.shouldLog(\"debug\")) {\n const countLabel = label ? this.formatMessage(label) : undefined;\n console.count(countLabel);\n }\n }\n\n /**\n * Count reset - resets the counter for the label\n */\n public countReset(label?: string): void {\n if (this.shouldLog(\"debug\")) {\n const countLabel = label ? this.formatMessage(label) : undefined;\n console.countReset(countLabel);\n }\n }\n\n /**\n * Assert logging - logs an error if assertion fails\n */\n public assert(condition: boolean, message?: string, ...args: any[]): void {\n if (this.shouldLog(\"error\")) {\n if (message) {\n console.assert(condition, this.formatMessage(message), ...args);\n } else {\n console.assert(condition, ...args);\n }\n }\n }\n\n /**\n * Clear console - clears the console if supported\n */\n public clear(): void {\n if (this.shouldLog(\"debug\") && console.clear) {\n console.clear();\n }\n }\n\n /**\n * Dir logging - displays an interactive list of object properties\n */\n public dir(obj: any, options?: any): void {\n if (this.shouldLog(\"info\")) {\n console.dir(obj, options);\n }\n }\n\n /**\n * DirXML logging - displays XML/HTML element representation\n */\n public dirxml(obj: any): void {\n if (this.shouldLog(\"info\")) {\n console.dirxml(obj);\n }\n }\n\n /**\n * MCP-safe error logging - always uses STDERR even in MCP mode\n *\n * STDERR is safe for MCP servers because the MCP protocol only uses STDOUT\n * for JSON-RPC messages. STDERR output appears in client logs without\n * interfering with protocol communication.\n *\n * Use this for critical errors, debugging info, and monitoring data that\n * needs to be visible even when the logger is in MCP mode.\n */\n public mcpError(message: string, ...args: any[]): void {\n console.error(this.formatMessage(message), ...args);\n }\n\n /**\n * Create a child logger with additional prefix\n */\n public child(prefix: string): Logger {\n return new Logger({\n ...this.config,\n prefix: this.config.prefix ? `${this.config.prefix}:${prefix}` : prefix,\n });\n }\n}\n\n/**\n * Global logger instance\n */\nexport const logger = new Logger();\n\n/**\n * Create a logger for MCP mode with options-based configuration\n *\n * @param options Configuration options for the MCP logger\n * @returns Logger instance configured for MCP compliance\n *\n * @example\n * ```typescript\n * // Basic MCP logger with default error level\n * const logger = createMcpLogger({ prefix: 'MyServer' });\n *\n * // MCP logger with comprehensive logging\n * const logger = createMcpLogger({\n * prefix: 'MyServer',\n * logToFile: './logs/mcp.log',\n * level: 'debug' // Capture all log levels\n * });\n * ```\n * @since 1.2.0\n */\nexport function createMcpLogger(options: McpLoggerOptions): Logger;\n\n/**\n * Create a logger for MCP mode (legacy signature)\n *\n * @deprecated Use createMcpLogger(options) instead. This signature will be removed in v2.0.0\n * @param prefix Optional prefix for all log messages\n * @param logToFile Optional file path for persistent logging. When provided, logs are written to file even in MCP mode while console output is suppressed.\n * @param options Additional options to override defaults\n * @returns Logger instance configured for MCP compliance\n *\n * @example\n * ```typescript\n * // Basic MCP logger (console suppressed)\n * const logger = createMcpLogger('MyServer');\n *\n * // MCP logger with file output (console suppressed, file enabled)\n * const fileLogger = createMcpLogger('MyServer', './logs/mcp.log');\n *\n * // MCP logger with custom options\n * const customLogger = createMcpLogger('MyServer', './logs/mcp.log', { level: 'debug' });\n * ```\n */\nexport function createMcpLogger(prefix?: string, logToFile?: string, options?: Partial<McpLoggerOptions>): Logger;\n\n/**\n * Implementation of createMcpLogger with function overloads\n */\nexport function createMcpLogger(\n prefixOrOptions?: string | McpLoggerOptions,\n logToFile?: string,\n options?: Partial<McpLoggerOptions>\n): Logger {\n // Handle new options-based signature\n if (typeof prefixOrOptions === 'object' && prefixOrOptions !== null) {\n const opts = prefixOrOptions as McpLoggerOptions;\n return new Logger({\n level: opts.level ?? 'error',\n mcpMode: opts.mcpMode ?? true,\n prefix: opts.prefix,\n logToFile: opts.logToFile,\n });\n }\n\n // Handle legacy signature\n const prefix = prefixOrOptions as string | undefined;\n const mergedOptions: McpLoggerOptions = {\n level: 'error',\n mcpMode: true,\n prefix,\n logToFile,\n ...options\n };\n\n return new Logger({\n level: mergedOptions.level!,\n mcpMode: mergedOptions.mcpMode!,\n prefix: mergedOptions.prefix,\n logToFile: mergedOptions.logToFile,\n });\n}\n\n/**\n * Create a logger for CLI mode\n */\nexport function createCliLogger(\n level: LogLevel = \"info\",\n prefix?: string,\n): Logger {\n return new Logger({\n level,\n mcpMode: false,\n prefix,\n });\n}\n\n/**\n * Default export - the Logger class\n */\nexport default Logger;\n"],"names":["path","fs"],"mappings":"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"}