#!/usr/bin/env node /** * WebFetch.MCP - Log Monitor Utility v0.1.7 * Real-time log monitoring for WebFetch.MCP server * * This utility monitors the detailed log file generated by the WebFetch.MCP server * and displays formatted, real-time log entries for debugging and monitoring. * * Usage: node monitor-logs.mjs * * @author Jay Leon (@manull) * @license MIT * @version 0.1.7 * @repository https://github.com/manull/webfetch-mcp * * Copyright (c) 2025 Jay Leon (@manull) * Licensed under the MIT License - see LICENSE file for details */ import { watchFile, existsSync, readFileSync, statSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const LOG_FILE = join(__dirname, 'mcp-server-detailed.log'); console.log('šŸ” MCP Server Log Monitor'); console.log('========================'); console.log(`Monitoring: ${LOG_FILE}`); console.log('Press Ctrl+C to stop\n'); // Check if log file exists if (!existsSync(LOG_FILE)) { console.log('ā³ Waiting for log file to be created...'); console.log(' (Start your MCP server to begin logging)\n'); } let lastSize = 0; // Function to read new content from log file function readNewContent() { if (!existsSync(LOG_FILE)) return; try { const stats = statSync(LOG_FILE); const currentSize = stats.size; if (currentSize > lastSize) { const content = readFileSync(LOG_FILE, 'utf8'); const newContent = content.slice(lastSize); // Parse and format the new content const lines = newContent.split('\n').filter(line => line.trim()); for (const line of lines) { try { // Try to parse as JSON log entry if (line.includes(']: ')) { const [timestamp, rest] = line.split(']: ', 2); const category = timestamp.split('] ')[1]; console.log(`\nšŸ”ø ${category} ${timestamp.split('] ')[0]}]`); try { const data = JSON.parse(rest); console.log(formatLogData(category, data)); } catch { console.log(rest); } } else { console.log(line); } } catch (error) { console.log(line); } } lastSize = currentSize; } } catch (error) { console.error('Error reading log file:', error.message); } } // Format log data for better readability function formatLogData(category, data) { switch (category) { case 'SERVER_STARTUP': return ` Server: ${data.server_info?.name} v${data.server_info?.version} SearxNG: ${data.server_info?.searxng_base} Debug: ${data.server_info?.debug_enabled} Node: ${data.environment?.node_version}`; case 'INCOMING_REQUEST': if (data.method === 'initialize') { return ` Initialize request from client: ${data.request?.params?.clientInfo?.name}`; } else if (data.method === 'tools/call') { return ` Tool call: ${data.tool_name} Arguments: ${JSON.stringify(data.arguments, null, 4)}`; } else if (data.method === 'tools/list') { return ` Tools list requested`; } return ` Method: ${data.method}`; case 'OUTGOING_RESPONSE': if (data.method === 'tools/call') { const responseText = data.response?.content?.[0]?.text; const preview = responseText ? responseText.substring(0, 200) + (responseText.length > 200 ? '...' : '') : 'No text content'; return ` Tool response (${data.tool_name}): ${preview}`; } else if (data.method === 'tools/list') { const toolNames = data.response?.tools?.map(t => t.name).join(', ') || 'none'; return ` Available tools: ${toolNames}`; } return ` Response for: ${data.method}`; case 'ERROR': return ` āŒ Tool: ${data.tool_name} Error: ${data.error_details?.message} Args: ${JSON.stringify(data.error_details?.arguments)}`; case 'SERVER_ERROR': return ` āŒ ${data.error?.message}`; case 'SERVER_SHUTDOWN': return ` Reason: ${data.reason}`; case 'UNCAUGHT_EXCEPTION': case 'UNHANDLED_REJECTION': return ` āŒ ${data.error?.message || data.reason}`; default: return ` ${JSON.stringify(data, null, 2)}`; } } // Initial read readNewContent(); // Watch for file changes watchFile(LOG_FILE, { interval: 500 }, () => { readNewContent(); }); // Handle graceful shutdown process.on('SIGINT', () => { console.log('\n\nšŸ‘‹ Log monitoring stopped'); process.exit(0); });