Skip to main content
Glama

Interactive Brokers MCP Server

by code-rabi
logger.ts4.89 kB
import { appendFileSync, existsSync, mkdirSync } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; // File-based logging utility that never interferes with MCP protocol export class Logger { private static logDir = process.env.IB_MCP_LOG_DIR || join(homedir(), '.ib-mcp'); private static logFile = join(Logger.logDir, 'ib-mcp.log'); private static enableLogging = process.env.IB_MCP_DISABLE_LOGGING !== 'true'; private static enableConsoleLogging = process.env.IB_MCP_CONSOLE_LOGGING === 'true' || process.argv.includes('--console-logging') || process.argv.includes('--log-console'); private static ensureLogDir() { if (Logger.enableLogging && !existsSync(Logger.logDir)) { try { mkdirSync(Logger.logDir, { recursive: true }); } catch (error) { // If we can't create log dir, disable logging Logger.enableLogging = false; } } } private static writeToFile(level: string, message: string, ...args: any[]) { if (!Logger.enableLogging) return; try { Logger.ensureLogDir(); const timestamp = new Date().toISOString(); const argsStr = args.length > 0 ? ' ' + args.map(arg => Logger.serializeArgument(arg) ).join(' ') : ''; const logLine = `${timestamp} [${level}] ${message}${argsStr}\n`; appendFileSync(Logger.logFile, logLine, 'utf8'); } catch (error) { // Silently fail to avoid recursive logging issues } } private static writeToConsole(level: string, message: string, ...args: any[]) { if (!Logger.enableConsoleLogging) return; const timestamp = new Date().toISOString(); const argsStr = args.length > 0 ? ' ' + args.map(arg => Logger.serializeArgument(arg) ).join(' ') : ''; const logLine = `${timestamp} [${level}] ${message}${argsStr}`; // Use stderr to avoid interfering with MCP JSON-RPC on stdout console.error(logLine); } private static writeLog(level: string, message: string, ...args: any[]) { Logger.writeToFile(level, message, ...args); Logger.writeToConsole(level, message, ...args); } static log(message: string, ...args: any[]) { Logger.writeLog('LOG', message, ...args); } static error(message: string, ...args: any[]) { Logger.writeLog('ERROR', message, ...args); } static info(message: string, ...args: any[]) { Logger.writeLog('INFO', message, ...args); } static debug(message: string, ...args: any[]) { if (process.env.DEBUG) { Logger.writeLog('DEBUG', message, ...args); } } static critical(message: string, ...args: any[]) { Logger.writeLog('CRITICAL', message, ...args); } static warn(message: string, ...args: any[]) { Logger.writeLog('WARN', message, ...args); } // Get the current log file path (useful for debugging) static getLogFile(): string | null { return Logger.enableLogging ? Logger.logFile : null; } // Log a startup message with log file location static logStartup() { if (Logger.enableLogging || Logger.enableConsoleLogging) { const logDestinations = []; if (Logger.enableLogging) logDestinations.push(`file: ${Logger.logFile}`); if (Logger.enableConsoleLogging) logDestinations.push('console'); Logger.info(`IB MCP Server started - logging to: ${logDestinations.join(', ')}`); } } // Check if console logging is enabled static isConsoleLoggingEnabled(): boolean { return Logger.enableConsoleLogging; } // Helper method to properly serialize arguments including error objects private static serializeArgument(arg: any): string { if (arg instanceof Error) { // For Error objects, extract all important properties including non-enumerable ones try { return JSON.stringify({ ...arg, name: arg.name, message: arg.message, stack: arg.stack, }); } catch (circularError) { // If even the error object has circular references, return basic info return `[Error: ${arg.name}: ${arg.message}]`; } } else if (typeof arg === 'object' && arg !== null) { try { return JSON.stringify(arg, Logger.getCircularReplacer()); } catch (circularError) { // Handle other serialization errors return '[Object with serialization error]'; } } else { return String(arg); } } // Helper method to create a replacer function for handling circular references private static getCircularReplacer() { const seen = new WeakSet(); return (key: string, value: any) => { if (typeof value === 'object' && value !== null) { if (seen.has(value)) { return '[Circular Reference]'; } seen.add(value); } return value; }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/code-rabi/interactive-brokers-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server