@playwright/mcp 1.52.0-alpha-2025-03-18 → 1.52.0-alpha-2025-03-19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +203 -13
- package/cli.js +1 -1
- package/lib/context.js +66 -0
- package/lib/program.js +55 -0
- package/lib/server.js +105 -0
- package/package.json +4 -2
- package/lib/utils.js +0 -67
package/README.md
CHANGED
|
@@ -1,10 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
## Playwright MCP
|
|
2
2
|
|
|
3
3
|
This package is experimental and not yet ready for production use.
|
|
4
4
|
It is a subject to change and will not respect semver versioning.
|
|
5
5
|
|
|
6
6
|
### Example config
|
|
7
7
|
|
|
8
|
+
```js
|
|
9
|
+
{
|
|
10
|
+
"mcpServers": {
|
|
11
|
+
"playwright": {
|
|
12
|
+
"command": "npx",
|
|
13
|
+
"args": [
|
|
14
|
+
"@playwright/mcp",
|
|
15
|
+
"--headless"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Running headed browser (Browser with GUI).
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"playwright": {
|
|
28
|
+
"command": "npx",
|
|
29
|
+
"args": [
|
|
30
|
+
"@playwright/mcp"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Running headed browser on Linux
|
|
38
|
+
|
|
39
|
+
When running headed browser on system w/o display or from worker processes of the IDEs,
|
|
40
|
+
you can run Playwright in a client-server manner. You'll run the Playwright server
|
|
41
|
+
from environment with the DISPLAY
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npx playwright run-server
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
And then in MCP config, add following to the `env`:
|
|
48
|
+
|
|
8
49
|
```js
|
|
9
50
|
{
|
|
10
51
|
"mcpServers": {
|
|
@@ -14,20 +55,169 @@ It is a subject to change and will not respect semver versioning.
|
|
|
14
55
|
"@playwright/mcp"
|
|
15
56
|
],
|
|
16
57
|
"env": {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* (Cline on Linux, etc), either set HEADLESS to 1.
|
|
20
|
-
*/
|
|
21
|
-
"PLAYWRIGHT_HEADLESS": "1",
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* ...or use Playwright Client/Server mode via
|
|
25
|
-
* running the Playwright Server
|
|
26
|
-
* npx playwright run-server
|
|
27
|
-
*/
|
|
28
|
-
"PLAYWRIGHT_WS_ENDPOINT": "ws://localhost:41541/"
|
|
58
|
+
// Use the endpoint from the output of the server above.
|
|
59
|
+
"PLAYWRIGHT_WS_ENDPOINT": "ws://localhost:<port>/"
|
|
29
60
|
}
|
|
30
61
|
}
|
|
31
62
|
}
|
|
32
63
|
}
|
|
33
64
|
```
|
|
65
|
+
|
|
66
|
+
### Tool Modes
|
|
67
|
+
|
|
68
|
+
The tools are available in two modes:
|
|
69
|
+
|
|
70
|
+
1. **Snapshot Mode** (default): Uses accessibility snapshots for better performance and reliability
|
|
71
|
+
2. **Vision Mode**: Uses screenshots for visual-based interactions
|
|
72
|
+
|
|
73
|
+
To use Vision Mode, add the `--vision` flag when starting the server:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"playwright": {
|
|
79
|
+
"command": "npx",
|
|
80
|
+
"args": [
|
|
81
|
+
"@playwright/mcp",
|
|
82
|
+
"--vision"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Vision Mode works best with the computer use models that are able to interact with elements using
|
|
90
|
+
X Y coordinate space, based on the provided screenshot.
|
|
91
|
+
|
|
92
|
+
### Snapshot Mode
|
|
93
|
+
|
|
94
|
+
The Playwright MCP provides a set of tools for browser automation. Here are all available tools:
|
|
95
|
+
|
|
96
|
+
- **browser_navigate**
|
|
97
|
+
- Description: Navigate to a URL
|
|
98
|
+
- Parameters:
|
|
99
|
+
- `url` (string): The URL to navigate to
|
|
100
|
+
|
|
101
|
+
- **browser_go_back**
|
|
102
|
+
- Description: Go back to the previous page
|
|
103
|
+
- Parameters: None
|
|
104
|
+
|
|
105
|
+
- **browser_go_forward**
|
|
106
|
+
- Description: Go forward to the next page
|
|
107
|
+
- Parameters: None
|
|
108
|
+
|
|
109
|
+
- **browser_click**
|
|
110
|
+
- Description: Perform click on a web page
|
|
111
|
+
- Parameters:
|
|
112
|
+
- `element` (string): Human-readable element description used to obtain the permission to interact with the element
|
|
113
|
+
- `ref` (string): Exact target element reference from the page snapshot
|
|
114
|
+
|
|
115
|
+
- **browser_hover**
|
|
116
|
+
- Description: Hover over element on page
|
|
117
|
+
- Parameters:
|
|
118
|
+
- `element` (string): Human-readable element description used to obtain the permission to interact with the element
|
|
119
|
+
- `ref` (string): Exact target element reference from the page snapshot
|
|
120
|
+
|
|
121
|
+
- **browser_drag**
|
|
122
|
+
- Description: Perform drag and drop between two elements
|
|
123
|
+
- Parameters:
|
|
124
|
+
- `startElement` (string): Human-readable source element description used to obtain the permission to interact with the element
|
|
125
|
+
- `startRef` (string): Exact source element reference from the page snapshot
|
|
126
|
+
- `endElement` (string): Human-readable target element description used to obtain the permission to interact with the element
|
|
127
|
+
- `endRef` (string): Exact target element reference from the page snapshot
|
|
128
|
+
|
|
129
|
+
- **browser_type**
|
|
130
|
+
- Description: Type text into editable element
|
|
131
|
+
- Parameters:
|
|
132
|
+
- `element` (string): Human-readable element description used to obtain the permission to interact with the element
|
|
133
|
+
- `ref` (string): Exact target element reference from the page snapshot
|
|
134
|
+
- `text` (string): Text to type into the element
|
|
135
|
+
- `submit` (boolean): Whether to submit entered text (press Enter after)
|
|
136
|
+
|
|
137
|
+
- **browser_press_key**
|
|
138
|
+
- Description: Press a key on the keyboard
|
|
139
|
+
- Parameters:
|
|
140
|
+
- `key` (string): Name of the key to press or a character to generate, such as `ArrowLeft` or `a`
|
|
141
|
+
|
|
142
|
+
- **browser_snapshot**
|
|
143
|
+
- Description: Capture accessibility snapshot of the current page (better than screenshot)
|
|
144
|
+
- Parameters: None
|
|
145
|
+
|
|
146
|
+
- **browser_save_as_pdf**
|
|
147
|
+
- Description: Save page as PDF
|
|
148
|
+
- Parameters: None
|
|
149
|
+
|
|
150
|
+
- **browser_wait**
|
|
151
|
+
- Description: Wait for a specified time in seconds
|
|
152
|
+
- Parameters:
|
|
153
|
+
- `time` (number): The time to wait in seconds (capped at 10 seconds)
|
|
154
|
+
|
|
155
|
+
- **browser_close**
|
|
156
|
+
- Description: Close the page
|
|
157
|
+
- Parameters: None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
### Vision Mode
|
|
161
|
+
|
|
162
|
+
Vision Mode provides tools for visual-based interactions using screenshots. Here are all available tools:
|
|
163
|
+
|
|
164
|
+
- **browser_navigate**
|
|
165
|
+
- Description: Navigate to a URL
|
|
166
|
+
- Parameters:
|
|
167
|
+
- `url` (string): The URL to navigate to
|
|
168
|
+
|
|
169
|
+
- **browser_go_back**
|
|
170
|
+
- Description: Go back to the previous page
|
|
171
|
+
- Parameters: None
|
|
172
|
+
|
|
173
|
+
- **browser_go_forward**
|
|
174
|
+
- Description: Go forward to the next page
|
|
175
|
+
- Parameters: None
|
|
176
|
+
|
|
177
|
+
- **browser_screenshot**
|
|
178
|
+
- Description: Capture screenshot of the current page
|
|
179
|
+
- Parameters: None
|
|
180
|
+
|
|
181
|
+
- **browser_move_mouse**
|
|
182
|
+
- Description: Move mouse to specified coordinates
|
|
183
|
+
- Parameters:
|
|
184
|
+
- `x` (number): X coordinate
|
|
185
|
+
- `y` (number): Y coordinate
|
|
186
|
+
|
|
187
|
+
- **browser_click**
|
|
188
|
+
- Description: Click at specified coordinates
|
|
189
|
+
- Parameters:
|
|
190
|
+
- `x` (number): X coordinate to click at
|
|
191
|
+
- `y` (number): Y coordinate to click at
|
|
192
|
+
|
|
193
|
+
- **browser_drag**
|
|
194
|
+
- Description: Perform drag and drop operation
|
|
195
|
+
- Parameters:
|
|
196
|
+
- `startX` (number): Start X coordinate
|
|
197
|
+
- `startY` (number): Start Y coordinate
|
|
198
|
+
- `endX` (number): End X coordinate
|
|
199
|
+
- `endY` (number): End Y coordinate
|
|
200
|
+
|
|
201
|
+
- **browser_type**
|
|
202
|
+
- Description: Type text at specified coordinates
|
|
203
|
+
- Parameters:
|
|
204
|
+
- `text` (string): Text to type
|
|
205
|
+
- `submit` (boolean): Whether to submit entered text (press Enter after)
|
|
206
|
+
|
|
207
|
+
- **browser_press_key**
|
|
208
|
+
- Description: Press a key on the keyboard
|
|
209
|
+
- Parameters:
|
|
210
|
+
- `key` (string): Name of the key to press or a character to generate, such as `ArrowLeft` or `a`
|
|
211
|
+
|
|
212
|
+
- **browser_save_as_pdf**
|
|
213
|
+
- Description: Save page as PDF
|
|
214
|
+
- Parameters: None
|
|
215
|
+
|
|
216
|
+
- **browser_wait**
|
|
217
|
+
- Description: Wait for a specified time in seconds
|
|
218
|
+
- Parameters:
|
|
219
|
+
- `time` (number): The time to wait in seconds (capped at 10 seconds)
|
|
220
|
+
|
|
221
|
+
- **browser_close**
|
|
222
|
+
- Description: Close the page
|
|
223
|
+
- Parameters: None
|
package/cli.js
CHANGED
package/lib/context.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Context = void 0;
|
|
7
|
+
var playwright = _interopRequireWildcard(require("playwright"));
|
|
8
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
9
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
10
|
+
/**
|
|
11
|
+
* Copyright (c) Microsoft Corporation.
|
|
12
|
+
*
|
|
13
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
* you may not use this file except in compliance with the License.
|
|
15
|
+
* You may obtain a copy of the License at
|
|
16
|
+
*
|
|
17
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
*
|
|
19
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
* See the License for the specific language governing permissions and
|
|
23
|
+
* limitations under the License.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
class Context {
|
|
27
|
+
constructor(launchOptions) {
|
|
28
|
+
this._launchOptions = void 0;
|
|
29
|
+
this._page = void 0;
|
|
30
|
+
this._console = [];
|
|
31
|
+
this._initializePromise = void 0;
|
|
32
|
+
this._launchOptions = launchOptions;
|
|
33
|
+
}
|
|
34
|
+
async ensurePage() {
|
|
35
|
+
await this._initialize();
|
|
36
|
+
return this._page;
|
|
37
|
+
}
|
|
38
|
+
async ensureConsole() {
|
|
39
|
+
await this._initialize();
|
|
40
|
+
return this._console;
|
|
41
|
+
}
|
|
42
|
+
async close() {
|
|
43
|
+
const page = await this.ensurePage();
|
|
44
|
+
await page.close();
|
|
45
|
+
this._initializePromise = undefined;
|
|
46
|
+
}
|
|
47
|
+
async _initialize() {
|
|
48
|
+
if (this._initializePromise) return this._initializePromise;
|
|
49
|
+
this._initializePromise = (async () => {
|
|
50
|
+
const browser = await this._createBrowser();
|
|
51
|
+
this._page = await browser.newPage();
|
|
52
|
+
this._page.on('console', event => this._console.push(event));
|
|
53
|
+
this._page.on('framenavigated', () => this._console.length = 0);
|
|
54
|
+
})();
|
|
55
|
+
return this._initializePromise;
|
|
56
|
+
}
|
|
57
|
+
async _createBrowser() {
|
|
58
|
+
if (process.env.PLAYWRIGHT_WS_ENDPOINT) {
|
|
59
|
+
const url = new URL(process.env.PLAYWRIGHT_WS_ENDPOINT);
|
|
60
|
+
url.searchParams.set('launch-options', JSON.stringify(this._launchOptions));
|
|
61
|
+
return await playwright.chromium.connect(String(url));
|
|
62
|
+
}
|
|
63
|
+
return await playwright.chromium.launch(this._launchOptions);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.Context = Context;
|
package/lib/program.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _commander = require("commander");
|
|
4
|
+
var _server = require("./server");
|
|
5
|
+
var snapshot = _interopRequireWildcard(require("./tools/snapshot"));
|
|
6
|
+
var common = _interopRequireWildcard(require("./tools/common"));
|
|
7
|
+
var screenshot = _interopRequireWildcard(require("./tools/screenshot"));
|
|
8
|
+
var _console = require("./resources/console");
|
|
9
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
10
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
11
|
+
/**
|
|
12
|
+
* Copyright (c) Microsoft Corporation.
|
|
13
|
+
*
|
|
14
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
15
|
+
* you may not use this file except in compliance with the License.
|
|
16
|
+
* You may obtain a copy of the License at
|
|
17
|
+
*
|
|
18
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
19
|
+
*
|
|
20
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
21
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
22
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
23
|
+
* See the License for the specific language governing permissions and
|
|
24
|
+
* limitations under the License.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const packageJSON = require('../package.json');
|
|
28
|
+
_commander.program.version('Version ' + packageJSON.version).name(packageJSON.name).option('--headless', 'Run browser in headless mode, headed by default').option('--vision', 'Run server that uses screenshots (Aria snapshots are used by default)').action(async options => {
|
|
29
|
+
const launchOptions = {
|
|
30
|
+
headless: !!options.headless
|
|
31
|
+
};
|
|
32
|
+
const tools = options.vision ? screenshotTools : snapshotTools;
|
|
33
|
+
const server = new _server.Server({
|
|
34
|
+
name: 'Playwright',
|
|
35
|
+
version: packageJSON.version,
|
|
36
|
+
tools,
|
|
37
|
+
resources
|
|
38
|
+
}, launchOptions);
|
|
39
|
+
setupExitWatchdog(server);
|
|
40
|
+
await server.start();
|
|
41
|
+
});
|
|
42
|
+
function setupExitWatchdog(server) {
|
|
43
|
+
process.stdin.on('close', async () => {
|
|
44
|
+
// eslint-disable-next-line no-restricted-properties
|
|
45
|
+
setTimeout(() => process.exit(0), 15000);
|
|
46
|
+
await (server === null || server === void 0 ? void 0 : server.stop());
|
|
47
|
+
// eslint-disable-next-line no-restricted-properties
|
|
48
|
+
process.exit(0);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const commonTools = [common.pressKey, common.wait, common.pdf, common.close];
|
|
52
|
+
const snapshotTools = [common.navigate(true), common.goBack(true), common.goForward(true), snapshot.snapshot, snapshot.click, snapshot.hover, snapshot.type, ...commonTools];
|
|
53
|
+
const screenshotTools = [common.navigate(false), common.goBack(false), common.goForward(false), screenshot.screenshot, screenshot.moveMouse, screenshot.click, screenshot.drag, screenshot.type, ...commonTools];
|
|
54
|
+
const resources = [_console.console];
|
|
55
|
+
_commander.program.parse(process.argv);
|
package/lib/server.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Server = void 0;
|
|
7
|
+
var _index = require("@modelcontextprotocol/sdk/server/index.js");
|
|
8
|
+
var _stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
|
+
var _types = require("@modelcontextprotocol/sdk/types.js");
|
|
10
|
+
var _context = require("./context");
|
|
11
|
+
/**
|
|
12
|
+
* Copyright (c) Microsoft Corporation.
|
|
13
|
+
*
|
|
14
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
15
|
+
* you may not use this file except in compliance with the License.
|
|
16
|
+
* You may obtain a copy of the License at
|
|
17
|
+
*
|
|
18
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
19
|
+
*
|
|
20
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
21
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
22
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
23
|
+
* See the License for the specific language governing permissions and
|
|
24
|
+
* limitations under the License.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
class Server {
|
|
28
|
+
constructor(options, launchOptions) {
|
|
29
|
+
this._server = void 0;
|
|
30
|
+
this._tools = void 0;
|
|
31
|
+
this._page = void 0;
|
|
32
|
+
this._context = void 0;
|
|
33
|
+
const {
|
|
34
|
+
name,
|
|
35
|
+
version,
|
|
36
|
+
tools,
|
|
37
|
+
resources
|
|
38
|
+
} = options;
|
|
39
|
+
this._context = new _context.Context(launchOptions);
|
|
40
|
+
this._server = new _index.Server({
|
|
41
|
+
name,
|
|
42
|
+
version
|
|
43
|
+
}, {
|
|
44
|
+
capabilities: {
|
|
45
|
+
tools: {},
|
|
46
|
+
resources: {}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
this._tools = tools;
|
|
50
|
+
this._server.setRequestHandler(_types.ListToolsRequestSchema, async () => {
|
|
51
|
+
return {
|
|
52
|
+
tools: tools.map(tool => tool.schema)
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
this._server.setRequestHandler(_types.ListResourcesRequestSchema, async () => {
|
|
56
|
+
return {
|
|
57
|
+
resources: resources.map(resource => resource.schema)
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
this._server.setRequestHandler(_types.CallToolRequestSchema, async request => {
|
|
61
|
+
const tool = this._tools.find(tool => tool.schema.name === request.params.name);
|
|
62
|
+
if (!tool) {
|
|
63
|
+
return {
|
|
64
|
+
content: [{
|
|
65
|
+
type: 'text',
|
|
66
|
+
text: `Tool "${request.params.name}" not found`
|
|
67
|
+
}],
|
|
68
|
+
isError: true
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const result = await tool.handle(this._context, request.params.arguments);
|
|
73
|
+
return result;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
content: [{
|
|
77
|
+
type: 'text',
|
|
78
|
+
text: String(error)
|
|
79
|
+
}],
|
|
80
|
+
isError: true
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
this._server.setRequestHandler(_types.ReadResourceRequestSchema, async request => {
|
|
85
|
+
const resource = resources.find(resource => resource.schema.uri === request.params.uri);
|
|
86
|
+
if (!resource) return {
|
|
87
|
+
contents: []
|
|
88
|
+
};
|
|
89
|
+
const contents = await resource.read(this._context, request.params.uri);
|
|
90
|
+
return {
|
|
91
|
+
contents
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async start() {
|
|
96
|
+
const transport = new _stdio.StdioServerTransport();
|
|
97
|
+
await this._server.connect(transport);
|
|
98
|
+
}
|
|
99
|
+
async stop() {
|
|
100
|
+
var _this$_page;
|
|
101
|
+
await this._server.close();
|
|
102
|
+
await ((_this$_page = this._page) === null || _this$_page === void 0 || (_this$_page = _this$_page.context()) === null || _this$_page === void 0 || (_this$_page = _this$_page.browser()) === null || _this$_page === void 0 ? void 0 : _this$_page.close());
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.Server = Server;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playwright/mcp",
|
|
3
|
-
"version": "1.52.0-alpha-2025-03-
|
|
3
|
+
"version": "1.52.0-alpha-2025-03-19",
|
|
4
4
|
"description": "Playwright Tools for MCP",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"./package.json": "./package.json"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"
|
|
27
|
+
"commander": "^13.1.0",
|
|
28
|
+
"playwright": "1.52.0-alpha-2025-03-19",
|
|
29
|
+
"zod-to-json-schema": "^3.24.4"
|
|
28
30
|
},
|
|
29
31
|
"devDependencies": {
|
|
30
32
|
"@modelcontextprotocol/sdk": "^1.6.1"
|
package/lib/utils.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.waitForCompletion = waitForCompletion;
|
|
7
|
-
/**
|
|
8
|
-
* Copyright (c) Microsoft Corporation.
|
|
9
|
-
*
|
|
10
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
11
|
-
* you may not use this file except in compliance with the License.
|
|
12
|
-
* You may obtain a copy of the License at
|
|
13
|
-
*
|
|
14
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
15
|
-
*
|
|
16
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
-
* See the License for the specific language governing permissions and
|
|
20
|
-
* limitations under the License.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
async function waitForCompletion(page, callback) {
|
|
24
|
-
const requests = new Set();
|
|
25
|
-
let frameNavigated = false;
|
|
26
|
-
let waitCallback = () => {};
|
|
27
|
-
const waitBarrier = new Promise(f => {
|
|
28
|
-
waitCallback = f;
|
|
29
|
-
});
|
|
30
|
-
const requestListener = request => requests.add(request);
|
|
31
|
-
const requestFinishedListener = request => {
|
|
32
|
-
requests.delete(request);
|
|
33
|
-
if (!requests.size) waitCallback();
|
|
34
|
-
};
|
|
35
|
-
const frameNavigateListener = frame => {
|
|
36
|
-
if (frame.parentFrame()) return;
|
|
37
|
-
frameNavigated = true;
|
|
38
|
-
dispose();
|
|
39
|
-
clearTimeout(timeout);
|
|
40
|
-
void frame.waitForLoadState('load').then(() => {
|
|
41
|
-
waitCallback();
|
|
42
|
-
});
|
|
43
|
-
};
|
|
44
|
-
const onTimeout = () => {
|
|
45
|
-
dispose();
|
|
46
|
-
waitCallback();
|
|
47
|
-
};
|
|
48
|
-
page.on('request', requestListener);
|
|
49
|
-
page.on('requestfinished', requestFinishedListener);
|
|
50
|
-
page.on('framenavigated', frameNavigateListener);
|
|
51
|
-
const timeout = setTimeout(onTimeout, 10000);
|
|
52
|
-
const dispose = () => {
|
|
53
|
-
page.off('request', requestListener);
|
|
54
|
-
page.off('requestfinished', requestFinishedListener);
|
|
55
|
-
page.off('framenavigated', frameNavigateListener);
|
|
56
|
-
clearTimeout(timeout);
|
|
57
|
-
};
|
|
58
|
-
try {
|
|
59
|
-
const result = await callback();
|
|
60
|
-
if (!requests.size && !frameNavigated) waitCallback();
|
|
61
|
-
await waitBarrier;
|
|
62
|
-
await page.evaluate(() => new Promise(f => setTimeout(f, 1000)));
|
|
63
|
-
return result;
|
|
64
|
-
} finally {
|
|
65
|
-
dispose();
|
|
66
|
-
}
|
|
67
|
-
}
|