@dynamicu/chromedebug-mcp 2.5.15 → 2.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamicu/chromedebug-mcp",
3
- "version": "2.5.15",
3
+ "version": "2.6.1",
4
4
  "description": "ChromeDebug MCP - MCP server that provides full control over a Chrome browser instance for debugging and automation with AI assistants like Claude Code",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -118,6 +118,8 @@
118
118
  "joi": "^18.0.0",
119
119
  "jsonwebtoken": "^9.0.2",
120
120
  "multer": "^2.0.2",
121
+ "pino": "^10.1.0",
122
+ "pino-pretty": "^13.1.2",
121
123
  "puppeteer": "^24.25.0",
122
124
  "uuid": "^11.1.0",
123
125
  "web-vitals": "^5.1.0",
@@ -62,8 +62,8 @@ module.exports = {
62
62
  { from: 'chrome-extension/logger.js', to: 'logger.js' },
63
63
  { from: 'chrome-extension/license-helper.js', to: 'license-helper.js' },
64
64
  { from: 'chrome-extension/firebase-client.js', to: 'firebase-client.js' },
65
- { from: 'chrome-extension/firebase-config.js', to: 'firebase-config.js' },
66
- { from: 'chrome-extension/firebase-config.module.js', to: 'firebase-config.module.js' },
65
+ { from: 'chrome-extension/firebase-config.public.js', to: 'firebase-config.public.js' },
66
+ { from: 'chrome-extension/firebase-config.public-sw.js', to: 'firebase-config.js' },
67
67
  { from: 'chrome-extension/activation-manager.js', to: 'activation-manager.js' },
68
68
  { from: 'chrome-extension/frame-capture.js', to: 'frame-capture.js' },
69
69
  { from: 'chrome-extension/chrome-session-manager.js', to: 'chrome-session-manager.js' },
package/src/cli.js CHANGED
@@ -18,8 +18,12 @@ import {
18
18
  findActiveSessions
19
19
  } from './services/unified-session-manager.js';
20
20
  import logger from './utils/logger.js';
21
+ import { interceptStdout } from './logger.js';
21
22
  import { installPro } from './commands/install-pro.js';
22
23
 
24
+ // CRITICAL: Intercept stdout FIRST to prevent MCP JSON-RPC pollution
25
+ interceptStdout();
26
+
23
27
  /**
24
28
  * Main CLI entry point
25
29
  */
@@ -8,6 +8,10 @@ import { findAvailablePort } from './utils.js';
8
8
  import { writePortFile, removePortFile } from './port-discovery.js';
9
9
  import { configLoader } from './config-loader.js';
10
10
  import { setupProcessTracking } from './services/process-tracker.js';
11
+ import { createLogger, logHttpRequest } from './logger.js';
12
+
13
+ // Create component-specific logger
14
+ const logger = createLogger('HTTP-Server');
11
15
 
12
16
  // Security imports
13
17
  import { authenticate, authorize, PERMISSIONS } from './middleware/auth.js';
@@ -83,10 +87,10 @@ async function startHttpServer(chromeController = null, targetPort = null) {
83
87
  const contentType = req.get('content-type') || 'none';
84
88
  const contentLength = req.get('content-length') || '0';
85
89
 
86
- console.error(`[RAW-REQUEST] ${timestamp} - ${req.method} ${req.originalUrl}`);
87
- console.error(`[RAW-REQUEST] IP: ${ip} | UA: ${userAgent}`);
88
- console.error(`[RAW-REQUEST] Content-Type: ${contentType} | Length: ${contentLength}bytes`);
89
- console.error(`[RAW-REQUEST] Headers:`, Object.keys(req.headers).map(h => `${h}=${req.headers[h]}`).join(', '));
90
+ logger.debug(` ${timestamp} - ${req.method} ${req.originalUrl}`);
91
+ logger.debug(` IP: ${ip} | UA: ${userAgent}`);
92
+ logger.debug(` Content-Type: ${contentType} | Length: ${contentLength}bytes`);
93
+ logger.debug(` Headers:`, Object.keys(req.headers).map(h => `${h}=${req.headers[h]}`).join(', '));
90
94
 
91
95
  // Capture body parsing errors
92
96
  const originalJson = express.json();
@@ -96,8 +100,8 @@ async function startHttpServer(chromeController = null, targetPort = null) {
96
100
  const originalStatus = res.status;
97
101
  res.status = function(code) {
98
102
  if (code === 400) {
99
- console.error(`[RAW-REQUEST] HTTP 400 ERROR for ${req.method} ${req.originalUrl}`);
100
- console.error(`[RAW-REQUEST] This request will be rejected with 400`);
103
+ logger.debug(` HTTP 400 ERROR for ${req.method} ${req.originalUrl}`);
104
+ logger.debug(` This request will be rejected with 400`);
101
105
  }
102
106
  return originalStatus.call(this, code);
103
107
  };
@@ -108,10 +112,10 @@ async function startHttpServer(chromeController = null, targetPort = null) {
108
112
  // ENHANCED BODY PARSING - For debugging body parsing issues (only for screen-interactions)
109
113
  app.use('/chromedebug/screen-interactions/*', (req, res, next) => {
110
114
  if (req.method === 'POST') {
111
- console.error(`[BODY-DEBUG] Processing screen-interactions request for ${req.originalUrl}`);
112
- console.error(`[BODY-DEBUG] Content-Type: ${req.get('content-type')}`);
113
- console.error(`[BODY-DEBUG] Content-Length: ${req.get('content-length')}`);
114
- console.error(`[BODY-DEBUG] User-Agent: ${req.get('user-agent')}`);
115
+ logger.debug(` Processing screen-interactions request for ${req.originalUrl}`);
116
+ logger.debug(` Content-Type: ${req.get('content-type')}`);
117
+ logger.debug(` Content-Length: ${req.get('content-length')}`);
118
+ logger.debug(` User-Agent: ${req.get('user-agent')}`);
115
119
 
116
120
  // Store original data handler to capture raw body
117
121
  const originalData = req.on;
@@ -130,16 +134,16 @@ async function startHttpServer(chromeController = null, targetPort = null) {
130
134
  const wrappedHandler = () => {
131
135
  if (chunks.length > 0) {
132
136
  const rawBody = Buffer.concat(chunks).toString();
133
- console.error(`[BODY-DEBUG] Raw body captured (${rawBody.length} chars):`, rawBody);
137
+ logger.debug(` Raw body captured (${rawBody.length} chars):`, rawBody);
134
138
  req.rawBody = rawBody;
135
139
 
136
140
  // Try to parse manually
137
141
  try {
138
142
  const parsedBody = JSON.parse(rawBody);
139
- console.error(`[BODY-DEBUG] Manual JSON parse successful`);
140
- console.error(`[BODY-DEBUG] Parsed data:`, JSON.stringify(parsedBody, null, 2));
143
+ logger.debug(` Manual JSON parse successful`);
144
+ logger.debug(` Parsed data:`, JSON.stringify(parsedBody, null, 2));
141
145
  } catch (parseError) {
142
- console.error(`[BODY-DEBUG] Manual JSON parse FAILED:`, parseError.message);
146
+ logger.debug(` Manual JSON parse FAILED:`, parseError.message);
143
147
  }
144
148
  }
145
149
  return originalHandler();
@@ -155,9 +159,9 @@ async function startHttpServer(chromeController = null, targetPort = null) {
155
159
  // JSON PARSING ERROR HANDLER - Catch malformed JSON before it reaches our handlers
156
160
  app.use('/chromedebug/screen-interactions/*', (err, req, res, next) => {
157
161
  if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
158
- console.error(`[JSON-PARSE-ERROR] Malformed JSON in screen-interactions request:`, err.message);
159
- console.error(`[JSON-PARSE-ERROR] Raw body:`, req.rawBody || 'not captured');
160
- console.error(`[JSON-PARSE-ERROR] Request body:`, req.body);
162
+ logger.error(` Malformed JSON in screen-interactions request:`, err.message);
163
+ logger.error(` Raw body:`, req.rawBody || 'not captured');
164
+ logger.error(` Request body:`, req.body);
161
165
  return res.status(400).json({
162
166
  error: 'Invalid JSON format',
163
167
  details: err.message
@@ -169,13 +173,13 @@ async function startHttpServer(chromeController = null, targetPort = null) {
169
173
  // ENHANCED VALIDATION ERROR HANDLER - Catch and log detailed validation errors
170
174
  app.use('/chromedebug/screen-interactions/*', (err, req, res, next) => {
171
175
  if (err && err.status === 400 && err.details) {
172
- console.error(`[VALIDATION-ERROR] Screen interactions validation failed:`);
173
- console.error(`[VALIDATION-ERROR] Request path:`, req.originalUrl);
174
- console.error(`[VALIDATION-ERROR] User agent:`, req.get('user-agent'));
175
- console.error(`[VALIDATION-ERROR] Content type:`, req.get('content-type'));
176
- console.error(`[VALIDATION-ERROR] Raw body:`, req.rawBody || 'not captured');
177
- console.error(`[VALIDATION-ERROR] Parsed body:`, JSON.stringify(req.body, null, 2));
178
- console.error(`[VALIDATION-ERROR] Validation errors:`, JSON.stringify(err.details, null, 2));
176
+ logger.warn(` Screen interactions validation failed:`);
177
+ logger.warn(` Request path:`, req.originalUrl);
178
+ logger.warn(` User agent:`, req.get('user-agent'));
179
+ logger.warn(` Content type:`, req.get('content-type'));
180
+ logger.warn(` Raw body:`, req.rawBody || 'not captured');
181
+ logger.warn(` Parsed body:`, JSON.stringify(req.body, null, 2));
182
+ logger.warn(` Validation errors:`, JSON.stringify(err.details, null, 2));
179
183
  }
180
184
  next(err);
181
185
  });
@@ -208,7 +212,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
208
212
  // JSON error handling middleware
209
213
  app.use((err, req, res, next) => {
210
214
  if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
211
- console.error('[JSON Parse Error]', {
215
+ logger.error('[JSON Parse Error]', {
212
216
  url: req.url,
213
217
  method: req.method,
214
218
  contentType: req.get('content-type'),
@@ -288,8 +292,8 @@ async function startHttpServer(chromeController = null, targetPort = null) {
288
292
  createValidator(workflowRecordingSchema),
289
293
  async (req, res) => {
290
294
  const { sessionId, url, title, includeLogs, actions, logs, functionTraces, name, screenshotSettings } = req.body;
291
- console.error('[HTTP Server] Received workflow recording with name:', name);
292
- console.error('[HTTP Server] Function traces count:', functionTraces ? functionTraces.length : 0);
295
+ logger.info('[HTTP Server] Received workflow recording with name:', name);
296
+ logger.info('[HTTP Server] Function traces count:', functionTraces ? functionTraces.length : 0);
293
297
  if (!sessionId || !actions) {
294
298
  return res.status(400).json({ error: 'sessionId and actions are required' });
295
299
  }
@@ -298,7 +302,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
298
302
  const result = await activeController.storeWorkflowRecording(sessionId, url, title, includeLogs, actions, logs, name, screenshotSettings, functionTraces);
299
303
  res.json(result);
300
304
  } catch (error) {
301
- console.error('Error storing workflow recording:', error);
305
+ logger.error('Error storing workflow recording:', error);
302
306
  res.status(500).json({ error: 'Failed to store workflow recording', details: error.message });
303
307
  }
304
308
  });
@@ -316,7 +320,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
316
320
  }
317
321
  res.json(result);
318
322
  } catch (error) {
319
- console.error('Error retrieving workflow recording:', error);
323
+ logger.error('Error retrieving workflow recording:', error);
320
324
  res.status(500).json({ error: 'Failed to retrieve workflow recording', details: error.message });
321
325
  }
322
326
  });
@@ -329,7 +333,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
329
333
  const result = await activeController.listWorkflowRecordings();
330
334
  res.json(result);
331
335
  } catch (error) {
332
- console.error('Error listing workflow recordings:', error);
336
+ logger.error('Error listing workflow recordings:', error);
333
337
  res.status(500).json({ error: 'Failed to list workflow recordings', details: error.message });
334
338
  }
335
339
  });
@@ -360,18 +364,18 @@ async function startHttpServer(chromeController = null, targetPort = null) {
360
364
  return res.status(400).json({ error: 'No data provided' });
361
365
  }
362
366
 
363
- console.error(`[Upload] Received batch ${batchId} for recording ${recordingId} with ${events.length} events`);
367
+ logger.debug(` Received batch ${batchId} for recording ${recordingId} with ${events.length} events`);
364
368
 
365
369
  // Ensure recording exists (auto-create if needed)
366
370
  try {
367
371
  const existingRecording = activeController.database.getRecording(recordingId);
368
372
  if (!existingRecording) {
369
- console.error(`[Upload] Auto-creating recording ${recordingId} for full data upload`);
373
+ logger.debug(` Auto-creating recording ${recordingId} for full data upload`);
370
374
  activeController.database.storeRecording(recordingId, 'full_data_recording');
371
375
  }
372
376
  } catch (error) {
373
377
  // Recording doesn't exist, create it
374
- console.error(`[Upload] Auto-creating recording ${recordingId} for full data upload (error case)`);
378
+ logger.debug(` Auto-creating recording ${recordingId} for full data upload (error case)`);
375
379
  activeController.database.storeRecording(recordingId, 'full_data_recording');
376
380
  }
377
381
 
@@ -394,7 +398,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
394
398
  await activeController.database.storePerformanceMetric(recordingId, event);
395
399
  break;
396
400
  default:
397
- console.error(`[Upload] Unknown event type: ${event.type}`);
401
+ logger.debug(` Unknown event type: ${event.type}`);
398
402
  }
399
403
  }
400
404
 
@@ -404,7 +408,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
404
408
  eventsProcessed: events.length
405
409
  });
406
410
  } catch (error) {
407
- console.error('[Upload] Error processing batch:', error);
411
+ logger.debug(' Error processing batch:', error);
408
412
  res.status(500).json({
409
413
  error: 'Failed to process batch',
410
414
  details: error.message
@@ -434,7 +438,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
434
438
  data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;
435
439
  }
436
440
 
437
- console.error(`[Upload] Received ${dataType} data for recording ${recordingId}`);
441
+ logger.debug(` Received ${dataType} data for recording ${recordingId}`);
438
442
 
439
443
  // Store data based on type
440
444
  const storeMethod = `store${dataType.charAt(0).toUpperCase() + dataType.slice(1).replace(/-/g, '')}`;
@@ -445,7 +449,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
445
449
  res.status(400).json({ error: `Unknown data type: ${dataType}` });
446
450
  }
447
451
  } catch (error) {
448
- console.error(`[Upload] Error processing ${req.params.dataType}:`, error);
452
+ logger.debug(` Error processing ${req.params.dataType}:`, error);
449
453
  res.status(500).json({
450
454
  error: 'Failed to process data',
451
455
  details: error.message
@@ -461,23 +465,23 @@ async function startHttpServer(chromeController = null, targetPort = null) {
461
465
  authorize(PERMISSIONS.FRAME_WRITE),
462
466
  upload.none(),
463
467
  async (req, res) => {
464
- console.error('[FORMDATA-DEBUG] ==========================================');
465
- console.error('[FORMDATA-DEBUG] Frame batch request received');
466
- console.error('[FORMDATA-DEBUG] Content-Type:', req.headers['content-type']);
467
- console.error('[FORMDATA-DEBUG] Request method:', req.method);
468
- console.error('[FORMDATA-DEBUG] User:', req.user?.name || 'unknown');
469
- console.error('[FORMDATA-DEBUG] Raw body keys:', Object.keys(req.body || {}));
470
- console.error('[FORMDATA-DEBUG] Raw body:', req.body);
468
+ logger.debug(' ==========================================');
469
+ logger.debug(' Frame batch request received');
470
+ logger.debug(' Content-Type:', req.headers['content-type']);
471
+ logger.debug(' Request method:', req.method);
472
+ logger.debug(' User:', req.user?.name || 'unknown');
473
+ logger.debug(' Raw body keys:', Object.keys(req.body || {}));
474
+ logger.debug(' Raw body:', req.body);
471
475
 
472
476
  // Handle both JSON and FormData
473
477
  let sessionId, frames, sessionName;
474
478
 
475
479
  if (req.headers['content-type']?.includes('application/json')) {
476
- console.error('[FORMDATA-DEBUG] Processing as JSON request');
480
+ logger.debug(' Processing as JSON request');
477
481
  // JSON request - validate with schema
478
482
  const { error, value } = frameBatchSchema.validate(req.body);
479
483
  if (error) {
480
- console.error('[FORMDATA-DEBUG] JSON validation failed:', error.details);
484
+ logger.debug(' JSON validation failed:', error.details);
481
485
  return res.status(400).json({
482
486
  error: 'Validation failed',
483
487
  details: error.details.map(detail => ({
@@ -490,37 +494,37 @@ async function startHttpServer(chromeController = null, targetPort = null) {
490
494
  frames = value.frames;
491
495
  sessionName = value.sessionName || null;
492
496
 
493
- console.error('[FORMDATA-DEBUG] JSON parsed successfully:', { sessionId, frameCount: frames?.length, sessionName });
497
+ logger.debug(' JSON parsed successfully:', { sessionId, frameCount: frames?.length, sessionName });
494
498
  } else {
495
- console.error('[FORMDATA-DEBUG] Processing as FormData request');
499
+ logger.debug(' Processing as FormData request');
496
500
 
497
501
  // FormData request - manual validation
498
502
  sessionId = req.body.sessionId;
499
503
  sessionName = req.body.sessionName || null;
500
504
 
501
- console.error('[FORMDATA-DEBUG] SessionId from FormData:', sessionId);
502
- console.error('[FORMDATA-DEBUG] SessionName from FormData:', sessionName);
503
- console.error('[FORMDATA-DEBUG] Frames field type:', typeof req.body.frames);
504
- console.error('[FORMDATA-DEBUG] Frames field length:', req.body.frames?.length);
505
- console.error('[FORMDATA-DEBUG] Frames field preview:', req.body.frames?.substring(0, 200));
505
+ logger.debug(' SessionId from FormData:', sessionId);
506
+ logger.debug(' SessionName from FormData:', sessionName);
507
+ logger.debug(' Frames field type:', typeof req.body.frames);
508
+ logger.debug(' Frames field length:', req.body.frames?.length);
509
+ logger.debug(' Frames field preview:', req.body.frames?.substring(0, 200));
506
510
 
507
511
  try {
508
512
  if (!req.body.frames) {
509
- console.error('[FORMDATA-DEBUG] ❌ No frames field in FormData');
513
+ logger.debug(' ❌ No frames field in FormData');
510
514
  frames = null;
511
515
  } else {
512
- console.error('[FORMDATA-DEBUG] 🔄 Attempting to parse frames JSON...');
516
+ logger.debug(' 🔄 Attempting to parse frames JSON...');
513
517
  frames = JSON.parse(req.body.frames);
514
- console.error('[FORMDATA-DEBUG] ✅ Frames JSON parsed successfully');
515
- console.error('[FORMDATA-DEBUG] Parsed frames count:', frames?.length);
516
- console.error('[FORMDATA-DEBUG] First frame structure:', frames?.[0] ? Object.keys(frames[0]) : 'no frames');
518
+ logger.debug(' ✅ Frames JSON parsed successfully');
519
+ logger.debug(' Parsed frames count:', frames?.length);
520
+ logger.debug(' First frame structure:', frames?.[0] ? Object.keys(frames[0]) : 'no frames');
517
521
  }
518
522
 
519
- console.error('[FORMDATA-DEBUG] 🔄 Validating parsed FormData...');
523
+ logger.debug(' 🔄 Validating parsed FormData...');
520
524
  // Validate the parsed data
521
525
  const { error, value } = frameBatchSchema.validate({ sessionId, frames, sessionName });
522
526
  if (error) {
523
- console.error('[FORMDATA-DEBUG] ❌ FormData validation failed:', error.details);
527
+ logger.debug(' ❌ FormData validation failed:', error.details);
524
528
  return res.status(400).json({
525
529
  error: 'Validation failed',
526
530
  details: error.details.map(detail => ({
@@ -533,47 +537,47 @@ async function startHttpServer(chromeController = null, targetPort = null) {
533
537
  frames = value.frames;
534
538
  sessionName = value.sessionName;
535
539
 
536
- console.error('[FORMDATA-DEBUG] ✅ FormData validation successful');
540
+ logger.debug(' ✅ FormData validation successful');
537
541
  } catch (parseError) {
538
- console.error('[FORMDATA-DEBUG] ❌ Failed to parse frames JSON:', parseError);
539
- console.error('[FORMDATA-DEBUG] Raw frames data:', req.body.frames);
542
+ logger.debug(' ❌ Failed to parse frames JSON:', parseError);
543
+ logger.debug(' Raw frames data:', req.body.frames);
540
544
  return res.status(400).json({ error: 'Invalid frames JSON format' });
541
545
  }
542
546
  }
543
547
 
544
548
  if (!sessionId || !frames) {
545
- console.error('[FORMDATA-DEBUG] ❌ Missing required data');
546
- console.error('[FORMDATA-DEBUG] SessionId:', sessionId);
547
- console.error('[FORMDATA-DEBUG] Frames:', frames ? 'present' : 'null');
549
+ logger.debug(' ❌ Missing required data');
550
+ logger.debug(' SessionId:', sessionId);
551
+ logger.debug(' Frames:', frames ? 'present' : 'null');
548
552
  return res.status(400).json({ error: 'sessionId and frames are required' });
549
553
  }
550
554
 
551
- console.error('[FORMDATA-DEBUG] ✅ All data validated, proceeding to storage');
552
- console.error('[FORMDATA-DEBUG] Session ID:', sessionId);
553
- console.error('[FORMDATA-DEBUG] Frame count:', frames?.length);
555
+ logger.debug(' ✅ All data validated, proceeding to storage');
556
+ logger.debug(' Session ID:', sessionId);
557
+ logger.debug(' Frame count:', frames?.length);
554
558
 
555
559
  try {
556
- console.error('[FORMDATA-DEBUG] 🔄 Calling activeController.storeFrameBatch...');
560
+ logger.debug(' 🔄 Calling activeController.storeFrameBatch...');
557
561
  if (frames && frames.length > 0) {
558
- console.error('[FORMDATA-DEBUG] First frame structure:', Object.keys(frames[0]));
559
- console.error('[FORMDATA-DEBUG] First frame has imageData:', !!frames[0].imageData);
560
- console.error('[FORMDATA-DEBUG] ImageData length:', frames[0].imageData ? frames[0].imageData.length : 'N/A');
562
+ logger.debug(' First frame structure:', Object.keys(frames[0]));
563
+ logger.debug(' First frame has imageData:', !!frames[0].imageData);
564
+ logger.debug(' ImageData length:', frames[0].imageData ? frames[0].imageData.length : 'N/A');
561
565
  }
562
566
 
563
- console.error('[FORMDATA-DEBUG] Session name:', sessionName || 'none');
567
+ logger.debug(' Session name:', sessionName || 'none');
564
568
 
565
569
  const result = await activeController.storeFrameBatch(sessionId, frames, sessionName);
566
- console.error('[FORMDATA-DEBUG] ✅ Frame batch storage result:', result);
570
+ logger.debug(' ✅ Frame batch storage result:', result);
567
571
 
568
- console.error('[FORMDATA-DEBUG] Result recording ID:', result?.id);
569
- console.error('[FORMDATA-DEBUG] Result frame count:', result?.total_frames);
570
- console.error('[FORMDATA-DEBUG] ==========================================');
572
+ logger.debug(' Result recording ID:', result?.id);
573
+ logger.debug(' Result frame count:', result?.total_frames);
574
+ logger.debug(' ==========================================');
571
575
 
572
576
  res.json(result);
573
577
  } catch (error) {
574
- console.error('[FORMDATA-DEBUG] ❌ Storage error:', error);
575
- console.error('[FORMDATA-DEBUG] Error stack:', error.stack);
576
- console.error('[FORMDATA-DEBUG] ==========================================');
578
+ logger.debug(' ❌ Storage error:', error);
579
+ logger.debug(' Error stack:', error.stack);
580
+ logger.debug(' ==========================================');
577
581
  res.status(500).json({ error: 'Failed to store frame batch', details: error.message });
578
582
  }
579
583
  });
@@ -592,12 +596,12 @@ async function startHttpServer(chromeController = null, targetPort = null) {
592
596
 
593
597
  // Debug: Verify logs are properly included and formatted
594
598
  if (result.frames?.length > 0 && result.frames[0]?.logs?.length > 0) {
595
- console.error(`[INFO] Frame session ${sessionId}: Loaded ${result.frames.length} frames with console logs`);
599
+ logger.info(` Frame session ${sessionId}: Loaded ${result.frames.length} frames with console logs`);
596
600
  }
597
601
 
598
602
  res.json(result);
599
603
  } catch (error) {
600
- console.error('Error retrieving frame session:', error);
604
+ logger.error('Error retrieving frame session:', error);
601
605
  res.status(500).json({ error: 'Failed to retrieve frame session', details: error.message });
602
606
  }
603
607
  });
@@ -633,7 +637,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
633
637
  limit: limit
634
638
  });
635
639
  } catch (error) {
636
- console.error('Error retrieving snapshots:', error);
640
+ logger.error('Error retrieving snapshots:', error);
637
641
  res.status(500).json({ error: 'Failed to retrieve snapshots', details: error.message });
638
642
  }
639
643
  });
@@ -655,7 +659,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
655
659
  if (req.body.logs) {
656
660
  const compliance = analyzeLogFieldCompliance(req.body.logs);
657
661
  if (!compliance.isCompliant) {
658
- console.error('[LogTransform] Applied field filtering:', {
662
+ logger.debug(' Applied field filtering:', {
659
663
  originalLogs: req.body.logs.length,
660
664
  nonSchemaFields: compliance.nonSchemaFields,
661
665
  missingFields: compliance.missingRequiredFields
@@ -690,7 +694,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
690
694
  if (logs) {
691
695
  const compliance = analyzeLogFieldCompliance(logs);
692
696
  if (!compliance.isCompliant) {
693
- console.error('[LogTransform] Applied field filtering (FormData):', {
697
+ logger.debug(' Applied field filtering (FormData):', {
694
698
  originalLogs: logs.length,
695
699
  nonSchemaFields: compliance.nonSchemaFields,
696
700
  missingFields: compliance.missingRequiredFields
@@ -724,7 +728,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
724
728
  const result = await activeController.associateLogsWithFrames(sessionId, logs);
725
729
  res.json(result);
726
730
  } catch (error) {
727
- console.error('Error associating logs with frames:', error);
731
+ logger.error('Error associating logs with frames:', error);
728
732
  res.status(500).json({ error: 'Failed to associate logs', details: error.message });
729
733
  }
730
734
  });
@@ -784,7 +788,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
784
788
  const result = await activeController.streamLogsToFrames(sessionId, logs);
785
789
  res.json(result);
786
790
  } catch (error) {
787
- console.error('Error streaming logs to frames:', error);
791
+ logger.error('Error streaming logs to frames:', error);
788
792
  res.status(500).json({ error: 'Failed to stream logs', details: error.message });
789
793
  }
790
794
  });
@@ -834,7 +838,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
834
838
  chromeConnected: status.connected
835
839
  });
836
840
  } catch (error) {
837
- console.error('Error handling DOM intent:', error);
841
+ logger.error('Error handling DOM intent:', error);
838
842
  res.status(500).json({
839
843
  error: 'Failed to process DOM intent',
840
844
  details: error.message
@@ -1060,7 +1064,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1060
1064
  res.status(404).json(result);
1061
1065
  }
1062
1066
  } catch (error) {
1063
- console.error('Error deleting recording:', error);
1067
+ logger.error('Error deleting recording:', error);
1064
1068
  res.status(500).json({ error: 'Failed to delete recording', details: error.message });
1065
1069
  }
1066
1070
  });
@@ -1080,7 +1084,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1080
1084
  res.status(404).json(result);
1081
1085
  }
1082
1086
  } catch (error) {
1083
- console.error('Error deleting workflow recording:', error);
1087
+ logger.error('Error deleting workflow recording:', error);
1084
1088
  res.status(500).json({ error: 'Failed to delete workflow recording', details: error.message });
1085
1089
  }
1086
1090
  });
@@ -1096,33 +1100,33 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1096
1100
  const { interactions } = req.body;
1097
1101
 
1098
1102
  // Debug logging to identify validation issues
1099
- console.error('[SCREEN-INTERACTIONS-DEBUG] ==========================================');
1100
- console.error('[SCREEN-INTERACTIONS-DEBUG] Request received for session:', sessionId);
1101
- console.error('[SCREEN-INTERACTIONS-DEBUG] Content-Type:', req.get('content-type'));
1102
- console.error('[SCREEN-INTERACTIONS-DEBUG] User agent:', req.get('user-agent'));
1103
- console.error('[SCREEN-INTERACTIONS-DEBUG] Auth user:', req.user?.name || 'unknown');
1104
- console.error('[SCREEN-INTERACTIONS-DEBUG] Request body keys:', Object.keys(req.body || {}));
1105
- console.error('[SCREEN-INTERACTIONS-DEBUG] Interactions field present:', !!interactions);
1106
- console.error('[SCREEN-INTERACTIONS-DEBUG] Interactions type:', typeof interactions);
1107
- console.error('[SCREEN-INTERACTIONS-DEBUG] Interactions length:', Array.isArray(interactions) ? interactions.length : 'N/A');
1103
+ logger.debug(' ==========================================');
1104
+ logger.debug(' Request received for session:', sessionId);
1105
+ logger.debug(' Content-Type:', req.get('content-type'));
1106
+ logger.debug(' User agent:', req.get('user-agent'));
1107
+ logger.debug(' Auth user:', req.user?.name || 'unknown');
1108
+ logger.debug(' Request body keys:', Object.keys(req.body || {}));
1109
+ logger.debug(' Interactions field present:', !!interactions);
1110
+ logger.debug(' Interactions type:', typeof interactions);
1111
+ logger.debug(' Interactions length:', Array.isArray(interactions) ? interactions.length : 'N/A');
1108
1112
  if (Array.isArray(interactions) && interactions.length > 0) {
1109
- console.error('[SCREEN-INTERACTIONS-DEBUG] First interaction:', JSON.stringify(interactions[0], null, 2));
1113
+ logger.debug(' First interaction:', JSON.stringify(interactions[0], null, 2));
1110
1114
  }
1111
- console.error('[SCREEN-INTERACTIONS-DEBUG] Raw body:', JSON.stringify(req.body, null, 2));
1112
- console.error('[SCREEN-INTERACTIONS-DEBUG] ==========================================');
1115
+ logger.debug(' Raw body:', JSON.stringify(req.body, null, 2));
1116
+ logger.debug(' ==========================================');
1113
1117
 
1114
1118
  try {
1115
1119
  const { database } = await import('./database.js');
1116
1120
  const result = database.storeScreenInteractions(sessionId, interactions);
1117
1121
 
1118
1122
  if (result.success) {
1119
- console.error(`[HTTP] Stored ${result.count} screen interactions for recording ${sessionId}`);
1123
+ logger.info(` Stored ${result.count} screen interactions for recording ${sessionId}`);
1120
1124
  res.json({ success: true, count: result.count });
1121
1125
  } else {
1122
1126
  res.status(500).json({ success: false, error: result.error });
1123
1127
  }
1124
1128
  } catch (error) {
1125
- console.error('Error storing screen interactions:', error);
1129
+ logger.error('Error storing screen interactions:', error);
1126
1130
  res.status(500).json({ error: error.message });
1127
1131
  }
1128
1132
  });
@@ -1131,14 +1135,14 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1131
1135
  app.post('/chromedebug/debug-interactions/:sessionId',
1132
1136
  authenticate,
1133
1137
  (req, res) => {
1134
- console.error('[DEBUG-ENDPOINT] ==========================================');
1135
- console.error('[DEBUG-ENDPOINT] Auth bypass test successful!');
1136
- console.error('[DEBUG-ENDPOINT] User:', JSON.stringify(req.user, null, 2));
1137
- console.error('[DEBUG-ENDPOINT] Session ID:', req.params.sessionId);
1138
- console.error('[DEBUG-ENDPOINT] Content-Type:', req.get('content-type'));
1139
- console.error('[DEBUG-ENDPOINT] Request headers:', Object.keys(req.headers));
1140
- console.error('[DEBUG-ENDPOINT] Request body:', JSON.stringify(req.body, null, 2));
1141
- console.error('[DEBUG-ENDPOINT] ==========================================');
1138
+ logger.debug(' ==========================================');
1139
+ logger.debug(' Auth bypass test successful!');
1140
+ logger.debug(' User:', JSON.stringify(req.user, null, 2));
1141
+ logger.debug(' Session ID:', req.params.sessionId);
1142
+ logger.debug(' Content-Type:', req.get('content-type'));
1143
+ logger.debug(' Request headers:', Object.keys(req.headers));
1144
+ logger.debug(' Request body:', JSON.stringify(req.body, null, 2));
1145
+ logger.debug(' ==========================================');
1142
1146
 
1143
1147
  res.json({
1144
1148
  debug: true,
@@ -1163,7 +1167,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1163
1167
  const interactions = database.getScreenInteractions(sessionId);
1164
1168
  res.json({ interactions });
1165
1169
  } catch (error) {
1166
- console.error('Error retrieving screen interactions:', error);
1170
+ logger.error('Error retrieving screen interactions:', error);
1167
1171
  res.status(500).json({ error: error.message });
1168
1172
  }
1169
1173
  });
@@ -1185,7 +1189,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1185
1189
 
1186
1190
  res.json(result);
1187
1191
  } catch (error) {
1188
- console.error('License validation error:', error);
1192
+ logger.error('License validation error:', error);
1189
1193
  res.status(500).json({ error: 'License validation failed' });
1190
1194
  }
1191
1195
  });
@@ -1202,7 +1206,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1202
1206
 
1203
1207
  res.json(result);
1204
1208
  } catch (error) {
1205
- console.error('Usage check error:', error);
1209
+ logger.error('Usage check error:', error);
1206
1210
  res.status(500).json({ error: 'Usage check failed' });
1207
1211
  }
1208
1212
  });
@@ -1220,7 +1224,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1220
1224
 
1221
1225
  res.json(result);
1222
1226
  } catch (error) {
1223
- console.error('Usage increment error:', error);
1227
+ logger.error('Usage increment error:', error);
1224
1228
  res.status(500).json({ error: 'Usage increment failed' });
1225
1229
  }
1226
1230
  });
@@ -1237,7 +1241,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1237
1241
 
1238
1242
  res.json(result);
1239
1243
  } catch (error) {
1240
- console.error('License info error:', error);
1244
+ logger.error('License info error:', error);
1241
1245
  res.status(500).json({ error: 'Failed to get license info' });
1242
1246
  }
1243
1247
  });
@@ -1258,7 +1262,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1258
1262
 
1259
1263
  res.json({ instances });
1260
1264
  } catch (error) {
1261
- console.error('List instances error:', error);
1265
+ logger.error('List instances error:', error);
1262
1266
  res.status(500).json({ error: 'Failed to list instances' });
1263
1267
  }
1264
1268
  });
@@ -1279,7 +1283,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1279
1283
 
1280
1284
  res.json(result);
1281
1285
  } catch (error) {
1282
- console.error('Deactivate instance error:', error);
1286
+ logger.info('Deactivate instance error:', error);
1283
1287
  res.status(500).json({ error: 'Failed to deactivate instance' });
1284
1288
  }
1285
1289
  });
@@ -1294,7 +1298,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1294
1298
 
1295
1299
  res.json(status);
1296
1300
  } catch (error) {
1297
- console.error('Cache status error:', error);
1301
+ logger.info('Cache status error:', error);
1298
1302
  res.status(500).json({ error: 'Failed to get cache status' });
1299
1303
  }
1300
1304
  });
@@ -1309,14 +1313,14 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1309
1313
 
1310
1314
  res.json({ success: true, message: 'License cache cleared' });
1311
1315
  } catch (error) {
1312
- console.error('Clear cache error:', error);
1316
+ logger.info('Clear cache error:', error);
1313
1317
  res.status(500).json({ error: 'Failed to clear cache' });
1314
1318
  }
1315
1319
  });
1316
1320
 
1317
1321
  // Add a catch-all route for debugging
1318
1322
  app.use('*', (req, res, next) => {
1319
- console.error(`[HTTP] ${req.method} ${req.originalUrl}`);
1323
+ logger.info(` ${req.method} ${req.originalUrl}`);
1320
1324
  next();
1321
1325
  });
1322
1326
 
@@ -1325,7 +1329,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1325
1329
 
1326
1330
  // 404 handler - must be last
1327
1331
  app.use((req, res) => {
1328
- console.error(`[HTTP] 404 - Route not found: ${req.method} ${req.originalUrl}`);
1332
+ logger.info(` 404 - Route not found: ${req.method} ${req.originalUrl}`);
1329
1333
  res.status(404).json({
1330
1334
  success: false,
1331
1335
  error: 'Endpoint not found',
@@ -1353,7 +1357,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1353
1357
  if (targetPort) {
1354
1358
  // Use the target port provided by session manager
1355
1359
  portsToTry = [targetPort];
1356
- console.error(`Attempting to start HTTP server on session manager allocated port: ${targetPort}`);
1360
+ logger.info(`Attempting to start HTTP server on session manager allocated port: ${targetPort}`);
1357
1361
  } else {
1358
1362
  // Fall back to configured ports
1359
1363
  const configuredPorts = configLoader.getHttpServerPorts();
@@ -1364,7 +1368,7 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1364
1368
  ? [startPort, ...configuredPorts.filter(p => p !== startPort)]
1365
1369
  : configuredPorts;
1366
1370
 
1367
- console.error('Attempting to start HTTP server on configured ports:', portsToTry);
1371
+ logger.info('Attempting to start HTTP server on configured ports:', portsToTry);
1368
1372
  }
1369
1373
 
1370
1374
  // Try configured ports in order
@@ -1373,14 +1377,14 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1373
1377
  await new Promise((resolve, reject) => {
1374
1378
  server = app.listen(port, () => {
1375
1379
  actualPort = port;
1376
- console.error(`HTTP server listening on port ${actualPort}`);
1380
+ logger.info(`HTTP server listening on port ${actualPort}`);
1377
1381
  resolve();
1378
1382
  }).on('error', reject);
1379
1383
  });
1380
1384
  break;
1381
1385
  } catch (error) {
1382
1386
  if (error.code === 'EADDRINUSE') {
1383
- console.error(`Port ${port} is in use, trying next...`);
1387
+ logger.info(`Port ${port} is in use, trying next...`);
1384
1388
  continue;
1385
1389
  }
1386
1390
  throw error;
@@ -1388,12 +1392,12 @@ async function startHttpServer(chromeController = null, targetPort = null) {
1388
1392
  }
1389
1393
 
1390
1394
  if (!server || !actualPort) {
1391
- console.error('ERROR: All configured ports are in use:', portsToTry);
1392
- console.error('Please free up one of these ports or configure different ports');
1395
+ logger.info('ERROR: All configured ports are in use:', portsToTry);
1396
+ logger.info('Please free up one of these ports or configure different ports');
1393
1397
  throw new Error(`Could not find available port for HTTP server. All ports in use: ${portsToTry.join(', ')}`);
1394
1398
  }
1395
1399
 
1396
- console.error(`HTTP server started on port ${actualPort}`);
1400
+ logger.info(`HTTP server started on port ${actualPort}`);
1397
1401
 
1398
1402
  // Write port file for discovery
1399
1403
  writePortFile(actualPort);
@@ -1419,7 +1423,7 @@ async function startWebSocketServer() {
1419
1423
  const wss = new WebSocketServer({ port: wsPort });
1420
1424
 
1421
1425
  wss.on('connection', async (ws) => {
1422
- console.error(`WebSocket client connected`);
1426
+ logger.info(`WebSocket client connected`);
1423
1427
  wsClients.add(ws);
1424
1428
 
1425
1429
  // Send initial status
@@ -1459,10 +1463,10 @@ async function startWebSocketServer() {
1459
1463
  break;
1460
1464
 
1461
1465
  default:
1462
- console.error(`Unknown WebSocket message type: ${msg.type}`);
1466
+ logger.info(`Unknown WebSocket message type: ${msg.type}`);
1463
1467
  }
1464
1468
  } catch (error) {
1465
- console.error('WebSocket message error:', error);
1469
+ logger.info('WebSocket message error:', error);
1466
1470
  ws.send(JSON.stringify({
1467
1471
  type: 'error',
1468
1472
  data: { message: error.message }
@@ -1471,17 +1475,17 @@ async function startWebSocketServer() {
1471
1475
  });
1472
1476
 
1473
1477
  ws.on('close', () => {
1474
- console.error('WebSocket client disconnected');
1478
+ logger.info('WebSocket client disconnected');
1475
1479
  wsClients.delete(ws);
1476
1480
  });
1477
1481
 
1478
1482
  ws.on('error', (error) => {
1479
- console.error('WebSocket error:', error);
1483
+ logger.info('WebSocket error:', error);
1480
1484
  wsClients.delete(ws);
1481
1485
  });
1482
1486
  });
1483
1487
 
1484
- console.error(`WebSocket server listening on port ${wsPort}`);
1488
+ logger.info(`WebSocket server listening on port ${wsPort}`);
1485
1489
  }
1486
1490
 
1487
1491
  // Export for use by MCP server
@@ -1499,12 +1503,12 @@ if (import.meta.url === `file://${process.argv[1]}`) {
1499
1503
  // Start WebSocket server for Chrome extension
1500
1504
  await startWebSocketServer();
1501
1505
 
1502
- console.error('ChromeDebug MCP HTTP server running with WebSocket support');
1506
+ logger.info('ChromeDebug MCP HTTP server running with WebSocket support');
1503
1507
  }
1504
1508
 
1505
1509
  // Clean shutdown
1506
1510
  const cleanup = async (signal) => {
1507
- console.error(`Received ${signal}, shutting down HTTP server...`);
1511
+ logger.info(`Received ${signal}, shutting down HTTP server...`);
1508
1512
  await activeController.close();
1509
1513
  process.exit(0);
1510
1514
  };
@@ -1512,5 +1516,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
1512
1516
  process.on('SIGINT', () => cleanup('SIGINT'));
1513
1517
  process.on('SIGTERM', () => cleanup('SIGTERM'));
1514
1518
 
1515
- main().catch(console.error);
1519
+ main().catch((err) => logger.error(err, "Fatal error in HTTP server"));
1516
1520
  }
package/src/index.js CHANGED
@@ -17,6 +17,11 @@ import {
17
17
  findActiveSessions
18
18
  } from './services/unified-session-manager.js';
19
19
  import logger from './utils/logger.js';
20
+ import { interceptStdout } from './logger.js';
21
+
22
+ // CRITICAL: Intercept stdout FIRST to prevent MCP JSON-RPC pollution
23
+ // This must happen before any other code runs
24
+ interceptStdout();
20
25
 
21
26
  /**
22
27
  * Main application class that orchestrates all components
package/src/logger.js CHANGED
@@ -1,11 +1,102 @@
1
- // Simple conditional logger for Chrome Debug
2
- // All logs go to stderr to avoid interfering with MCP stdio communication
3
- const isDev = process.env.NODE_ENV === 'development';
4
-
5
- module.exports = {
6
- log: (...args) => { if (isDev) console.error(...args); },
7
- error: (...args) => console.error(...args),
8
- warn: (...args) => { if (isDev) console.error(...args); },
9
- info: (...args) => { if (isDev) console.error(...args); },
10
- debug: (...args) => { if (isDev) console.error(...args); }
11
- };
1
+ /**
2
+ * Structured logging for ChromeDebug MCP
3
+ * Uses pino for performance and structured output
4
+ * All logs go to stderr to preserve MCP stdout for JSON-RPC
5
+ */
6
+
7
+ import pino from 'pino';
8
+
9
+ // Determine log level based on environment
10
+ const getLogLevel = () => {
11
+ if (process.env.LOG_LEVEL) {
12
+ return process.env.LOG_LEVEL;
13
+ }
14
+ if (process.env.DEBUG === 'true' || process.env.DEBUG === '1') {
15
+ return 'debug';
16
+ }
17
+ if (process.env.NODE_ENV === 'production') {
18
+ return 'warn';
19
+ }
20
+ return 'info'; // Default for development
21
+ };
22
+
23
+ // Create base pino instance
24
+ const baseLogger = pino({
25
+ level: getLogLevel(),
26
+ transport: {
27
+ target: 'pino-pretty',
28
+ options: {
29
+ destination: 2, // stderr
30
+ colorize: true,
31
+ ignore: 'pid,hostname',
32
+ translateTime: 'HH:MM:ss',
33
+ singleLine: false
34
+ }
35
+ }
36
+ });
37
+
38
+ /**
39
+ * Create a child logger with component context
40
+ * @param {string} component - Component name for log context
41
+ * @returns {pino.Logger} Configured pino logger
42
+ */
43
+ export function createLogger(component) {
44
+ return baseLogger.child({ component });
45
+ }
46
+
47
+ /**
48
+ * HTTP request logger helper
49
+ * Only logs if DEBUG_HTTP is enabled or in debug level
50
+ * @param {pino.Logger} logger - Pino logger instance
51
+ * @param {object} req - Express request object
52
+ * @param {object} options - Logging options
53
+ */
54
+ export function logHttpRequest(logger, req, options = {}) {
55
+ const shouldLog = process.env.DEBUG_HTTP === 'true' ||
56
+ process.env.DEBUG === 'true' ||
57
+ logger.level === 'debug';
58
+
59
+ if (!shouldLog) return;
60
+
61
+ const requestData = {
62
+ method: req.method,
63
+ url: req.originalUrl || req.url,
64
+ ip: req.ip,
65
+ userAgent: req.get('user-agent')
66
+ };
67
+
68
+ if (options.verbose) {
69
+ requestData.headers = req.headers;
70
+ requestData.contentType = req.get('content-type');
71
+ requestData.contentLength = req.get('content-length');
72
+ }
73
+
74
+ logger.debug(requestData, 'HTTP Request');
75
+ }
76
+
77
+ /**
78
+ * Intercept stdout to prevent MCP JSON-RPC pollution
79
+ * Call this at application startup for MCP servers
80
+ */
81
+ export function interceptStdout() {
82
+ const originalWrite = process.stdout.write;
83
+
84
+ process.stdout.write = function(chunk, encoding, callback) {
85
+ // Redirect all stdout to stderr with a warning prefix
86
+ const message = chunk.toString();
87
+ const redirectedMessage = `[STDOUT-REDIRECT] ${message}`;
88
+
89
+ return process.stderr.write(redirectedMessage, encoding, callback);
90
+ };
91
+
92
+ baseLogger.info('Stdout interception enabled - all output redirected to stderr for MCP safety');
93
+ }
94
+
95
+ // Default export for backward compatibility
96
+ export default {
97
+ error: (...args) => baseLogger.error(...args),
98
+ warn: (...args) => baseLogger.warn(...args),
99
+ info: (...args) => baseLogger.info(...args),
100
+ debug: (...args) => baseLogger.debug(...args),
101
+ log: (...args) => baseLogger.info(...args)
102
+ };
@@ -5,6 +5,10 @@
5
5
 
6
6
  import { startHttpServer, startWebSocketServer } from './http-server.js';
7
7
  import { removePortFile } from './port-discovery.js';
8
+ import { interceptStdout } from './logger.js';
9
+
10
+ // CRITICAL: Intercept stdout FIRST to prevent MCP JSON-RPC pollution
11
+ interceptStdout();
8
12
 
9
13
  console.error('╔════════════════════════════════════════════════════════════════╗');
10
14
  console.error('║ Chrome Debug HTTP Server ║');