@dynamicu/chromedebug-mcp 2.6.6 → 2.7.0

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.
Files changed (46) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +1 -1
  3. package/chrome-extension/activation-manager.js +18 -4
  4. package/chrome-extension/background.js +1044 -552
  5. package/chrome-extension/browser-recording-manager.js +256 -0
  6. package/chrome-extension/chrome-debug-logger.js +168 -0
  7. package/chrome-extension/console-interception-library.js +430 -0
  8. package/chrome-extension/content.css +16 -16
  9. package/chrome-extension/content.js +617 -215
  10. package/chrome-extension/data-buffer.js +206 -17
  11. package/chrome-extension/extension-config.js +1 -1
  12. package/chrome-extension/frame-capture.js +52 -15
  13. package/chrome-extension/license-helper.js +26 -0
  14. package/chrome-extension/manifest.free.json +3 -6
  15. package/chrome-extension/options.js +1 -1
  16. package/chrome-extension/popup.html +315 -181
  17. package/chrome-extension/popup.js +673 -526
  18. package/chrome-extension/pro/enhanced-capture.js +406 -0
  19. package/chrome-extension/pro/frame-editor.html +410 -0
  20. package/chrome-extension/pro/frame-editor.js +1496 -0
  21. package/chrome-extension/pro/function-tracker.js +843 -0
  22. package/chrome-extension/pro/jszip.min.js +13 -0
  23. package/config/chromedebug-config.json +101 -0
  24. package/dist/chromedebug-extension-free.zip +0 -0
  25. package/package.json +3 -1
  26. package/scripts/package-pro-extension.js +1 -1
  27. package/scripts/webpack.config.free.cjs +11 -8
  28. package/scripts/webpack.config.pro.cjs +5 -0
  29. package/src/chrome-controller.js +7 -7
  30. package/src/cli.js +2 -2
  31. package/src/database.js +61 -9
  32. package/src/http-server.js +3 -2
  33. package/src/index.js +9 -6
  34. package/src/mcp/server.js +2 -2
  35. package/src/services/process-manager.js +10 -6
  36. package/src/services/process-tracker.js +10 -5
  37. package/src/services/profile-manager.js +17 -2
  38. package/src/validation/schemas.js +36 -6
  39. package/src/index-direct.js +0 -157
  40. package/src/index-modular.js +0 -219
  41. package/src/index-monolithic-backup.js +0 -2230
  42. package/src/legacy/chrome-controller-old.js +0 -1406
  43. package/src/legacy/index-express.js +0 -625
  44. package/src/legacy/index-old.js +0 -977
  45. package/src/legacy/routes.js +0 -260
  46. package/src/legacy/shared-storage.js +0 -101
package/src/database.js CHANGED
@@ -12,7 +12,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  // Initialize project manager for path resolution
13
13
  const projectManager = new ProjectManager();
14
14
 
15
- class ChromePilotDatabase {
15
+ class ChromeDebugDatabase {
16
16
  constructor(options = {}) {
17
17
  this.db = null;
18
18
  this.initialized = false;
@@ -27,10 +27,54 @@ class ChromePilotDatabase {
27
27
 
28
28
  /**
29
29
  * Gets the centralized database path in user's home directory
30
+ * Performs atomic migration from old chrome-pilot.db to new chromedebug.db
30
31
  * @returns {string} Centralized database file path
31
32
  */
32
33
  getCentralDbPath() {
33
- return path.join(os.homedir(), '.chromedebug', 'chrome-pilot.db');
34
+ const dbDir = path.join(os.homedir(), '.chromedebug');
35
+ const newPath = path.join(dbDir, 'chromedebug.db');
36
+ const oldPath = path.join(dbDir, 'chrome-pilot.db');
37
+ const lockFile = path.join(dbDir, '.migration.lock');
38
+
39
+ // Ensure directory exists
40
+ if (!fs.existsSync(dbDir)) {
41
+ fs.mkdirSync(dbDir, { recursive: true });
42
+ }
43
+
44
+ // Check if migration is needed
45
+ if (fs.existsSync(oldPath) && !fs.existsSync(newPath)) {
46
+ // Try to acquire lock (simple file-based lock)
47
+ try {
48
+ if (!fs.existsSync(lockFile)) {
49
+ fs.writeFileSync(lockFile, process.pid.toString());
50
+ logger.info('[Database] Migrating chrome-pilot.db to chromedebug.db');
51
+ fs.renameSync(oldPath, newPath);
52
+ fs.unlinkSync(lockFile);
53
+ logger.info('[Database] Migration complete');
54
+ } else {
55
+ // Wait for other process to finish migration
56
+ let attempts = 0;
57
+ while (fs.existsSync(lockFile) && attempts < 10) {
58
+ // Sleep for 100ms (blocking - acceptable for startup migration)
59
+ const start = Date.now();
60
+ while (Date.now() - start < 100) { /* busy wait */ }
61
+ attempts++;
62
+ }
63
+ // Clean up stale lock if needed
64
+ if (fs.existsSync(lockFile) && !fs.existsSync(newPath)) {
65
+ try { fs.unlinkSync(lockFile); } catch {}
66
+ }
67
+ }
68
+ } catch (error) {
69
+ logger.error('[Database] Migration failed:', error);
70
+ // Clean up lock file if we created it
71
+ if (fs.existsSync(lockFile)) {
72
+ try { fs.unlinkSync(lockFile); } catch {}
73
+ }
74
+ }
75
+ }
76
+
77
+ return newPath;
34
78
  }
35
79
 
36
80
  /**
@@ -1366,11 +1410,12 @@ class ChromePilotDatabase {
1366
1410
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
1367
1411
  `);
1368
1412
 
1369
- const recordingId = `workflow_${sessionId}`;
1413
+ // sessionId already contains workflow_ prefix from extension, use it directly
1414
+ const recordingId = sessionId;
1370
1415
  const timestamp = Date.now();
1371
-
1416
+
1372
1417
  stmt.run(
1373
- recordingId,
1418
+ recordingId,
1374
1419
  sessionId,
1375
1420
  name,
1376
1421
  url,
@@ -1414,6 +1459,8 @@ class ChromePilotDatabase {
1414
1459
  return action[camelCaseName] || action[snakeCaseName] || null;
1415
1460
  };
1416
1461
 
1462
+ const screenshotDataToStore = action.screenshot_data || null;
1463
+
1417
1464
  stmt.run(
1418
1465
  workflowId,
1419
1466
  action.index,
@@ -1425,7 +1472,7 @@ class ChromePilotDatabase {
1425
1472
  action.text || null,
1426
1473
  action.placeholder || null,
1427
1474
  action.timestamp,
1428
- action.screenshot_data || null,
1475
+ screenshotDataToStore,
1429
1476
  action.component || null, // For function traces
1430
1477
  action.args ? JSON.stringify(action.args) : null, // Store args as JSON string
1431
1478
  action.stack || null, // For function traces
@@ -1436,6 +1483,7 @@ class ChromePilotDatabase {
1436
1483
  safeJsonStringify(getEnhancedFieldData(action, 'elementState', 'element_state'), 2048), // 2KB limit
1437
1484
  safeJsonStringify(getEnhancedFieldData(action, 'performanceMetrics', 'performance_metrics'), 1024) // 1KB limit
1438
1485
  );
1486
+
1439
1487
  storedCount++;
1440
1488
  }
1441
1489
 
@@ -1452,6 +1500,7 @@ class ChromePilotDatabase {
1452
1500
  `);
1453
1501
  updateStmt.run(storedCount, workflowId);
1454
1502
 
1503
+ logger.debug('[SCREENSHOT-DEBUG] database.storeWorkflowActions - EXIT');
1455
1504
  return { success: true, storedCount };
1456
1505
  }
1457
1506
 
@@ -1560,7 +1609,7 @@ class ChromePilotDatabase {
1560
1609
  text: action.text,
1561
1610
  placeholder: action.placeholder,
1562
1611
  timestamp: action.timestamp,
1563
- screenshot_data: action.screenshot_data
1612
+ screenshot: action.screenshot_data // Map screenshot_data to screenshot for consistency
1564
1613
  };
1565
1614
 
1566
1615
  // Add function trace fields if they exist
@@ -2218,7 +2267,10 @@ class ChromePilotDatabase {
2218
2267
  }
2219
2268
 
2220
2269
  // Export singleton instance (uses default centralized path)
2221
- export const database = new ChromePilotDatabase();
2270
+ export const database = new ChromeDebugDatabase();
2222
2271
 
2223
2272
  // Export class for custom instantiation if needed
2224
- export { ChromePilotDatabase };
2273
+ export { ChromeDebugDatabase };
2274
+
2275
+ // Backward compatibility alias
2276
+ export { ChromeDebugDatabase as ChromePilotDatabase };
@@ -286,18 +286,19 @@ async function startHttpServer(chromeController = null, targetPort = null) {
286
286
  });
287
287
 
288
288
  // Protected workflow recording endpoints
289
- app.post('/chromedebug/workflow-recording',
289
+ app.post('/chromedebug/workflow-recording',
290
290
  authenticate,
291
291
  authorize(PERMISSIONS.WORKFLOW_WRITE),
292
292
  createValidator(workflowRecordingSchema),
293
293
  async (req, res) => {
294
294
  const { sessionId, url, title, includeLogs, actions, logs, functionTraces, name, screenshotSettings } = req.body;
295
+
295
296
  logger.info('[HTTP Server] Received workflow recording with name:', name);
296
297
  logger.info('[HTTP Server] Function traces count:', functionTraces ? functionTraces.length : 0);
297
298
  if (!sessionId || !actions) {
298
299
  return res.status(400).json({ error: 'sessionId and actions are required' });
299
300
  }
300
-
301
+
301
302
  try {
302
303
  const result = await activeController.storeWorkflowRecording(sessionId, url, title, includeLogs, actions, logs, name, screenshotSettings, functionTraces);
303
304
  res.json(result);
package/src/index.js CHANGED
@@ -26,7 +26,7 @@ interceptStdout();
26
26
  /**
27
27
  * Main application class that orchestrates all components
28
28
  */
29
- class ChromePilotApp {
29
+ class ChromeDebugApp {
30
30
  constructor() {
31
31
  this.chromeController = null;
32
32
  this.chromeService = null;
@@ -105,7 +105,7 @@ class ChromePilotApp {
105
105
 
106
106
  try {
107
107
  // Clean up any existing Chrome Debug processes
108
- const cleanupResult = await this.processManager.cleanupChromePilotProcesses();
108
+ const cleanupResult = await this.processManager.cleanupChromeDebugProcesses();
109
109
  if (cleanupResult.total > 0) {
110
110
  logger.debug(`Cleaned up ${cleanupResult.killed.length} existing processes`);
111
111
  }
@@ -242,7 +242,7 @@ Options:
242
242
  --watch Watch for file changes (development mode)
243
243
  --debug Enable debug logging
244
244
  --session-id ID Set custom session ID for resource isolation
245
- --db-path PATH Set custom database file path (default: ~/.chromedebug/chrome-pilot.db)
245
+ --db-path PATH Set custom database file path (default: ~/.chromedebug/chromedebug.db)
246
246
  --no-cleanup Skip cleanup of dead processes on startup
247
247
  --verbose Enable verbose logging including session info
248
248
  --allow-multiple Allow multiple ChromeDebug instances (default: kill others on startup)
@@ -255,7 +255,7 @@ Session Isolation:
255
255
  without interfering with each other.
256
256
 
257
257
  Database Location:
258
- By default, ChromeDebug uses a centralized database at ~/.chromedebug/chrome-pilot.db
258
+ By default, ChromeDebug uses a centralized database at ~/.chromedebug/chromedebug.db
259
259
  This allows recordings from all projects to be accessible in one place.
260
260
  Use --db-path to override, or set CHROMEDEBUG_DB_PATH environment variable.
261
261
 
@@ -296,11 +296,14 @@ For more information, see the documentation in CLAUDE.md
296
296
  }
297
297
 
298
298
  // Export for library usage and testing
299
- export { ChromePilotApp };
299
+ export { ChromeDebugApp };
300
+
301
+ // Backward compatibility alias
302
+ export { ChromeDebugApp as ChromePilotApp };
300
303
 
301
304
  // Auto-start when run directly (not imported as a module)
302
305
  if (import.meta.url === `file://${process.argv[1]}`) {
303
- const app = new ChromePilotApp();
306
+ const app = new ChromeDebugApp();
304
307
 
305
308
  // Handle graceful shutdown
306
309
  process.on('SIGINT', async () => {
package/src/mcp/server.js CHANGED
@@ -20,8 +20,8 @@ export class MCPServer {
20
20
 
21
21
  this.server = new Server(
22
22
  {
23
- name: 'chrome-pilot',
24
- version: '1.0.0',
23
+ name: 'chromedebug',
24
+ version: '2.6.7',
25
25
  },
26
26
  {
27
27
  capabilities: {
@@ -62,7 +62,7 @@ async function safeKillProcess(pid, signal = 'SIGTERM') {
62
62
  * Process cleanup is skipped on Windows to allow the server to start. A proper cross-platform
63
63
  * solution will be implemented in a future update.
64
64
  */
65
- async function findChromePilotProcesses() {
65
+ async function findChromeDebugProcesses() {
66
66
  // Skip process cleanup on Windows - ps command doesn't exist
67
67
  // This allows the server to start on Windows without crashing
68
68
  // TODO: Implement proper Windows process management using tasklist or Node.js process handles
@@ -107,6 +107,7 @@ async function findChromePilotProcesses() {
107
107
  if (!pid || pid === currentPid) continue;
108
108
 
109
109
  // Check if it's a ChromeDebug MCP related process
110
+ // Look for both new and old process names for backward compatibility
110
111
  let processType = '';
111
112
  if (line.includes('src/index.js')) {
112
113
  processType = 'MCP server';
@@ -117,7 +118,7 @@ async function findChromePilotProcesses() {
117
118
  } else if (line.includes('http-server.js')) {
118
119
  processType = 'HTTP server';
119
120
  pidsToKill.push(pid);
120
- } else if (line.includes('chrome-pilot') && line.includes('node')) {
121
+ } else if ((line.includes('chromedebug') || line.includes('chrome-pilot') || line.includes('ChromePilot')) && line.includes('node')) {
121
122
  processType = 'ChromeDebug MCP process';
122
123
  pidsToKill.push(pid);
123
124
  }
@@ -221,8 +222,8 @@ export class ProcessManager {
221
222
  * Finds and kills ChromeDebug MCP processes
222
223
  * @returns {Promise<Object>} Cleanup results
223
224
  */
224
- async cleanupChromePilotProcesses() {
225
- const { pidsToKill, processDescriptions } = await findChromePilotProcesses();
225
+ async cleanupChromeDebugProcesses() {
226
+ const { pidsToKill, processDescriptions } = await findChromeDebugProcesses();
226
227
 
227
228
  const results = {
228
229
  found: processDescriptions,
@@ -326,5 +327,8 @@ export class ProcessManager {
326
327
  }
327
328
  }
328
329
 
329
- // Export utility functions for backward compatibility
330
- export { validateProcessId, safeKillProcess, findChromePilotProcesses };
330
+ // Export utility functions
331
+ export { validateProcessId, safeKillProcess, findChromeDebugProcesses };
332
+
333
+ // Backward compatibility aliases
334
+ export { findChromeDebugProcesses as findChromePilotProcesses };
@@ -306,7 +306,7 @@ export async function killAllRegisteredProcesses() {
306
306
  * Find ChromeDebug processes using pattern matching (for untracked processes)
307
307
  * This is a fallback for processes started before tracking was implemented
308
308
  */
309
- export async function findUntrackedChromePilotProcesses() {
309
+ export async function findUntrackedChromeDebugProcesses() {
310
310
  const currentPid = process.pid;
311
311
  const foundProcesses = [];
312
312
 
@@ -342,6 +342,7 @@ export async function findUntrackedChromePilotProcesses() {
342
342
  if (registeredPids.has(pid)) continue;
343
343
 
344
344
  // Check if it's a ChromeDebug/ChromePilot process
345
+ // Look for both new and old process names for backward compatibility
345
346
  let processType = '';
346
347
  if (line.includes('src/index.js')) {
347
348
  processType = 'untracked-mcp-server';
@@ -349,7 +350,7 @@ export async function findUntrackedChromePilotProcesses() {
349
350
  processType = 'untracked-http-server';
350
351
  } else if (line.includes('standalone-server.js')) {
351
352
  processType = 'untracked-standalone-server';
352
- } else if (line.includes('chromedebug-mcp') && line.includes('node')) {
353
+ } else if ((line.includes('chromedebug-mcp') || line.includes('chrome-pilot') || line.includes('ChromePilot')) && line.includes('node')) {
353
354
  processType = 'untracked-chromedebug-process';
354
355
  }
355
356
 
@@ -380,8 +381,8 @@ export async function findUntrackedChromePilotProcesses() {
380
381
  /**
381
382
  * Kill untracked ChromeDebug processes (emergency cleanup)
382
383
  */
383
- export async function killUntrackedChromePilotProcesses() {
384
- const untrackedProcesses = await findUntrackedChromePilotProcesses();
384
+ export async function killUntrackedChromeDebugProcesses() {
385
+ const untrackedProcesses = await findUntrackedChromeDebugProcesses();
385
386
  const results = {
386
387
  found: untrackedProcesses,
387
388
  killed: [],
@@ -571,4 +572,8 @@ export async function cleanupCurrentSession() {
571
572
  }
572
573
 
573
574
  return results;
574
- }
575
+ }
576
+
577
+ // Backward compatibility aliases
578
+ export { findUntrackedChromeDebugProcesses as findUntrackedChromePilotProcesses };
579
+ export { killUntrackedChromeDebugProcesses as killUntrackedChromePilotProcesses };
@@ -13,14 +13,29 @@
13
13
  * - Profile lifecycle management with proper resource tracking
14
14
  */
15
15
 
16
- import { promises as fs } from 'fs';
16
+ import { promises as fs, existsSync } from 'fs';
17
17
  import path from 'path';
18
18
  import os from 'os';
19
19
  import { randomUUID } from 'crypto';
20
20
 
21
21
  export class ProfileManager {
22
22
  constructor(options = {}) {
23
- this.profilesDir = options.profilesDir || path.join(os.tmpdir(), 'chrome-pilot-profiles');
23
+ // Use new directory name, but check old directory for migration
24
+ const defaultDir = path.join(os.tmpdir(), 'chromedebug-profiles');
25
+ const oldDir = path.join(os.tmpdir(), 'chrome-pilot-profiles');
26
+
27
+ // If old directory exists and new doesn't, use old directory for compatibility
28
+ let profilesDir = defaultDir;
29
+ try {
30
+ if (!options.profilesDir && existsSync(oldDir) && !existsSync(defaultDir)) {
31
+ profilesDir = oldDir;
32
+ console.error(`[ProfileManager] Using existing chrome-pilot-profiles directory for backward compatibility`);
33
+ }
34
+ } catch (error) {
35
+ // Ignore errors, use default
36
+ }
37
+
38
+ this.profilesDir = options.profilesDir || profilesDir;
24
39
  this.activeProfiles = new Map(); // sessionId -> { profilePath, claudeInstanceId, createdAt, size }
25
40
  this.initialized = false;
26
41
 
@@ -20,8 +20,35 @@ export const workflowRecordingSchema = Joi.object({
20
20
  Joi.object({
21
21
  type: Joi.string().required(),
22
22
  timestamp: Joi.number().required(),
23
- data: Joi.object().optional()
24
- })
23
+ data: Joi.object().optional(),
24
+ // Screenshot field - base64 encoded image data
25
+ screenshot_data: Joi.string().max(10485760).optional(), // 10MB max for base64 string
26
+ // Click/interaction fields
27
+ selector: Joi.string().optional(),
28
+ x: Joi.number().optional(),
29
+ y: Joi.number().optional(),
30
+ value: Joi.string().allow('').optional(),
31
+ text: Joi.string().allow('').optional(),
32
+ placeholder: Joi.string().optional(),
33
+ index: Joi.number().optional(),
34
+ // Enhanced click tracking fields
35
+ element_html: Joi.string().max(10240).optional(),
36
+ elementHTML: Joi.string().max(10240).optional(), // camelCase variant
37
+ component_data: Joi.string().max(3072).optional(),
38
+ componentData: Joi.string().max(3072).optional(), // camelCase variant
39
+ event_handlers: Joi.string().max(2048).optional(),
40
+ eventHandlers: Joi.string().max(2048).optional(), // camelCase variant
41
+ element_state: Joi.string().max(2048).optional(),
42
+ elementState: Joi.string().max(2048).optional(), // camelCase variant
43
+ performance_metrics: Joi.string().max(1024).optional(),
44
+ performanceMetrics: Joi.string().max(1024).optional(), // camelCase variant
45
+ // Function trace fields
46
+ component: Joi.string().optional(),
47
+ args: Joi.alternatives().try(Joi.array(), Joi.string()).optional(),
48
+ stack: Joi.string().optional(),
49
+ // Attached logs
50
+ logs: Joi.array().optional()
51
+ }).unknown(true) // Allow additional fields for future extensibility
25
52
  ).required(),
26
53
  logs: Joi.array().items(
27
54
  Joi.object({
@@ -42,10 +69,13 @@ export const workflowRecordingSchema = Joi.object({
42
69
  ).optional(),
43
70
  name: Joi.any().optional(),
44
71
  screenshotSettings: Joi.object({
45
- width: Joi.number().integer().min(100).max(4000).optional(),
46
- height: Joi.number().integer().min(100).max(4000).optional(),
47
- quality: Joi.number().min(10).max(100).optional()
48
- }).optional()
72
+ width: Joi.number().integer().min(100).max(4000).optional().allow(null),
73
+ height: Joi.number().integer().min(100).max(4000).optional().allow(null),
74
+ quality: Joi.number().min(10).max(100).optional().allow(null),
75
+ format: Joi.string().valid('png', 'jpeg').optional().allow(null),
76
+ enabled: Joi.boolean().optional().allow(null),
77
+ maxWidth: Joi.number().integer().optional().allow(null)
78
+ }).optional().allow(null)
49
79
  });
50
80
 
51
81
  // Frame batch schema
@@ -1,157 +0,0 @@
1
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
- import {
4
- ListToolsRequestSchema,
5
- CallToolRequestSchema
6
- } from '@modelcontextprotocol/sdk/types.js';
7
- import { ChromeController } from './chrome-controller.js';
8
-
9
- // Create a single Chrome controller instance for this MCP server
10
- const chromeController = new ChromeController();
11
-
12
- const server = new Server(
13
- {
14
- name: 'chrome-pilot',
15
- version: '1.0.0',
16
- },
17
- {
18
- capabilities: {
19
- tools: {},
20
- },
21
- }
22
- );
23
-
24
- const tools = [
25
- {
26
- name: 'launch_chrome',
27
- description: 'Launch a Chrome browser instance for this MCP server',
28
- inputSchema: {
29
- type: 'object',
30
- properties: {},
31
- },
32
- },
33
- {
34
- name: 'get_chrome_info',
35
- description: 'Get Chrome debugging port and connection info for extensions',
36
- inputSchema: {
37
- type: 'object',
38
- properties: {},
39
- },
40
- },
41
- {
42
- name: 'connect_to_existing_chrome',
43
- description: 'Connect to an existing Chrome instance with debugging enabled',
44
- inputSchema: {
45
- type: 'object',
46
- properties: {
47
- port: {
48
- type: 'number',
49
- description: 'Debugging port Chrome is running on (default: 9222)',
50
- default: 9222,
51
- },
52
- },
53
- },
54
- },
55
- // ... other tools remain the same
56
- ];
57
-
58
- server.setRequestHandler(ListToolsRequestSchema, async () => {
59
- return { tools };
60
- });
61
-
62
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
63
- const { name, arguments: args } = request.params;
64
-
65
- try {
66
- switch (name) {
67
- case 'launch_chrome': {
68
- const result = await chromeController.launch();
69
-
70
- // Write Chrome info to a known location for the extension
71
- const chromeInfo = {
72
- debugPort: result.debugPort,
73
- wsEndpoint: result.browserWSEndpoint,
74
- timestamp: new Date().toISOString()
75
- };
76
-
77
- // The extension can read this file or connect directly to debugPort
78
- await fs.writeFile(
79
- '/tmp/chrome-pilot-current.json',
80
- JSON.stringify(chromeInfo, null, 2)
81
- );
82
-
83
- return {
84
- content: [
85
- {
86
- type: 'text',
87
- text: `Chrome launched on debugging port ${result.debugPort}\n\n` +
88
- `The Chrome extension can now connect directly to:\n` +
89
- `http://localhost:${result.debugPort}\n\n` +
90
- JSON.stringify(result, null, 2),
91
- },
92
- ],
93
- };
94
- }
95
-
96
- case 'get_chrome_info': {
97
- const status = await chromeController.getConnectionStatus();
98
- if (!status.connected) {
99
- return {
100
- content: [
101
- {
102
- type: 'text',
103
- text: 'Chrome is not connected. Launch Chrome first using launch_chrome.',
104
- },
105
- ],
106
- };
107
- }
108
-
109
- return {
110
- content: [
111
- {
112
- type: 'text',
113
- text: `Chrome debugging info:\n` +
114
- `Port: ${status.debugPort}\n` +
115
- `WebSocket: ${status.browserWSEndpoint}\n\n` +
116
- `Extensions can connect to: http://localhost:${status.debugPort}`,
117
- },
118
- ],
119
- };
120
- }
121
-
122
- // ... rest of the cases
123
- }
124
- } catch (error) {
125
- return {
126
- content: [
127
- {
128
- type: 'text',
129
- text: `Error: ${error.message}`,
130
- },
131
- ],
132
- isError: true,
133
- };
134
- }
135
- });
136
-
137
- async function main() {
138
- const transport = new StdioServerTransport();
139
- await server.connect(transport);
140
- console.error('Chrome Debug MCP server running (direct mode - no Express)');
141
- }
142
-
143
- // Clean shutdown
144
- process.on('SIGINT', async () => {
145
- await chromeController.close();
146
- process.exit(0);
147
- });
148
-
149
- process.on('SIGTERM', async () => {
150
- await chromeController.close();
151
- process.exit(0);
152
- });
153
-
154
- main().catch((error) => {
155
- console.error('Fatal error:', error);
156
- process.exit(1);
157
- });