@playwright/mcp 1.52.0-alpha-2025-03-17 → 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 CHANGED
@@ -1,10 +1,51 @@
1
- ### Playwright MCP
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
- * If you run this server in a headless environment
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
@@ -15,4 +15,4 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- require('./lib/servers/snapshot');
18
+ require('./lib/program');
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-17",
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
- "playwright": "1.52.0-alpha-2025-03-17"
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
- }