chrometools-mcp 2.2.0 → 2.4.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.
- package/CHANGELOG.md +105 -0
- package/README.md +207 -57
- package/index.js +216 -74
- package/package.json +1 -1
- package/server/tool-definitions.js +41 -6
- package/server/tool-groups.js +82 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,111 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.4.0] - 2025-12-29
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Tool Filtering by Groups** - New `ENABLED_TOOLS` environment variable for selective group enabling
|
|
9
|
+
- Configure via `env.ENABLED_TOOLS` in MCP client config (comma-separated list of group names)
|
|
10
|
+
- Available groups: `core`, `interaction`, `inspection`, `debug`, `advanced`, `recorder`, `figma`
|
|
11
|
+
- Group structure optimized:
|
|
12
|
+
- `debug` - NEW group for debugging tools (console logs, network monitoring)
|
|
13
|
+
- `advanced` - Combined with AI tools (now includes smartFindElement, analyzePage, etc.)
|
|
14
|
+
- If not set, all tools are enabled (default behavior)
|
|
15
|
+
- If set, only tools from specified groups are available to AI
|
|
16
|
+
- Primary benefit: **Token optimization** - all 43 tools consume ~28k tokens (~14% of context). Enable only needed groups to reduce token usage and costs
|
|
17
|
+
- Additional use cases: security/compliance restrictions, workflow simplification, improved AI focus
|
|
18
|
+
- Examples:
|
|
19
|
+
- Basic automation: `ENABLED_TOOLS=core,interaction,inspection`
|
|
20
|
+
- Advanced with AI: `ENABLED_TOOLS=core,interaction,advanced`
|
|
21
|
+
- With debugging: `ENABLED_TOOLS=core,interaction,inspection,debug`
|
|
22
|
+
- Figma validation: `ENABLED_TOOLS=core,figma`
|
|
23
|
+
- Location:
|
|
24
|
+
- `server/tool-groups.js` - group definitions (7 groups, 43 tools total)
|
|
25
|
+
- `index.js:36` - import groups module
|
|
26
|
+
- `index.js:77-92` - parsing and filtering logic
|
|
27
|
+
- `index.js:195-203` - apply filter to ListTools handler
|
|
28
|
+
- Documentation: README.md section "Tool Filtering with ENABLED_TOOLS"
|
|
29
|
+
|
|
30
|
+
## [2.3.2] - 2025-12-25
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- **appendScenarioToFile** - Simplified architecture: MCP server no longer reads test files
|
|
34
|
+
- Removed `FileAppender.validateFile()` and `FileAppender.readFile()` calls
|
|
35
|
+
- Removed `generator.appendTest()` call - no longer merges content server-side
|
|
36
|
+
- Returns only test code (without imports) via `testCode` field
|
|
37
|
+
- Changed `action: "write_file"` → `action: "append_test"` (more accurate description)
|
|
38
|
+
- Claude Code now responsible for reading file, appending test, and writing back
|
|
39
|
+
- Clearer separation of concerns: MCP generates code, Claude Code handles file I/O
|
|
40
|
+
- Location: `index.js:2126-2215`
|
|
41
|
+
|
|
42
|
+
## [2.3.1] - 2024-12-25
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
- **executeScenario** - Fixed scenario execution when current page URL doesn't match scenario's entryUrl
|
|
46
|
+
- Added automatic navigation to entryUrl before executing scenario
|
|
47
|
+
- Normalizes URLs for comparison (ignores trailing slashes, query params like `nr`, `redirect_ts`)
|
|
48
|
+
- Prevents timeout errors when scenario expects different page than currently open
|
|
49
|
+
- Example: If scenario recorded on `ya.ru` but current page is `ya.ru/search/`, automatically navigates to `ya.ru`
|
|
50
|
+
- Logs navigation events to console for debugging
|
|
51
|
+
- Location: `index.js:1951-1987`
|
|
52
|
+
|
|
53
|
+
- **appendScenarioToFile** - Unified response format with exportScenarioAsCode for better Claude Code compatibility
|
|
54
|
+
- Changed `action: "append_to_file"` → `action: "write_file"` (clearer action)
|
|
55
|
+
- Changed `updatedContent` → `testCode` (same field name as exportScenarioAsCode)
|
|
56
|
+
- Added `content` field (duplicate of testCode for compatibility)
|
|
57
|
+
- Simplified instruction: single-step "Write the testCode..." instead of two-step "Read... then write..."
|
|
58
|
+
- Improved error message when file not found: now suggests using exportScenarioAsCode instead
|
|
59
|
+
- Location: `index.js:2191-2229`
|
|
60
|
+
|
|
61
|
+
## [2.3.0] - 2024-12-25
|
|
62
|
+
|
|
63
|
+
### Breaking Changes
|
|
64
|
+
- **exportScenarioAsCode** - Removed append-to-file functionality to eliminate confusion
|
|
65
|
+
- **REMOVED** parameters: `appendToFile`, `testName`, `insertPosition`, `referenceTestName`
|
|
66
|
+
- Now exclusively creates NEW test files (returns JSON with `action: "create_new_file"`)
|
|
67
|
+
- Returns JSON format with `action`, `suggestedFileName`, `testCode`, `instruction` fields
|
|
68
|
+
- Claude Code writes files based on returned JSON (MCP server no longer writes files directly)
|
|
69
|
+
- Migration: Use new `appendScenarioToFile` tool instead of `appendToFile` parameter
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
- **appendScenarioToFile** - NEW tool for appending tests to existing files
|
|
73
|
+
- Parameters: `scenarioName`, `language`, `targetFile` (required)
|
|
74
|
+
- Optional: `testName`, `insertPosition`, `referenceTestName`, `cleanSelectors`, `includeComments`, `generatePageObject`, `pageObjectClassName`
|
|
75
|
+
- Returns JSON with `action: "append_to_file"`, `targetFile`, `updatedContent`, `instruction`
|
|
76
|
+
- Safely appends tests without overwriting existing tests
|
|
77
|
+
- Claude Code writes updated file content based on returned JSON
|
|
78
|
+
- Location: `index.js:2041-2186`, `server/tool-definitions.js:577-628`
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
- **exportScenarioAsCode** - Changed return format from plain text to structured JSON
|
|
82
|
+
- Returns: `{action, suggestedFileName, testCode, instruction, pageObject?}`
|
|
83
|
+
- Suggests filename based on scenario name and language
|
|
84
|
+
- Includes clear instructions for Claude Code to create files
|
|
85
|
+
- Location: `index.js:2188-2357`, `server/tool-definitions.js:542-576`
|
|
86
|
+
- **File writing responsibility** - Moved from MCP server to Claude Code
|
|
87
|
+
- MCP tools now return JSON with file content + instructions
|
|
88
|
+
- Claude Code uses Write tool to create/update files
|
|
89
|
+
- Eliminates risk of MCP server directly overwriting files
|
|
90
|
+
|
|
91
|
+
### Documentation
|
|
92
|
+
- **README.md** - Split exportScenarioAsCode documentation into two sections
|
|
93
|
+
- exportScenarioAsCode: For creating new test files
|
|
94
|
+
- appendScenarioToFile: For appending to existing files
|
|
95
|
+
- Updated tool count: 39+ → 40+ tools
|
|
96
|
+
- Added clear examples for both tools with JSON response formats
|
|
97
|
+
- Location: `README.md:11,17,652-806`
|
|
98
|
+
|
|
99
|
+
## [2.2.1] - 2024-12-24
|
|
100
|
+
|
|
101
|
+
### Improved
|
|
102
|
+
- **exportScenarioAsCode** - Clarified tool description to prevent accidental test file overwrites
|
|
103
|
+
- Added explicit warning: default mode (without `appendToFile`) returns code for NEW file
|
|
104
|
+
- Emphasized that `appendToFile` parameter is **REQUIRED** to safely add tests to existing files
|
|
105
|
+
- Updated `appendToFile` parameter description to highlight safety aspect
|
|
106
|
+
- Updated `insertPosition` and `referenceTestName` descriptions to clarify they only work with `appendToFile`
|
|
107
|
+
- Added warning section in README.md documentation
|
|
108
|
+
- Location: `server/tool-definitions.js:544,575-589`, `README.md:667-669`
|
|
109
|
+
|
|
5
110
|
## [2.2.0] - 2025-12-21
|
|
6
111
|
|
|
7
112
|
### Added
|
package/README.md
CHANGED
|
@@ -8,13 +8,13 @@ MCP server for Chrome automation using Puppeteer with persistent browser session
|
|
|
8
8
|
- [Usage](#usage)
|
|
9
9
|
- [AI Optimization Features](#ai-optimization-features) ⭐ **NEW**
|
|
10
10
|
- [Scenario Recorder](#scenario-recorder) ⭐ **NEW** - Visual UI-based recording with smart optimization
|
|
11
|
-
- [Available Tools](#available-tools) - **
|
|
11
|
+
- [Available Tools](#available-tools) - **40+ Tools Total**
|
|
12
12
|
- [AI-Powered Tools](#ai-powered-tools) ⭐ **NEW** - smartFindElement, analyzePage, getAllInteractiveElements, findElementsByText
|
|
13
13
|
- [Core Tools](#1-core-tools) - ping, openBrowser
|
|
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, exportScenarioAsCode, generatePageObject
|
|
17
|
+
- [Recorder Tools](#6-recorder-tools) ⭐ **NEW** - enableRecorder, executeScenario, listScenarios, searchScenarios, getScenarioInfo, deleteScenario, exportScenarioAsCode, appendScenarioToFile, generatePageObject
|
|
18
18
|
- [Typical Workflow Example](#typical-workflow-example)
|
|
19
19
|
- [Tool Usage Tips](#tool-usage-tips)
|
|
20
20
|
- [Configuration](#configuration)
|
|
@@ -650,7 +650,7 @@ Delete a scenario and its associated secrets. Searches all projects to find the
|
|
|
650
650
|
- **Returns**: Success confirmation
|
|
651
651
|
|
|
652
652
|
#### exportScenarioAsCode ⭐ **NEW**
|
|
653
|
-
Export recorded scenario as executable test code for
|
|
653
|
+
Export recorded scenario as executable test code for creating a **NEW** test file. Automatically cleans unstable selectors (CSS Modules, styled-components, Emotion). Optionally generates Page Object class. Returns JSON with code and suggested filename - Claude Code will create the file. To add tests to **EXISTING** files, use `appendScenarioToFile` instead.
|
|
654
654
|
|
|
655
655
|
- **Parameters**:
|
|
656
656
|
- `scenarioName` (required): Name of scenario to export
|
|
@@ -659,38 +659,34 @@ Export recorded scenario as executable test code for various frameworks. Automat
|
|
|
659
659
|
- `includeComments` (optional): Include descriptive comments (default: true)
|
|
660
660
|
- `generatePageObject` (optional): Also generate Page Object class for the page (default: false)
|
|
661
661
|
- `pageObjectClassName` (optional): Custom Page Object class name (auto-generated if not provided)
|
|
662
|
-
- `appendToFile` (optional): Path to existing test file to append to (enables **append mode**) ⭐ **NEW**
|
|
663
|
-
- `testName` (optional): Override test name (default: from scenario name) ⭐ **NEW**
|
|
664
|
-
- `insertPosition` (optional): Where to insert: `'end'` (default), `'before'`, `'after'` ⭐ **NEW**
|
|
665
|
-
- `referenceTestName` (optional): Reference test name for before/after insertion ⭐ **NEW**
|
|
666
662
|
|
|
667
|
-
- **Use case**:
|
|
663
|
+
- **Use case**: Create new test files from recorded scenarios with optional Page Objects
|
|
668
664
|
|
|
669
|
-
- **Returns**:
|
|
670
|
-
-
|
|
671
|
-
-
|
|
665
|
+
- **Returns**: JSON with:
|
|
666
|
+
- `action`: `"create_new_file"`
|
|
667
|
+
- `suggestedFileName`: Suggested test filename
|
|
668
|
+
- `testCode`: Full test code with imports
|
|
669
|
+
- `instruction`: Instructions for Claude Code
|
|
670
|
+
- `pageObject` (if `generatePageObject=true`): Page Object code and metadata
|
|
672
671
|
|
|
673
|
-
- **Example 1 - Test only
|
|
672
|
+
- **Example 1 - Test only**:
|
|
674
673
|
```javascript
|
|
675
|
-
// Export scenario as Playwright TypeScript
|
|
674
|
+
// Export scenario as new Playwright TypeScript file
|
|
676
675
|
exportScenarioAsCode({
|
|
677
676
|
scenarioName: "checkout_flow",
|
|
678
|
-
language: "playwright-typescript"
|
|
679
|
-
cleanSelectors: true,
|
|
680
|
-
includeComments: true
|
|
677
|
+
language: "playwright-typescript"
|
|
681
678
|
})
|
|
682
679
|
|
|
683
|
-
// Returns
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
// });
|
|
680
|
+
// Returns JSON:
|
|
681
|
+
{
|
|
682
|
+
"action": "create_new_file",
|
|
683
|
+
"suggestedFileName": "checkout_flow.spec.ts",
|
|
684
|
+
"testCode": "import { test, expect } from '@playwright/test';\n\ntest('checkout_flow', async ({ page }) => {\n await page.goto('https://example.com');\n await page.locator('button[data-testid=\"add-to-cart\"]').click();\n await expect(page).toHaveURL(/checkout/);\n});",
|
|
685
|
+
"instruction": "Create a new test file 'checkout_flow.spec.ts' with the testCode."
|
|
686
|
+
}
|
|
691
687
|
```
|
|
692
688
|
|
|
693
|
-
- **Example 2 - Test + Page Object
|
|
689
|
+
- **Example 2 - Test + Page Object**:
|
|
694
690
|
```javascript
|
|
695
691
|
// Export with Page Object class
|
|
696
692
|
exportScenarioAsCode({
|
|
@@ -702,63 +698,117 @@ Export recorded scenario as executable test code for various frameworks. Automat
|
|
|
702
698
|
|
|
703
699
|
// Returns JSON with both files:
|
|
704
700
|
{
|
|
705
|
-
"
|
|
701
|
+
"action": "create_new_file",
|
|
702
|
+
"suggestedFileName": "login_test.spec.ts",
|
|
706
703
|
"testCode": "import { test } from '@playwright/test';\nimport { LoginPage } from './LoginPage';\n\ntest('login_test', async ({ page }) => {\n const loginPage = new LoginPage(page);\n await loginPage.goto();\n await loginPage.fillEmailInput('user@test.com');\n await loginPage.clickLoginButton();\n});",
|
|
707
|
-
"
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
704
|
+
"pageObject": {
|
|
705
|
+
"code": "import { Page, Locator } from '@playwright/test';\n\nexport class LoginPage { ... }",
|
|
706
|
+
"className": "LoginPage",
|
|
707
|
+
"suggestedFileName": "LoginPage.ts",
|
|
708
|
+
"elementCount": 12
|
|
709
|
+
},
|
|
710
|
+
"instruction": "Create a new test file 'login_test.spec.ts' with the testCode. Also create a Page Object file 'LoginPage.ts' with the pageObject.code."
|
|
711
711
|
}
|
|
712
712
|
```
|
|
713
713
|
|
|
714
|
-
- **
|
|
714
|
+
- **Selector Cleaning**: Automatically removes unstable patterns:
|
|
715
|
+
- CSS Modules: `Button_primary__2x3yZ` → removed
|
|
716
|
+
- Styled-components: `sc-AbCdEf-0` → removed
|
|
717
|
+
- Emotion: `css-1a2b3c4d` → removed
|
|
718
|
+
- Hash suffixes: `component_a1b2c3d` → removed
|
|
719
|
+
- Prefers stable selectors: `data-testid`, `role`, `aria-label`, semantic attributes
|
|
720
|
+
|
|
721
|
+
#### appendScenarioToFile ⭐ **NEW v2.3.0**
|
|
722
|
+
Append recorded scenario as test code to an **EXISTING** test file. Automatically cleans unstable selectors (CSS Modules, styled-components, Emotion). Optionally generates Page Object class. Returns JSON with test code (without imports) - Claude Code will read the file, append the test, and write back. To create **NEW** test files, use `exportScenarioAsCode` instead.
|
|
723
|
+
|
|
724
|
+
- **Parameters**:
|
|
725
|
+
- `scenarioName` (required): Name of scenario to export
|
|
726
|
+
- `language` (required): Target framework - `"playwright-typescript"`, `"playwright-python"`, `"selenium-python"`, `"selenium-java"`
|
|
727
|
+
- `targetFile` (required): Path to existing test file to append to
|
|
728
|
+
- `testName` (optional): Override test name (default: from scenario name)
|
|
729
|
+
- `insertPosition` (optional): Where to insert: `'end'` (default), `'before'`, `'after'`
|
|
730
|
+
- `referenceTestName` (optional): Reference test name for 'before'/'after' insertion
|
|
731
|
+
- `cleanSelectors` (optional): Remove unstable CSS classes (default: true)
|
|
732
|
+
- `includeComments` (optional): Include descriptive comments (default: true)
|
|
733
|
+
- `generatePageObject` (optional): Also generate Page Object class for the page (default: false)
|
|
734
|
+
- `pageObjectClassName` (optional): Custom Page Object class name (auto-generated if not provided)
|
|
735
|
+
|
|
736
|
+
- **Use case**: Add tests to existing test files without overwriting current tests
|
|
737
|
+
|
|
738
|
+
- **Architecture**: MCP server generates only test code (without imports). Claude Code reads the target file, appends the test at the specified position, and writes the file back. This separation ensures MCP doesn't need file system access to test files.
|
|
739
|
+
|
|
740
|
+
- **Returns**: JSON with:
|
|
741
|
+
- `action`: `"append_test"`
|
|
742
|
+
- `targetFile`: Path to file to update
|
|
743
|
+
- `testCode`: Test code only (without imports/headers)
|
|
744
|
+
- `testName`: Name of test to append
|
|
745
|
+
- `insertPosition`: Where to insert test
|
|
746
|
+
- `referenceTestName`: Reference test for 'before'/'after' positioning
|
|
747
|
+
- `instruction`: Instructions for Claude Code to read/append/write
|
|
748
|
+
- `pageObject` (if `generatePageObject=true`): Page Object code and metadata
|
|
749
|
+
|
|
750
|
+
- **Example 1 - Append to end**:
|
|
715
751
|
```javascript
|
|
716
752
|
// Append test to end of existing file
|
|
717
|
-
|
|
753
|
+
appendScenarioToFile({
|
|
718
754
|
scenarioName: "new_feature_test",
|
|
719
755
|
language: "playwright-typescript",
|
|
720
|
-
|
|
756
|
+
targetFile: "./tests/features.spec.ts"
|
|
721
757
|
})
|
|
722
758
|
|
|
723
|
-
// Returns:
|
|
759
|
+
// Returns JSON:
|
|
724
760
|
{
|
|
725
|
-
"
|
|
726
|
-
"
|
|
727
|
-
"
|
|
761
|
+
"action": "append_test",
|
|
762
|
+
"targetFile": "./tests/features.spec.ts",
|
|
763
|
+
"testCode": "test('new_feature_test', async ({ page }) => {\n // Test implementation\n await page.click('#submit');\n await expect(page.locator('.result')).toBeVisible();\n});",
|
|
728
764
|
"testName": "new_feature_test",
|
|
729
765
|
"insertPosition": "end",
|
|
730
|
-
"
|
|
766
|
+
"referenceTestName": null,
|
|
767
|
+
"instruction": "Read file './tests/features.spec.ts', append the testCode at position 'end', then write the file back."
|
|
731
768
|
}
|
|
732
769
|
```
|
|
733
770
|
|
|
734
|
-
- **Example
|
|
771
|
+
- **Example 2 - Insert before specific test**:
|
|
735
772
|
```javascript
|
|
736
773
|
// Insert test before specific test
|
|
737
|
-
|
|
774
|
+
appendScenarioToFile({
|
|
738
775
|
scenarioName: "setup_test",
|
|
739
776
|
language: "selenium-python",
|
|
740
|
-
|
|
777
|
+
targetFile: "./tests/test_suite.py",
|
|
741
778
|
insertPosition: "before",
|
|
742
|
-
referenceTestName: "
|
|
743
|
-
testName: "test_setup_data"
|
|
779
|
+
referenceTestName: "test_main",
|
|
780
|
+
testName: "test_setup_data"
|
|
744
781
|
})
|
|
782
|
+
```
|
|
745
783
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
784
|
+
- **Example 3 - Append with Page Object**:
|
|
785
|
+
```javascript
|
|
786
|
+
// Append test and generate Page Object
|
|
787
|
+
appendScenarioToFile({
|
|
788
|
+
scenarioName: "login_test",
|
|
789
|
+
language: "playwright-typescript",
|
|
790
|
+
targetFile: "./tests/auth.spec.ts",
|
|
791
|
+
generatePageObject: true,
|
|
792
|
+
pageObjectClassName: "LoginPage"
|
|
753
793
|
})
|
|
754
|
-
```
|
|
755
794
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
795
|
+
// Returns JSON with both test code and Page Object:
|
|
796
|
+
{
|
|
797
|
+
"action": "append_test",
|
|
798
|
+
"targetFile": "./tests/auth.spec.ts",
|
|
799
|
+
"testCode": "test('login_test', async ({ page }) => {\n await page.fill('#username', 'user');\n await page.fill('#password', 'pass');\n await page.click('button[type=\"submit\"]');\n});",
|
|
800
|
+
"testName": "login_test",
|
|
801
|
+
"insertPosition": "end",
|
|
802
|
+
"referenceTestName": null,
|
|
803
|
+
"pageObject": {
|
|
804
|
+
"code": "export class LoginPage { ... }",
|
|
805
|
+
"className": "LoginPage",
|
|
806
|
+
"suggestedFileName": "LoginPage.ts",
|
|
807
|
+
"elementCount": 8
|
|
808
|
+
},
|
|
809
|
+
"instruction": "Read file './tests/auth.spec.ts', append the testCode at position 'end', then write the file back. Also create a Page Object file 'LoginPage.ts' with the provided pageObject.code."
|
|
810
|
+
}
|
|
811
|
+
```
|
|
762
812
|
|
|
763
813
|
#### generatePageObject ⭐ **NEW**
|
|
764
814
|
Generate Page Object Model (POM) class from current page structure. Analyzes page, extracts interactive elements, and generates framework-specific code with smart naming and helper methods.
|
|
@@ -915,6 +965,106 @@ If you don't need to see the browser window, you can use xvfb (virtual X server)
|
|
|
915
965
|
|
|
916
966
|
This runs Chrome in GUI mode but on a virtual display (window is not visible).
|
|
917
967
|
|
|
968
|
+
### Tool Filtering with ENABLED_TOOLS
|
|
969
|
+
|
|
970
|
+
By default, all tools are enabled. You can selectively enable only specific tool groups using the `ENABLED_TOOLS` environment variable.
|
|
971
|
+
|
|
972
|
+
**Why filter tools?**
|
|
973
|
+
|
|
974
|
+
Each tool definition is sent to the AI in every request, consuming context tokens. All 43 tools consume ~28k tokens (~14% of context window). By enabling only the groups you need, you can significantly reduce token usage:
|
|
975
|
+
|
|
976
|
+
- **Save tokens:** Fewer tools = less context consumed per request
|
|
977
|
+
- **Reduce costs:** Lower token usage means lower API costs
|
|
978
|
+
- **Improve focus:** AI sees only relevant tools for your workflow
|
|
979
|
+
- **Security/compliance:** Restrict available capabilities when needed
|
|
980
|
+
|
|
981
|
+
**Available Tool Groups:**
|
|
982
|
+
|
|
983
|
+
| Group | Description | Tools (count) |
|
|
984
|
+
|-------|-------------|---------------|
|
|
985
|
+
| `core` | Basic tools | `ping`, `openBrowser` (2) |
|
|
986
|
+
| `interaction` | User interaction | `click`, `type`, `scrollTo`, `waitForElement`, `hover` (5) |
|
|
987
|
+
| `inspection` | Page inspection | `getElement`, `getComputedCss`, `getBoxModel`, `screenshot`, `saveScreenshot` (5) |
|
|
988
|
+
| `debug` | Debugging & network | `getConsoleLogs`, `listNetworkRequests`, `getNetworkRequest`, `filterNetworkRequests` (4) |
|
|
989
|
+
| `advanced` | Advanced automation & AI | `executeScript`, `setStyles`, `setViewport`, `getViewport`, `navigateTo`, `smartFindElement`, `analyzePage`, `getAllInteractiveElements`, `findElementsByText` (9) |
|
|
990
|
+
| `recorder` | Scenario recording | `enableRecorder`, `executeScenario`, `listScenarios`, `searchScenarios`, `getScenarioInfo`, `deleteScenario`, `exportScenarioAsCode`, `appendScenarioToFile`, `generatePageObject` (9) |
|
|
991
|
+
| `figma` | Figma integration | `getFigmaFrame`, `compareFigmaToElement`, `getFigmaSpecs`, `parseFigmaUrl`, `listFigmaPages`, `searchFigmaFrames`, `getFigmaComponents`, `getFigmaStyles`, `getFigmaColorPalette` (9) |
|
|
992
|
+
|
|
993
|
+
**Total:** 43 tools across 7 groups
|
|
994
|
+
|
|
995
|
+
**Configuration:**
|
|
996
|
+
|
|
997
|
+
**Claude Desktop** (`~/.claude/mcp_config.json`):
|
|
998
|
+
|
|
999
|
+
```json
|
|
1000
|
+
{
|
|
1001
|
+
"mcpServers": {
|
|
1002
|
+
"chrometools": {
|
|
1003
|
+
"command": "npx",
|
|
1004
|
+
"args": ["-y", "chrometools-mcp"],
|
|
1005
|
+
"env": {
|
|
1006
|
+
"ENABLED_TOOLS": "core,interaction,inspection"
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
**Claude Code** (`~/.claude.json`):
|
|
1014
|
+
|
|
1015
|
+
```json
|
|
1016
|
+
{
|
|
1017
|
+
"mcpServers": {
|
|
1018
|
+
"chrometools": {
|
|
1019
|
+
"type": "stdio",
|
|
1020
|
+
"command": "npx",
|
|
1021
|
+
"args": ["-y", "chrometools-mcp"],
|
|
1022
|
+
"env": {
|
|
1023
|
+
"ENABLED_TOOLS": "core,interaction,advanced"
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
**Format:**
|
|
1031
|
+
- Comma-separated list of group names (e.g., `"core,interaction,advanced"`)
|
|
1032
|
+
- Spaces are automatically trimmed
|
|
1033
|
+
- If not set or empty, all tools are enabled (default behavior)
|
|
1034
|
+
|
|
1035
|
+
**Example configurations:**
|
|
1036
|
+
|
|
1037
|
+
**Basic automation only:**
|
|
1038
|
+
```json
|
|
1039
|
+
"ENABLED_TOOLS": "core,interaction,inspection"
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
**Advanced automation with AI:**
|
|
1043
|
+
```json
|
|
1044
|
+
"ENABLED_TOOLS": "core,interaction,advanced"
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
**With debugging tools:**
|
|
1048
|
+
```json
|
|
1049
|
+
"ENABLED_TOOLS": "core,interaction,inspection,debug"
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
**Figma design validation:**
|
|
1053
|
+
```json
|
|
1054
|
+
"ENABLED_TOOLS": "core,figma"
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
**Full automation with recording:**
|
|
1058
|
+
```json
|
|
1059
|
+
"ENABLED_TOOLS": "core,interaction,inspection,debug,advanced,recorder"
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
**All tools (default):**
|
|
1063
|
+
```json
|
|
1064
|
+
"env": {}
|
|
1065
|
+
```
|
|
1066
|
+
or omit the `env` field entirely.
|
|
1067
|
+
|
|
918
1068
|
### Figma API Token Setup
|
|
919
1069
|
|
|
920
1070
|
To use Figma tools, you need to configure your Figma Personal Access Token.
|
package/index.js
CHANGED
|
@@ -33,6 +33,7 @@ import {filterCssStyles} from './utils/css-utils.js';
|
|
|
33
33
|
// Import tool schemas and definitions
|
|
34
34
|
import * as schemas from './server/tool-schemas.js';
|
|
35
35
|
import {toolDefinitions} from './server/tool-definitions.js';
|
|
36
|
+
import {getToolsFromGroups, getAllGroupNames} from './server/tool-groups.js';
|
|
36
37
|
|
|
37
38
|
// Import element actions helper
|
|
38
39
|
import {executeElementAction} from './utils/element-actions.js';
|
|
@@ -73,6 +74,16 @@ const debugLog = DEBUG_MODE ? console.error : () => {};
|
|
|
73
74
|
// Figma token from environment variable (can be set in MCP config)
|
|
74
75
|
const FIGMA_TOKEN = process.env.FIGMA_TOKEN || null;
|
|
75
76
|
|
|
77
|
+
// Tool filtering - read ENABLED_TOOLS environment variable
|
|
78
|
+
// If set, only enable specified tool groups (comma-separated list)
|
|
79
|
+
// If not set, enable all tools (default behavior)
|
|
80
|
+
const ENABLED_TOOLS = process.env.ENABLED_TOOLS;
|
|
81
|
+
let enabledToolsSet = null;
|
|
82
|
+
|
|
83
|
+
if (ENABLED_TOOLS) {
|
|
84
|
+
const groupNames = ENABLED_TOOLS.split(',').map(g => g.trim()).filter(g => g.length > 0);
|
|
85
|
+
enabledToolsSet = getToolsFromGroups(groupNames);
|
|
86
|
+
}
|
|
76
87
|
// Get current directory for loading utils
|
|
77
88
|
const __filename = fileURLToPath(import.meta.url);
|
|
78
89
|
const __dirname = dirname(__filename);
|
|
@@ -164,7 +175,7 @@ try {
|
|
|
164
175
|
const server = new Server(
|
|
165
176
|
{
|
|
166
177
|
name: "chrometools-mcp",
|
|
167
|
-
version: "2.
|
|
178
|
+
version: "2.4.0",
|
|
168
179
|
},
|
|
169
180
|
{
|
|
170
181
|
capabilities: {
|
|
@@ -175,8 +186,13 @@ const server = new Server(
|
|
|
175
186
|
|
|
176
187
|
// List available tools
|
|
177
188
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
189
|
+
// Filter tools based on ENABLED_TOOLS environment variable
|
|
190
|
+
const tools = enabledToolsSet
|
|
191
|
+
? toolDefinitions.filter(tool => enabledToolsSet.has(tool.name))
|
|
192
|
+
: toolDefinitions;
|
|
193
|
+
|
|
178
194
|
return {
|
|
179
|
-
tools
|
|
195
|
+
tools,
|
|
180
196
|
};
|
|
181
197
|
});
|
|
182
198
|
|
|
@@ -1948,6 +1964,44 @@ async function executeToolInternal(name, args) {
|
|
|
1948
1964
|
}
|
|
1949
1965
|
}
|
|
1950
1966
|
|
|
1967
|
+
// Check if current page URL matches scenario's entryUrl
|
|
1968
|
+
// If not, navigate to entryUrl before executing scenario
|
|
1969
|
+
const entryUrl = scenario.metadata?.entryUrl;
|
|
1970
|
+
if (entryUrl) {
|
|
1971
|
+
try {
|
|
1972
|
+
const currentUrl = page.url();
|
|
1973
|
+
|
|
1974
|
+
// Normalize URLs for comparison (remove trailing slashes, hash, some query params)
|
|
1975
|
+
const normalizeUrl = (url) => {
|
|
1976
|
+
try {
|
|
1977
|
+
const urlObj = new URL(url);
|
|
1978
|
+
// Keep protocol, hostname, pathname - ignore some query params like nr, redirect_ts
|
|
1979
|
+
return `${urlObj.protocol}//${urlObj.hostname}${urlObj.pathname}`.replace(/\/$/, '');
|
|
1980
|
+
} catch (e) {
|
|
1981
|
+
return url;
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
|
|
1985
|
+
const normalizedCurrent = normalizeUrl(currentUrl);
|
|
1986
|
+
const normalizedEntry = normalizeUrl(entryUrl);
|
|
1987
|
+
|
|
1988
|
+
if (normalizedCurrent !== normalizedEntry) {
|
|
1989
|
+
console.error(`[executeScenario] Current URL (${currentUrl}) doesn't match scenario entryUrl (${entryUrl})`);
|
|
1990
|
+
console.error(`[executeScenario] Navigating to entryUrl...`);
|
|
1991
|
+
|
|
1992
|
+
await page.goto(entryUrl, {
|
|
1993
|
+
waitUntil: 'networkidle2',
|
|
1994
|
+
timeout: 30000
|
|
1995
|
+
});
|
|
1996
|
+
|
|
1997
|
+
console.error(`[executeScenario] Navigation completed`);
|
|
1998
|
+
}
|
|
1999
|
+
} catch (navError) {
|
|
2000
|
+
console.error(`[executeScenario] Warning: Failed to navigate to entryUrl: ${navError.message}`);
|
|
2001
|
+
// Continue anyway - scenario might still work
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1951
2005
|
const options = {};
|
|
1952
2006
|
|
|
1953
2007
|
// Pass executeDependencies option if provided
|
|
@@ -2038,7 +2092,7 @@ async function executeToolInternal(name, args) {
|
|
|
2038
2092
|
};
|
|
2039
2093
|
}
|
|
2040
2094
|
|
|
2041
|
-
if (name === "
|
|
2095
|
+
if (name === "appendScenarioToFile") {
|
|
2042
2096
|
const scenario = await loadScenario(args.scenarioName, false, null);
|
|
2043
2097
|
|
|
2044
2098
|
if (!scenario) {
|
|
@@ -2085,83 +2139,157 @@ async function executeToolInternal(name, args) {
|
|
|
2085
2139
|
};
|
|
2086
2140
|
}
|
|
2087
2141
|
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
// Read existing file content
|
|
2095
|
-
const existingContent = FileAppender.readFile(args.appendToFile);
|
|
2142
|
+
try {
|
|
2143
|
+
// Generate test code only (without imports)
|
|
2144
|
+
const testOnly = generator.generateTestOnly(scenario, {
|
|
2145
|
+
...options,
|
|
2146
|
+
testName: args.testName
|
|
2147
|
+
});
|
|
2096
2148
|
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2149
|
+
// Prepare append options for Claude Code
|
|
2150
|
+
const appendOptions = {
|
|
2151
|
+
insertPosition: args.insertPosition || 'end',
|
|
2152
|
+
referenceTestName: args.referenceTestName
|
|
2153
|
+
};
|
|
2101
2154
|
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2155
|
+
// Generate Page Object if requested
|
|
2156
|
+
let pageObjectData = null;
|
|
2157
|
+
if (args.generatePageObject) {
|
|
2158
|
+
try {
|
|
2159
|
+
const entryUrl = scenario.metadata?.entryUrl;
|
|
2160
|
+
if (entryUrl) {
|
|
2161
|
+
let page;
|
|
2162
|
+
try {
|
|
2163
|
+
page = await getLastOpenPage();
|
|
2164
|
+
const currentUrl = page.url();
|
|
2165
|
+
if (currentUrl !== entryUrl) {
|
|
2166
|
+
await page.goto(entryUrl, { waitUntil: 'networkidle2' });
|
|
2167
|
+
}
|
|
2168
|
+
} catch (error) {
|
|
2169
|
+
page = await getOrCreatePage(entryUrl);
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
const pageObjectOptions = {
|
|
2173
|
+
className: args.pageObjectClassName || null,
|
|
2174
|
+
framework: args.language,
|
|
2175
|
+
includeComments: args.includeComments !== false,
|
|
2176
|
+
groupElements: true
|
|
2177
|
+
};
|
|
2178
|
+
|
|
2179
|
+
const pageObjectResult = await generatePageObject(page, pageObjectOptions);
|
|
2180
|
+
if (pageObjectResult.success) {
|
|
2181
|
+
// Suggest filename based on className
|
|
2182
|
+
const extension = args.language.includes('typescript') ? '.ts' :
|
|
2183
|
+
args.language.includes('java') ? '.java' : '.py';
|
|
2184
|
+
pageObjectData = {
|
|
2185
|
+
code: pageObjectResult.code,
|
|
2186
|
+
className: pageObjectResult.className,
|
|
2187
|
+
suggestedFileName: `${pageObjectResult.className}${extension}`,
|
|
2188
|
+
elementCount: pageObjectResult.elementCount
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
} catch (error) {
|
|
2193
|
+
// Page Object generation failed, continue without it
|
|
2114
2194
|
}
|
|
2195
|
+
}
|
|
2115
2196
|
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2197
|
+
// Return JSON with instructions for Claude Code to append the test
|
|
2198
|
+
const result = {
|
|
2199
|
+
action: 'append_test',
|
|
2200
|
+
targetFile: args.targetFile,
|
|
2201
|
+
testCode: testOnly, // Only test code, no imports
|
|
2202
|
+
testName: args.testName || scenario.metadata?.name,
|
|
2203
|
+
insertPosition: appendOptions.insertPosition,
|
|
2204
|
+
referenceTestName: appendOptions.referenceTestName,
|
|
2205
|
+
instruction: `Read file '${args.targetFile}', append the testCode at position '${appendOptions.insertPosition}', then write the file back.`
|
|
2206
|
+
};
|
|
2121
2207
|
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
};
|
|
2208
|
+
if (pageObjectData) {
|
|
2209
|
+
result.pageObject = pageObjectData;
|
|
2210
|
+
result.instruction += ` Also create a Page Object file '${pageObjectData.suggestedFileName}' with the provided pageObject.code.`;
|
|
2211
|
+
}
|
|
2127
2212
|
|
|
2128
|
-
|
|
2213
|
+
return {
|
|
2214
|
+
content: [{
|
|
2215
|
+
type: 'text',
|
|
2216
|
+
text: JSON.stringify(result, null, 2)
|
|
2217
|
+
}]
|
|
2218
|
+
};
|
|
2219
|
+
} catch (error) {
|
|
2220
|
+
return {
|
|
2221
|
+
content: [{
|
|
2222
|
+
type: 'text',
|
|
2223
|
+
text: JSON.stringify({
|
|
2224
|
+
error: error.message,
|
|
2225
|
+
action: 'append_test',
|
|
2226
|
+
targetFile: args.targetFile
|
|
2227
|
+
}, null, 2)
|
|
2228
|
+
}],
|
|
2229
|
+
isError: true
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2129
2233
|
|
|
2130
|
-
|
|
2131
|
-
|
|
2234
|
+
if (name === "exportScenarioAsCode") {
|
|
2235
|
+
const scenario = await loadScenario(args.scenarioName, false, null);
|
|
2132
2236
|
|
|
2237
|
+
if (!scenario) {
|
|
2238
|
+
return {
|
|
2239
|
+
content: [{
|
|
2240
|
+
type: 'text',
|
|
2241
|
+
text: JSON.stringify({
|
|
2242
|
+
error: `Scenario "${args.scenarioName}" not found`
|
|
2243
|
+
}, null, 2)
|
|
2244
|
+
}],
|
|
2245
|
+
isError: true
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
// Select generator based on language
|
|
2250
|
+
let generator;
|
|
2251
|
+
const options = {
|
|
2252
|
+
cleanSelectors: args.cleanSelectors !== false, // default true
|
|
2253
|
+
includeComments: args.includeComments !== false, // default true
|
|
2254
|
+
};
|
|
2255
|
+
|
|
2256
|
+
switch (args.language) {
|
|
2257
|
+
case 'playwright-typescript':
|
|
2258
|
+
generator = new PlaywrightTypeScriptGenerator(options);
|
|
2259
|
+
break;
|
|
2260
|
+
case 'playwright-python':
|
|
2261
|
+
generator = new PlaywrightPythonGenerator(options);
|
|
2262
|
+
break;
|
|
2263
|
+
case 'selenium-python':
|
|
2264
|
+
generator = new SeleniumPythonGenerator(options);
|
|
2265
|
+
break;
|
|
2266
|
+
case 'selenium-java':
|
|
2267
|
+
generator = new SeleniumJavaGenerator(options);
|
|
2268
|
+
break;
|
|
2269
|
+
default:
|
|
2133
2270
|
return {
|
|
2134
2271
|
content: [{
|
|
2135
2272
|
type: 'text',
|
|
2136
2273
|
text: JSON.stringify({
|
|
2137
|
-
|
|
2138
|
-
mode: 'append',
|
|
2139
|
-
file: args.appendToFile,
|
|
2140
|
-
testName: args.testName || scenario.metadata?.name,
|
|
2141
|
-
insertPosition: appendOptions.insertPosition,
|
|
2142
|
-
message: `Test '${args.testName || scenario.metadata?.name}' successfully appended to ${args.appendToFile}`
|
|
2143
|
-
}, null, 2)
|
|
2144
|
-
}]
|
|
2145
|
-
};
|
|
2146
|
-
} catch (error) {
|
|
2147
|
-
return {
|
|
2148
|
-
content: [{
|
|
2149
|
-
type: 'text',
|
|
2150
|
-
text: JSON.stringify({
|
|
2151
|
-
success: false,
|
|
2152
|
-
mode: 'append',
|
|
2153
|
-
file: args.appendToFile,
|
|
2154
|
-
error: error.message
|
|
2274
|
+
error: `Unknown language: ${args.language}. Supported: playwright-typescript, playwright-python, selenium-python, selenium-java`
|
|
2155
2275
|
}, null, 2)
|
|
2156
2276
|
}],
|
|
2157
2277
|
isError: true
|
|
2158
2278
|
};
|
|
2159
|
-
}
|
|
2160
2279
|
}
|
|
2161
2280
|
|
|
2162
|
-
//
|
|
2281
|
+
// Generate test code with full imports
|
|
2163
2282
|
const testCode = generator.generate(scenario, options);
|
|
2164
2283
|
|
|
2284
|
+
// Generate suggested filename
|
|
2285
|
+
const testName = scenario.metadata?.name || 'test';
|
|
2286
|
+
const extension = args.language.includes('typescript') ? '.spec.ts' :
|
|
2287
|
+
args.language.includes('java') ? 'Test.java' :
|
|
2288
|
+
args.language.includes('python') ? '_test.py' : '.test.js';
|
|
2289
|
+
const suggestedFileName = args.language.includes('java')
|
|
2290
|
+
? testName.charAt(0).toUpperCase() + testName.slice(1) + 'Test.java'
|
|
2291
|
+
: testName.replace(/\s+/g, '_').toLowerCase() + extension;
|
|
2292
|
+
|
|
2165
2293
|
// If generatePageObject is requested, also generate Page Object class
|
|
2166
2294
|
if (args.generatePageObject) {
|
|
2167
2295
|
try {
|
|
@@ -2205,19 +2333,26 @@ async function executeToolInternal(name, args) {
|
|
|
2205
2333
|
const pageObjectResult = await generatePageObject(page, pageObjectOptions);
|
|
2206
2334
|
|
|
2207
2335
|
if (pageObjectResult.success) {
|
|
2336
|
+
// Suggest Page Object filename
|
|
2337
|
+
const poExtension = args.language.includes('typescript') ? '.ts' :
|
|
2338
|
+
args.language.includes('java') ? '.java' : '.py';
|
|
2339
|
+
const pageObjectFileName = `${pageObjectResult.className}${poExtension}`;
|
|
2340
|
+
|
|
2208
2341
|
// Return both test code and Page Object code
|
|
2209
2342
|
return {
|
|
2210
2343
|
content: [{
|
|
2211
2344
|
type: 'text',
|
|
2212
2345
|
text: JSON.stringify({
|
|
2213
|
-
|
|
2346
|
+
action: 'create_new_file',
|
|
2347
|
+
suggestedFileName: suggestedFileName,
|
|
2214
2348
|
testCode: testCode,
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2349
|
+
pageObject: {
|
|
2350
|
+
code: pageObjectResult.code,
|
|
2351
|
+
className: pageObjectResult.className,
|
|
2352
|
+
suggestedFileName: pageObjectFileName,
|
|
2353
|
+
elementCount: pageObjectResult.elementCount
|
|
2354
|
+
},
|
|
2355
|
+
instruction: `Create a new test file '${suggestedFileName}' with the testCode. Also create a Page Object file '${pageObjectFileName}' with the pageObject.code.`
|
|
2221
2356
|
}, null, 2)
|
|
2222
2357
|
}]
|
|
2223
2358
|
};
|
|
@@ -2227,10 +2362,11 @@ async function executeToolInternal(name, args) {
|
|
|
2227
2362
|
content: [{
|
|
2228
2363
|
type: 'text',
|
|
2229
2364
|
text: JSON.stringify({
|
|
2230
|
-
|
|
2365
|
+
action: 'create_new_file',
|
|
2366
|
+
suggestedFileName: suggestedFileName,
|
|
2231
2367
|
testCode: testCode,
|
|
2232
|
-
|
|
2233
|
-
|
|
2368
|
+
warning: 'Page Object generation failed: ' + (pageObjectResult.error || 'Unknown error'),
|
|
2369
|
+
instruction: `Create a new test file '${suggestedFileName}' with the testCode.`
|
|
2234
2370
|
}, null, 2)
|
|
2235
2371
|
}]
|
|
2236
2372
|
};
|
|
@@ -2241,10 +2377,11 @@ async function executeToolInternal(name, args) {
|
|
|
2241
2377
|
content: [{
|
|
2242
2378
|
type: 'text',
|
|
2243
2379
|
text: JSON.stringify({
|
|
2244
|
-
|
|
2380
|
+
action: 'create_new_file',
|
|
2381
|
+
suggestedFileName: suggestedFileName,
|
|
2245
2382
|
testCode: testCode,
|
|
2246
|
-
|
|
2247
|
-
|
|
2383
|
+
warning: 'Page Object generation error: ' + error.message,
|
|
2384
|
+
instruction: `Create a new test file '${suggestedFileName}' with the testCode.`
|
|
2248
2385
|
}, null, 2)
|
|
2249
2386
|
}]
|
|
2250
2387
|
};
|
|
@@ -2255,7 +2392,12 @@ async function executeToolInternal(name, args) {
|
|
|
2255
2392
|
return {
|
|
2256
2393
|
content: [{
|
|
2257
2394
|
type: 'text',
|
|
2258
|
-
text:
|
|
2395
|
+
text: JSON.stringify({
|
|
2396
|
+
action: 'create_new_file',
|
|
2397
|
+
suggestedFileName: suggestedFileName,
|
|
2398
|
+
testCode: testCode,
|
|
2399
|
+
instruction: `Create a new test file '${suggestedFileName}' with the testCode.`
|
|
2400
|
+
}, null, 2)
|
|
2259
2401
|
}]
|
|
2260
2402
|
};
|
|
2261
2403
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrometools-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server for Chrome automation using Puppeteer. Persistent browser sessions, visual testing, Figma comparison, and design validation. Works seamlessly in WSL, Linux, and macOS.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -541,7 +541,7 @@ export const toolDefinitions = [
|
|
|
541
541
|
},
|
|
542
542
|
{
|
|
543
543
|
name: "exportScenarioAsCode",
|
|
544
|
-
description: "Export recorded scenario as executable test code for
|
|
544
|
+
description: "Export recorded scenario as executable test code for creating a NEW test file. Automatically cleans unstable selectors (CSS modules, styled-components). Optionally generates Page Object class. Returns JSON with code and suggested filename - Claude Code will create the file. To add tests to EXISTING files, use 'appendScenarioToFile' instead. Scenarios are stored in ~/.config/chrometools-mcp/projects/{projectName}/scenarios/. Use global index at ~/.config/chrometools-mcp/index.json to discover available projects and scenarios.",
|
|
545
545
|
inputSchema: {
|
|
546
546
|
type: "object",
|
|
547
547
|
properties: {
|
|
@@ -570,9 +570,28 @@ export const toolDefinitions = [
|
|
|
570
570
|
type: "string",
|
|
571
571
|
description: "Page Object class name (optional, auto-generated if not provided)"
|
|
572
572
|
},
|
|
573
|
-
|
|
573
|
+
},
|
|
574
|
+
required: ["scenarioName", "language"],
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
name: "appendScenarioToFile",
|
|
579
|
+
description: "Append recorded scenario as test code to an EXISTING test file. Automatically cleans unstable selectors (CSS modules, styled-components). Optionally generates Page Object class. Returns JSON with test code and target file - Claude Code will append to the file without overwriting existing tests. To create NEW test files, use 'exportScenarioAsCode' instead. Scenarios are stored in ~/.config/chrometools-mcp/projects/{projectName}/scenarios/. Use global index at ~/.config/chrometools-mcp/index.json to discover available projects and scenarios.",
|
|
580
|
+
inputSchema: {
|
|
581
|
+
type: "object",
|
|
582
|
+
properties: {
|
|
583
|
+
scenarioName: {
|
|
584
|
+
type: "string",
|
|
585
|
+
description: "Name of scenario to export"
|
|
586
|
+
},
|
|
587
|
+
language: {
|
|
588
|
+
type: "string",
|
|
589
|
+
enum: ["playwright-typescript", "playwright-python", "selenium-python", "selenium-java"],
|
|
590
|
+
description: "Target test framework and language"
|
|
591
|
+
},
|
|
592
|
+
targetFile: {
|
|
574
593
|
type: "string",
|
|
575
|
-
description: "Path to existing test file to append to (
|
|
594
|
+
description: "Path to existing test file to append to (REQUIRED)"
|
|
576
595
|
},
|
|
577
596
|
testName: {
|
|
578
597
|
type: "string",
|
|
@@ -581,14 +600,30 @@ export const toolDefinitions = [
|
|
|
581
600
|
insertPosition: {
|
|
582
601
|
type: "string",
|
|
583
602
|
enum: ["end", "before", "after"],
|
|
584
|
-
description: "Where to insert test: 'end' (default), 'before', or 'after'
|
|
603
|
+
description: "Where to insert test: 'end' (default - after all tests), 'before' (before reference test), or 'after' (after reference test)"
|
|
585
604
|
},
|
|
586
605
|
referenceTestName: {
|
|
587
606
|
type: "string",
|
|
588
|
-
description: "Reference test name for 'before'/'after' insertion"
|
|
607
|
+
description: "Reference test name for 'before'/'after' insertion. Required when insertPosition is 'before' or 'after'"
|
|
608
|
+
},
|
|
609
|
+
cleanSelectors: {
|
|
610
|
+
type: "boolean",
|
|
611
|
+
description: "Remove unstable CSS classes (default: true)"
|
|
612
|
+
},
|
|
613
|
+
includeComments: {
|
|
614
|
+
type: "boolean",
|
|
615
|
+
description: "Include descriptive comments (default: true)"
|
|
616
|
+
},
|
|
617
|
+
generatePageObject: {
|
|
618
|
+
type: "boolean",
|
|
619
|
+
description: "Also generate Page Object class for the page (default: false)"
|
|
620
|
+
},
|
|
621
|
+
pageObjectClassName: {
|
|
622
|
+
type: "string",
|
|
623
|
+
description: "Page Object class name (optional, auto-generated if not provided)"
|
|
589
624
|
},
|
|
590
625
|
},
|
|
591
|
-
required: ["scenarioName", "language"],
|
|
626
|
+
required: ["scenarioName", "language", "targetFile"],
|
|
592
627
|
},
|
|
593
628
|
},
|
|
594
629
|
{
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server/tool-groups.js
|
|
3
|
+
*
|
|
4
|
+
* Tool group definitions for filtering
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const toolGroups = {
|
|
8
|
+
core: ['ping', 'openBrowser'],
|
|
9
|
+
|
|
10
|
+
interaction: ['click', 'type', 'scrollTo', 'waitForElement', 'hover'],
|
|
11
|
+
|
|
12
|
+
inspection: ['getElement', 'getComputedCss', 'getBoxModel', 'screenshot', 'saveScreenshot'],
|
|
13
|
+
|
|
14
|
+
debug: [
|
|
15
|
+
'getConsoleLogs',
|
|
16
|
+
'listNetworkRequests',
|
|
17
|
+
'getNetworkRequest',
|
|
18
|
+
'filterNetworkRequests'
|
|
19
|
+
],
|
|
20
|
+
|
|
21
|
+
advanced: [
|
|
22
|
+
'executeScript',
|
|
23
|
+
'setStyles',
|
|
24
|
+
'setViewport',
|
|
25
|
+
'getViewport',
|
|
26
|
+
'navigateTo',
|
|
27
|
+
'smartFindElement',
|
|
28
|
+
'analyzePage',
|
|
29
|
+
'getAllInteractiveElements',
|
|
30
|
+
'findElementsByText'
|
|
31
|
+
],
|
|
32
|
+
|
|
33
|
+
recorder: [
|
|
34
|
+
'enableRecorder',
|
|
35
|
+
'executeScenario',
|
|
36
|
+
'listScenarios',
|
|
37
|
+
'searchScenarios',
|
|
38
|
+
'getScenarioInfo',
|
|
39
|
+
'deleteScenario',
|
|
40
|
+
'exportScenarioAsCode',
|
|
41
|
+
'appendScenarioToFile',
|
|
42
|
+
'generatePageObject'
|
|
43
|
+
],
|
|
44
|
+
|
|
45
|
+
figma: [
|
|
46
|
+
'getFigmaFrame',
|
|
47
|
+
'compareFigmaToElement',
|
|
48
|
+
'getFigmaSpecs',
|
|
49
|
+
'parseFigmaUrl',
|
|
50
|
+
'listFigmaPages',
|
|
51
|
+
'searchFigmaFrames',
|
|
52
|
+
'getFigmaComponents',
|
|
53
|
+
'getFigmaStyles',
|
|
54
|
+
'getFigmaColorPalette'
|
|
55
|
+
]
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get all tool names from specified groups
|
|
60
|
+
* @param {string[]} groupNames - Array of group names (e.g., ['core', 'interaction'])
|
|
61
|
+
* @returns {Set<string>} - Set of tool names
|
|
62
|
+
*/
|
|
63
|
+
export function getToolsFromGroups(groupNames) {
|
|
64
|
+
const tools = new Set();
|
|
65
|
+
|
|
66
|
+
for (const groupName of groupNames) {
|
|
67
|
+
const group = toolGroups[groupName];
|
|
68
|
+
if (group) {
|
|
69
|
+
group.forEach(tool => tools.add(tool));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return tools;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get all available group names
|
|
78
|
+
* @returns {string[]} - Array of group names
|
|
79
|
+
*/
|
|
80
|
+
export function getAllGroupNames() {
|
|
81
|
+
return Object.keys(toolGroups);
|
|
82
|
+
}
|