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:
// ❌ 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
- Quick Start
- MCP Server Usage
- API Reference
- Adapters for Existing Codebases
- MCP Best Practices
- Browser Usage
- File Logging
- Migration Guide
- Why This Matters for MCP Development
Installation
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!
// 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
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:
// ❌ 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:
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:
// ❌ 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):
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:
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:
// 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
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)
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
new Logger(config?: Partial<LoggerConfig>)
Configuration Options
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[]): voidenvDebug(message: string, ...args: any[]): void- Environment-aware debug logging (only outputs whenDEBUGenv var is truthy)info(message: string, ...args: any[]): voidwarn(message: string, ...args: any[]): voiderror(message: string, ...args: any[]): voidlog(message: string, ...args: any[]): void- Alias for infotrace(message?: string, ...args: any[]): voidtable(data: any, columns?: string[]): voidgroup(label?: string): voidgroupCollapsed(label?: string): voidgroupEnd(): voidtime(label?: string): voidtimeEnd(label?: string): voidtimeLog(label?: string, ...args: any[]): voidcount(label?: string): voidcountReset(label?: string): voidassert(condition: boolean, message?: string, ...args: any[]): voidclear(): voiddir(obj: any, options?: any): voiddirxml(obj: any): void
Special Methods
mcpError(message: string, ...args: any[]): void- Always logs even in MCP modechild(prefix: string): Logger- Create child logger with combined prefixsetMcpMode(enabled: boolean): void- Toggle MCP modesetLevel(level: LogLevel): void- Change log levelsetPrefix(prefix: string): void- Change prefixsetLogFile(filePath: string): Promise<void>- Set or change log file pathclose(): Promise<void>- Close file stream and flush pending writes
Factory Functions
createMcpLogger
New Options-Based API (v1.2.0+) - Recommended:
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):
createMcpLogger(prefix?: string, logToFile?: string): Logger
createMcpLogger(prefix?: string, logToFile?: string, options?: Partial<McpLoggerOptions>): Logger
Examples:
// ✅ 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
// 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:
# 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:
// 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:
// 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:
// ✅ 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";
// ❌ 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:
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.
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=trueorDEBUG=1orDEBUG=anything→ Logging enabledDEBUG=falseorDEBUG=0orDEBUG=""or unset → Logging disabled
Usage Examples:
# 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:
// 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
// ❌ 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:
// ✅ 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:
// ✅ 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:
// 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
<!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.)
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
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
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:
// 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
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:
// 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
-
Always close loggers when your application shuts down:
process.on("SIGINT", async () => { await logger.close(); process.exit(0); }); -
Use absolute paths for production deployments:
import { resolve } from "node:path"; const logFile = resolve(process.cwd(), "logs", "app.log"); const logger = new Logger({ logToFile: logFile }); -
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
// 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
// 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
// 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:
// 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 file for details.
Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.