@nutrient-sdk/document-engine-mcp-server
Version:
MCP server for Nutrient Document Engine
148 lines (147 loc) • 5.52 kB
JavaScript
import { getEnvironment } from './Environment.js';
import * as process from 'node:process';
export var LogLevel;
(function (LogLevel) {
LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
LogLevel[LogLevel["WARNING"] = 1] = "WARNING";
LogLevel[LogLevel["INFO"] = 2] = "INFO";
LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
})(LogLevel || (LogLevel = {}));
class Logger {
logLevel;
serviceName;
mcpServer = null;
constructor(serviceName = 'document-engine-mcp') {
this.serviceName = serviceName;
// Parse log level from validated environment
try {
const env = getEnvironment();
switch (env.LOG_LEVEL) {
case 'ERROR':
this.logLevel = LogLevel.ERROR;
break;
case 'WARN':
case 'WARNING':
this.logLevel = LogLevel.WARNING;
break;
case 'INFO':
this.logLevel = LogLevel.INFO;
break;
case 'DEBUG':
this.logLevel = LogLevel.DEBUG;
break;
default:
this.logLevel = LogLevel.INFO;
}
}
catch {
// If environment validation fails, use default log level
this.logLevel = LogLevel.INFO;
}
}
/**
* Set the MCP server instance to enable client logging
*/
setMCPServer(server) {
this.mcpServer = server;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context can be any type of metadata
formatLogEntry(level, message, context) {
const entry = {
timestamp: new Date().toISOString(),
level,
message: `[${this.serviceName}] ${message}`,
};
if (context !== undefined) {
entry.context = context;
}
return JSON.stringify(entry);
}
shouldLog(level) {
return level <= this.logLevel;
}
writeLog(level, stderrLevel, message,
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context can be any type of metadata
context) {
const logLine = this.formatLogEntry(stderrLevel, message, context);
const env = getEnvironment();
const isServerConnected = this.mcpServer?.isConnected();
const isStdIOMode = env.MCP_TRANSPORT === 'stdio';
// Send to server notifications when connected
if (isServerConnected) {
const params = {
level,
data: `[${this.serviceName}] ${message}`,
};
try {
this.mcpServer.server.sendLoggingMessage(params);
}
catch {
// Fall through to console fallback if notification fails
}
}
// Console output handling based on mode and connection status
if (isStdIOMode) {
if (!isServerConnected) {
process.stderr.write(logLine + '\n');
}
}
else {
// HTTP mode: write to stderr for errors, stdout for other log levels
if (level === 'error') {
process.stderr.write(logLine + '\n');
}
else {
process.stdout.write(logLine + '\n');
}
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context can be any type of metadata
error(message, context) {
if (!this.shouldLog(LogLevel.ERROR))
return;
this.writeLog('error', 'ERROR', message, context);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context can be any type of metadata
warn(message, context) {
if (this.shouldLog(LogLevel.WARNING)) {
this.writeLog('warning', 'WARNING', message, context);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context can be any type of metadata
info(message, context) {
if (this.shouldLog(LogLevel.INFO)) {
this.writeLog('info', 'INFO', message, context);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context can be any type of metadata
debug(message, context) {
if (this.shouldLog(LogLevel.DEBUG)) {
this.writeLog('debug', 'DEBUG', message, context);
}
}
// Convenience methods for common logging patterns
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context can be any type of metadata
request(method, url, context) {
this.info(`Request: ${method?.toUpperCase()} ${url}`, context);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context can be any type of metadata
response(status, url, context) {
const level = status >= 400 ? LogLevel.ERROR : LogLevel.INFO;
const message = `Response: ${status} ${url}`;
if (level === LogLevel.ERROR) {
this.error(message, context);
}
else {
this.info(message, context);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context can be any type of metadata
retry(attempt, maxRetries, delay, context) {
this.warn(`Retry attempt ${attempt}/${maxRetries} after ${delay}ms`, context);
}
}
// Create a singleton logger instance
export const logger = new Logger();
// Export the Logger class for testing
export { Logger };