chrometools-mcp 1.7.0 → 1.8.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/CHANGELOG.md +111 -0
- package/README.md +122 -4
- package/element-finder-utils.js +27 -8
- package/index.js +340 -157
- package/package.json +1 -1
- package/recorder/recorder-script.js +1667 -1598
- package/recorder/scenario-executor.js +946 -942
- package/recorder/scenario-storage.js +85 -58
- package/utils/code-generators/code-generator-base.js +237 -0
- package/utils/code-generators/playwright-python.js +253 -0
- package/utils/code-generators/playwright-typescript.js +230 -0
- package/utils/code-generators/selenium-java.js +309 -0
- package/utils/code-generators/selenium-python.js +328 -0
- package/utils/project-detector.js +87 -0
- package/utils/selector-cleaner.js +352 -0
- package/RECORDER_SPEC.md +0 -911
- package/REFACTORING-SUMMARY.md +0 -132
- package/REFACTORING.md +0 -57
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,117 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.8.2] - 2025-12-19
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Code Generator Bugs** - Fixed multiple issues in test code generators
|
|
9
|
+
- **Python generators**: Fixed comment syntax - now use `#` instead of `//`
|
|
10
|
+
- **Python generators**: Moved `import re` to top of file instead of inline
|
|
11
|
+
- **Java generator**: Fixed variable name conflicts - now uses unique names (`typeElement`, `hoverElement`, etc.) instead of reusing `element`
|
|
12
|
+
- **Java generator**: Added missing imports (`JavascriptExecutor`, `Select`)
|
|
13
|
+
- **All generators**: Implemented language-specific comment generation via `generateComment()` method
|
|
14
|
+
- Location: `utils/code-generators/code-generator-base.js`, `playwright-python.js`, `selenium-python.js`, `selenium-java.js`
|
|
15
|
+
|
|
16
|
+
## [1.8.1] - 2025-12-19
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **Smart Project Directory Detection** - Scenarios now automatically save to the correct project directory
|
|
20
|
+
- Auto-detection cascade: `CLAUDE_PROJECT_DIR` env var → `PROJECT_DIR` env var → Git root → current working directory
|
|
21
|
+
- Optional `directory` parameter on all recorder tools (`enableRecorder`, `executeScenario`, `listScenarios`, `searchScenarios`, `getScenarioInfo`, `deleteScenario`, `exportScenarioAsCode`)
|
|
22
|
+
- Session memory: once directory is set (explicitly or auto-detected), it's remembered for the entire MCP server session
|
|
23
|
+
- Solves issue where scenarios were saved to unpredictable locations based on where MCP process was launched
|
|
24
|
+
- Location: `utils/project-detector.js`, `index.js:97-115`
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- **All recorder storage functions now accept `baseDir` parameter** - Breaking change for direct API usage
|
|
28
|
+
- Updated: `saveScenario()`, `loadScenario()`, `listScenarios()`, `searchScenarios()`, `deleteScenario()`, `loadIndex()`, and all other storage functions
|
|
29
|
+
- MCP tool users: no breaking changes, just new optional `directory` parameter
|
|
30
|
+
- Location: `recorder/scenario-storage.js`, `recorder/scenario-executor.js`, `recorder/recorder-script.js`
|
|
31
|
+
|
|
32
|
+
## [1.8.0] - 2025-12-19
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- **Test Code Generation** - New MCP tool `exportScenarioAsCode` for generating executable test code from recorded scenarios
|
|
36
|
+
- Supports 4 test frameworks: Playwright (TypeScript/Python), Selenium (Python/Java)
|
|
37
|
+
- Automatic selector cleaning - removes unstable CSS classes (CSS Modules, styled-components, Emotion, hashed classes)
|
|
38
|
+
- Generates clean, readable test code with comments
|
|
39
|
+
- Smart selector stability analysis with pattern-based detection
|
|
40
|
+
- Fallback selector selection - chooses most stable selector from fallbacks
|
|
41
|
+
- Location: `utils/code-generators/`, `utils/selector-cleaner.js`
|
|
42
|
+
|
|
43
|
+
**Unstable patterns detected:**
|
|
44
|
+
- CSS Modules: `Button_primary__2x3yZ`
|
|
45
|
+
- Styled-components: `sc-AbCdEf-0`
|
|
46
|
+
- Emotion: `css-1a2b3c4d`
|
|
47
|
+
- Hash suffixes: `component_a1b2c3d`
|
|
48
|
+
- Random hashes: `_1a2b3c4d`
|
|
49
|
+
|
|
50
|
+
**Usage:**
|
|
51
|
+
```javascript
|
|
52
|
+
exportScenarioAsCode('checkout', {
|
|
53
|
+
language: 'playwright-typescript',
|
|
54
|
+
cleanSelectors: true,
|
|
55
|
+
includeComments: true
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## [1.7.4] - 2025-12-19
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
- **executeScenario auto-opens browser** - No longer throws error when no page is open
|
|
63
|
+
- Automatically opens browser at scenario's `entryUrl` if no page is currently open
|
|
64
|
+
- Eliminates need to manually call `openBrowser` before executing scenarios
|
|
65
|
+
- Improves user experience by treating browser opening as a side effect
|
|
66
|
+
- Falls back gracefully: shows error only if scenario has no `entryUrl`
|
|
67
|
+
- Location: `index.js:3417-3469`
|
|
68
|
+
|
|
69
|
+
## [1.7.3] - 2025-12-19
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
- **Fixed CSS selector generation crash** - `analyzePage` now handles attribute values with special characters
|
|
73
|
+
- Added `isSafeSelectorValue()` function to validate attribute values before using in selectors
|
|
74
|
+
- Filters out attributes containing problematic characters: `["'\\[]{}()]`
|
|
75
|
+
- Added try-catch blocks to prevent selector syntax errors
|
|
76
|
+
- Example: `button[data-counter="["b"]"]` (invalid) is now skipped
|
|
77
|
+
- Location: `element-finder-utils.js:302-360`
|
|
78
|
+
|
|
79
|
+
### Improved
|
|
80
|
+
- **Recorder now skips hidden elements** - Prevents recording actions on invisible elements
|
|
81
|
+
- Added `isElementVisible()` function to check element visibility before recording
|
|
82
|
+
- Checks: offsetWidth/Height, display, visibility, opacity
|
|
83
|
+
- Applies to all event types: click, type, select, upload, hover, drag
|
|
84
|
+
- Prevents scenarios with duplicate/invisible element actions (e.g., Yandex search with hidden input)
|
|
85
|
+
- Console logs when skipping hidden elements for debugging
|
|
86
|
+
- Location: `recorder/recorder-script.js:1173-1188`
|
|
87
|
+
|
|
88
|
+
## [1.7.2] - 2025-12-16
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
- **Figma API Token Setup documentation** - Added comprehensive guide on how to obtain and configure Figma Personal Access Token
|
|
92
|
+
- Step-by-step instructions for getting token from Figma account settings
|
|
93
|
+
- Configuration examples for both Claude Desktop and Claude Code
|
|
94
|
+
- Environment variable setup (`FIGMA_TOKEN`)
|
|
95
|
+
- Note about alternative parameter-based token passing
|
|
96
|
+
|
|
97
|
+
## [1.7.1] - 2025-12-15
|
|
98
|
+
|
|
99
|
+
### Performance
|
|
100
|
+
- **Optimized tool descriptions** - Reduced token usage by 35-45% (~1,500-2,000 tokens)
|
|
101
|
+
- Shortened main tool descriptions from verbose to concise format
|
|
102
|
+
- Reduced parameter descriptions (e.g., "CSS selector for element to click" → "CSS selector")
|
|
103
|
+
- Standardized Figma tool parameters (7 tools optimized)
|
|
104
|
+
- Pattern-based reductions across all 41 tools
|
|
105
|
+
- Impact: Saves 1,500-2,000 tokens in every request to Claude
|
|
106
|
+
- Examples:
|
|
107
|
+
- `analyzePage`: 95 tokens → 30 tokens (68% reduction)
|
|
108
|
+
- `screenshot`: 75 tokens → 25 tokens (66% reduction)
|
|
109
|
+
- `listNetworkRequests`: 50 tokens → 20 tokens (60% reduction)
|
|
110
|
+
|
|
111
|
+
### Changed
|
|
112
|
+
- All tool descriptions now use imperative voice and remove redundancy
|
|
113
|
+
- Figma tools: "Figma API token (optional if FIGMA_TOKEN env var is set)" → "API token (optional)"
|
|
114
|
+
- Common patterns: "Milliseconds to wait" → "Wait ms", "Maximum" → "Max", etc.
|
|
115
|
+
|
|
5
116
|
## [1.7.0] - 2025-12-14
|
|
6
117
|
|
|
7
118
|
### Removed
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ MCP server for Chrome automation using Puppeteer with persistent browser session
|
|
|
14
14
|
- [Interaction Tools](#2-interaction-tools) - click, type, scrollTo
|
|
15
15
|
- [Inspection Tools](#3-inspection-tools) - getElement, getComputedCss, getBoxModel, screenshot
|
|
16
16
|
- [Advanced Tools](#4-advanced-tools) - executeScript, getConsoleLogs, listNetworkRequests, getNetworkRequest, filterNetworkRequests, hover, setStyles, setViewport, getViewport, navigateTo
|
|
17
|
-
- [Recorder Tools](#6-recorder-tools) ⭐ **NEW** - enableRecorder, executeScenario, listScenarios, searchScenarios, getScenarioInfo, deleteScenario
|
|
17
|
+
- [Recorder Tools](#6-recorder-tools) ⭐ **NEW** - enableRecorder, executeScenario, listScenarios, searchScenarios, getScenarioInfo, deleteScenario, exportScenarioAsCode
|
|
18
18
|
- [Typical Workflow Example](#typical-workflow-example)
|
|
19
19
|
- [Tool Usage Tips](#tool-usage-tips)
|
|
20
20
|
- [Configuration](#configuration)
|
|
@@ -506,9 +506,32 @@ Extract detailed design specifications from Figma including text content, colors
|
|
|
506
506
|
|
|
507
507
|
### 6. Recorder Tools ⭐ NEW
|
|
508
508
|
|
|
509
|
+
**Directory Management**: All recorder tools support an optional `directory` parameter to specify where scenarios are stored. If not provided, the directory is auto-detected using this cascade:
|
|
510
|
+
1. `CLAUDE_PROJECT_DIR` environment variable (set by Claude Code)
|
|
511
|
+
2. `PROJECT_DIR` environment variable (custom)
|
|
512
|
+
3. Git repository root (detected via `git rev-parse --show-toplevel`)
|
|
513
|
+
4. Current working directory (fallback)
|
|
514
|
+
|
|
515
|
+
Once a directory is set (explicitly or auto-detected), it's remembered for the entire MCP server session.
|
|
516
|
+
|
|
517
|
+
**Example**:
|
|
518
|
+
```javascript
|
|
519
|
+
// Let it auto-detect (recommended)
|
|
520
|
+
enableRecorder()
|
|
521
|
+
|
|
522
|
+
// Or specify explicitly
|
|
523
|
+
enableRecorder({ directory: "/path/to/project" })
|
|
524
|
+
|
|
525
|
+
// Later calls reuse the same directory automatically
|
|
526
|
+
executeScenario({ name: "test" }) // Uses remembered directory
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
509
531
|
#### enableRecorder
|
|
510
532
|
Inject visual recorder UI widget into the current page.
|
|
511
|
-
- **Parameters**:
|
|
533
|
+
- **Parameters**:
|
|
534
|
+
- `directory` (optional): Directory to save scenarios (auto-detected if not provided)
|
|
512
535
|
- **Use case**: Start recording user interactions visually
|
|
513
536
|
- **Returns**: Success status
|
|
514
537
|
- **Features**:
|
|
@@ -524,6 +547,7 @@ Execute a previously recorded scenario by name.
|
|
|
524
547
|
- `name` (required): Scenario name
|
|
525
548
|
- `parameters` (optional): Runtime parameters (e.g., { email: "user@test.com" })
|
|
526
549
|
- `executeDependencies` (optional): Execute dependencies before running scenario (default: true)
|
|
550
|
+
- `directory` (optional): Directory where scenarios are stored (auto-detected if not provided)
|
|
527
551
|
- **Use case**: Run automated test scenarios
|
|
528
552
|
- **Returns**: Execution result with success/failure status
|
|
529
553
|
- **Features**:
|
|
@@ -541,7 +565,8 @@ Execute a previously recorded scenario by name.
|
|
|
541
565
|
|
|
542
566
|
#### listScenarios
|
|
543
567
|
Get all available scenarios with metadata.
|
|
544
|
-
- **Parameters**:
|
|
568
|
+
- **Parameters**:
|
|
569
|
+
- `directory` (optional): Directory where scenarios are stored (auto-detected if not provided)
|
|
545
570
|
- **Use case**: Browse recorded scenarios
|
|
546
571
|
- **Returns**: Array of scenarios with names, descriptions, tags, timestamps
|
|
547
572
|
|
|
@@ -550,6 +575,7 @@ Search scenarios by text or tags.
|
|
|
550
575
|
- **Parameters**:
|
|
551
576
|
- `text` (optional): Search in name/description
|
|
552
577
|
- `tags` (optional): Array of tags to filter
|
|
578
|
+
- `directory` (optional): Directory where scenarios are stored (auto-detected if not provided)
|
|
553
579
|
- **Use case**: Find specific scenarios
|
|
554
580
|
- **Returns**: Matching scenarios
|
|
555
581
|
|
|
@@ -558,15 +584,59 @@ Get detailed information about a scenario.
|
|
|
558
584
|
- **Parameters**:
|
|
559
585
|
- `name` (required): Scenario name
|
|
560
586
|
- `includeSecrets` (optional): Include secret values (default: false)
|
|
587
|
+
- `directory` (optional): Directory where scenarios are stored (auto-detected if not provided)
|
|
561
588
|
- **Use case**: Inspect scenario actions and dependencies
|
|
562
589
|
- **Returns**: Full scenario details (actions, metadata, dependencies)
|
|
563
590
|
|
|
564
591
|
#### deleteScenario
|
|
565
592
|
Delete a scenario and its associated secrets.
|
|
566
|
-
- **Parameters**:
|
|
593
|
+
- **Parameters**:
|
|
594
|
+
- `name` (required): Scenario name
|
|
595
|
+
- `directory` (optional): Directory where scenarios are stored (auto-detected if not provided)
|
|
567
596
|
- **Use case**: Clean up unused scenarios
|
|
568
597
|
- **Returns**: Success confirmation
|
|
569
598
|
|
|
599
|
+
#### exportScenarioAsCode ⭐ **NEW**
|
|
600
|
+
Export recorded scenario as executable test code for various frameworks. Automatically cleans unstable selectors (CSS Modules, styled-components, Emotion).
|
|
601
|
+
|
|
602
|
+
- **Parameters**:
|
|
603
|
+
- `scenarioName` (required): Name of scenario to export
|
|
604
|
+
- `language` (required): Target framework - `"playwright-typescript"`, `"playwright-python"`, `"selenium-python"`, `"selenium-java"`
|
|
605
|
+
- `cleanSelectors` (optional): Remove unstable CSS classes (default: true)
|
|
606
|
+
- `includeComments` (optional): Include descriptive comments (default: true)
|
|
607
|
+
- `directory` (optional): Directory where scenarios are stored (auto-detected if not provided)
|
|
608
|
+
|
|
609
|
+
- **Use case**: Convert recorded scenarios into maintainable test code
|
|
610
|
+
|
|
611
|
+
- **Returns**: Generated test code as string
|
|
612
|
+
|
|
613
|
+
- **Example**:
|
|
614
|
+
```javascript
|
|
615
|
+
// Export scenario as Playwright TypeScript
|
|
616
|
+
exportScenarioAsCode({
|
|
617
|
+
scenarioName: "checkout_flow",
|
|
618
|
+
language: "playwright-typescript",
|
|
619
|
+
cleanSelectors: true,
|
|
620
|
+
includeComments: true
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
// Returns clean test code:
|
|
624
|
+
// import { test, expect } from '@playwright/test';
|
|
625
|
+
//
|
|
626
|
+
// test('checkout_flow', async ({ page }) => {
|
|
627
|
+
// await page.goto('https://example.com');
|
|
628
|
+
// await page.locator('button[data-testid="add-to-cart"]').click();
|
|
629
|
+
// await expect(page).toHaveURL(/checkout/);
|
|
630
|
+
// });
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
- **Selector Cleaning**: Automatically removes unstable patterns:
|
|
634
|
+
- CSS Modules: `Button_primary__2x3yZ` → removed
|
|
635
|
+
- Styled-components: `sc-AbCdEf-0` → removed
|
|
636
|
+
- Emotion: `css-1a2b3c4d` → removed
|
|
637
|
+
- Hash suffixes: `component_a1b2c3d` → removed
|
|
638
|
+
- Prefers stable selectors: `data-testid`, `role`, `aria-label`, semantic attributes
|
|
639
|
+
|
|
570
640
|
---
|
|
571
641
|
|
|
572
642
|
## Typical Workflow Example
|
|
@@ -667,6 +737,54 @@ If you don't need to see the browser window, you can use xvfb (virtual X server)
|
|
|
667
737
|
|
|
668
738
|
This runs Chrome in GUI mode but on a virtual display (window is not visible).
|
|
669
739
|
|
|
740
|
+
### Figma API Token Setup
|
|
741
|
+
|
|
742
|
+
To use Figma tools, you need to configure your Figma Personal Access Token.
|
|
743
|
+
|
|
744
|
+
**How to get your Figma token:**
|
|
745
|
+
1. Go to your Figma account settings: https://www.figma.com/settings
|
|
746
|
+
2. Scroll down to "Personal access tokens"
|
|
747
|
+
3. Click "Create a new personal access token"
|
|
748
|
+
4. Give it a name (e.g., "chrometools-mcp")
|
|
749
|
+
5. Copy the generated token
|
|
750
|
+
|
|
751
|
+
**Add token to MCP configuration:**
|
|
752
|
+
|
|
753
|
+
**Claude Desktop** (`~/.claude/mcp_config.json` or `~/AppData/Roaming/Claude/mcp_config.json` on Windows):
|
|
754
|
+
|
|
755
|
+
```json
|
|
756
|
+
{
|
|
757
|
+
"mcpServers": {
|
|
758
|
+
"chrometools": {
|
|
759
|
+
"command": "npx",
|
|
760
|
+
"args": ["-y", "chrometools-mcp"],
|
|
761
|
+
"env": {
|
|
762
|
+
"FIGMA_TOKEN": "your-figma-token-here"
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**Claude Code** (`~/.claude.json`):
|
|
770
|
+
|
|
771
|
+
```json
|
|
772
|
+
{
|
|
773
|
+
"mcpServers": {
|
|
774
|
+
"chrometools": {
|
|
775
|
+
"type": "stdio",
|
|
776
|
+
"command": "npx",
|
|
777
|
+
"args": ["-y", "chrometools-mcp"],
|
|
778
|
+
"env": {
|
|
779
|
+
"FIGMA_TOKEN": "your-figma-token-here"
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
**Note:** Alternatively, you can pass the token directly in each Figma tool call using the `figmaToken` parameter, but using the environment variable is more convenient.
|
|
787
|
+
|
|
670
788
|
---
|
|
671
789
|
|
|
672
790
|
## WSL Setup Guide
|
package/element-finder-utils.js
CHANGED
|
@@ -295,6 +295,16 @@ function scoreInputField(element, context, description) {
|
|
|
295
295
|
return score;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Check if attribute value is safe to use in CSS selector
|
|
300
|
+
* Returns false if value contains characters that could break selector syntax
|
|
301
|
+
*/
|
|
302
|
+
function isSafeSelectorValue(value) {
|
|
303
|
+
if (!value || typeof value !== 'string') return false;
|
|
304
|
+
// Check for problematic characters: quotes, brackets, backslashes
|
|
305
|
+
return !/["'\\[\]{}()]/.test(value);
|
|
306
|
+
}
|
|
307
|
+
|
|
298
308
|
/**
|
|
299
309
|
* Generate unique CSS selector for an element
|
|
300
310
|
*/
|
|
@@ -320,23 +330,32 @@ function getUniqueSelectorInPage(element) {
|
|
|
320
330
|
}
|
|
321
331
|
}
|
|
322
332
|
|
|
323
|
-
// Try name attribute
|
|
324
|
-
if (element.name) {
|
|
333
|
+
// Try name attribute (only if value is safe)
|
|
334
|
+
if (element.name && isSafeSelectorValue(element.name)) {
|
|
325
335
|
const selector = `${element.tagName.toLowerCase()}[name="${element.name}"]`;
|
|
326
|
-
|
|
327
|
-
|
|
336
|
+
try {
|
|
337
|
+
if (document.querySelectorAll(selector).length === 1) {
|
|
338
|
+
return selector;
|
|
339
|
+
}
|
|
340
|
+
} catch (e) {
|
|
341
|
+
// Invalid selector, skip
|
|
328
342
|
}
|
|
329
343
|
}
|
|
330
344
|
|
|
331
|
-
// Try data attributes
|
|
345
|
+
// Try data attributes (only if values are safe)
|
|
332
346
|
const dataAttrs = Array.from(element.attributes)
|
|
333
|
-
.filter(attr => attr.name.startsWith('data-'))
|
|
347
|
+
.filter(attr => attr.name.startsWith('data-') && isSafeSelectorValue(attr.value))
|
|
334
348
|
.slice(0, 2);
|
|
335
349
|
|
|
336
350
|
for (const attr of dataAttrs) {
|
|
337
351
|
const selector = `${element.tagName.toLowerCase()}[${attr.name}="${attr.value}"]`;
|
|
338
|
-
|
|
339
|
-
|
|
352
|
+
try {
|
|
353
|
+
if (document.querySelectorAll(selector).length === 1) {
|
|
354
|
+
return selector;
|
|
355
|
+
}
|
|
356
|
+
} catch (e) {
|
|
357
|
+
// Invalid selector, skip
|
|
358
|
+
continue;
|
|
340
359
|
}
|
|
341
360
|
}
|
|
342
361
|
|