UNPKG

mcp-use

Version:

A utility library for integrating Model Context Protocol (MCP) with LangChain, Zod, and related tools. Provides helpers for schema conversion, event streaming, and SDK usage.

218 lines (217 loc) 8.03 kB
import fs from 'node:fs'; import path from 'node:path'; import { createLogger, format, transports } from 'winston'; const { combine, timestamp, label, printf, colorize, splat } = format; const DEFAULT_LOGGER_NAME = 'mcp-use'; // Environment detection function (similar to telemetry) function isNodeJSEnvironment() { try { // Check for Cloudflare Workers specifically if (typeof navigator !== 'undefined' && navigator.userAgent?.includes('Cloudflare-Workers')) { return false; } // Check for other edge runtime indicators if (typeof globalThis.EdgeRuntime !== 'undefined' || typeof globalThis.Deno !== 'undefined') { return false; } // Check for Node.js specific globals that are not available in edge environments const hasNodeGlobals = (typeof process !== 'undefined' && typeof process.platform !== 'undefined' && typeof __dirname !== 'undefined'); // Check for Node.js modules const hasNodeModules = (typeof fs !== 'undefined' && typeof createLogger === 'function'); return hasNodeGlobals && hasNodeModules; } catch { return false; } } // Simple console logger for non-Node.js environments class SimpleConsoleLogger { _level; name; constructor(name = DEFAULT_LOGGER_NAME, level = 'info') { this.name = name; this._level = level; } shouldLog(level) { const levels = ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly']; const currentIndex = levels.indexOf(this._level); const messageIndex = levels.indexOf(level); return messageIndex <= currentIndex; } formatMessage(level, message) { const timestamp = new Date().toLocaleTimeString('en-US', { hour12: false }); return `${timestamp} [${this.name}] ${level}: ${message}`; } error(message) { if (this.shouldLog('error')) { console.error(this.formatMessage('error', message)); } } warn(message) { if (this.shouldLog('warn')) { console.warn(this.formatMessage('warn', message)); } } info(message) { if (this.shouldLog('info')) { console.info(this.formatMessage('info', message)); // eslint-disable-line no-console } } debug(message) { if (this.shouldLog('debug')) { console.debug(this.formatMessage('debug', message)); // eslint-disable-line no-console } } http(message) { if (this.shouldLog('http')) { console.log(this.formatMessage('http', message)); // eslint-disable-line no-console } } verbose(message) { if (this.shouldLog('verbose')) { console.log(this.formatMessage('verbose', message)); // eslint-disable-line no-console } } silly(message) { if (this.shouldLog('silly')) { console.log(this.formatMessage('silly', message)); // eslint-disable-line no-console } } // Make it compatible with Winston interface get level() { return this._level; } set level(newLevel) { this._level = newLevel; } } function resolveLevel(env) { // Safely access environment variables const envValue = (typeof process !== 'undefined' && process.env) ? env : undefined; switch (envValue?.trim()) { case '2': return 'debug'; case '1': return 'info'; default: return 'info'; } } const minimalFormatter = printf(({ level, message, label, timestamp }) => { return `${timestamp} [${label}] ${level}: ${message}`; }); const detailedFormatter = printf(({ level, message, label, timestamp }) => { return `${timestamp} [${label}] ${level.toUpperCase()}: ${message}`; }); const emojiFormatter = printf(({ level, message, label, timestamp }) => { return `${timestamp} [${label}] ${level.toUpperCase()}: ${message}`; }); export class Logger { static instances = {}; static simpleInstances = {}; static currentFormat = 'minimal'; static get(name = DEFAULT_LOGGER_NAME) { // Use simple console logger in non-Node.js environments if (!isNodeJSEnvironment()) { if (!this.simpleInstances[name]) { const debugEnv = (typeof process !== 'undefined' && process.env?.DEBUG) || undefined; this.simpleInstances[name] = new SimpleConsoleLogger(name, resolveLevel(debugEnv)); } return this.simpleInstances[name]; } // Use Winston logger in Node.js environments if (!this.instances[name]) { this.instances[name] = createLogger({ level: resolveLevel(process.env.DEBUG), format: combine(colorize(), splat(), label({ label: name }), timestamp({ format: 'HH:mm:ss' }), this.getFormatter()), transports: [], }); } return this.instances[name]; } static getFormatter() { switch (this.currentFormat) { case 'minimal': return minimalFormatter; case 'detailed': return detailedFormatter; case 'emoji': return emojiFormatter; default: return minimalFormatter; } } static configure(options = {}) { const { level, console = true, file, format = 'minimal' } = options; const debugEnv = (typeof process !== 'undefined' && process.env?.DEBUG) || undefined; const resolvedLevel = level ?? resolveLevel(debugEnv); this.currentFormat = format; const root = this.get(); root.level = resolvedLevel; // For non-Node.js environments, just update the level if (!isNodeJSEnvironment()) { Object.values(this.simpleInstances).forEach((logger) => { logger.level = resolvedLevel; }); return; } // Winston-specific configuration for Node.js environments const winstonRoot = root; winstonRoot.clear(); if (console) { winstonRoot.add(new transports.Console()); } if (file) { const dir = path.dirname(path.resolve(file)); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } winstonRoot.add(new transports.File({ filename: file })); } // Update all existing Winston loggers with new format Object.values(this.instances).forEach((logger) => { if (logger && 'format' in logger) { logger.level = resolvedLevel; logger.format = combine(colorize(), splat(), label({ label: DEFAULT_LOGGER_NAME }), timestamp({ format: 'HH:mm:ss' }), this.getFormatter()); } }); } static setDebug(enabled) { let level; if (enabled === 2 || enabled === true) level = 'debug'; else if (enabled === 1) level = 'info'; else level = 'info'; // Update both simple and Winston loggers Object.values(this.simpleInstances).forEach((logger) => { logger.level = level; }); Object.values(this.instances).forEach((logger) => { if (logger) { logger.level = level; } }); // Safely set environment variable if (typeof process !== 'undefined' && process.env) { process.env.DEBUG = enabled ? (enabled === true ? '2' : String(enabled)) : '0'; } } static setFormat(format) { this.currentFormat = format; this.configure({ format }); } } // Only configure Winston features if in Node.js environment if (isNodeJSEnvironment()) { Logger.configure(); } else { // For non-Node.js environments, just initialize with defaults Logger.configure({ console: true }); } export const logger = Logger.get();