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.

937 lines (689 loc) 29.6 kB
# SimpleMcpLogger **The logging solution for MCP (Model Context Protocol) servers** SimpleMcpLogger solves a critical problem in MCP development: **preventing console output from breaking MCP communication**. When building MCP servers, any stray `console.log()` or logging output to STDOUT can corrupt the JSON-RPC protocol, causing client communication failures. This library provides a **drop-in replacement** for console and popular loggers (Winston, Pino) that automatically suppresses output in MCP mode while preserving full logging functionality during development and testing. ## The MCP Problem MCP servers communicate via JSON-RPC over STDOUT/STDIN. Any non-MCP output to **STDOUT** breaks the protocol, but **STDERR is perfectly safe** for debugging: ```typescript // ❌ This breaks MCP communication (writes to STDOUT) console.log("Debug info"); // Corrupts STDOUT → Protocol failure logger.info("Processing request"); // Invalid MCP message → Connection lost // ✅ This works perfectly (suppressed STDOUT, safe STDERR) mcpLogger.info("Processing request"); // Suppressed in MCP mode mcpLogger.mcpError("Debug info", data); // Safe: writes to STDERR ``` **Key insight**: STDOUT is reserved for MCP protocol messages, but STDERR is available for debugging and logging without breaking communication. **SimpleMcpLogger ensures your MCP servers work reliably** by preventing accidental STDOUT output while providing safe STDERR channels for debugging. ## Features - **MCP-compliant** - Automatically suppresses STDOUT output in MCP mode to prevent protocol corruption - **Drop-in replacement** - Compatible with console, Winston, and Pino APIs - **Protocol protection** - Prevents accidental console output from breaking MCP communication - **File logging** - Persistent logging to files with automatic directory creation - **Development-friendly** - Full logging during development, silent in production MCP mode - **Bundling optimized** - Modular design with separate adapter packages - **TypeScript-first** - Complete type safety and IntelliSense support - **Zero dependencies** - Core logger has no external dependencies - **Adapter ecosystem** - Winston and Pino transports for existing codebases - **Battle-tested** - Comprehensive test suite with real-world MCP scenarios ## Table of Contents - [Installation](#installation) - [Quick Start](#quick-start) - [MCP Server Usage](#mcp-server-usage-primary-use-case) - [Enhanced MCP Logger (v1.2.0+)](#enhanced-mcp-logger-v120) - [Hijacking Console for MCP Safety](#hijacking-console-for-mcp-safety) - [Environment Detection](#environment-detection) - [API Reference](#api-reference) - [Logger Class](#logger-class) - [Factory Functions](#factory-functions) - [Adapters for Existing Codebases](#adapters-for-existing-codebases) - [Winston Adapter](#winston-adapter) - [Pino Adapter](#pino-adapter) - [MCP Best Practices](#mcp-best-practices) - [Migration to Enhanced API](#-migration-to-enhanced-api) - [Browser Usage](#browser-usage) - [File Logging](#file-logging) - [Migration Guide](#migration-guide) - [Why This Matters for MCP Development](#why-this-matters-for-mcp-development) ## Installation ```bash npm install @alcyone-labs/simple-mcp-logger ``` ### Bundling-Friendly Design SimpleMcpLogger uses a modular design to keep your bundles small: - **Main package** (`@alcyone-labs/simple-mcp-logger`) - Core logger with zero external dependencies - **Adapters** (`@alcyone-labs/simple-mcp-logger/adapters`) - Winston/Pino adapters with peer dependencies This means you only bundle what you actually use! ```typescript // Core logger (no external dependencies bundled) import { Logger } from "@alcyone-labs/simple-mcp-logger"; // Adapters (requires peer dependencies) import { SimpleMcpWinstonTransport } from "@alcyone-labs/simple-mcp-logger/adapters"; ``` ## Quick Start ### Basic Usage ```typescript import { Logger, logger } from "@alcyone-labs/simple-mcp-logger"; // Use the global logger instance logger.info("Hello, world!"); logger.error("Something went wrong"); // Create a custom logger const myLogger = new Logger({ level: "debug", prefix: "MyApp", mcpMode: false, }); myLogger.debug("Debug message"); myLogger.info("Info message"); // Create a logger with file output const fileLogger = new Logger({ level: "info", prefix: "MyApp", logToFile: "./logs/app.log", // Logs to file, directory created automatically }); fileLogger.info("This goes to both console and file"); // For MCP servers: use mcpError() for debugging (safe STDERR output) myLogger.mcpError("Debug info visible in client logs"); ``` ## MCP Server Usage (Primary Use Case) **This is why SimpleMcpLogger exists**: to prevent console output from corrupting MCP protocol communication. ### The Problem MCP servers communicate via JSON-RPC over STDOUT. Any logging to **STDOUT** breaks this, but **STDERR is safe**: ```typescript // ❌ BROKEN: These write to STDOUT and corrupt MCP communication console.log("Processing request"); // STDOUT → Protocol corruption logger.info("Debug info"); // STDOUT → JSON-RPC breaks // Client receives: {"jsonrpc":"2.0",...}Processing request{"id":1,...} // Result: Invalid JSON, connection fails // ✅ SAFE: STDERR doesn't interfere with MCP protocol console.error("Debug info"); // STDERR → Safe for debugging process.stderr.write("Log data"); // STDERR → Visible to client logs ``` ### The Solution SimpleMcpLogger automatically suppresses **STDOUT** output in MCP mode while preserving **STDERR** for debugging: ```typescript import { createMcpLogger } from "@alcyone-labs/simple-mcp-logger"; // Create MCP-safe logger (automatically detects MCP environment) const logger = createMcpLogger("MyMcpServer"); // ✅ SAFE: These are suppressed in MCP mode (no STDOUT output) logger.info("Processing request"); // Silent in MCP mode logger.debug("User data:", userData); // Silent in MCP mode logger.warn("Rate limit approaching"); // Silent in MCP mode // ✅ SAFE: Critical debugging via STDERR (visible to client logs) logger.mcpError("Database connection failed"); // STDERR → Always visible logger.mcpError("Request state:", requestData); // STDERR → Safe debugging // ✅ SAFE: MCP logger with file output (console suppressed, file enabled) const fileLogger = createMcpLogger("MyMcpServer", "./logs/mcp.log"); fileLogger.info("Processing request"); // Silent in MCP mode, written to file fileLogger.error("Error occurred"); // Silent in MCP mode, written to file ``` ### Enhanced MCP Logger (v1.2.0+) **🚨 Important**: The default `createMcpLogger()` only captures **error-level logs**. For comprehensive logging in MCP servers, use the new options-based API: ```typescript // ❌ DEFAULT: Only captures errors (backward compatible) const basicLogger = createMcpLogger("MyServer", "./logs/mcp.log"); basicLogger.debug("Not captured"); // Silent - below error level basicLogger.info("Not captured"); // Silent - below error level basicLogger.error("Captured"); // ✅ Written to file // ✅ ENHANCED: Capture ALL log levels with options API const comprehensiveLogger = createMcpLogger({ prefix: "MyServer", logToFile: "./logs/mcp.log", level: "debug", // Captures debug, info, warn, error mcpMode: true // MCP compliant (default) }); comprehensiveLogger.debug("✅ Captured"); // Written to file comprehensiveLogger.info("✅ Captured"); // Written to file comprehensiveLogger.warn("✅ Captured"); // Written to file comprehensiveLogger.error("✅ Captured"); // Written to file ``` **Options-Based API** (Recommended for new projects): ```typescript interface McpLoggerOptions { level?: LogLevel; // 'debug' | 'info' | 'warn' | 'error' | 'silent' mcpMode?: boolean; // Default: true (MCP compliant) prefix?: string; // Optional prefix for all messages logToFile?: string; // Optional file path for persistent logging } // Comprehensive MCP server logging const logger = createMcpLogger({ prefix: "MCP-Server", logToFile: "./logs/server.log", level: "info", // Captures info, warn, error (recommended) mcpMode: true // MCP compliant }); // Development/debugging with all levels const debugLogger = createMcpLogger({ prefix: "Debug", logToFile: "./logs/debug.log", level: "debug", // Captures everything mcpMode: true }); // Non-MCP mode for testing const testLogger = createMcpLogger({ prefix: "Test", level: "debug", mcpMode: false // Enable console output for testing }); ``` ### Hijacking Console for MCP Safety Replace console globally to catch all logging in your MCP server: ```typescript import { createMcpLogger } from "@alcyone-labs/simple-mcp-logger"; // Replace console at startup (before any other code runs) const mcpLogger = createMcpLogger("MCP-Server"); globalThis.console = mcpLogger as any; // Now ALL console calls are MCP-safe console.log("This is safe"); // Suppressed in MCP mode console.error("This is safe too"); // Suppressed in MCP mode someLibrary.log("Third-party logs"); // Also safe! ``` ### Environment Detection SimpleMcpLogger automatically detects MCP environments: ```typescript // Automatically enables MCP mode when: // - No TTY detected (typical MCP server environment) // - MCP_MODE environment variable is set // - Explicitly configured const logger = createMcpLogger(); // Auto-detects MCP mode ``` ### Console Replacement ```typescript import { Logger } from "@alcyone-labs/simple-mcp-logger"; // Replace console globally (do this at application startup) const logger = new Logger({ level: "info", prefix: "App" }); globalThis.console = logger as any; // Now all console calls use SimpleMcpLogger console.log("This uses SimpleMcpLogger"); console.error("This too"); ``` **⚠️ Important:** Replace console at application startup before any other logging occurs to avoid infinite loops. ### General Purpose Logging (Non-MCP) ```typescript import { Logger, createCliLogger } from "@alcyone-labs/simple-mcp-logger"; // Perfect for web apps, APIs, CLI tools, etc. const appLogger = createCliLogger("info", "MyApp"); appLogger.info("Server starting on port 3000"); appLogger.warn("High memory usage detected"); appLogger.error("Database connection failed"); // Use all console methods appLogger.table([{ user: "john", status: "active" }]); appLogger.time("API Response"); // ... some operation appLogger.timeEnd("API Response"); ``` ## API Reference ### Logger Class #### Constructor ```typescript new Logger(config?: Partial<LoggerConfig>) ``` #### Configuration Options ```typescript interface LoggerConfig { level: LogLevel; // 'debug' | 'info' | 'warn' | 'error' | 'silent' mcpMode: boolean; // Suppress output when true prefix?: string; // Prefix for all messages logToFile?: string; // Optional file path for persistent logging } ``` #### Methods All standard console methods are supported: - `debug(message: string, ...args: any[]): void` - `envDebug(message: string, ...args: any[]): void` - Environment-aware debug logging (only outputs when `DEBUG` env var is truthy) - `info(message: string, ...args: any[]): void` - `warn(message: string, ...args: any[]): void` - `error(message: string, ...args: any[]): void` - `log(message: string, ...args: any[]): void` - Alias for info - `trace(message?: string, ...args: any[]): void` - `table(data: any, columns?: string[]): void` - `group(label?: string): void` - `groupCollapsed(label?: string): void` - `groupEnd(): void` - `time(label?: string): void` - `timeEnd(label?: string): void` - `timeLog(label?: string, ...args: any[]): void` - `count(label?: string): void` - `countReset(label?: string): void` - `assert(condition: boolean, message?: string, ...args: any[]): void` - `clear(): void` - `dir(obj: any, options?: any): void` - `dirxml(obj: any): void` #### Special Methods - `mcpError(message: string, ...args: any[]): void` - Always logs even in MCP mode - `child(prefix: string): Logger` - Create child logger with combined prefix - `setMcpMode(enabled: boolean): void` - Toggle MCP mode - `setLevel(level: LogLevel): void` - Change log level - `setPrefix(prefix: string): void` - Change prefix - `setLogFile(filePath: string): Promise<void>` - Set or change log file path - `close(): Promise<void>` - Close file stream and flush pending writes ### Factory Functions #### createMcpLogger **New Options-Based API (v1.2.0+)** - Recommended: ```typescript interface McpLoggerOptions { level?: LogLevel; // Default: 'error' (for backward compatibility) mcpMode?: boolean; // Default: true prefix?: string; // Optional prefix logToFile?: string; // Optional file path } createMcpLogger(options: McpLoggerOptions): Logger ``` **Legacy API** (Deprecated, will be removed in v2.0.0): ```typescript createMcpLogger(prefix?: string, logToFile?: string): Logger createMcpLogger(prefix?: string, logToFile?: string, options?: Partial<McpLoggerOptions>): Logger ``` **Examples:** ```typescript // ✅ NEW: Options-based API (recommended) const logger = createMcpLogger({ prefix: "MyServer", logToFile: "./logs/mcp.log", level: "debug" // Capture all levels }); // ⚠️ LEGACY: Still works but only captures errors by default const legacyLogger = createMcpLogger("MyServer", "./logs/mcp.log"); // ⚠️ LEGACY: With options override const enhancedLegacy = createMcpLogger("MyServer", "./logs/mcp.log", { level: "info" // Override to capture more levels }); ``` #### createCliLogger ```typescript // Create logger for CLI mode createCliLogger(level?: LogLevel, prefix?: string): Logger ``` ## Adapters for Existing Codebases **Migrate existing MCP servers to be protocol-safe** without changing your logging code. If you have an existing codebase using Winston or Pino, you can add SimpleMcpLogger as a transport to make it MCP-compliant without refactoring your logging calls. **Bundling-Friendly Design**: Adapters are available as a separate import to avoid bundling dependencies you don't need. ### Installation For adapters, you'll need to install the peer dependencies: ```bash # For Winston adapter npm install winston winston-transport # For Pino adapter npm install pino # Or install both npm install winston winston-transport pino ``` ### Winston Adapter Make your existing Winston-based MCP server protocol-safe: ```typescript // Import adapters separately to avoid bundling unused dependencies import { createWinstonTransport } from "@alcyone-labs/simple-mcp-logger/adapters"; import winston from "winston"; // Replace your existing Winston transports with MCP-safe transport const logger = winston.createLogger({ transports: [ createWinstonTransport({ level: "debug", mcpMode: true, // Automatically suppresses STDOUT in MCP mode prefix: "MCP-Server", logToFile: "./logs/mcp-server.log", // Optional: log to file }), ], }); // Your existing logging code works unchanged logger.info("Processing MCP request"); // Safe in MCP mode, written to file logger.error("Request failed"); // Safe in MCP mode, written to file ``` ### Pino Adapter Make your existing Pino-based MCP server protocol-safe: ```typescript // Import adapters separately to avoid bundling unused dependencies import { createPinoDestination } from "@alcyone-labs/simple-mcp-logger/adapters"; import pino from "pino"; // Replace your existing Pino destination with MCP-safe destination const destination = createPinoDestination({ level: "debug", mcpMode: true, // Automatically suppresses STDOUT in MCP mode prefix: "MCP-Server", logToFile: "./logs/mcp-server.log", // Optional: log to file }); const logger = pino({ level: "debug" }, destination); // Your existing logging code works unchanged logger.info("Processing MCP request"); // Safe in MCP mode, written to file logger.error("Request failed"); // Safe in MCP mode, written to file ``` ## MCP Best Practices ### 🚨 Critical: Initialize Before Any Logging Replace console **immediately** at application startup to catch all logging: ```typescript // ✅ CORRECT: Do this FIRST, before importing any other modules import { createMcpLogger } from "@alcyone-labs/simple-mcp-logger"; globalThis.console = createMcpLogger("MCP-Server") as any; // Now import your application code import "./my-mcp-server.js"; ``` ```typescript // ❌ WRONG: Too late, some logging may have already occurred import "./my-mcp-server.js"; import { createMcpLogger } from "@alcyone-labs/simple-mcp-logger"; globalThis.console = createMcpLogger("MCP-Server") as any; ``` ### 🔍 Debugging MCP Servers Use `mcpError()` for debugging that needs to be visible - it writes to **STDERR** which is safe for MCP: ```typescript const logger = createMcpLogger("MCP-Server"); // Silent in MCP mode (suppressed STDOUT - good for normal operation) logger.info("Processing request"); // No output in MCP mode logger.debug("User data:", userData); // No output in MCP mode // Always visible via STDERR (safe for MCP protocol - good for debugging) logger.mcpError("Critical error:", error); // STDERR → Visible in client logs logger.mcpError("Server state:", serverState); // STDERR → Safe debugging logger.mcpError("Performance metric:", timing); // STDERR → Monitoring data ``` **Why STDERR is safe**: MCP protocol only uses STDOUT for JSON-RPC messages. STDERR output appears in client logs without interfering with protocol communication. ### 🔧 Environment-Aware Debug Logging The `envDebug()` method provides controlled debug logging that only outputs when the `DEBUG` environment variable is set. This allows you to safely add debug information throughout your codebase without worrying about output pollution in production. ```typescript import { Logger, createMcpLogger } from "@alcyone-labs/simple-mcp-logger"; const logger = createMcpLogger("MyApp"); // These will only output when DEBUG environment variable is truthy logger.envDebug("Processing user request", { userId: 123 }); logger.envDebug("Database query", { sql: "SELECT * FROM users" }); logger.envDebug("API response time", { duration: "245ms" }); // Regular debug logging (always respects log level) logger.debug("This always logs when level allows"); ``` **Environment Variable Behavior:** - `DEBUG=true` or `DEBUG=1` or `DEBUG=anything` → Logging enabled - `DEBUG=false` or `DEBUG=0` or `DEBUG=""` or unset → Logging disabled **Usage Examples:** ```bash # Enable debug logging DEBUG=1 node my-mcp-server.js # Disable debug logging (production) node my-mcp-server.js # Enable with custom value DEBUG=verbose node my-mcp-server.js ``` **Benefits:** - **Safe for production**: No output pollution when DEBUG is not set - **Works with all transports**: Console, file logging, MCP mode, etc. - **Respects all logger settings**: Log levels, prefixes, MCP mode - **Clear identification**: Debug messages are prefixed with `[ENV-DEBUG]` **File Logging Example:** ```typescript // Logs to file only when DEBUG is set const logger = createMcpLogger("MCP-Server", "./logs/debug.log"); logger.envDebug("Server state", serverState); // Only written when DEBUG=1 ``` ### 📡 Understanding STDOUT vs STDERR in MCP **STDOUT (Protocol Channel)**: - Reserved exclusively for MCP JSON-RPC messages - Any non-MCP output breaks protocol communication - Must be kept clean for reliable client connections **STDERR (Debugging Channel)**: - Safe for logging, debugging, and monitoring output - Visible in client logs without protocol interference - Perfect for error reporting and diagnostic information ```typescript // ❌ STDOUT - Reserved for MCP protocol process.stdout.write('{"jsonrpc":"2.0",...}'); // MCP messages only // ✅ STDERR - Safe for debugging process.stderr.write("Debug: Processing request\n"); console.error("Server metrics:", metrics); logger.mcpError("Performance data:", data); ``` ### 🧪 Testing MCP Servers Disable MCP mode during testing to see all logs: ```typescript // ✅ NEW: Options-based API const logger = createMcpLogger({ prefix: "Test-Server", level: "debug", mcpMode: false // Enable console output for testing }); // ⚠️ LEGACY: Still works const legacyLogger = createMcpLogger("Test-Server", undefined, { mcpMode: false }); // OR use environment variable process.env.MCP_MODE = "false"; const autoLogger = createMcpLogger({ prefix: "Test-Server" }); // Auto-detects ``` ### 🔄 Migration to Enhanced API **For New Projects** - Use the options-based API: ```typescript // ✅ RECOMMENDED: Comprehensive logging const logger = createMcpLogger({ prefix: "MyMcpServer", logToFile: "./logs/server.log", level: "info", // Captures info, warn, error mcpMode: true }); ``` **For Existing Projects** - Gradual migration: ```typescript // Step 1: Keep existing code working (no changes needed) const logger = createMcpLogger("MyServer", "./logs/mcp.log"); // Step 2: Add comprehensive logging where needed const debugLogger = createMcpLogger({ prefix: "MyServer-Debug", logToFile: "./logs/debug.log", level: "debug" // Capture everything for debugging }); // Step 3: Eventually migrate to options-based API const logger = createMcpLogger({ prefix: "MyServer", logToFile: "./logs/mcp.log", level: "info" // Better than error-only default }); ``` **Why Migrate?** - **🔍 See Everything**: Capture debug, info, warn messages (not just errors) - **🎛️ Better Control**: Fine-tune log levels per logger instance - **🚀 Future-Proof**: Prepared for v2.0 when legacy API is removed - **📖 Clearer Intent**: Options object makes configuration explicit ## Browser Usage SimpleMcpLogger works seamlessly in browser environments! The core logger and most adapters are browser-compatible. ### Basic Browser Usage ```html <!DOCTYPE html> <html> <head> <script type="module"> import { Logger, logger, createMcpLogger, } from "https://unpkg.com/@alcyone-labs/simple-mcp-logger/dist/index.mjs"; // Use the global logger logger.info("Hello from browser!"); // Create a custom logger const browserLogger = new Logger({ level: "debug", prefix: "Browser", mcpMode: false, }); browserLogger.debug("Debug message in browser"); browserLogger.table([{ name: "John", age: 30 }]); // Replace console globally globalThis.console = browserLogger; console.log("Now using SimpleMcpLogger!"); </script> </head> <body> <h1>SimpleMcpLogger Browser Demo</h1> <p>Check the browser console for log messages!</p> </body> </html> ``` ### Browser with Bundlers (Webpack, Vite, etc.) ```typescript import { Logger, createMcpLogger } from "@alcyone-labs/simple-mcp-logger"; // Create logger for browser app const appLogger = new Logger({ level: "info", prefix: "MyApp", mcpMode: false, }); // Use all console methods appLogger.log("Application started"); appLogger.group("User Actions"); appLogger.info("User clicked button"); appLogger.warn("Form validation warning"); appLogger.groupEnd(); // Time operations appLogger.time("API Call"); // ... some async operation appLogger.timeEnd("API Call"); ``` ### Browser Adapter Support | Adapter | Browser Support | Notes | | ----------------------- | --------------- | --------------------------------------- | | **Core Logger** | ✅ Full support | All console methods work | | **Winston Adapter** | ✅ Full support | Works if Winston is browser-compatible | | **Pino Transport** | ✅ Full support | Use `createPinoDestination()` | | **Pino Logger Factory** | ❌ Node.js only | Use destination with browser Pino build | ### Browser + Pino Example ```typescript import { createPinoDestination } from "@alcyone-labs/simple-mcp-logger"; // Import browser-compatible Pino build import pino from "pino/browser"; const destination = createPinoDestination({ level: "info", prefix: "Browser", }); const logger = pino({ level: "info" }, destination); logger.info("Hello from Pino in browser!"); ``` ## File Logging SimpleMcpLogger supports persistent logging to files with automatic directory creation and proper file stream management. ### Basic File Logging ```typescript import { Logger, createMcpLogger } from "@alcyone-labs/simple-mcp-logger"; // Create logger with file output const logger = new Logger({ level: "info", logToFile: "./logs/app.log", // Directory created automatically }); logger.info("This goes to both console and file"); logger.error("Errors are logged to file too"); // Always close the logger when done to flush pending writes await logger.close(); ``` ### MCP Mode with File Logging Perfect for MCP servers - suppress console output but maintain file logs: ```typescript // MCP logger with file output (console suppressed, file enabled) const mcpLogger = createMcpLogger("MCP-Server", "./logs/mcp.log"); mcpLogger.info("Processing request"); // Silent in MCP mode, written to file mcpLogger.error("Error occurred"); // Silent in MCP mode, written to file mcpLogger.mcpError("Debug info"); // Always visible via STDERR + written to file // Gracefully close when shutting down await mcpLogger.close(); ``` ### Dynamic File Path Changes ```typescript const logger = new Logger({ level: "info" }); // Start logging to one file await logger.setLogFile("./logs/startup.log"); logger.info("Application starting"); // Switch to a different file await logger.setLogFile("./logs/runtime.log"); logger.info("Now logging to runtime file"); // Disable file logging await logger.setLogFile(""); // Empty string disables file logging ``` ### File Logging with Adapters Both Winston and Pino adapters support file logging: ```typescript // Winston with file logging import { createWinstonTransport } from "@alcyone-labs/simple-mcp-logger/adapters"; const transport = createWinstonTransport({ logToFile: "./logs/winston.log", mcpMode: true, }); // Pino with file logging import { createPinoDestination } from "@alcyone-labs/simple-mcp-logger/adapters"; const destination = createPinoDestination({ logToFile: "./logs/pino.log", mcpMode: true, }); ``` ### File Logging Best Practices 1. **Always close loggers** when your application shuts down: ```typescript process.on("SIGINT", async () => { await logger.close(); process.exit(0); }); ``` 2. **Use absolute paths** for production deployments: ```typescript import { resolve } from "node:path"; const logFile = resolve(process.cwd(), "logs", "app.log"); const logger = new Logger({ logToFile: logFile }); ``` 3. **Handle file errors gracefully** - SimpleMcpLogger automatically handles permission errors and continues logging to console. ### Bundle Size The browser build is optimized and lightweight: - **ESM build**: ~11KB (2.4KB gzipped) - **Tree-shakeable**: Import only what you need - **Zero dependencies**: No external runtime dependencies ## Migration Guide ### From console ```typescript // Before console.log("Hello"); console.error("Error"); // After import { logger } from "@alcyone-labs/simple-mcp-logger"; logger.log("Hello"); logger.error("Error"); // Or replace globally globalThis.console = logger as any; ``` ### From Winston ```typescript // Before import winston from "winston"; const logger = winston.createLogger({ transports: [new winston.transports.Console()], }); // After import { createWinstonTransport } from "@alcyone-labs/simple-mcp-logger"; const logger = winston.createLogger({ transports: [createWinstonTransport()], }); ``` ### From Pino ```typescript // Before import pino from "pino"; const logger = pino(); // After import { createPinoLogger } from "@alcyone-labs/simple-mcp-logger"; const logger = createPinoLogger(); ``` ## Why This Matters for MCP Development ### The Hidden Problem Many MCP servers fail in production due to **STDOUT contamination**. Even a single `console.log()` can break the entire MCP communication channel: ```typescript // This innocent debug line breaks everything (writes to STDOUT): console.log("Debug: processing request"); // MCP client expects: {"jsonrpc":"2.0","id":1,"result":{...}} // But receives: Debug: processing request{"jsonrpc":"2.0","id":1,"result":{...}} // Result: JSON parse error, connection terminated // The fix is simple - use STDERR instead: console.error("Debug: processing request"); // Safe: goes to STDERR ``` ### The Solution Impact SimpleMcpLogger has prevented countless MCP server failures by: - **Catching stray console calls** before they reach STDOUT - **Preserving development logging** while ensuring production safety - **Enabling gradual migration** of existing codebases to MCP compliance - **Providing safe STDERR channels** for debugging without protocol interference - **Maintaining visibility** into server operations via client-visible STDERR logs ### Real-World Success Teams using SimpleMcpLogger report: - **Zero MCP protocol corruption** issues in production - **Faster debugging** with safe error logging channels - **Seamless migration** of existing Node.js services to MCP servers - **Confident deployment** knowing logging won't break client connections **SimpleMcpLogger isn't just a logger—it's MCP reliability insurance.** ## License MIT License - see [LICENSE](LICENSE) file for details. ## Contributing Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.