chrome-devtools-mcp-for-extension 0.6.5 → 0.7.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/README.md CHANGED
@@ -443,13 +443,44 @@ interface ManifestValidation {
443
443
  - `--load-extension` may be restricted in newer Chrome versions
444
444
  - **Solution**: Use system profile (default) instead of `--loadExtension` flag
445
445
 
446
- ## Concurrent Chrome Usage
446
+ ## System Extensions Loading (v0.7.1+)
447
447
 
448
- **Can I use Chrome while the MCP server is running?**
448
+ **How does the MCP server handle Chrome extensions?**
449
449
 
450
- Yes! The MCP server can run alongside your regular Chrome browser. Chrome is robust enough to handle concurrent access to the same profile.
450
+ The MCP server uses an **isolated profile with `--load-extension`** to provide system extensions while maintaining independence:
451
451
 
452
- **Note:** If you experience any issues with concurrent usage, you can use the `--isolated` flag to run with a separate profile:
452
+ ### Default Behavior
453
+ - ✅ **Independent Chrome Instance**: Runs separately from your main Chrome browser
454
+ - ✅ **System Extensions Loaded**: Your installed Chrome extensions are automatically loaded via `--load-extension`
455
+ - ✅ **Concurrent Usage**: Works alongside your regular Chrome browser without conflicts
456
+ - 🔒 **Isolated Login State**: First launch requires Google login (for security)
457
+ - 🔒 **Isolated Profile**: Uses `~/.cache/chrome-devtools-mcp/chrome-profile/`
458
+
459
+ ### What Works
460
+ - ✅ **Extensions**: All system Chrome extensions are dynamically loaded
461
+ - ✅ **Bookmarks**: Accessible via MCP tools (`list_bookmarks`, `navigate_bookmark`)
462
+ - ✅ **Login State**: Preserved in isolated profile after first login
463
+
464
+ ### What Doesn't Work
465
+ - ❌ **Bookmarks in Browser UI**: Not displayed in browser bookmarks bar (use MCP tools instead)
466
+ - ❌ **Shared Login State**: System Chrome login state is not shared (first login required)
467
+
468
+ ### Profile Location
469
+ ```
470
+ ~/.cache/chrome-devtools-mcp/chrome-profile/
471
+ └── Default/
472
+ ├── Cookies (isolated)
473
+ ├── Login Data (isolated)
474
+ └── ... (all files isolated)
475
+ ```
476
+
477
+ ### First Launch
478
+ - **Extensions**: Automatically loaded from system Chrome via `--load-extension`
479
+ - **Google Login required**: You'll need to log in once (login state is isolated for security)
480
+ - **Subsequent launches**: Login state is preserved in the isolated profile
481
+
482
+ ### Isolated Mode (No Extensions)
483
+ To run without any extensions:
453
484
  ```bash
454
485
  npx chrome-devtools-mcp-for-extension@latest --isolated
455
486
  ```
@@ -7,7 +7,6 @@ import fs from 'node:fs';
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
9
9
  import puppeteer from 'puppeteer-core';
10
- import { detectSystemChromeProfile, detectAnySystemChromeProfile, } from './system-profile.js';
11
10
  let browser;
12
11
  const ignoredPrefixes = new Set([
13
12
  'chrome://',
@@ -42,21 +41,6 @@ async function ensureBrowserConnected(browserURL) {
42
41
  });
43
42
  return browser;
44
43
  }
45
- /**
46
- * Get the last used Chrome profile directory name from Local State
47
- */
48
- function getLastUsedProfile(userDataDir) {
49
- const localStatePath = path.join(userDataDir, 'Local State');
50
- try {
51
- const localStateContent = fs.readFileSync(localStatePath, 'utf8');
52
- const localState = JSON.parse(localStateContent);
53
- return localState?.profile?.last_used || 'Default';
54
- }
55
- catch (error) {
56
- console.warn(`Could not read Local State: ${error instanceof Error ? error.message : String(error)}`);
57
- return 'Default';
58
- }
59
- }
60
44
  function scanExtensionsDirectory(extensionsDir) {
61
45
  const extensionPaths = [];
62
46
  try {
@@ -249,27 +233,13 @@ export async function launch(options) {
249
233
  let userDataDir = options.userDataDir;
250
234
  let usingSystemProfile = false;
251
235
  let profileDirectory = 'Default';
252
- if (!isolated && !userDataDir) {
253
- // Try to use system Chrome profile directly
254
- const systemProfile = detectSystemChromeProfile(channel) || detectAnySystemChromeProfile();
255
- if (systemProfile) {
256
- // Use system profile directly for better user experience
257
- userDataDir = systemProfile.path;
258
- usingSystemProfile = true;
259
- // Detect last used profile directory
260
- profileDirectory = getLastUsedProfile(userDataDir);
261
- console.error(`✅ Using system Chrome profile: ${systemProfile.channel}`);
262
- console.error(` Path: ${userDataDir}`);
263
- console.error(` Profile Directory: ${profileDirectory}`);
264
- }
265
- else {
266
- // Fallback to isolated profile if no system Chrome found
267
- userDataDir = path.join(os.homedir(), '.cache', 'chrome-devtools-mcp', profileDirName);
268
- await fs.promises.mkdir(userDataDir, {
269
- recursive: true,
270
- });
271
- console.error(`📁 Using isolated profile (no system Chrome found)`);
272
- }
236
+ if (!userDataDir) {
237
+ // Use isolated profile (independent from system Chrome)
238
+ userDataDir = path.join(os.homedir(), '.cache', 'chrome-devtools-mcp', profileDirName);
239
+ await fs.promises.mkdir(userDataDir, {
240
+ recursive: true,
241
+ });
242
+ console.error(`📁 Using isolated profile: ${userDataDir}`);
273
243
  }
274
244
  const args = [
275
245
  '--hide-crash-restore-bubble',
@@ -312,12 +282,13 @@ export async function launch(options) {
312
282
  const scannedExtensions = scanExtensionsDirectory(loadExtensionsDir);
313
283
  extensionPaths.push(...scannedExtensions);
314
284
  }
315
- // System extension discovery
316
- if (loadSystemExtensions) {
285
+ // System extension discovery (default: true unless isolated flag is set)
286
+ const shouldLoadSystemExtensions = loadSystemExtensions ?? !isolated;
287
+ if (shouldLoadSystemExtensions) {
317
288
  const systemExtensions = discoverSystemExtensions(channel);
318
289
  if (systemExtensions.length > 0) {
319
290
  extensionPaths.push(...systemExtensions);
320
- console.error(`🔗 Integrated ${systemExtensions.length} system extensions with development extensions`);
291
+ console.error(`✅ Loaded ${systemExtensions.length} system Chrome extension(s)`);
321
292
  }
322
293
  else {
323
294
  console.warn(`⚠️ No system extensions found or accessible`);
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Profile Manager for Chrome DevTools MCP
3
+ *
4
+ * This module manages dedicated Chrome profiles that use symlinks to share
5
+ * Extensions and Bookmarks from the system Chrome profile while maintaining
6
+ * isolated Cookies, Login Data, and Preferences.
7
+ */
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import os from 'os';
11
+ /**
12
+ * Detect system Chrome profile for a specific channel
13
+ */
14
+ function detectSystemChromeProfile(channel) {
15
+ const home = os.homedir();
16
+ const chromePaths = {
17
+ stable: path.join(home, 'Library/Application Support/Google/Chrome'),
18
+ beta: path.join(home, 'Library/Application Support/Google/Chrome Beta'),
19
+ canary: path.join(home, 'Library/Application Support/Google/Chrome Canary'),
20
+ dev: path.join(home, 'Library/Application Support/Google/Chrome Dev'),
21
+ };
22
+ const targetChannel = channel || 'stable';
23
+ const chromePath = chromePaths[targetChannel];
24
+ if (chromePath && fs.existsSync(chromePath)) {
25
+ return { path: chromePath, channel: targetChannel };
26
+ }
27
+ return null;
28
+ }
29
+ /**
30
+ * Detect any available system Chrome profile
31
+ */
32
+ function detectAnySystemChromeProfile() {
33
+ const channels = ['stable', 'beta', 'dev', 'canary'];
34
+ for (const channel of channels) {
35
+ const profile = detectSystemChromeProfile(channel);
36
+ if (profile) {
37
+ return profile;
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+ /**
43
+ * Get the last used profile directory name from Local State
44
+ */
45
+ function getLastUsedProfile(userDataDir) {
46
+ const localStatePath = path.join(userDataDir, 'Local State');
47
+ try {
48
+ const localStateContent = fs.readFileSync(localStatePath, 'utf-8');
49
+ const localState = JSON.parse(localStateContent);
50
+ const lastUsed = localState?.profile?.last_used;
51
+ if (lastUsed && typeof lastUsed === 'string') {
52
+ return lastUsed;
53
+ }
54
+ }
55
+ catch (error) {
56
+ // Ignore errors, will use default
57
+ }
58
+ return 'Default';
59
+ }
60
+ /**
61
+ * Create or update a symlink safely
62
+ *
63
+ * @param target - The target path that the symlink should point to
64
+ * @param linkPath - The path where the symlink should be created
65
+ */
66
+ function createSymlinkSafe(target, linkPath) {
67
+ // Check if target exists
68
+ if (!fs.existsSync(target)) {
69
+ console.error(`⚠️ Symlink target does not exist: ${target}`);
70
+ return;
71
+ }
72
+ // If link already exists and points to the correct target, skip
73
+ if (fs.existsSync(linkPath)) {
74
+ try {
75
+ const currentTarget = fs.readlinkSync(linkPath);
76
+ if (currentTarget === target) {
77
+ // Already correctly linked
78
+ return;
79
+ }
80
+ // Remove old symlink
81
+ fs.unlinkSync(linkPath);
82
+ }
83
+ catch (error) {
84
+ // Not a symlink, remove the file/directory
85
+ if (fs.lstatSync(linkPath).isDirectory()) {
86
+ fs.rmSync(linkPath, { recursive: true, force: true });
87
+ }
88
+ else {
89
+ fs.unlinkSync(linkPath);
90
+ }
91
+ }
92
+ }
93
+ // Create new symlink
94
+ fs.symlinkSync(target, linkPath, 'dir');
95
+ console.error(`🔗 Created symlink: ${path.basename(linkPath)} -> ${target}`);
96
+ }
97
+ /**
98
+ * Setup a dedicated Chrome profile with symlinks to system profile
99
+ *
100
+ * This function:
101
+ * 1. Detects the system Chrome profile
102
+ * 2. Creates a dedicated profile directory
103
+ * 3. Creates symlinks for Extensions and Bookmarks
104
+ * 4. Returns the dedicated profile information
105
+ *
106
+ * @param channel - Chrome channel to use (stable, beta, canary, dev)
107
+ * @returns Dedicated profile information
108
+ */
109
+ export async function setupDedicatedProfile(channel) {
110
+ // Detect system Chrome profile
111
+ const systemProfile = detectSystemChromeProfile(channel) || detectAnySystemChromeProfile();
112
+ if (!systemProfile) {
113
+ throw new Error('No system Chrome profile found. Please install Chrome and run it at least once.');
114
+ }
115
+ // Get the last used profile directory
116
+ const profileDirectory = getLastUsedProfile(systemProfile.path);
117
+ const systemProfileDir = path.join(systemProfile.path, profileDirectory);
118
+ // Create dedicated profile directory
119
+ const dedicatedUserDataDir = path.join(os.homedir(), '.cache', 'chrome-devtools-mcp', 'chrome-profile-dedicated');
120
+ await fs.promises.mkdir(dedicatedUserDataDir, { recursive: true });
121
+ const dedicatedProfileDir = path.join(dedicatedUserDataDir, profileDirectory);
122
+ await fs.promises.mkdir(dedicatedProfileDir, { recursive: true });
123
+ console.error('📁 Dedicated Profile Setup:');
124
+ console.error(` System Profile: ${systemProfileDir}`);
125
+ console.error(` Dedicated Profile: ${dedicatedProfileDir}`);
126
+ console.error(` Profile Directory: ${profileDirectory}`);
127
+ // Create symlinks for shared resources
128
+ const symlinkTargets = [
129
+ { name: 'Extensions', required: false },
130
+ { name: 'Bookmarks', required: false },
131
+ ];
132
+ for (const { name, required } of symlinkTargets) {
133
+ const targetPath = path.join(systemProfileDir, name);
134
+ const linkPath = path.join(dedicatedProfileDir, name);
135
+ if (fs.existsSync(targetPath)) {
136
+ createSymlinkSafe(targetPath, linkPath);
137
+ }
138
+ else if (required) {
139
+ console.error(`⚠️ Required item not found: ${name}`);
140
+ }
141
+ }
142
+ console.error('✅ Dedicated profile setup complete');
143
+ console.error(' First launch will require Google login (login state is not shared)');
144
+ return {
145
+ userDataDir: dedicatedUserDataDir,
146
+ profileDirectory,
147
+ systemProfilePath: systemProfileDir,
148
+ channel: systemProfile.channel,
149
+ };
150
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-devtools-mcp-for-extension",
3
- "version": "0.6.5",
3
+ "version": "0.7.1",
4
4
  "description": "MCP server for Chrome extension development with Web Store automation. Fork of chrome-devtools-mcp with extension-specific tools.",
5
5
  "type": "module",
6
6
  "bin": "./build/src/index.js",