@dynamicu/chromedebug-mcp 2.6.3 → 2.6.6

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/CLAUDE.md CHANGED
@@ -23,6 +23,9 @@ npm run server # Run standalone server
23
23
  npm run single-server # Run with --single-server flag
24
24
  npm run cleanup # Clean up stale Chrome processes
25
25
  npm run cleanup-force # Force cleanup of all Chrome processes
26
+
27
+ # Persistent server (won't be killed by other instances)
28
+ chromedebug-mcp --persistent # Start a persistent server for manual debugging
26
29
  ```
27
30
 
28
31
  ### Testing
@@ -235,8 +238,30 @@ Chrome Debug provides a CLI tool that can be used directly:
235
238
  ```bash
236
239
  chromedebug-mcp # Runs the MCP server (equivalent to npm start)
237
240
  npx chromedebug-mcp # Run without global installation
241
+
242
+ # Persistent server mode
243
+ chromedebug-mcp --persistent # Won't be killed by other instances
244
+ ```
245
+
246
+ ### Persistent Server Mode
247
+
248
+ The `--persistent` flag marks a server as persistent, preventing it from being killed when other ChromeDebug instances start. This is useful for:
249
+
250
+ - **Manual debugging servers**: Keep a debug server running while using Claude Code
251
+ - **Multiple projects**: Run persistent servers for different projects simultaneously
252
+ - **Development workflows**: Maintain a stable server instance across multiple Claude Code sessions
253
+
254
+ Example:
255
+ ```bash
256
+ # Terminal 1: Start persistent debug server
257
+ chromedebug-mcp --persistent --verbose
258
+
259
+ # Terminal 2: Use Claude Code normally
260
+ # Claude's MCP server will NOT kill the persistent server
238
261
  ```
239
262
 
263
+ See `PERSISTENT-FLAG-README.md` for detailed documentation and examples.
264
+
240
265
  The package name is `chromedebug-mcp` but the project is commonly referred to as Chrome Debug.
241
266
 
242
267
  ## Additional Architecture Notes
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "manifest_version": 3,
3
- "name": "ChromeDebug MCP Assistant FREE v2.6.0",
4
- "version": "2.6.0",
5
- "description": "ChromeDebug MCP visual element selector [FREE Edition] [Build: 2025-10-24-v2.6.0]",
3
+ "name": "ChromeDebug MCP Assistant FREE v2.6.1",
4
+ "version": "2.6.1",
5
+ "description": "ChromeDebug MCP visual element selector [FREE Edition] [Build: 2025-10-28-v2.6.1]",
6
6
  "permissions": [
7
7
  "activeTab",
8
8
  "scripting",
9
9
  "tabs",
10
10
  "storage",
11
11
  "tabCapture",
12
- "desktopCapture",
13
12
  "notifications",
14
13
  "offscreen"
15
14
  ],
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamicu/chromedebug-mcp",
3
- "version": "2.6.3",
3
+ "version": "2.6.6",
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",
@@ -11,7 +11,7 @@ module.exports = {
11
11
  options: './chrome-extension/options.js'
12
12
  },
13
13
  output: {
14
- path: path.resolve(__dirname, 'dist/free'),
14
+ path: path.resolve(__dirname, '../dist/free'),
15
15
  filename: '[name].js',
16
16
  clean: true
17
17
  },
@@ -12,7 +12,7 @@ module.exports = {
12
12
  'pro/frame-editor': './chrome-extension/pro/frame-editor.js'
13
13
  },
14
14
  output: {
15
- path: path.resolve(__dirname, 'dist/pro'),
15
+ path: path.resolve(__dirname, '../dist/pro'),
16
16
  filename: '[name].js',
17
17
  clean: true
18
18
  },
package/src/cli.js CHANGED
@@ -81,6 +81,17 @@ For details: https://github.com/dynamicupgrade/ChromeDebug#windows-setup
81
81
  verbose: args.verbose
82
82
  });
83
83
 
84
+ // Mark as persistent if requested
85
+ if (args.persistent) {
86
+ await sessionManager.markAsPersistent();
87
+ console.error('Session marked as persistent - will not be killed by other instances');
88
+ }
89
+
90
+ // Kill other instances unless --allow-multiple is set
91
+ if (!args.allowMultiple) {
92
+ await sessionManager.killOtherInstances();
93
+ }
94
+
84
95
  // Register this process for tracking
85
96
  await registerProcess(process.pid, 'mcp-server');
86
97
 
package/src/index.js CHANGED
@@ -152,7 +152,9 @@ class ChromePilotApp {
152
152
  sessionId: null,
153
153
  noCleanup: false,
154
154
  verbose: false,
155
- dbPath: null
155
+ dbPath: null,
156
+ allowMultiple: false,
157
+ persistent: false
156
158
  };
157
159
 
158
160
  const argv = process.argv.slice(2);
@@ -191,6 +193,12 @@ class ChromePilotApp {
191
193
  case '--verbose':
192
194
  args.verbose = true;
193
195
  break;
196
+ case '--allow-multiple':
197
+ args.allowMultiple = true;
198
+ break;
199
+ case '--persistent':
200
+ args.persistent = true;
201
+ break;
194
202
  case '--help':
195
203
  this.showHelp();
196
204
  process.exit(0);
@@ -237,6 +245,8 @@ Options:
237
245
  --db-path PATH Set custom database file path (default: ~/.chromedebug/chrome-pilot.db)
238
246
  --no-cleanup Skip cleanup of dead processes on startup
239
247
  --verbose Enable verbose logging including session info
248
+ --allow-multiple Allow multiple ChromeDebug instances (default: kill others on startup)
249
+ --persistent Mark as persistent server (won't be killed by other instances)
240
250
  --help Show this help message
241
251
 
242
252
  Session Isolation:
@@ -31,7 +31,8 @@ export class UnifiedSessionManager {
31
31
  port: null,
32
32
  processes: [],
33
33
  locks: [],
34
- heartbeat: Date.now()
34
+ heartbeat: Date.now(),
35
+ persistent: false // If true, won't be killed by other instances
35
36
  };
36
37
 
37
38
  this.heartbeatInterval = null;
@@ -354,6 +355,18 @@ export class UnifiedSessionManager {
354
355
  }
355
356
  }
356
357
 
358
+ /**
359
+ * Mark this session as persistent (won't be killed by other instances)
360
+ */
361
+ async markAsPersistent() {
362
+ this.state.persistent = true;
363
+ await this.updateSessionFile();
364
+
365
+ if (this.verbose) {
366
+ console.error(`Session ${this.sessionId} marked as persistent`);
367
+ }
368
+ }
369
+
357
370
  /**
358
371
  * Start heartbeat to keep session alive
359
372
  */
@@ -446,6 +459,106 @@ export class UnifiedSessionManager {
446
459
  return sessions;
447
460
  }
448
461
 
462
+ /**
463
+ * Kill all other ChromeDebug instances
464
+ * Uses session files to find PIDs, then kills them gracefully
465
+ * Skips sessions marked as persistent
466
+ * @returns {Array} Array of killed PIDs
467
+ */
468
+ async killOtherInstances() {
469
+ const sessions = await UnifiedSessionManager.findActiveSessions();
470
+ const currentPid = process.pid;
471
+ const killedPids = [];
472
+
473
+ for (const session of sessions) {
474
+ if (session.pid === currentPid) continue; // Don't kill ourselves
475
+
476
+ // Skip persistent sessions
477
+ if (session.persistent) {
478
+ console.error(`Skipping persistent session: PID ${session.pid} (session ${session.sessionId})`);
479
+ continue;
480
+ }
481
+
482
+ try {
483
+ // Check if process exists
484
+ process.kill(session.pid, 0); // Signal 0 = check existence
485
+
486
+ // Verify this is actually a ChromeDebug process
487
+ const isChromeDebug = await this.isChromeDebugProcess(session.pid);
488
+ if (!isChromeDebug) {
489
+ console.error(`Skipping PID ${session.pid} - not a ChromeDebug process`);
490
+ continue;
491
+ }
492
+
493
+ console.error(`Killing ChromeDebug instance: PID ${session.pid} (session ${session.sessionId})`);
494
+
495
+ // Send SIGTERM for graceful shutdown
496
+ process.kill(session.pid, 'SIGTERM');
497
+ killedPids.push(session.pid);
498
+
499
+ // Wait 2 seconds, then SIGKILL if still alive
500
+ setTimeout(() => {
501
+ try {
502
+ process.kill(session.pid, 0); // Check if still alive
503
+ console.error(` Process ${session.pid} didn't terminate, sending SIGKILL`);
504
+ process.kill(session.pid, 'SIGKILL');
505
+ } catch (err) {
506
+ // Process already dead, good
507
+ }
508
+ }, 2000);
509
+
510
+ // Clean up session file
511
+ const sessionFile = path.join(this.sessionDir, `${session.sessionId}.json`);
512
+ try {
513
+ await fs.promises.unlink(sessionFile);
514
+ } catch (err) {
515
+ // Ignore errors
516
+ }
517
+
518
+ } catch (error) {
519
+ if (error.code === 'ESRCH') {
520
+ // Process doesn't exist, just clean up the session file
521
+ const sessionFile = path.join(this.sessionDir, `${session.sessionId}.json`);
522
+ try {
523
+ await fs.promises.unlink(sessionFile);
524
+ } catch (err) {
525
+ // Ignore errors
526
+ }
527
+ }
528
+ // Ignore other errors (permission denied, etc.)
529
+ }
530
+ }
531
+
532
+ if (killedPids.length > 0) {
533
+ console.error(`Killed ${killedPids.length} other ChromeDebug instance(s)`);
534
+ }
535
+
536
+ return killedPids;
537
+ }
538
+
539
+ /**
540
+ * Verify if a PID belongs to a ChromeDebug process
541
+ * @param {number} pid - Process ID to check
542
+ * @returns {boolean} True if it's a ChromeDebug process
543
+ */
544
+ async isChromeDebugProcess(pid) {
545
+ try {
546
+ const { execSync } = require('child_process');
547
+
548
+ // Get process command line
549
+ const cmd = execSync(`ps -p ${pid} -o command=`, { encoding: 'utf8' }).trim();
550
+
551
+ // Check if it's a ChromeDebug process (node running chromedebug or chrome-pilot)
552
+ return cmd.includes('chromedebug') ||
553
+ cmd.includes('chrome-pilot') ||
554
+ cmd.includes('ChromePilot') ||
555
+ cmd.includes('ChromeDebug');
556
+ } catch (error) {
557
+ // If we can't determine, be safe and don't kill
558
+ return false;
559
+ }
560
+ }
561
+
449
562
  /**
450
563
  * Clean up stale sessions
451
564
  */