@dynamicu/chromedebug-mcp 2.3.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  registerProcess,
15
15
  findActiveSessions
16
16
  } from './services/unified-session-manager.js';
17
+ import logger from './utils/logger.js';
17
18
 
18
19
  /**
19
20
  * Main application class that orchestrates all components
@@ -46,13 +47,23 @@ class ChromePilotApp {
46
47
  this.processManager = this.chromeService.getProcessManager();
47
48
 
48
49
  // Initialize Chrome service (starts HTTP server if needed)
50
+ logger.info('🚀 Starting ChromeDebug MCP services...');
51
+
49
52
  const serviceResult = await this.chromeService.initialize({
50
53
  ...finalOptions,
51
54
  sessionManager: this.sessionManager
52
55
  });
53
-
56
+
54
57
  if (serviceResult.httpServer) {
55
- console.error(`Chrome service initialized with HTTP server on port ${serviceResult.httpServer.port}`);
58
+ logger.info(`✅ HTTP Server Started on port ${serviceResult.httpServer.port}`);
59
+ logger.debug(` Status: ${serviceResult.httpServer.status}`);
60
+ } else if (finalOptions.singleServer) {
61
+ logger.info('â„šī¸ HTTP server disabled (--single-server mode)');
62
+ } else {
63
+ logger.error('âš ī¸ HTTP server failed to start - Chrome extension will not be able to connect');
64
+ if (serviceResult.httpServer?.error) {
65
+ logger.error(` Error: ${serviceResult.httpServer.error}`);
66
+ }
56
67
  }
57
68
 
58
69
  // Create and configure MCP server
@@ -65,7 +76,7 @@ class ChromePilotApp {
65
76
  serviceResult
66
77
  };
67
78
  } catch (error) {
68
- console.error('Failed to initialize Chrome Debug:', error);
79
+ logger.error('Failed to initialize Chrome Debug:', error);
69
80
  throw error;
70
81
  }
71
82
  }
@@ -82,13 +93,13 @@ class ChromePilotApp {
82
93
  // Clean up any existing Chrome Debug processes
83
94
  const cleanupResult = await this.processManager.cleanupChromePilotProcesses();
84
95
  if (cleanupResult.total > 0) {
85
- console.error(`Cleaned up ${cleanupResult.killed.length} existing processes`);
96
+ logger.debug(`Cleaned up ${cleanupResult.killed.length} existing processes`);
86
97
  }
87
98
 
88
99
  // Start the MCP server
89
100
  await this.mcpServer.start();
90
101
  } catch (error) {
91
- console.error('Failed to start Chrome Debug MCP server:', error);
102
+ logger.error('Failed to start Chrome Debug MCP server:', error);
92
103
  process.exit(1);
93
104
  }
94
105
  }
@@ -147,7 +158,7 @@ class ChromePilotApp {
147
158
  if (i + 1 < argv.length) {
148
159
  args.sessionId = argv[++i];
149
160
  } else {
150
- console.error('--session-id requires a value');
161
+ logger.error('--session-id requires a value');
151
162
  process.exit(1);
152
163
  }
153
164
  break;
@@ -165,7 +176,7 @@ class ChromePilotApp {
165
176
  if (arg.startsWith('--session-id=')) {
166
177
  args.sessionId = arg.split('=')[1];
167
178
  } else if (arg.startsWith('--')) {
168
- console.error(`Unknown argument: ${arg}`);
179
+ logger.error(`Unknown argument: ${arg}`);
169
180
  this.showHelp();
170
181
  process.exit(1);
171
182
  }
@@ -215,20 +226,20 @@ For more information, see the documentation in CLAUDE.md
215
226
  * Graceful shutdown
216
227
  */
217
228
  async shutdown() {
218
- console.error('Shutting down Chrome Debug...');
219
-
229
+ logger.info('Shutting down Chrome Debug...');
230
+
220
231
  try {
221
232
  if (this.chromeService) {
222
233
  await this.chromeService.cleanup();
223
234
  }
224
-
235
+
225
236
  if (this.processManager) {
226
237
  await this.processManager.killAllManagedProcesses();
227
238
  }
228
-
229
- console.error('Chrome Debug shutdown complete');
239
+
240
+ logger.info('Chrome Debug shutdown complete');
230
241
  } catch (error) {
231
- console.error('Error during shutdown:', error);
242
+ logger.error('Error during shutdown:', error);
232
243
  }
233
244
  }
234
245
  }
@@ -256,18 +267,18 @@ async function main() {
256
267
  // Log session information if verbose
257
268
  if (args.verbose) {
258
269
  const sessionInfo = getSessionInfo();
259
- console.log('Session Information:');
260
- console.log(` Session ID: ${sessionInfo.sessionId}`);
261
- console.log(` PID: ${sessionInfo.pid}`);
262
- console.log(` Port: ${sessionInfo.port}`);
263
- console.log(` Session File: ${sessionInfo.sessionFile}`);
270
+ logger.debug('Session Information:');
271
+ logger.debug(` Session ID: ${sessionInfo.sessionId}`);
272
+ logger.debug(` PID: ${sessionInfo.pid}`);
273
+ logger.debug(` Port: ${sessionInfo.port}`);
274
+ logger.debug(` Session File: ${sessionInfo.sessionFile}`);
264
275
 
265
276
  // Show other active sessions
266
277
  const activeSessions = await findActiveSessions();
267
278
  if (activeSessions.length > 1) {
268
- console.log(` Other active sessions: ${activeSessions.length - 1}`);
279
+ logger.debug(` Other active sessions: ${activeSessions.length - 1}`);
269
280
  activeSessions.filter(s => s.sessionId !== sessionInfo.sessionId).forEach(session => {
270
- console.log(` - ${session.sessionId} (PID: ${session.pid}, Port: ${session.port})`);
281
+ logger.debug(` - ${session.sessionId} (PID: ${session.pid}, Port: ${session.port})`);
271
282
  });
272
283
  }
273
284
  }
@@ -277,19 +288,19 @@ async function main() {
277
288
 
278
289
  await app.start();
279
290
  } catch (error) {
280
- console.error('Fatal error starting Chrome Debug:', error);
291
+ logger.error('Fatal error starting Chrome Debug:', error);
281
292
  process.exit(1);
282
293
  }
283
294
  }
284
295
 
285
296
  // Handle graceful shutdown
286
297
  process.on('SIGINT', async () => {
287
- console.error('Received SIGINT, shutting down gracefully...');
298
+ logger.info('Received SIGINT, shutting down gracefully...');
288
299
  process.exit(0);
289
300
  });
290
301
 
291
302
  process.on('SIGTERM', async () => {
292
- console.error('Received SIGTERM, shutting down gracefully...');
303
+ logger.info('Received SIGTERM, shutting down gracefully...');
293
304
  process.exit(0);
294
305
  });
295
306
 
@@ -299,7 +310,7 @@ export { ChromePilotApp };
299
310
  // Run if this is the main module
300
311
  if (import.meta.url === `file://${process.argv[1]}`) {
301
312
  main().catch((error) => {
302
- console.error('Unhandled error:', error);
313
+ logger.error('Unhandled error:', error);
303
314
  process.exit(1);
304
315
  });
305
316
  }
@@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid';
4
4
  import path from 'path';
5
5
  import fs from 'fs';
6
6
  import { fileURLToPath } from 'url';
7
+ import logger from '../utils/logger.js';
7
8
 
8
9
  const __filename = fileURLToPath(import.meta.url);
9
10
  const __dirname = path.dirname(__filename);
@@ -26,7 +27,7 @@ try {
26
27
  apiKeys = JSON.parse(fs.readFileSync(API_KEYS_FILE, 'utf8'));
27
28
  }
28
29
  } catch (error) {
29
- console.error('Error loading API keys:', error);
30
+ logger.error('Error loading API keys:', error);
30
31
  apiKeys = [];
31
32
  }
32
33
 
@@ -35,7 +36,7 @@ function saveApiKeys() {
35
36
  try {
36
37
  fs.writeFileSync(API_KEYS_FILE, JSON.stringify(apiKeys, null, 2));
37
38
  } catch (error) {
38
- console.error('Error saving API keys:', error);
39
+ logger.error('Error saving API keys:', error);
39
40
  }
40
41
  }
41
42
 
@@ -134,17 +135,17 @@ function verifyJWT(token) {
134
135
  // Authentication middleware
135
136
  export function authenticate(req, res, next) {
136
137
  // Debug logging for authentication flow
137
- console.log(`[AUTH-DEBUG] ${new Date().toISOString()} - ${req.method} ${req.originalUrl}`);
138
- console.log(`[AUTH-DEBUG] Client IP: ${req.ip}`);
139
- console.log(`[AUTH-DEBUG] X-Forwarded-For: ${req.headers['x-forwarded-for'] || 'none'}`);
140
- console.log(`[AUTH-DEBUG] User-Agent: ${req.headers['user-agent'] || 'none'}`);
141
- console.log(`[AUTH-DEBUG] Authorization header present: ${!!req.headers.authorization}`);
142
- console.log(`[AUTH-DEBUG] X-API-Key header present: ${!!req.headers['x-api-key']}`);
143
- console.log(`[AUTH-DEBUG] X-Service-Token header present: ${!!req.headers['x-service-token']}`);
144
-
138
+ logger.debug(`[AUTH-DEBUG] ${new Date().toISOString()} - ${req.method} ${req.originalUrl}`);
139
+ logger.debug(`[AUTH-DEBUG] Client IP: ${req.ip}`);
140
+ logger.debug(`[AUTH-DEBUG] X-Forwarded-For: ${req.headers['x-forwarded-for'] || 'none'}`);
141
+ logger.debug(`[AUTH-DEBUG] User-Agent: ${req.headers['user-agent'] || 'none'}`);
142
+ logger.debug(`[AUTH-DEBUG] Authorization header present: ${!!req.headers.authorization}`);
143
+ logger.debug(`[AUTH-DEBUG] X-API-Key header present: ${!!req.headers['x-api-key']}`);
144
+ logger.debug(`[AUTH-DEBUG] X-Service-Token header present: ${!!req.headers['x-service-token']}`);
145
+
145
146
  // Allow localhost requests from Chrome extension
146
147
  const isLocalhost = req.ip === '::1' || req.ip === '127.0.0.1' || req.ip === '::ffff:127.0.0.1';
147
- console.log(`[AUTH-DEBUG] Is localhost: ${isLocalhost}`);
148
+ logger.debug(`[AUTH-DEBUG] Is localhost: ${isLocalhost}`);
148
149
 
149
150
  if (isLocalhost) {
150
151
  // Set a synthetic user for localhost requests with explicit permissions
@@ -162,7 +163,7 @@ export function authenticate(req, res, next) {
162
163
  PERMISSIONS.FRAME_WRITE
163
164
  ]
164
165
  };
165
- console.log(`[AUTH-DEBUG] ✅ Localhost bypass granted for user:`, req.user);
166
+ logger.debug(`[AUTH-DEBUG] ✅ Localhost bypass granted for user:`, req.user);
166
167
  return next();
167
168
  }
168
169
 
@@ -208,7 +209,7 @@ export function authenticate(req, res, next) {
208
209
  }
209
210
 
210
211
  if (!user) {
211
- console.log(`[AUTH-DEBUG] ❌ Authentication failed - no valid credentials found`);
212
+ logger.debug(`[AUTH-DEBUG] ❌ Authentication failed - no valid credentials found`);
212
213
  return res.status(401).json({
213
214
  error: 'Authentication required',
214
215
  message: 'Please provide a valid API key, Bearer token, or service token',
@@ -219,8 +220,8 @@ export function authenticate(req, res, next) {
219
220
  }
220
221
  });
221
222
  }
222
-
223
- console.log(`[AUTH-DEBUG] ✅ Authentication successful for user:`, {
223
+
224
+ logger.debug(`[AUTH-DEBUG] ✅ Authentication successful for user:`, {
224
225
  id: user.id,
225
226
  name: user.name,
226
227
  role: user.role,
@@ -316,11 +317,11 @@ export function deleteApiKey(keyId) {
316
317
  // Initialize with default admin key if no keys exist
317
318
  if (apiKeys.length === 0) {
318
319
  const defaultAdmin = generateApiKey('Default Admin', ROLES.ADMIN);
319
- console.log('=== Chrome Debug Security Initialization ===');
320
- console.log('Generated default admin API key:');
321
- console.log(`API Key: ${defaultAdmin.apiKey}`);
322
- console.log('Please save this key securely and create additional keys as needed.');
323
- console.log('============================================');
320
+ logger.info('=== Chrome Debug Security Initialization ===');
321
+ logger.info('Generated default admin API key:');
322
+ logger.info(`API Key: ${defaultAdmin.apiKey}`);
323
+ logger.info('Please save this key securely and create additional keys as needed.');
324
+ logger.info('============================================');
324
325
  }
325
326
 
326
327
  // Generate MCP service token
@@ -333,7 +334,7 @@ export function generateServiceToken() {
333
334
  function validateServiceToken(token) {
334
335
  const expectedToken = process.env.MCP_SERVICE_TOKEN;
335
336
  if (!expectedToken) {
336
- console.warn('MCP_SERVICE_TOKEN not set. Service authentication disabled.');
337
+ logger.warn('MCP_SERVICE_TOKEN not set. Service authentication disabled.');
337
338
  return false;
338
339
  }
339
340
  return token === expectedToken;
@@ -344,23 +345,23 @@ function initializeMcpServiceToken() {
344
345
  if (!process.env.MCP_SERVICE_TOKEN) {
345
346
  const serviceToken = generateServiceToken();
346
347
  process.env.MCP_SERVICE_TOKEN = serviceToken;
347
- console.log('=== ChromeDebug MCP Service Token ===');
348
- console.log(`Generated MCP service token: ${serviceToken}`);
349
- console.log('This token allows MCP clients to access recordings.');
350
- console.log('======================================');
351
-
348
+ logger.info('=== ChromeDebug MCP Service Token ===');
349
+ logger.info(`Generated MCP service token: ${serviceToken}`);
350
+ logger.info('This token allows MCP clients to access recordings.');
351
+ logger.info('======================================');
352
+
352
353
  // Optionally save to .env file for persistence
353
354
  try {
354
355
  const envPath = path.join(__dirname, '../../.env');
355
356
  const envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8') : '';
356
-
357
+
357
358
  if (!envContent.includes('MCP_SERVICE_TOKEN=')) {
358
359
  const newEnvContent = envContent + (envContent.endsWith('\n') ? '' : '\n') + `MCP_SERVICE_TOKEN=${serviceToken}\n`;
359
360
  fs.writeFileSync(envPath, newEnvContent);
360
- console.log('MCP service token saved to .env file');
361
+ logger.info('MCP service token saved to .env file');
361
362
  }
362
363
  } catch (error) {
363
- console.log('Could not save MCP service token to .env file:', error.message);
364
+ logger.debug('Could not save MCP service token to .env file:', error.message);
364
365
  }
365
366
  }
366
367
  }
@@ -12,6 +12,7 @@
12
12
  import fs from 'fs';
13
13
  import path from 'path';
14
14
  import { fileURLToPath } from 'url';
15
+ import logger from './utils/logger.js';
15
16
 
16
17
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
18
 
@@ -31,9 +32,9 @@ export function writePortFile(port) {
31
32
 
32
33
  try {
33
34
  fs.writeFileSync(portFilePath, port.toString());
34
- console.log(`Port ${port} written to ${portFilePath}`);
35
+ logger.debug(`Port ${port} written to ${portFilePath}`);
35
36
  } catch (error) {
36
- console.error('Failed to write port file:', error);
37
+ logger.error('Failed to write port file:', error);
37
38
  }
38
39
  }
39
40
 
@@ -52,7 +53,7 @@ export function readPortFile() {
52
53
  }
53
54
  }
54
55
  } catch (error) {
55
- console.error('Failed to read port file:', error);
56
+ logger.error('Failed to read port file:', error);
56
57
  }
57
58
 
58
59
  return null;
@@ -67,10 +68,10 @@ export function removePortFile() {
67
68
  try {
68
69
  if (fs.existsSync(portFilePath)) {
69
70
  fs.unlinkSync(portFilePath);
70
- console.log(`Removed port file: ${portFilePath}`);
71
+ logger.debug(`Removed port file: ${portFilePath}`);
71
72
  }
72
73
  } catch (error) {
73
- console.error('Failed to remove port file:', error);
74
+ logger.error('Failed to remove port file:', error);
74
75
  }
75
76
  }
76
77
 
@@ -114,7 +115,7 @@ export async function discoverServer(preferredPorts = [3000, 3001, 3002, 3028],
114
115
  if (response.ok) {
115
116
  const data = await response.json();
116
117
  if (data.status === 'online') {
117
- console.log(`Discovered ChromeDebug server on port ${port}`);
118
+ logger.debug(`Discovered ChromeDebug server on port ${port}`);
118
119
  return port;
119
120
  }
120
121
  }
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import { EventEmitter } from 'events';
17
+ import logger from '../utils/logger.js';
17
18
 
18
19
  export class FailoverManager extends EventEmitter {
19
20
  constructor(options = {}) {
@@ -51,7 +52,7 @@ export class FailoverManager extends EventEmitter {
51
52
  this.healthCheckTimer = null;
52
53
  this.recoveryTimer = null;
53
54
 
54
- console.log('[FailoverManager] Initialized with failover management');
55
+ logger.debug('[FailoverManager] Initialized with failover management');
55
56
  }
56
57
 
57
58
  /**
@@ -65,9 +66,9 @@ export class FailoverManager extends EventEmitter {
65
66
  this.startHealthMonitoring();
66
67
 
67
68
  this.initialized = true;
68
- console.log('[FailoverManager] Initialized successfully');
69
+ logger.debug('[FailoverManager] Initialized successfully');
69
70
  } catch (error) {
70
- console.error('[FailoverManager] Initialization failed:', error);
71
+ logger.error('[FailoverManager] Initialization failed:', error);
71
72
  throw error;
72
73
  }
73
74
  }
@@ -111,7 +112,7 @@ export class FailoverManager extends EventEmitter {
111
112
  this.daemonHealthy = false;
112
113
  this.lastDaemonCheck = new Date();
113
114
 
114
- console.warn(`[FailoverManager] Daemon health check failed: ${error.message}`);
115
+ logger.warn(`[FailoverManager] Daemon health check failed: ${error.message}`);
115
116
  return false;
116
117
  }
117
118
  }
@@ -145,7 +146,7 @@ export class FailoverManager extends EventEmitter {
145
146
  async shouldUseFallback(operation = 'unknown') {
146
147
  // Always use fallback if daemon is known to be unhealthy
147
148
  if (!this.daemonHealthy) {
148
- console.log(`[FailoverManager] Using fallback for ${operation}: daemon is unhealthy`);
149
+ logger.debug(`[FailoverManager] Using fallback for ${operation}: daemon is unhealthy`);
149
150
  return true;
150
151
  }
151
152
 
@@ -153,14 +154,14 @@ export class FailoverManager extends EventEmitter {
153
154
  if (this.metrics.lastFailureTime) {
154
155
  const timeSinceFailure = Date.now() - this.metrics.lastFailureTime;
155
156
  if (timeSinceFailure < this.config.fallbackCooldownMs) {
156
- console.log(`[FailoverManager] Using fallback for ${operation}: in cooldown period`);
157
+ logger.debug(`[FailoverManager] Using fallback for ${operation}: in cooldown period`);
157
158
  return true;
158
159
  }
159
160
  }
160
161
 
161
162
  // Check if we've had too many consecutive failures
162
163
  if (this.metrics.consecutiveFailures >= this.config.maxConsecutiveFailures) {
163
- console.log(`[FailoverManager] Using fallback for ${operation}: too many consecutive failures`);
164
+ logger.debug(`[FailoverManager] Using fallback for ${operation}: too many consecutive failures`);
164
165
  return true;
165
166
  }
166
167
 
@@ -170,7 +171,7 @@ export class FailoverManager extends EventEmitter {
170
171
  // Perform a quick health check before critical operations
171
172
  const isHealthy = await this.checkDaemonHealth();
172
173
  if (!isHealthy) {
173
- console.log(`[FailoverManager] Using fallback for critical ${operation}: health check failed`);
174
+ logger.debug(`[FailoverManager] Using fallback for critical ${operation}: health check failed`);
174
175
  return true;
175
176
  }
176
177
  }
@@ -222,7 +223,7 @@ export class FailoverManager extends EventEmitter {
222
223
  this.fallbackActive = true;
223
224
  this.metrics.totalFallbacks++;
224
225
 
225
- console.warn('[FailoverManager] Triggering fallback to individual browsers');
226
+ logger.warn('[FailoverManager] Triggering fallback to individual browsers');
226
227
 
227
228
  // Start recovery monitoring
228
229
  this.startRecoveryMonitoring();
@@ -247,7 +248,7 @@ export class FailoverManager extends EventEmitter {
247
248
  }
248
249
  }, this.config.healthCheckIntervalMs);
249
250
 
250
- console.log('[FailoverManager] Started daemon health monitoring');
251
+ logger.debug('[FailoverManager] Started daemon health monitoring');
251
252
  }
252
253
 
253
254
  /**
@@ -264,7 +265,7 @@ export class FailoverManager extends EventEmitter {
264
265
  }
265
266
  }, this.config.recoveryCheckIntervalMs);
266
267
 
267
- console.log('[FailoverManager] Started recovery monitoring');
268
+ logger.debug('[FailoverManager] Started recovery monitoring');
268
269
  }
269
270
 
270
271
  /**
@@ -276,7 +277,7 @@ export class FailoverManager extends EventEmitter {
276
277
  this.recoveryInProgress = true;
277
278
 
278
279
  try {
279
- console.log('[FailoverManager] Checking for daemon recovery...');
280
+ logger.debug('[FailoverManager] Checking for daemon recovery...');
280
281
 
281
282
  // Perform multiple health checks to ensure stability
282
283
  const healthChecks = [];
@@ -289,13 +290,13 @@ export class FailoverManager extends EventEmitter {
289
290
  const allHealthy = results.every(result => result === true);
290
291
 
291
292
  if (allHealthy) {
292
- console.log('[FailoverManager] Daemon has recovered, disabling fallback');
293
+ logger.debug('[FailoverManager] Daemon has recovered, disabling fallback');
293
294
  this.recoveredFromFallback();
294
295
  } else {
295
- console.log('[FailoverManager] Daemon still unhealthy, continuing fallback');
296
+ logger.debug('[FailoverManager] Daemon still unhealthy, continuing fallback');
296
297
  }
297
298
  } catch (error) {
298
- console.warn('[FailoverManager] Error during recovery check:', error);
299
+ logger.warn('[FailoverManager] Error during recovery check:', error);
299
300
  } finally {
300
301
  this.recoveryInProgress = false;
301
302
  }
@@ -354,7 +355,7 @@ export class FailoverManager extends EventEmitter {
354
355
  this.recoveryTimer = null;
355
356
  }
356
357
 
357
- console.log('[FailoverManager] Failover state reset');
358
+ logger.debug('[FailoverManager] Failover state reset');
358
359
  this.emit('state-reset');
359
360
  }
360
361
 
@@ -391,7 +392,7 @@ export class FailoverManager extends EventEmitter {
391
392
  * Cleanup method for graceful shutdown
392
393
  */
393
394
  async cleanup() {
394
- console.log('[FailoverManager] Cleaning up...');
395
+ logger.debug('[FailoverManager] Cleaning up...');
395
396
 
396
397
  // Stop all timers
397
398
  if (this.healthCheckTimer) {
@@ -407,6 +408,6 @@ export class FailoverManager extends EventEmitter {
407
408
  // Remove all listeners
408
409
  this.removeAllListeners();
409
410
 
410
- console.log('[FailoverManager] Cleanup completed');
411
+ logger.debug('[FailoverManager] Cleanup completed');
411
412
  }
412
413
  }
@@ -15,6 +15,7 @@
15
15
  import { promises as fs, existsSync as fsExistsSync } from 'fs';
16
16
  import path from 'path';
17
17
  import os from 'os';
18
+ import logger from '../utils/logger.js';
18
19
 
19
20
  export class ProjectManager {
20
21
  constructor() {
@@ -34,7 +35,7 @@ export class ProjectManager {
34
35
  const projectRoot = this.findProjectRoot(resolvedPath);
35
36
 
36
37
  if (!projectRoot) {
37
- console.log('[ProjectManager] No valid project detected from:', resolvedPath);
38
+ logger.debug('[ProjectManager] No valid project detected from:', resolvedPath);
38
39
  return null;
39
40
  }
40
41
 
@@ -48,12 +49,12 @@ export class ProjectManager {
48
49
  hasMarkers: markers
49
50
  };
50
51
 
51
- console.log(`[ProjectManager] Detected ${projectType} project at: ${projectRoot}`);
52
- console.log(`[ProjectManager] Project markers found: ${markers.join(', ')}`);
53
-
52
+ logger.debug(`[ProjectManager] Detected ${projectType} project at: ${projectRoot}`);
53
+ logger.debug(`[ProjectManager] Project markers found: ${markers.join(', ')}`);
54
+
54
55
  return this.projectInfo;
55
56
  } catch (error) {
56
- console.warn('[ProjectManager] Error during project detection:', error.message);
57
+ logger.warn('[ProjectManager] Error during project detection:', error.message);
57
58
  return null;
58
59
  }
59
60
  }
@@ -220,7 +221,7 @@ export class ProjectManager {
220
221
  logsDir: path.join(projectDir, '.chromedebug', 'logs')
221
222
  };
222
223
 
223
- console.log(`[ProjectManager] Initializing project structure in: ${this.projectStructure.baseDir}`);
224
+ logger.debug(`[ProjectManager] Initializing project structure in: ${this.projectStructure.baseDir}`);
224
225
 
225
226
  try {
226
227
  // Create all necessary directories
@@ -235,12 +236,12 @@ export class ProjectManager {
235
236
  await this.updateGitignore(projectDir);
236
237
  }
237
238
 
238
- console.log('[ProjectManager] Project structure initialized successfully');
239
- console.log('[ProjectManager] Structure:', this.projectStructure);
240
-
239
+ logger.debug('[ProjectManager] Project structure initialized successfully');
240
+ logger.debug('[ProjectManager] Structure:', this.projectStructure);
241
+
241
242
  this.initialized = true;
242
243
  } catch (error) {
243
- console.error('[ProjectManager] Failed to initialize project structure:', error);
244
+ logger.error('[ProjectManager] Failed to initialize project structure:', error);
244
245
  throw new Error(`Project structure initialization failed: ${error.message}`);
245
246
  }
246
247
  }
@@ -272,11 +273,11 @@ export class ProjectManager {
272
273
 
273
274
  if (hasEntry) {
274
275
  needsUpdate = false;
275
- console.log('[ProjectManager] .chromedebug already in .gitignore');
276
+ logger.debug('[ProjectManager] .chromedebug already in .gitignore');
276
277
  }
277
278
  } catch (error) {
278
279
  // .gitignore doesn't exist, will create it
279
- console.log('[ProjectManager] Creating new .gitignore file');
280
+ logger.debug('[ProjectManager] Creating new .gitignore file');
280
281
  }
281
282
 
282
283
  if (needsUpdate) {
@@ -290,14 +291,14 @@ export class ProjectManager {
290
291
  # Chrome Debug project-local data
291
292
  ${chromePilotEntry}
292
293
  `;
293
-
294
+
294
295
  gitignoreContent += chromePilotSection;
295
296
 
296
297
  await fs.writeFile(gitignorePath, gitignoreContent, 'utf8');
297
- console.log('[ProjectManager] Added .chromedebug to .gitignore');
298
+ logger.debug('[ProjectManager] Added .chromedebug to .gitignore');
298
299
  }
299
300
  } catch (error) {
300
- console.warn('[ProjectManager] Warning: Could not update .gitignore:', error.message);
301
+ logger.warn('[ProjectManager] Warning: Could not update .gitignore:', error.message);
301
302
  // Don't fail initialization if .gitignore update fails
302
303
  }
303
304
  }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Logger utility for ChromeDebug MCP
3
+ *
4
+ * Logging Levels:
5
+ * - ERROR: Critical errors, always shown
6
+ * - WARN: Warnings and fallback scenarios, always shown
7
+ * - INFO: Important events (startup, connections), shown in development/debug mode
8
+ * - DEBUG: Verbose traces, shown only with DEBUG=true
9
+ *
10
+ * Usage:
11
+ * import logger from './utils/logger.js';
12
+ * logger.error('Connection failed:', error);
13
+ * logger.warn('Falling back to default port');
14
+ * logger.info('Server started on port', port);
15
+ * logger.debug('Query executed:', sql);
16
+ *
17
+ * Environment Variables:
18
+ * NODE_ENV=development - Shows INFO level and above
19
+ * DEBUG=true or DEBUG=1 - Shows DEBUG level (all logs)
20
+ */
21
+
22
+ const isDevelopment = process.env.NODE_ENV === 'development';
23
+ const isDebug = process.env.DEBUG === 'true' || process.env.DEBUG === '1';
24
+
25
+ /**
26
+ * Logger with level-based filtering
27
+ * All output goes to stderr to keep stdout clean for MCP JSON-RPC protocol
28
+ */
29
+ const logger = {
30
+ /**
31
+ * Log critical errors - always shown
32
+ */
33
+ error: (...args) => {
34
+ console.error('[ERROR]', ...args);
35
+ },
36
+
37
+ /**
38
+ * Log warnings and fallback scenarios - always shown
39
+ */
40
+ warn: (...args) => {
41
+ console.error('[WARN]', ...args);
42
+ },
43
+
44
+ /**
45
+ * Log important events - shown in development or debug mode
46
+ */
47
+ info: (...args) => {
48
+ if (isDevelopment || isDebug) {
49
+ console.error('[INFO]', ...args);
50
+ }
51
+ },
52
+
53
+ /**
54
+ * Log verbose traces - shown only with DEBUG=true
55
+ */
56
+ debug: (...args) => {
57
+ if (isDebug) {
58
+ console.error('[DEBUG]', ...args);
59
+ }
60
+ }
61
+ };
62
+
63
+ export default logger;