mcp-accessibility-scanner 1.0.10 → 1.1.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/NOTICE.md +21 -0
- package/Readme.md +194 -68
- package/cli.js +18 -0
- package/lib/browserContextFactory.js +228 -0
- package/lib/browserContextFactory.js.map +1 -0
- package/lib/browserServer.js +152 -0
- package/lib/browserServer.js.map +1 -0
- package/lib/config.js +190 -0
- package/lib/config.js.map +1 -0
- package/lib/connection.js +83 -0
- package/lib/connection.js.map +1 -0
- package/lib/context.js +292 -0
- package/lib/context.js.map +1 -0
- package/lib/fileUtils.js +33 -0
- package/lib/fileUtils.js.map +1 -0
- package/lib/httpServer.js +202 -0
- package/lib/httpServer.js.map +1 -0
- package/lib/index.js +37 -0
- package/lib/index.js.map +1 -0
- package/lib/javascript.js +50 -0
- package/lib/javascript.js.map +1 -0
- package/lib/manualPromise.js +112 -0
- package/lib/manualPromise.js.map +1 -0
- package/lib/package.js +21 -0
- package/lib/package.js.map +1 -0
- package/lib/pageSnapshot.js +44 -0
- package/lib/pageSnapshot.js.map +1 -0
- package/lib/program.js +72 -0
- package/lib/program.js.map +1 -0
- package/lib/server.js +49 -0
- package/lib/server.js.map +1 -0
- package/lib/tab.js +102 -0
- package/lib/tab.js.map +1 -0
- package/lib/tools/common.js +69 -0
- package/lib/tools/common.js.map +1 -0
- package/lib/tools/console.js +45 -0
- package/lib/tools/console.js.map +1 -0
- package/lib/tools/dialogs.js +53 -0
- package/lib/tools/dialogs.js.map +1 -0
- package/lib/tools/files.js +52 -0
- package/lib/tools/files.js.map +1 -0
- package/lib/tools/install.js +57 -0
- package/lib/tools/install.js.map +1 -0
- package/lib/tools/keyboard.js +47 -0
- package/lib/tools/keyboard.js.map +1 -0
- package/lib/tools/navigate.js +94 -0
- package/lib/tools/navigate.js.map +1 -0
- package/lib/tools/network.js +52 -0
- package/lib/tools/network.js.map +1 -0
- package/lib/tools/pdf.js +50 -0
- package/lib/tools/pdf.js.map +1 -0
- package/lib/tools/screenshot.js +78 -0
- package/lib/tools/screenshot.js.map +1 -0
- package/lib/tools/snapshot.js +245 -0
- package/lib/tools/snapshot.js.map +1 -0
- package/lib/tools/tabs.js +119 -0
- package/lib/tools/tabs.js.map +1 -0
- package/lib/tools/tool.js +19 -0
- package/lib/tools/tool.js.map +1 -0
- package/lib/tools/utils.js +81 -0
- package/lib/tools/utils.js.map +1 -0
- package/lib/tools/vision.js +190 -0
- package/lib/tools/vision.js.map +1 -0
- package/lib/tools/wait.js +60 -0
- package/lib/tools/wait.js.map +1 -0
- package/lib/tools.js +59 -0
- package/lib/tools.js.map +1 -0
- package/lib/transport.js +134 -0
- package/lib/transport.js.map +1 -0
- package/package.json +41 -13
- package/build/accessibilityChecker.js +0 -379
- package/build/index.js +0 -958
- package/build/server.js +0 -50
package/NOTICE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# NOTICE
|
|
2
|
+
|
|
3
|
+
## mcp-accessibility-scanner
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2024 Justas Monkev
|
|
6
|
+
|
|
7
|
+
This project is licensed under the MIT License.
|
|
8
|
+
|
|
9
|
+
## Third-Party Code Attribution
|
|
10
|
+
|
|
11
|
+
This project includes code adapted from:
|
|
12
|
+
|
|
13
|
+
### Microsoft Playwright MCP
|
|
14
|
+
- Copyright (c) Microsoft Corporation
|
|
15
|
+
- Licensed under the Apache License, Version 2.0
|
|
16
|
+
- Original source: https://github.com/microsoft/playwright-mcp
|
|
17
|
+
|
|
18
|
+
Adapted code has been modified to:
|
|
19
|
+
- Convert from ESM to CommonJS module format
|
|
20
|
+
- Integrate with axe-core accessibility scanning
|
|
21
|
+
- Work with existing Playwright instances rather than spawning new ones
|
package/Readme.md
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
|
-
[](https://mseep.ai/app/justasmonkev-mcp-accessibility-scanner)
|
|
2
1
|
|
|
3
2
|
# MCP Accessibility Scanner 🔍
|
|
4
3
|
|
|
4
|
+
[](https://mseep.ai/app/justasmonkev-mcp-accessibility-scanner)
|
|
5
|
+
|
|
5
6
|
A Model Context Protocol (MCP) server that provides automated web accessibility scanning using Playwright and Axe-core. This server enables LLMs to perform WCAG compliance checks, capture annotated screenshots, and generate detailed accessibility reports.
|
|
7
|
+
A powerful Model Context Protocol (MCP) server that provides automated web accessibility scanning and browser automation using Playwright and Axe-core. This server enables LLMs to perform WCAG compliance checks, interact with web pages, manage persistent browser sessions, and generate detailed accessibility reports with visual annotations.
|
|
6
8
|
|
|
7
9
|
## Features
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
### Accessibility Scanning
|
|
12
|
+
✅ Full WCAG 2.0/2.1/2.2 compliance checking (A, AA, AAA levels)
|
|
10
13
|
🖼️ Automatic screenshot capture with violation highlighting
|
|
11
|
-
📄 Detailed JSON reports with remediation guidance
|
|
14
|
+
📄 Detailed JSON reports with remediation guidance
|
|
15
|
+
🎯 Support for specific violation categories (color contrast, ARIA, forms, keyboard navigation, etc.)
|
|
16
|
+
|
|
17
|
+
### Browser Automation
|
|
18
|
+
🖱️ Click, hover, and drag elements using accessibility snapshots
|
|
19
|
+
⌨️ Type text and handle keyboard inputs
|
|
20
|
+
🔍 Capture page snapshots to discover all interactive elements
|
|
21
|
+
📸 Take screenshots and save PDFs
|
|
22
|
+
🎯 Support for both element-based and coordinate-based interactions
|
|
23
|
+
|
|
24
|
+
### Advanced Features
|
|
25
|
+
📑 Tab management for multi-page workflows
|
|
26
|
+
🌐 Monitor console messages and network requests
|
|
27
|
+
⏱️ Wait for dynamic content to load
|
|
28
|
+
📁 Handle file uploads and browser dialogs
|
|
29
|
+
🔄 Navigate through browser history
|
|
12
30
|
|
|
13
31
|
## Installation
|
|
14
32
|
|
|
@@ -19,25 +37,6 @@ Using npm:
|
|
|
19
37
|
npm install -g mcp-accessibility-scanner
|
|
20
38
|
```
|
|
21
39
|
|
|
22
|
-
### Docker Installation
|
|
23
|
-
|
|
24
|
-
The project includes a Dockerfile that sets up all necessary dependencies including Node.js v22 and Python 3.13.
|
|
25
|
-
|
|
26
|
-
1. Build the Docker image:
|
|
27
|
-
```bash
|
|
28
|
-
docker build -t mcp-server .
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
2. Run the container:
|
|
32
|
-
```bash
|
|
33
|
-
docker run -it -e MCP_PROXY_DEBUG=true mcp-server
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
You can also run it in the background:
|
|
37
|
-
```bash
|
|
38
|
-
docker run -d -p 3000:3000 mcp-server
|
|
39
|
-
```
|
|
40
|
-
|
|
41
40
|
### Installation in VS Code
|
|
42
41
|
|
|
43
42
|
Install the Accessibility Scanner in VS Code using the VS Code CLI:
|
|
@@ -67,72 +66,199 @@ Here's the Claude Desktop configuration:
|
|
|
67
66
|
}
|
|
68
67
|
```
|
|
69
68
|
|
|
70
|
-
##
|
|
69
|
+
## Available Tools
|
|
70
|
+
|
|
71
|
+
The MCP server provides comprehensive browser automation and accessibility scanning tools:
|
|
72
|
+
|
|
73
|
+
### Core Accessibility Tool
|
|
74
|
+
|
|
75
|
+
#### `scan_page`
|
|
76
|
+
Performs a comprehensive accessibility scan on the current page using Axe-core.
|
|
77
|
+
|
|
78
|
+
**Parameters:**
|
|
79
|
+
- `violationsTag`: Array of WCAG/violation tags to check
|
|
80
|
+
|
|
81
|
+
**Supported Violation Tags:**
|
|
82
|
+
- WCAG standards: `wcag2a`, `wcag2aa`, `wcag2aaa`, `wcag21a`, `wcag21aa`, `wcag21aaa`, `wcag22a`, `wcag22aa`, `wcag22aaa`
|
|
83
|
+
- Section 508: `section508`
|
|
84
|
+
- Categories: `cat.aria`, `cat.color`, `cat.forms`, `cat.keyboard`, `cat.language`, `cat.name-role-value`, `cat.parsing`, `cat.semantics`, `cat.sensory-and-visual-cues`, `cat.structure`, `cat.tables`, `cat.text-alternatives`, `cat.time-and-media`
|
|
85
|
+
|
|
86
|
+
### Navigation Tools
|
|
87
|
+
|
|
88
|
+
#### `browser_navigate`
|
|
89
|
+
Navigate to a URL.
|
|
90
|
+
- Parameters: `url` (string)
|
|
91
|
+
|
|
92
|
+
#### `browser_navigate_back`
|
|
93
|
+
Go back to the previous page.
|
|
94
|
+
|
|
95
|
+
#### `browser_navigate_forward`
|
|
96
|
+
Go forward to the next page.
|
|
97
|
+
|
|
98
|
+
### Page Interaction Tools
|
|
99
|
+
|
|
100
|
+
#### `browser_snapshot`
|
|
101
|
+
Capture accessibility snapshot of the current page (better than screenshot for analysis).
|
|
102
|
+
|
|
103
|
+
#### `browser_click`
|
|
104
|
+
Perform click on a web page element.
|
|
105
|
+
- Parameters: `element` (description), `ref` (element reference), `doubleClick` (optional)
|
|
106
|
+
|
|
107
|
+
#### `browser_type`
|
|
108
|
+
Type text into editable element.
|
|
109
|
+
- Parameters: `element`, `ref`, `text`, `submit` (optional), `slowly` (optional)
|
|
110
|
+
|
|
111
|
+
#### `browser_hover`
|
|
112
|
+
Hover over element on page.
|
|
113
|
+
- Parameters: `element`, `ref`
|
|
114
|
+
|
|
115
|
+
#### `browser_drag`
|
|
116
|
+
Perform drag and drop between two elements.
|
|
117
|
+
- Parameters: `startElement`, `startRef`, `endElement`, `endRef`
|
|
118
|
+
|
|
119
|
+
#### `browser_select_option`
|
|
120
|
+
Select an option in a dropdown.
|
|
121
|
+
- Parameters: `element`, `ref`, `values` (array)
|
|
122
|
+
|
|
123
|
+
#### `browser_press_key`
|
|
124
|
+
Press a key on the keyboard.
|
|
125
|
+
- Parameters: `key` (e.g., 'ArrowLeft' or 'a')
|
|
126
|
+
|
|
127
|
+
### Screenshot & Visual Tools
|
|
128
|
+
|
|
129
|
+
#### `browser_take_screenshot`
|
|
130
|
+
Take a screenshot of the current page.
|
|
131
|
+
- Parameters: `raw` (optional), `filename` (optional), `element` (optional), `ref` (optional)
|
|
132
|
+
|
|
133
|
+
#### `browser_pdf_save`
|
|
134
|
+
Save page as PDF.
|
|
135
|
+
- Parameters: `filename` (optional, defaults to `page-{timestamp}.pdf`)
|
|
136
|
+
|
|
137
|
+
### Browser Management
|
|
138
|
+
|
|
139
|
+
#### `browser_close`
|
|
140
|
+
Close the page.
|
|
141
|
+
|
|
142
|
+
#### `browser_resize`
|
|
143
|
+
Resize the browser window.
|
|
144
|
+
- Parameters: `width`, `height`
|
|
145
|
+
|
|
146
|
+
### Tab Management
|
|
147
|
+
|
|
148
|
+
#### `browser_tab_list`
|
|
149
|
+
List all open browser tabs.
|
|
71
150
|
|
|
72
|
-
|
|
151
|
+
#### `browser_tab_new`
|
|
152
|
+
Open a new tab.
|
|
153
|
+
- Parameters: `url` (optional)
|
|
73
154
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- `
|
|
77
|
-
- `width`: number (default: 1920)
|
|
78
|
-
- `height`: number (default: 1080)
|
|
79
|
-
- `shouldRunInHeadless`: Optional boolean to control headless mode (default: true)
|
|
155
|
+
#### `browser_tab_select`
|
|
156
|
+
Select a tab by index.
|
|
157
|
+
- Parameters: `index`
|
|
80
158
|
|
|
81
|
-
|
|
159
|
+
#### `browser_tab_close`
|
|
160
|
+
Close a tab.
|
|
161
|
+
- Parameters: `index` (optional, closes current tab if not provided)
|
|
82
162
|
|
|
83
|
-
|
|
163
|
+
### Information & Monitoring Tools
|
|
164
|
+
|
|
165
|
+
#### `browser_console_messages`
|
|
166
|
+
Returns all console messages from the page.
|
|
167
|
+
|
|
168
|
+
#### `browser_network_requests`
|
|
169
|
+
Returns all network requests since loading the page.
|
|
170
|
+
|
|
171
|
+
### Utility Tools
|
|
172
|
+
|
|
173
|
+
#### `browser_wait_for`
|
|
174
|
+
Wait for text to appear/disappear or time to pass.
|
|
175
|
+
- Parameters: `time` (optional), `text` (optional), `textGone` (optional)
|
|
176
|
+
|
|
177
|
+
#### `browser_handle_dialog`
|
|
178
|
+
Handle browser dialogs (alerts, confirms, prompts).
|
|
179
|
+
- Parameters: `accept` (boolean), `promptText` (optional)
|
|
180
|
+
|
|
181
|
+
#### `browser_file_upload`
|
|
182
|
+
Upload files to the page.
|
|
183
|
+
- Parameters: `paths` (array of absolute file paths)
|
|
184
|
+
|
|
185
|
+
### Vision Mode Tools (Coordinate-based Interaction)
|
|
186
|
+
|
|
187
|
+
#### `browser_screen_capture`
|
|
188
|
+
Take a screenshot for coordinate-based interaction.
|
|
189
|
+
|
|
190
|
+
#### `browser_screen_move_mouse`
|
|
191
|
+
Move mouse to specific coordinates.
|
|
192
|
+
- Parameters: `element`, `x`, `y`
|
|
193
|
+
|
|
194
|
+
#### `browser_screen_click`
|
|
195
|
+
Click at specific coordinates.
|
|
196
|
+
- Parameters: `element`, `x`, `y`
|
|
197
|
+
|
|
198
|
+
#### `browser_screen_drag`
|
|
199
|
+
Drag from one coordinate to another.
|
|
200
|
+
- Parameters: `element`, `startX`, `startY`, `endX`, `endY`
|
|
201
|
+
|
|
202
|
+
#### `browser_screen_type`
|
|
203
|
+
Type text (coordinate-independent).
|
|
204
|
+
- Parameters: `text`, `submit` (optional)
|
|
205
|
+
|
|
206
|
+
## Usage Examples
|
|
207
|
+
|
|
208
|
+
### Basic Accessibility Scan
|
|
84
209
|
```
|
|
85
|
-
|
|
210
|
+
1. Navigate to example.com using browser_navigate
|
|
211
|
+
2. Run scan_page with violationsTag: ["wcag21aa"]
|
|
86
212
|
```
|
|
87
213
|
|
|
88
|
-
|
|
214
|
+
### Color Contrast Check
|
|
89
215
|
```
|
|
90
|
-
|
|
216
|
+
1. Use browser_navigate to go to example.com
|
|
217
|
+
2. Run scan_page with violationsTag: ["cat.color"]
|
|
91
218
|
```
|
|
92
219
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
Clone and set up the project:
|
|
96
|
-
```bash
|
|
97
|
-
git clone https://github.com/JustasMonkev/mcp-accessibility-scanner.git
|
|
98
|
-
cd mcp-accessibility-scanner
|
|
99
|
-
npm install
|
|
220
|
+
### Multi-step Workflow
|
|
100
221
|
```
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
222
|
+
1. Navigate to example.com with browser_navigate
|
|
223
|
+
2. Take a browser_snapshot to see available elements
|
|
224
|
+
3. Click the "Sign In" button using browser_click
|
|
225
|
+
4. Type "user@example.com" using browser_type
|
|
226
|
+
5. Run scan_page on the login page
|
|
227
|
+
6. Take a browser_take_screenshot to capture the final state
|
|
105
228
|
```
|
|
106
229
|
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
|
|
230
|
+
### Page Analysis
|
|
231
|
+
```
|
|
232
|
+
1. Navigate to example.com
|
|
233
|
+
2. Use browser_snapshot to capture all interactive elements
|
|
234
|
+
3. Review console messages with browser_console_messages
|
|
235
|
+
4. Check network activity with browser_network_requests
|
|
110
236
|
```
|
|
111
237
|
|
|
112
|
-
###
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
docker build -t mcp-server-dev .
|
|
238
|
+
### Tab Management
|
|
239
|
+
```
|
|
240
|
+
1. Open a new tab with browser_tab_new
|
|
241
|
+
2. Navigate to different pages in each tab
|
|
242
|
+
3. Switch between tabs using browser_tab_select
|
|
243
|
+
4. List all tabs with browser_tab_list
|
|
119
244
|
```
|
|
120
245
|
|
|
121
|
-
|
|
122
|
-
```
|
|
123
|
-
|
|
246
|
+
### Waiting for Dynamic Content
|
|
247
|
+
```
|
|
248
|
+
1. Navigate to a page
|
|
249
|
+
2. Use browser_wait_for to wait for specific text to appear
|
|
250
|
+
3. Interact with the dynamically loaded content
|
|
124
251
|
```
|
|
125
252
|
|
|
126
|
-
|
|
253
|
+
**Note:** Most interaction tools require element references from browser_snapshot. Always capture a snapshot before attempting to interact with page elements.
|
|
127
254
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
└── tsconfig.json # TypeScript configuration
|
|
255
|
+
## Development
|
|
256
|
+
|
|
257
|
+
Clone and set up the project:
|
|
258
|
+
```bash
|
|
259
|
+
git clone https://github.com/JustasMonkev/mcp-accessibility-scanner.git
|
|
260
|
+
cd mcp-accessibility-scanner
|
|
261
|
+
npm install
|
|
136
262
|
```
|
|
137
263
|
|
|
138
264
|
## License
|
package/cli.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Microsoft Corporation.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import './lib/program.js';
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import net from 'node:net';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import debug from 'debug';
|
|
21
|
+
import * as playwright from 'playwright';
|
|
22
|
+
import { userDataDir } from './fileUtils.js';
|
|
23
|
+
const testDebug = debug('pw:mcp:test');
|
|
24
|
+
export function contextFactory(browserConfig) {
|
|
25
|
+
if (browserConfig.remoteEndpoint)
|
|
26
|
+
return new RemoteContextFactory(browserConfig);
|
|
27
|
+
if (browserConfig.cdpEndpoint)
|
|
28
|
+
return new CdpContextFactory(browserConfig);
|
|
29
|
+
if (browserConfig.isolated)
|
|
30
|
+
return new IsolatedContextFactory(browserConfig);
|
|
31
|
+
if (browserConfig.browserAgent)
|
|
32
|
+
return new BrowserServerContextFactory(browserConfig);
|
|
33
|
+
return new PersistentContextFactory(browserConfig);
|
|
34
|
+
}
|
|
35
|
+
class BaseContextFactory {
|
|
36
|
+
browserConfig;
|
|
37
|
+
_browserPromise;
|
|
38
|
+
name;
|
|
39
|
+
constructor(name, browserConfig) {
|
|
40
|
+
this.name = name;
|
|
41
|
+
this.browserConfig = browserConfig;
|
|
42
|
+
}
|
|
43
|
+
async _obtainBrowser() {
|
|
44
|
+
if (this._browserPromise)
|
|
45
|
+
return this._browserPromise;
|
|
46
|
+
testDebug(`obtain browser (${this.name})`);
|
|
47
|
+
this._browserPromise = this._doObtainBrowser();
|
|
48
|
+
void this._browserPromise.then(browser => {
|
|
49
|
+
browser.on('disconnected', () => {
|
|
50
|
+
this._browserPromise = undefined;
|
|
51
|
+
});
|
|
52
|
+
}).catch(() => {
|
|
53
|
+
this._browserPromise = undefined;
|
|
54
|
+
});
|
|
55
|
+
return this._browserPromise;
|
|
56
|
+
}
|
|
57
|
+
async _doObtainBrowser() {
|
|
58
|
+
throw new Error('Not implemented');
|
|
59
|
+
}
|
|
60
|
+
async createContext() {
|
|
61
|
+
testDebug(`create browser context (${this.name})`);
|
|
62
|
+
const browser = await this._obtainBrowser();
|
|
63
|
+
const browserContext = await this._doCreateContext(browser);
|
|
64
|
+
return { browserContext, close: () => this._closeBrowserContext(browserContext, browser) };
|
|
65
|
+
}
|
|
66
|
+
async _doCreateContext(browser) {
|
|
67
|
+
throw new Error('Not implemented');
|
|
68
|
+
}
|
|
69
|
+
async _closeBrowserContext(browserContext, browser) {
|
|
70
|
+
testDebug(`close browser context (${this.name})`);
|
|
71
|
+
if (browser.contexts().length === 1)
|
|
72
|
+
this._browserPromise = undefined;
|
|
73
|
+
await browserContext.close().catch(() => { });
|
|
74
|
+
if (browser.contexts().length === 0) {
|
|
75
|
+
testDebug(`close browser (${this.name})`);
|
|
76
|
+
await browser.close().catch(() => { });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
class IsolatedContextFactory extends BaseContextFactory {
|
|
81
|
+
constructor(browserConfig) {
|
|
82
|
+
super('isolated', browserConfig);
|
|
83
|
+
}
|
|
84
|
+
async _doObtainBrowser() {
|
|
85
|
+
await injectCdpPort(this.browserConfig);
|
|
86
|
+
const browserType = playwright[this.browserConfig.browserName];
|
|
87
|
+
return browserType.launch({
|
|
88
|
+
...this.browserConfig.launchOptions,
|
|
89
|
+
handleSIGINT: false,
|
|
90
|
+
handleSIGTERM: false,
|
|
91
|
+
}).catch(error => {
|
|
92
|
+
if (error.message.includes('Executable doesn\'t exist'))
|
|
93
|
+
throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
|
|
94
|
+
throw error;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async _doCreateContext(browser) {
|
|
98
|
+
return browser.newContext(this.browserConfig.contextOptions);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
class CdpContextFactory extends BaseContextFactory {
|
|
102
|
+
constructor(browserConfig) {
|
|
103
|
+
super('cdp', browserConfig);
|
|
104
|
+
}
|
|
105
|
+
async _doObtainBrowser() {
|
|
106
|
+
return playwright.chromium.connectOverCDP(this.browserConfig.cdpEndpoint);
|
|
107
|
+
}
|
|
108
|
+
async _doCreateContext(browser) {
|
|
109
|
+
return this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
class RemoteContextFactory extends BaseContextFactory {
|
|
113
|
+
constructor(browserConfig) {
|
|
114
|
+
super('remote', browserConfig);
|
|
115
|
+
}
|
|
116
|
+
async _doObtainBrowser() {
|
|
117
|
+
const url = new URL(this.browserConfig.remoteEndpoint);
|
|
118
|
+
url.searchParams.set('browser', this.browserConfig.browserName);
|
|
119
|
+
if (this.browserConfig.launchOptions)
|
|
120
|
+
url.searchParams.set('launch-options', JSON.stringify(this.browserConfig.launchOptions));
|
|
121
|
+
return playwright[this.browserConfig.browserName].connect(String(url));
|
|
122
|
+
}
|
|
123
|
+
async _doCreateContext(browser) {
|
|
124
|
+
return browser.newContext();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
class PersistentContextFactory {
|
|
128
|
+
browserConfig;
|
|
129
|
+
_userDataDirs = new Set();
|
|
130
|
+
constructor(browserConfig) {
|
|
131
|
+
this.browserConfig = browserConfig;
|
|
132
|
+
}
|
|
133
|
+
async createContext() {
|
|
134
|
+
await injectCdpPort(this.browserConfig);
|
|
135
|
+
testDebug('create browser context (persistent)');
|
|
136
|
+
const userDataDir = this.browserConfig.userDataDir ?? await this._createUserDataDir();
|
|
137
|
+
this._userDataDirs.add(userDataDir);
|
|
138
|
+
testDebug('lock user data dir', userDataDir);
|
|
139
|
+
const browserType = playwright[this.browserConfig.browserName];
|
|
140
|
+
for (let i = 0; i < 5; i++) {
|
|
141
|
+
try {
|
|
142
|
+
const browserContext = await browserType.launchPersistentContext(userDataDir, {
|
|
143
|
+
...this.browserConfig.launchOptions,
|
|
144
|
+
...this.browserConfig.contextOptions,
|
|
145
|
+
handleSIGINT: false,
|
|
146
|
+
handleSIGTERM: false,
|
|
147
|
+
});
|
|
148
|
+
const close = () => this._closeBrowserContext(browserContext, userDataDir);
|
|
149
|
+
return { browserContext, close };
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (error.message.includes('Executable doesn\'t exist'))
|
|
153
|
+
throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
|
|
154
|
+
if (error.message.includes('ProcessSingleton') || error.message.includes('Invalid URL')) {
|
|
155
|
+
// User data directory is already in use, try again.
|
|
156
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`);
|
|
163
|
+
}
|
|
164
|
+
async _closeBrowserContext(browserContext, userDataDir) {
|
|
165
|
+
testDebug('close browser context (persistent)');
|
|
166
|
+
testDebug('release user data dir', userDataDir);
|
|
167
|
+
await browserContext.close().catch(() => { });
|
|
168
|
+
this._userDataDirs.delete(userDataDir);
|
|
169
|
+
testDebug('close browser context complete (persistent)');
|
|
170
|
+
}
|
|
171
|
+
async _createUserDataDir() {
|
|
172
|
+
let cacheDirectory;
|
|
173
|
+
if (process.platform === 'linux')
|
|
174
|
+
cacheDirectory = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
|
|
175
|
+
else if (process.platform === 'darwin')
|
|
176
|
+
cacheDirectory = path.join(os.homedir(), 'Library', 'Caches');
|
|
177
|
+
else if (process.platform === 'win32')
|
|
178
|
+
cacheDirectory = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
179
|
+
else
|
|
180
|
+
throw new Error('Unsupported platform: ' + process.platform);
|
|
181
|
+
const result = path.join(cacheDirectory, 'ms-playwright', `mcp-${this.browserConfig.launchOptions?.channel ?? this.browserConfig?.browserName}-profile`);
|
|
182
|
+
await fs.promises.mkdir(result, { recursive: true });
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
export class BrowserServerContextFactory extends BaseContextFactory {
|
|
187
|
+
constructor(browserConfig) {
|
|
188
|
+
super('persistent', browserConfig);
|
|
189
|
+
}
|
|
190
|
+
async _doObtainBrowser() {
|
|
191
|
+
const response = await fetch(new URL(`/json/launch`, this.browserConfig.browserAgent), {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
body: JSON.stringify({
|
|
194
|
+
browserType: this.browserConfig.browserName,
|
|
195
|
+
userDataDir: this.browserConfig.userDataDir ?? await this._createUserDataDir(),
|
|
196
|
+
launchOptions: this.browserConfig.launchOptions,
|
|
197
|
+
contextOptions: this.browserConfig.contextOptions,
|
|
198
|
+
}),
|
|
199
|
+
});
|
|
200
|
+
const info = await response.json();
|
|
201
|
+
if (info.error)
|
|
202
|
+
throw new Error(info.error);
|
|
203
|
+
return await playwright.chromium.connectOverCDP(`http://localhost:${info.cdpPort}/`);
|
|
204
|
+
}
|
|
205
|
+
async _doCreateContext(browser) {
|
|
206
|
+
return this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
|
|
207
|
+
}
|
|
208
|
+
async _createUserDataDir() {
|
|
209
|
+
const dir = await userDataDir(this.browserConfig);
|
|
210
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
211
|
+
return dir;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function injectCdpPort(browserConfig) {
|
|
215
|
+
if (browserConfig.browserName === 'chromium')
|
|
216
|
+
browserConfig.launchOptions.cdpPort = await findFreePort();
|
|
217
|
+
}
|
|
218
|
+
async function findFreePort() {
|
|
219
|
+
return new Promise((resolve, reject) => {
|
|
220
|
+
const server = net.createServer();
|
|
221
|
+
server.listen(0, () => {
|
|
222
|
+
const { port } = server.address();
|
|
223
|
+
server.close(() => resolve(port));
|
|
224
|
+
});
|
|
225
|
+
server.on('error', reject);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=browserContextFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browserContextFactory.js","sourceRoot":"","sources":["../src/browserContextFactory.ts"],"names":[],"mappings":"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"}
|