/**
* MCP Protocol Logging
*
* Bridges application logging to MCP protocol log notifications.
* This allows MCP clients to receive log messages from the server.
*
* MCP Spec Reference:
* https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging
*/
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
/**
* MCP log levels as defined in the specification
* Maps to syslog severity levels (RFC 5424)
*/
export type McpLogLevel =
| "debug" // Detailed debug information
| "info" // Informational messages
| "notice" // Normal but significant events
| "warning" // Warning conditions
| "error" // Error conditions
| "critical" // Critical conditions
| "alert" // Action must be taken immediately
| "emergency"; // System is unusable
/**
* MCP Logger class that sends log messages to connected clients
*/
class McpLogger {
private server: McpServer | null = null;
private loggerName = "mysql-mcp";
private enabled = true;
private minLevel: McpLogLevel = "info";
private isConnected = false;
/**
* Level priority for filtering
*/
private readonly levelPriority: Record<McpLogLevel, number> = {
debug: 0,
info: 1,
notice: 2,
warning: 3,
error: 4,
critical: 5,
alert: 6,
emergency: 7,
};
/**
* Initialize the MCP logger with the SDK server instance
*/
setServer(server: McpServer): void {
this.server = server;
}
/**
* Mark the logger as connected (call after transport is established)
*/
setConnected(connected: boolean): void {
this.isConnected = connected;
}
/**
* Set the logger name (appears in log messages)
*/
setLoggerName(name: string): void {
this.loggerName = name;
}
/**
* Enable or disable MCP logging
*/
setEnabled(enabled: boolean): void {
this.enabled = enabled;
}
/**
* Set minimum log level
*/
setMinLevel(level: McpLogLevel): void {
this.minLevel = level;
}
/**
* Check if a level should be logged
*/
private shouldLog(level: McpLogLevel): boolean {
return this.levelPriority[level] >= this.levelPriority[this.minLevel];
}
/**
* Send a log message to connected MCP clients
*/
log(
level: McpLogLevel,
message: string,
data?: Record<string, unknown>,
): void {
// Only send logs if connected - prevents "Not connected" errors
if (
!this.enabled ||
!this.server ||
!this.isConnected ||
!this.shouldLog(level)
) {
return;
}
try {
// Use the SDK's sendLoggingMessage method
void this.server.sendLoggingMessage({
level,
logger: this.loggerName,
data: data ? { message, ...data } : message,
});
} catch {
// Silently fail if logging fails - don't crash the server
// The MCP transport might not be connected yet
}
}
// Convenience methods for each log level
debug(message: string, data?: Record<string, unknown>): void {
this.log("debug", message, data);
}
info(message: string, data?: Record<string, unknown>): void {
this.log("info", message, data);
}
notice(message: string, data?: Record<string, unknown>): void {
this.log("notice", message, data);
}
warning(message: string, data?: Record<string, unknown>): void {
this.log("warning", message, data);
}
error(message: string, data?: Record<string, unknown>): void {
this.log("error", message, data);
}
critical(message: string, data?: Record<string, unknown>): void {
this.log("critical", message, data);
}
alert(message: string, data?: Record<string, unknown>): void {
this.log("alert", message, data);
}
emergency(message: string, data?: Record<string, unknown>): void {
this.log("emergency", message, data);
}
}
/**
* Singleton MCP logger instance
*
* Usage:
* import { mcpLogger } from './logging/McpLogging.js';
*
* // In server startup:
* mcpLogger.setServer(sdkServer);
*
* // In application code:
* mcpLogger.info('Query executed', { rowCount: 100 });
* mcpLogger.error('Connection failed', { host: 'localhost' });
*/
export const mcpLogger = new McpLogger();