chrome-devtools-mcp-for-extension 0.7.0 → 0.7.2

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
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![npm chrome-devtools-mcp-for-extension package](https://img.shields.io/npm/v/chrome-devtools-mcp-for-extension.svg)](https://npmjs.org/package/chrome-devtools-mcp-for-extension)
4
4
 
5
- **Zero-config Chrome extension testing with your real browser environment**
5
+ **AI-powered Chrome extension development with automated testing, debugging, and Web Store submission**
6
6
 
7
- AI-powered Chrome extension development with automated testing, debugging, and Web Store submission.
7
+ System extensions auto-load, development extensions easy to configure.
8
8
 
9
9
  **Built for:** Claude Code, Cursor, VS Code Copilot, Cline, and other MCP-compatible AI tools
10
10
 
@@ -18,46 +18,75 @@ AI-powered Chrome extension development with automated testing, debugging, and W
18
18
  - **Extension Development**: Can't test in real user environments
19
19
 
20
20
  ### The Solution
21
- - ✅ **Zero setup**: Uses your system Chrome profile automatically
21
+ - ✅ **System extensions auto-load**: Your installed Chrome extensions work automatically
22
+ - ✅ **Easy dev extension setup**: Simple `--loadExtensionsDir` configuration for development
22
23
  - ✅ **Real environment**: Tests with your actual extensions and settings
23
- - ✅ **No copying**: Direct profile access, instant sync
24
- - ✅ **Always enabled**: Extensions work automatically, no configuration needed
24
+ - ✅ **Independent instance**: Runs alongside your regular Chrome without conflicts
25
25
 
26
26
  ---
27
27
 
28
28
  ## 🚀 Quick Start
29
29
 
30
- ### 1. Install (30 seconds)
30
+ ### 1. Add Configuration
31
31
 
32
- **Claude Code users (recommended):**
33
- ```bash
34
- claude mcp add --scope user chrome-devtools-extension npx chrome-devtools-mcp-for-extension@latest
32
+ Add the following to `~/.claude.json`:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "chrome-devtools-extension": {
38
+ "type": "stdio",
39
+ "command": "npx",
40
+ "args": ["chrome-devtools-mcp-for-extension@latest"],
41
+ "env": {}
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ > **Note**: For other MCP clients (Cursor, VS Code Copilot, Cline), add to your client's global configuration file.
48
+
49
+ ### 2. Restart Your AI Client
50
+
51
+ ### 3. Test It
52
+
53
+ Ask your AI:
54
+ ```
55
+ "List all my Chrome extensions"
35
56
  ```
36
57
 
37
- <summary>Other MCP clients (Cursor, VS Code Copilot, Cline)</summary>
58
+ You should see your installed Chrome extensions
59
+
60
+ ---
61
+
62
+ ## 🔧 Load Development Extensions (Optional)
38
63
 
39
- Add to your MCP configuration file:
64
+ To test your own extensions under development, add `--loadExtensionsDir`:
40
65
 
41
66
  ```json
42
67
  {
43
68
  "mcpServers": {
44
69
  "chrome-devtools-extension": {
45
70
  "command": "npx",
46
- "args": ["chrome-devtools-mcp-for-extension@latest"]
71
+ "args": [
72
+ "chrome-devtools-mcp-for-extension@latest",
73
+ "--loadExtensionsDir=/path/to/your/extensions"
74
+ ]
47
75
  }
48
76
  }
49
77
  }
50
78
  ```
51
79
 
80
+ **Directory structure example:**
81
+ ```
82
+ /path/to/your/extensions/
83
+ ├── my-extension-1/
84
+ │ └── manifest.json
85
+ ├── my-extension-2/
86
+ │ └── manifest.json
87
+ ```
52
88
 
53
- ### 2. Restart your AI client
54
-
55
- ### 3. Verify installation
56
- Ask your AI: **"List all my Chrome extensions"**
57
- ✅ You should see your installed extensions listed
58
-
59
- ### 4. Start developing
60
- See [Common Workflows](#-common-workflows) below for typical use cases
89
+ **More options**: See [MCP Configuration Guide](docs/mcp-configuration-guide.md)
61
90
 
62
91
  ---
63
92
 
@@ -159,15 +188,20 @@ Quick reference for the 3 core extension tools:
159
188
  <summary>⚙️ Advanced Configuration</summary>
160
189
 
161
190
  ## Auto-load Development Extension
191
+
192
+ Add to `~/.claude.json`:
193
+
162
194
  ```json
163
195
  {
164
196
  "mcpServers": {
165
197
  "chrome-devtools-extension": {
198
+ "type": "stdio",
166
199
  "command": "npx",
167
200
  "args": [
168
201
  "chrome-devtools-mcp-for-extension@latest",
169
202
  "--loadExtension=/path/to/your/extension"
170
- ]
203
+ ],
204
+ "env": {}
171
205
  }
172
206
  }
173
207
  }
@@ -176,10 +210,14 @@ Quick reference for the 3 core extension tools:
176
210
  ⚠️ **Note**: `--loadExtension` flag may be deprecated in Chrome 137+. Using system profile (default) is recommended.
177
211
 
178
212
  ## Debug Mode
213
+
214
+ Add to `~/.claude.json`:
215
+
179
216
  ```json
180
217
  {
181
218
  "mcpServers": {
182
219
  "chrome-devtools-extension": {
220
+ "type": "stdio",
183
221
  "command": "npx",
184
222
  "args": ["chrome-devtools-mcp-for-extension@latest"],
185
223
  "env": {
@@ -191,15 +229,20 @@ Quick reference for the 3 core extension tools:
191
229
  ```
192
230
 
193
231
  ## Custom Chrome Channel
232
+
233
+ Add to `~/.claude.json`:
234
+
194
235
  ```json
195
236
  {
196
237
  "mcpServers": {
197
238
  "chrome-devtools-extension": {
239
+ "type": "stdio",
198
240
  "command": "npx",
199
241
  "args": [
200
242
  "chrome-devtools-mcp-for-extension@latest",
201
243
  "--channel=canary"
202
- ]
244
+ ],
245
+ "env": {}
203
246
  }
204
247
  }
205
248
  }
@@ -208,15 +251,20 @@ Quick reference for the 3 core extension tools:
208
251
  Options: `stable` (default), `beta`, `dev`, `canary`
209
252
 
210
253
  ## Isolated Profile Mode
254
+
255
+ Add to `~/.claude.json`:
256
+
211
257
  ```json
212
258
  {
213
259
  "mcpServers": {
214
260
  "chrome-devtools-extension": {
261
+ "type": "stdio",
215
262
  "command": "npx",
216
263
  "args": [
217
264
  "chrome-devtools-mcp-for-extension@latest",
218
265
  "--isolated"
219
- ]
266
+ ],
267
+ "env": {}
220
268
  }
221
269
  }
222
270
  }
@@ -408,9 +456,22 @@ interface ManifestValidation {
408
456
  - Not: `/your-extension/dist/manifest.json`
409
457
 
410
458
  **Solution:**
459
+
460
+ Update `~/.claude.json`:
411
461
  ```json
412
- // Use --loadExtension with correct path
413
- "args": ["chrome-devtools-mcp-for-extension@latest", "--loadExtension=/correct/path"]
462
+ {
463
+ "mcpServers": {
464
+ "chrome-devtools-extension": {
465
+ "type": "stdio",
466
+ "command": "npx",
467
+ "args": [
468
+ "chrome-devtools-mcp-for-extension@latest",
469
+ "--loadExtension=/correct/path"
470
+ ],
471
+ "env": {}
472
+ }
473
+ }
474
+ }
414
475
  ```
415
476
 
416
477
  ## Service Worker Not Inspecting
@@ -443,41 +504,44 @@ interface ManifestValidation {
443
504
  - `--load-extension` may be restricted in newer Chrome versions
444
505
  - **Solution**: Use system profile (default) instead of `--loadExtension` flag
445
506
 
446
- ## Dedicated Profile Architecture
507
+ ## System Extensions Loading (v0.7.1+)
447
508
 
448
- **How does the MCP server handle Chrome profiles?**
509
+ **How does the MCP server handle Chrome extensions?**
449
510
 
450
- The MCP server uses a **dedicated profile with symlinks** to provide the best of both worlds:
511
+ The MCP server uses an **isolated profile with `--load-extension`** to provide system extensions while maintaining independence:
451
512
 
452
- ### What Gets Shared (via Symlinks)
453
- - ✅ **Extensions**: Your installed Chrome extensions are accessible via symlink
454
- - ✅ **Bookmarks**: Your bookmarks are shared (read-only)
513
+ ### Default Behavior
514
+ - ✅ **Independent Chrome Instance**: Runs separately from your main Chrome browser
515
+ - ✅ **System Extensions Loaded**: Your installed Chrome extensions are automatically loaded via `--load-extension`
516
+ - ✅ **Concurrent Usage**: Works alongside your regular Chrome browser without conflicts
517
+ - 🔒 **Isolated Login State**: First launch requires Google login (for security)
518
+ - 🔒 **Isolated Profile**: Uses `~/.cache/chrome-devtools-mcp/chrome-profile/`
455
519
 
456
- ### What Stays Private (Dedicated Profile)
457
- - 🔒 **Cookies & Login State**: Separate login state for security
458
- - 🔒 **Browsing History**: Independent history
459
- - 🔒 **Preferences**: MCP-specific settings
520
+ ### What Works
521
+ - **Extensions**: All system Chrome extensions are dynamically loaded
522
+ - **Bookmarks**: Accessible via MCP tools (`list_bookmarks`, `navigate_bookmark`)
523
+ - **Login State**: Preserved in isolated profile after first login
524
+
525
+ ### What Doesn't Work
526
+ - ❌ **Bookmarks in Browser UI**: Not displayed in browser bookmarks bar (use MCP tools instead)
527
+ - ❌ **Shared Login State**: System Chrome login state is not shared (first login required)
460
528
 
461
529
  ### Profile Location
462
530
  ```
463
- ~/.cache/chrome-devtools-mcp/chrome-profile-dedicated/
531
+ ~/.cache/chrome-devtools-mcp/chrome-profile/
464
532
  └── Default/
465
- ├── Extensions/ → (symlink to system Chrome)
466
- ├── Bookmarks → (symlink to system Chrome)
467
- ├── Cookies (dedicated)
468
- └── ...
533
+ ├── Cookies (isolated)
534
+ ├── Login Data (isolated)
535
+ └── ... (all files isolated)
469
536
  ```
470
537
 
471
538
  ### First Launch
472
- - **Initial setup**: Extensions and bookmarks are automatically linked
473
- - **Google Login required**: You'll need to log in once (login state is not shared from your main Chrome)
474
- - **Subsequent launches**: Login state is preserved in the dedicated profile
475
-
476
- ### Concurrent Usage
477
- ✅ **Yes!** The MCP server runs alongside your regular Chrome browser without conflicts. Each uses its own profile directory.
539
+ - **Extensions**: Automatically loaded from system Chrome via `--load-extension`
540
+ - **Google Login required**: You'll need to log in once (login state is isolated for security)
541
+ - **Subsequent launches**: Login state is preserved in the isolated profile
478
542
 
479
- ### Isolated Mode
480
- For testing or if you prefer a completely empty profile:
543
+ ### Isolated Mode (No Extensions)
544
+ To run without any extensions:
481
545
  ```bash
482
546
  npx chrome-devtools-mcp-for-extension@latest --isolated
483
547
  ```
@@ -490,34 +554,66 @@ npx chrome-devtools-mcp-for-extension@latest --isolated
490
554
 
491
555
  **Chrome拡張機能開発用のAI支援MCPサーバー**
492
556
 
493
- ゼロコンフィグで実環境テストが可能なChrome拡張機能開発ツール
557
+ システム拡張機能を自動ロード、開発用拡張機能も簡単設定
494
558
 
495
- ## インストール
559
+ ## クイックスタート
496
560
 
497
- **Claude Code ユーザー:**
498
- ```bash
499
- claude mcp add --scope user chrome-devtools-extension npx chrome-devtools-mcp-for-extension@latest
561
+ ### 1. 設定を追加
562
+
563
+ `~/.claude.json` に以下を追加:
564
+
565
+ ```json
566
+ {
567
+ "mcpServers": {
568
+ "chrome-devtools-extension": {
569
+ "type": "stdio",
570
+ "command": "npx",
571
+ "args": ["chrome-devtools-mcp-for-extension@latest"],
572
+ "env": {}
573
+ }
574
+ }
575
+ }
576
+ ```
577
+
578
+ ### 2. AIクライアントを再起動
579
+
580
+ ### 3. 動作確認
581
+
582
+ AIに質問:
583
+ ```
584
+ 「Chrome拡張機能を一覧表示して」
500
585
  ```
501
586
 
502
- **その他のMCPクライアント:**
587
+ ✅ インストール済みの拡張機能が表示されます
588
+
589
+ ---
590
+
591
+ ## 開発用拡張機能のロード(--loadExtensionsDirあり)
592
+
593
+ 開発中の拡張機能をテストする場合:
594
+
503
595
  ```json
504
596
  {
505
597
  "mcpServers": {
506
598
  "chrome-devtools-extension": {
507
599
  "command": "npx",
508
- "args": ["chrome-devtools-mcp-for-extension@latest"]
600
+ "args": [
601
+ "chrome-devtools-mcp-for-extension@latest",
602
+ "--loadExtensionsDir=/path/to/your/extensions"
603
+ ]
509
604
  }
510
605
  }
511
606
  }
512
607
  ```
513
608
 
609
+ ---
610
+
514
611
  ## 主な機能
515
612
 
516
613
  - 🧩 **拡張機能の開発・デバッグ・リロード**: ライブ開発環境
517
614
  - 🏪 **Chrome Web Store への自動申請**: スクリーンショット生成付き
518
615
  - 🔧 **実環境でのブラウザテスト**: 既存の拡張機能と共存
519
616
  - 🐛 **高度なデバッグ**: サービスワーカー検査、コンソール監視
520
- - 📸 **ストア用画像の自動生成**: 複数サイズ対応
521
617
 
522
618
  ## 使用例
523
619
 
@@ -528,14 +624,7 @@ claude mcp add --scope user chrome-devtools-extension npx chrome-devtools-mcp-fo
528
624
  「Web Storeに申請して」
529
625
  ```
530
626
 
531
- ## なぜこのツールか?
532
-
533
- - ✅ **設定不要**: システムのChromeプロファイルを自動使用
534
- - ✅ **実環境テスト**: 実際の拡張機能・設定でテスト可能
535
- - ✅ **コピー不要**: プロファイルの直接アクセス、即座に同期
536
- - ✅ **常時有効**: 拡張機能が自動的に有効化
537
-
538
- 詳細は英語セクションを参照してください。
627
+ その他の詳細は英語セクションを参照してください。
539
628
 
540
629
  ---
541
630
 
@@ -1,6 +1,8 @@
1
1
  import { formatConsoleEvent } from './formatters/consoleFormatter.js';
2
2
  import { getFormattedHeaderValue, getShortDescriptionForRequest, getStatusFromRequest, } from './formatters/networkFormatter.js';
3
3
  import { formatA11ySnapshot } from './formatters/snapshotFormatter.js';
4
+ import { formatExtensionsPage } from './formatters/extensionsPageFormatter.js';
5
+ import { getDevelopmentExtensionPaths } from './browser.js';
4
6
  import { paginate } from './utils/pagination.js';
5
7
  export class McpResponse {
6
8
  #includePages = false;
@@ -120,9 +122,21 @@ Call browser_handle_dialog to handle it before continuing.`);
120
122
  if (this.#includeSnapshot) {
121
123
  const snapshot = context.getTextSnapshot();
122
124
  if (snapshot) {
123
- const formattedSnapshot = formatA11ySnapshot(snapshot.root);
124
- response.push('## Page content');
125
- response.push(formattedSnapshot);
125
+ const pageUrl = context.getSelectedPage().url();
126
+ const isExtensionsPage = pageUrl.startsWith('chrome://extensions');
127
+ let formattedSnapshot;
128
+ if (isExtensionsPage) {
129
+ // Use structured extensions page formatter
130
+ const developmentPaths = getDevelopmentExtensionPaths();
131
+ formattedSnapshot = formatExtensionsPage(snapshot.root, developmentPaths);
132
+ response.push(formattedSnapshot);
133
+ }
134
+ else {
135
+ // Use standard accessibility tree formatter
136
+ formattedSnapshot = formatA11ySnapshot(snapshot.root);
137
+ response.push('## Page content');
138
+ response.push(formattedSnapshot);
139
+ }
126
140
  }
127
141
  }
128
142
  response.push(...this.#getIncludeNetworkRequestsData(context));
@@ -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 { setupDedicatedProfile } from './profile-manager.js';
11
10
  let browser;
12
11
  const ignoredPrefixes = new Set([
13
12
  'chrome://',
@@ -226,31 +225,28 @@ function discoverSystemExtensions(channel) {
226
225
  }
227
226
  return extensionPaths;
228
227
  }
228
+ // Store development extension paths globally for later retrieval
229
+ let developmentExtensionPaths = [];
230
+ export function getDevelopmentExtensionPaths() {
231
+ return developmentExtensionPaths;
232
+ }
229
233
  export async function launch(options) {
230
234
  const { channel, executablePath, customDevTools, headless, isolated, loadExtension, loadExtensionsDir, loadSystemExtensions, } = options;
235
+ // Reset development extension paths
236
+ developmentExtensionPaths = [];
231
237
  const profileDirName = channel && channel !== 'stable'
232
238
  ? `chrome-profile-${channel}`
233
239
  : 'chrome-profile';
234
240
  let userDataDir = options.userDataDir;
235
241
  let usingSystemProfile = false;
236
242
  let profileDirectory = 'Default';
237
- if (!isolated && !userDataDir) {
238
- // Use dedicated profile with symlinks to system extensions and bookmarks
239
- try {
240
- const dedicatedProfile = await setupDedicatedProfile(channel);
241
- userDataDir = dedicatedProfile.userDataDir;
242
- profileDirectory = dedicatedProfile.profileDirectory;
243
- usingSystemProfile = false; // Using dedicated profile, not system profile
244
- }
245
- catch (error) {
246
- // Fallback to isolated profile if setup fails
247
- console.error(`⚠️ Failed to setup dedicated profile: ${error instanceof Error ? error.message : String(error)}`);
248
- userDataDir = path.join(os.homedir(), '.cache', 'chrome-devtools-mcp', profileDirName);
249
- await fs.promises.mkdir(userDataDir, {
250
- recursive: true,
251
- });
252
- console.error(`📁 Using isolated profile (fallback)`);
253
- }
243
+ if (!userDataDir) {
244
+ // Use isolated profile (independent from system Chrome)
245
+ userDataDir = path.join(os.homedir(), '.cache', 'chrome-devtools-mcp', profileDirName);
246
+ await fs.promises.mkdir(userDataDir, {
247
+ recursive: true,
248
+ });
249
+ console.error(`📁 Using isolated profile: ${userDataDir}`);
254
250
  }
255
251
  const args = [
256
252
  '--hide-crash-restore-bubble',
@@ -271,6 +267,7 @@ export async function launch(options) {
271
267
  const manifest = JSON.parse(manifestContent);
272
268
  if (manifest.manifest_version) {
273
269
  extensionPaths.push(loadExtension);
270
+ developmentExtensionPaths.push(loadExtension); // Track as development extension
274
271
  console.error(`✅ Single extension validated: ${loadExtension}`);
275
272
  }
276
273
  else {
@@ -292,13 +289,15 @@ export async function launch(options) {
292
289
  if (loadExtensionsDir) {
293
290
  const scannedExtensions = scanExtensionsDirectory(loadExtensionsDir);
294
291
  extensionPaths.push(...scannedExtensions);
292
+ developmentExtensionPaths.push(...scannedExtensions); // Track as development extensions
295
293
  }
296
- // System extension discovery
297
- if (loadSystemExtensions) {
294
+ // System extension discovery (default: true unless isolated flag is set)
295
+ const shouldLoadSystemExtensions = loadSystemExtensions ?? !isolated;
296
+ if (shouldLoadSystemExtensions) {
298
297
  const systemExtensions = discoverSystemExtensions(channel);
299
298
  if (systemExtensions.length > 0) {
300
299
  extensionPaths.push(...systemExtensions);
301
- console.error(`🔗 Integrated ${systemExtensions.length} system extensions with development extensions`);
300
+ console.error(`✅ Loaded ${systemExtensions.length} system Chrome extension(s)`);
302
301
  }
303
302
  else {
304
303
  console.warn(`⚠️ No system extensions found or accessible`);
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Format chrome://extensions page with structured output
3
+ */
4
+ export function formatExtensionsPage(root, developmentExtensionPaths) {
5
+ const groups = groupExtensions(root, developmentExtensionPaths);
6
+ let result = '# Chrome Extensions Page\n\n';
7
+ // Developer mode section
8
+ const devModeSwitch = findDeveloperModeSwitch(root);
9
+ if (devModeSwitch) {
10
+ const isChecked = devModeSwitch.checked === true;
11
+ result += `## Developer Mode: ${isChecked ? 'ON ✓' : 'OFF ✗'}\n`;
12
+ result += ` uid=${devModeSwitch.id} switch "${devModeSwitch.name || devModeSwitch.description || 'Developer mode'}" ${isChecked ? '[checked]' : '[unchecked]'}\n\n`;
13
+ }
14
+ // Development extensions first
15
+ const devGroups = groups.filter(g => g.isDevelopment);
16
+ const systemGroups = groups.filter(g => !g.isDevelopment);
17
+ if (devGroups.length > 0) {
18
+ result += `## 🔧 Development Extensions (Your Project)\n\n`;
19
+ for (const group of devGroups) {
20
+ result += formatExtensionGroup(group);
21
+ }
22
+ }
23
+ if (systemGroups.length > 0) {
24
+ result += `## 📦 System Extensions\n\n`;
25
+ for (const group of systemGroups) {
26
+ result += formatExtensionGroup(group);
27
+ }
28
+ }
29
+ return result;
30
+ }
31
+ function formatExtensionGroup(group) {
32
+ let result = '';
33
+ // Header with status indicators
34
+ const statusIcon = group.enabled ? '✓' : '✗';
35
+ const errorIcon = group.errors.length > 0 ? ' ⚠️ HAS ERRORS' : '';
36
+ const devIcon = group.isDevelopment ? ' ⭐' : '';
37
+ result += `### Extension: "${group.name}" v${group.version} [${group.enabled ? 'ENABLED' : 'DISABLED'}]${statusIcon}${devIcon}${errorIcon}\n`;
38
+ if (group.location) {
39
+ result += ` Location: ${group.location}\n`;
40
+ }
41
+ // Find and label key buttons
42
+ const buttons = {
43
+ details: findButton(group.nodes, ['Details', 'View', 'Show details']),
44
+ remove: findButton(group.nodes, ['Remove', 'Delete', 'Uninstall']),
45
+ reload: findButton(group.nodes, ['Reload', 'Refresh']),
46
+ errors: findButton(group.nodes, ['Errors', 'Error', 'View errors']),
47
+ enabled: findSwitch(group.nodes, ['Enabled', 'Enable']),
48
+ };
49
+ // Display buttons with clear labels
50
+ if (buttons.details) {
51
+ result += ` uid=${buttons.details.id} button "Details"\n`;
52
+ }
53
+ if (buttons.remove) {
54
+ result += ` uid=${buttons.remove.id} button "Remove"\n`;
55
+ }
56
+ if (buttons.reload) {
57
+ result += ` uid=${buttons.reload.id} button "Reload" ${group.isDevelopment ? '← USE THIS TO RELOAD YOUR EXTENSION' : ''}\n`;
58
+ }
59
+ if (buttons.errors) {
60
+ result += ` uid=${buttons.errors.id} button "Errors" [clickable]\n`;
61
+ }
62
+ if (buttons.enabled) {
63
+ const isChecked = buttons.enabled.checked === true;
64
+ result += ` uid=${buttons.enabled.id} switch "Enabled" ${isChecked ? '[checked]' : '[unchecked]'}\n`;
65
+ }
66
+ // Display errors if any
67
+ if (group.errors.length > 0) {
68
+ result += `\n ⚠️ Errors:\n`;
69
+ for (const error of group.errors) {
70
+ result += ` - ${error}\n`;
71
+ }
72
+ }
73
+ result += '\n';
74
+ return result;
75
+ }
76
+ function groupExtensions(root, developmentPaths) {
77
+ const groups = [];
78
+ // Find extension cards (typically have role="article" or contain extension name as heading)
79
+ const findExtensionCards = (node) => {
80
+ // Look for extension name patterns
81
+ if (node.role === 'heading' &&
82
+ node.level === 3 &&
83
+ node.name &&
84
+ !node.name.includes('Chrome Web Store')) {
85
+ // This is likely an extension name
86
+ const extensionName = node.name;
87
+ // Find sibling nodes that belong to this extension
88
+ const siblings = [];
89
+ let version = '';
90
+ let enabled = true;
91
+ let location = '';
92
+ const errors = [];
93
+ // Collect related nodes (buttons, switches, text)
94
+ const collectNodes = (n, depth = 0) => {
95
+ if (depth > 5)
96
+ return; // Limit depth
97
+ siblings.push(n);
98
+ // Extract version
99
+ if (n.role === 'text' && n.name && /^v?\d+\.\d+/.test(n.name)) {
100
+ version = n.name;
101
+ }
102
+ // Extract location for development extensions
103
+ if (n.role === 'text' &&
104
+ n.name &&
105
+ (n.name.includes('/') || n.name.includes('\\'))) {
106
+ location = n.name;
107
+ }
108
+ // Extract errors
109
+ if ((n.role === 'text' || n.role === 'paragraph') &&
110
+ n.name &&
111
+ (n.name.toLowerCase().includes('error') ||
112
+ n.name.toLowerCase().includes('failed') ||
113
+ n.name.toLowerCase().includes('warning'))) {
114
+ errors.push(n.name);
115
+ }
116
+ for (const child of n.children) {
117
+ collectNodes(child, depth + 1);
118
+ }
119
+ };
120
+ // Collect from parent's children
121
+ collectNodes(node);
122
+ // Determine if it's a development extension
123
+ const isDevelopment = developmentPaths.some(path => location.includes(path) ||
124
+ extensionName.toLowerCase().includes('development'));
125
+ groups.push({
126
+ name: extensionName,
127
+ version: version || '0.0.0',
128
+ enabled,
129
+ isDevelopment,
130
+ location: location || undefined,
131
+ nodes: siblings,
132
+ errors,
133
+ });
134
+ }
135
+ for (const child of node.children) {
136
+ findExtensionCards(child);
137
+ }
138
+ };
139
+ findExtensionCards(root);
140
+ return groups;
141
+ }
142
+ function findButton(nodes, keywords) {
143
+ for (const node of nodes) {
144
+ if (node.role === 'button') {
145
+ const text = node.name || node.description || node.roledescription || '';
146
+ for (const keyword of keywords) {
147
+ if (text.toLowerCase().includes(keyword.toLowerCase())) {
148
+ return node;
149
+ }
150
+ }
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+ function findSwitch(nodes, keywords) {
156
+ for (const node of nodes) {
157
+ if (node.role === 'switch') {
158
+ const text = node.name || node.description || node.roledescription || '';
159
+ for (const keyword of keywords) {
160
+ if (text.toLowerCase().includes(keyword.toLowerCase())) {
161
+ return node;
162
+ }
163
+ }
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ function findDeveloperModeSwitch(root) {
169
+ const search = (node) => {
170
+ if (node.role === 'switch') {
171
+ const text = node.name || node.description || node.roledescription || '';
172
+ if (text.toLowerCase().includes('developer')) {
173
+ return node;
174
+ }
175
+ }
176
+ for (const child of node.children) {
177
+ const found = search(child);
178
+ if (found)
179
+ return found;
180
+ }
181
+ return null;
182
+ };
183
+ return search(root);
184
+ }
@@ -9,10 +9,15 @@ export function formatA11ySnapshot(serializedAXNodeRoot, depth = 0) {
9
9
  return result;
10
10
  }
11
11
  function getAttributes(serializedAXNodeRoot) {
12
+ // Prefer description for buttons/switches with empty names
13
+ const displayLabel = serializedAXNodeRoot.name ||
14
+ serializedAXNodeRoot.description ||
15
+ serializedAXNodeRoot.roledescription ||
16
+ '';
12
17
  const attributes = [
13
18
  `uid=${serializedAXNodeRoot.id}`,
14
19
  serializedAXNodeRoot.role,
15
- `"${serializedAXNodeRoot.name || ''}"`, // Corrected: Added quotes around name
20
+ `"${displayLabel}"`,
16
21
  ];
17
22
  // Value properties
18
23
  const valueProperties = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-devtools-mcp-for-extension",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
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",