diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7f84d06 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,31 @@ +## Commit Convention + +Semantic commit messages: `label(scope): description` + +Labels: `fix`, `feat`, `chore`, `docs`, `test`, `devops` + +```bash +git checkout -b fix-39562 +# ... make changes ... +git add +git commit -m "$(cat <<'EOF' +fix(proxy): handle SOCKS proxy authentication + +Fixes: https://github.com/microsoft/playwright/issues/39562 +EOF +)" +git push origin fix-39562 +gh pr create --repo microsoft/playwright --head username:fix-39562 \ + --title "fix(proxy): handle SOCKS proxy authentication" \ + --body "$(cat <<'EOF' +## Summary +- + +Fixes https://github.com/microsoft/playwright/issues/39562 +EOF +)" +``` + +Never add Co-Authored-By agents in commit message. +Branch naming for issue fixes: `fix-` + diff --git a/README.md b/README.md index 50600c2..b0bf8a4 100644 --- a/README.md +++ b/README.md @@ -386,8 +386,6 @@ Playwright MCP server supports following arguments. They can be provided in the | --proxy-server | specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"
*env* `PLAYWRIGHT_MCP_PROXY_SERVER` | | --sandbox | enable the sandbox for all process types that are normally not sandboxed.
*env* `PLAYWRIGHT_MCP_SANDBOX` | | --save-session | Whether to save the Playwright MCP session into the output directory.
*env* `PLAYWRIGHT_MCP_SAVE_SESSION` | -| --save-trace | Whether to save the Playwright Trace of the session into the output directory.
*env* `PLAYWRIGHT_MCP_SAVE_TRACE` | -| --save-video | Whether to save the video of the session into the output directory. For example "--save-video=800x600"
*env* `PLAYWRIGHT_MCP_SAVE_VIDEO` | | --secrets | path to a file containing secrets in the dotenv format
*env* `PLAYWRIGHT_MCP_SECRETS` | | --shared-browser-context | reuse the same browser context between all connected HTTP clients.
*env* `PLAYWRIGHT_MCP_SHARED_BROWSER_CONTEXT` | | --snapshot-mode | when taking snapshots for responses, specifies the mode to use. Can be "incremental", "full", or "none". Default is incremental.
*env* `PLAYWRIGHT_MCP_SNAPSHOT_MODE` | @@ -598,19 +596,6 @@ npx @playwright/mcp@latest --config path/to/config.json */ saveSession?: boolean; - /** - * Whether to save the Playwright trace of the session into the output directory. - */ - saveTrace?: boolean; - - /** - * If specified, saves the Playwright video of the session into the output directory. - */ - saveVideo?: { - width: number; - height: number; - }; - /** * Reuse the same browser context between all connected HTTP clients. */ @@ -675,6 +660,11 @@ npx @playwright/mcp@latest --config path/to/config.json * Configures default navigation timeout: https://playwright.dev/docs/api/class-page#page-set-default-navigation-timeout. Defaults to 60000ms. */ navigation?: number; + + /** + * Configures default expect timeout: https://playwright.dev/docs/test-timeouts#expect-timeout. Defaults to 5000ms. + */ + expect?: number; }; /** @@ -804,6 +794,7 @@ http.createServer(async (req, res) => { - Parameters: - `element` (string, optional): Human-readable element description used to obtain permission to interact with the element - `ref` (string): Exact target element reference from the page snapshot + - `selector` (string, optional): CSS or role selector for the target element, when "ref" is not available - `doubleClick` (boolean, optional): Whether to perform a double click instead of a single click - `button` (string, optional): Button to click, defaults to left - `modifiers` (array, optional): Modifier keys to press @@ -824,6 +815,7 @@ http.createServer(async (req, res) => { - Description: Returns all console messages - Parameters: - `level` (string): Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info". + - `all` (boolean, optional): Return all console messages since the beginning of the session, not just since the last navigation. Defaults to false. - `filename` (string, optional): Filename to save the console messages to. If not provided, messages are returned as text. - Read-only: **true** @@ -835,8 +827,10 @@ http.createServer(async (req, res) => { - Parameters: - `startElement` (string): Human-readable source element description used to obtain the permission to interact with the element - `startRef` (string): Exact source element reference from the page snapshot + - `startSelector` (string, optional): CSS or role selector for the source element, when ref is not available - `endElement` (string): Human-readable target element description used to obtain the permission to interact with the element - `endRef` (string): Exact target element reference from the page snapshot + - `endSelector` (string, optional): CSS or role selector for the target element, when ref is not available - Read-only: **false** @@ -848,6 +842,7 @@ http.createServer(async (req, res) => { - `function` (string): () => { /* code */ } or (element) => { /* code */ } when element is provided - `element` (string, optional): Human-readable element description used to obtain permission to interact with the element - `ref` (string, optional): Exact target element reference from the page snapshot + - `selector` (string, optional): CSS or role selector for the target element, when "ref" is not available. - Read-only: **false** @@ -886,6 +881,7 @@ http.createServer(async (req, res) => { - Parameters: - `element` (string, optional): Human-readable element description used to obtain permission to interact with the element - `ref` (string): Exact target element reference from the page snapshot + - `selector` (string, optional): CSS or role selector for the target element, when "ref" is not available - Read-only: **false** @@ -951,6 +947,7 @@ http.createServer(async (req, res) => { - Parameters: - `element` (string, optional): Human-readable element description used to obtain permission to interact with the element - `ref` (string): Exact target element reference from the page snapshot + - `selector` (string, optional): CSS or role selector for the target element, when "ref" is not available - `values` (array): Array of values to select in the dropdown. This can be a single value or multiple values. - Read-only: **false** @@ -961,6 +958,7 @@ http.createServer(async (req, res) => { - Description: Capture accessibility snapshot of the current page, this is better than screenshot - Parameters: - `filename` (string, optional): Save snapshot to markdown file instead of returning it in the response. + - `selector` (string, optional): Element selector of the root element to capture a partial snapshot instead of the whole page - Read-only: **true** @@ -973,6 +971,7 @@ http.createServer(async (req, res) => { - `filename` (string, optional): File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory. - `element` (string, optional): Human-readable element description used to obtain permission to screenshot the element. If not provided, the screenshot will be taken of viewport. If element is provided, ref must be provided too. - `ref` (string, optional): Exact target element reference from the page snapshot. If not provided, the screenshot will be taken of viewport. If ref is provided, element must be provided too. + - `selector` (string, optional): CSS or role selector for the target element, when "ref" is not available. - `fullPage` (boolean, optional): When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots. - Read-only: **true** @@ -984,6 +983,7 @@ http.createServer(async (req, res) => { - Parameters: - `element` (string, optional): Human-readable element description used to obtain permission to interact with the element - `ref` (string): Exact target element reference from the page snapshot + - `selector` (string, optional): CSS or role selector for the target element, when "ref" is not available - `text` (string): Text to type into the element - `submit` (boolean, optional): Whether to submit entered text (press Enter after) - `slowly` (boolean, optional): Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once. @@ -1020,14 +1020,6 @@ http.createServer(async (req, res) => {
Browser installation - - -- **browser_install** - - Title: Install the browser specified in the config - - Description: Install the browser specified in the config. Call this if you get an error about the browser not being installed. - - Parameters: None - - Read-only: **false** -
@@ -1048,6 +1040,15 @@ http.createServer(async (req, res) => { +- **browser_network_state_set** + - Title: Set network state + - Description: Sets the browser network state to online or offline. When offline, all network requests will fail. + - Parameters: + - `state` (string): Set to "offline" to simulate offline mode, "online" to restore network connectivity + - Read-only: **false** + + + - **browser_route** - Title: Mock network requests - Description: Set up a route to mock network requests matching a URL pattern @@ -1288,10 +1289,13 @@ http.createServer(async (req, res) => { - **browser_mouse_click_xy** - Title: Click - - Description: Click left mouse button at a given position + - Description: Click mouse button at a given position - Parameters: - `x` (number): X coordinate - `y` (number): Y coordinate + - `button` (string, optional): Button to click, defaults to left + - `clickCount` (number, optional): Number of clicks, defaults to 1 + - `delay` (number, optional): Time to wait between mouse down and mouse up in milliseconds, defaults to 0 - Read-only: **false** @@ -1371,6 +1375,7 @@ http.createServer(async (req, res) => { - Parameters: - `element` (string, optional): Human-readable element description used to obtain permission to interact with the element - `ref` (string): Exact target element reference from the page snapshot + - `selector` (string, optional): CSS or role selector for the target element, when "ref" is not available - Read-only: **true** @@ -1391,6 +1396,7 @@ http.createServer(async (req, res) => { - Parameters: - `element` (string): Human-readable list description - `ref` (string): Exact target element reference that points to the list + - `selector` (string, optional): CSS or role selector for the target list, when "ref" is not available. - `items` (array): Items to verify - Read-only: **false** @@ -1411,7 +1417,8 @@ http.createServer(async (req, res) => { - Parameters: - `type` (string): Type of the element - `element` (string): Human-readable element description - - `ref` (string): Exact target element reference that points to the element + - `ref` (string): Exact target element reference from the page snapshot + - `selector` (string, optional): CSS or role selector for the target element, when "ref" is not available - `value` (string): Value to verify. For checkbox, use "true" or "false". - Read-only: **false** diff --git a/package-lock.json b/package-lock.json index 28cb612..07c3ff0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "devDependencies": { "@modelcontextprotocol/sdk": "^1.25.2", - "@playwright/test": "1.59.0-alpha-1771104257000", + "@playwright/test": "1.59.0-alpha-1773608981000", "@types/node": "^24.3.0" } }, @@ -854,13 +854,13 @@ "link": true }, "node_modules/@playwright/test": { - "version": "1.59.0-alpha-1771104257000", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.0-alpha-1771104257000.tgz", - "integrity": "sha512-0zUPgLuSbyO2xtA+FdEWejFpA5tYU1dINMj2D6KGbB7dgxW8V/4bOrpYS38hizSrzpdSiuRcIK7UgiNFxEeK3A==", + "version": "1.59.0-alpha-1773608981000", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.0-alpha-1773608981000.tgz", + "integrity": "sha512-px+GAf8KIaMcPsCUPG3+xqPRSIPHgnizH7ygUjo6OXT1AigXTNCsIIVrPY3C5GjouM2MI4CQOkIKcSEjO84ZTg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.59.0-alpha-1771104257000" + "playwright": "1.59.0-alpha-1773608981000" }, "bin": { "playwright": "cli.js" @@ -2626,12 +2626,12 @@ } }, "node_modules/playwright": { - "version": "1.59.0-alpha-1771104257000", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.0-alpha-1771104257000.tgz", - "integrity": "sha512-6SCMMMJaDRsSqiKVLmb2nhtLES7iTYawTWWrQK6UdIGNzXi8lka4sLKRec3L4DnTWwddAvCuRn8035dhNiHzbg==", + "version": "1.59.0-alpha-1773608981000", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.0-alpha-1773608981000.tgz", + "integrity": "sha512-nb+BzawNj48eH6NdxecsysLuhCAB/p18FG7LLJp3MBfRGUkCAFtax0CFo/BhD+r0V4+0EW7llPK0p4cJQEIwUQ==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.59.0-alpha-1771104257000" + "playwright-core": "1.59.0-alpha-1773608981000" }, "bin": { "playwright": "cli.js" @@ -2648,9 +2648,9 @@ "link": true }, "node_modules/playwright-core": { - "version": "1.59.0-alpha-1771104257000", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.0-alpha-1771104257000.tgz", - "integrity": "sha512-YiXup3pnpQUCBMSIW5zx8CErwRx4K6O5Kojkw2BzJui8MazoMUDU6E3xGsb1kzFviEAE09LFQ+y1a0RhIJQ5SA==", + "version": "1.59.0-alpha-1773608981000", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.0-alpha-1773608981000.tgz", + "integrity": "sha512-w6E5Q0Wleek3Wp7gtlSPGXuKeQ5eg6QPPJNNwgMHQRpkxgqOwgN2mX7x6Z52HJE10HFC88U5HQzOLMbag928Lg==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -3448,8 +3448,8 @@ "version": "0.0.68", "license": "Apache-2.0", "dependencies": { - "playwright": "1.59.0-alpha-1771104257000", - "playwright-core": "1.59.0-alpha-1771104257000" + "playwright": "1.59.0-alpha-1773608981000", + "playwright-core": "1.59.0-alpha-1773608981000" }, "bin": { "playwright-mcp": "cli.js" diff --git a/package.json b/package.json index 74f9400..c61c06a 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ ], "devDependencies": { "@modelcontextprotocol/sdk": "^1.25.2", - "@playwright/test": "1.59.0-alpha-1771104257000", + "@playwright/test": "1.59.0-alpha-1773608981000", "@types/node": "^24.3.0" } } diff --git a/packages/playwright-mcp/cli.js b/packages/playwright-mcp/cli.js index ffab11e..9f58e91 100755 --- a/packages/playwright-mcp/cli.js +++ b/packages/playwright-mcp/cli.js @@ -16,9 +16,17 @@ */ const { program } = require('playwright-core/lib/utilsBundle'); -const { decorateMCPCommand } = require('playwright/lib/mcp/program'); +const { decorateMCPCommand } = require('playwright-core/lib/tools/mcp/program'); + +if (process.argv.includes('install-browser')) { + const argv = process.argv.map(arg => arg === 'install-browser' ? 'install' : arg); + const { program: mainProgram } = require('playwright-core/lib/cli/program'); + mainProgram.parse(argv); + return; +} const packageJSON = require('./package.json'); const p = program.version('Version ' + packageJSON.version).name('Playwright MCP'); decorateMCPCommand(p, packageJSON.version) + void program.parseAsync(process.argv); diff --git a/packages/playwright-mcp/config.d.ts b/packages/playwright-mcp/config.d.ts index 36dbfc4..f664670 100644 --- a/packages/playwright-mcp/config.d.ts +++ b/packages/playwright-mcp/config.d.ts @@ -137,19 +137,6 @@ export type Config = { */ saveSession?: boolean; - /** - * Whether to save the Playwright trace of the session into the output directory. - */ - saveTrace?: boolean; - - /** - * If specified, saves the Playwright video of the session into the output directory. - */ - saveVideo?: { - width: number; - height: number; - }; - /** * Reuse the same browser context between all connected HTTP clients. */ @@ -214,6 +201,11 @@ export type Config = { * Configures default navigation timeout: https://playwright.dev/docs/api/class-page#page-set-default-navigation-timeout. Defaults to 60000ms. */ navigation?: number; + + /** + * Configures default expect timeout: https://playwright.dev/docs/test-timeouts#expect-timeout. Defaults to 5000ms. + */ + expect?: number; }; /** diff --git a/packages/playwright-mcp/index.js b/packages/playwright-mcp/index.js index 5df2c29..eddee70 100755 --- a/packages/playwright-mcp/index.js +++ b/packages/playwright-mcp/index.js @@ -15,5 +15,5 @@ * limitations under the License. */ -const { createConnection } = require('playwright/lib/mcp/index'); +const { createConnection } = require('playwright-core/lib/tools/exports'); module.exports = { createConnection }; diff --git a/packages/playwright-mcp/package.json b/packages/playwright-mcp/package.json index d4a6cbb..33afa92 100644 --- a/packages/playwright-mcp/package.json +++ b/packages/playwright-mcp/package.json @@ -33,8 +33,8 @@ } }, "dependencies": { - "playwright": "1.59.0-alpha-1771104257000", - "playwright-core": "1.59.0-alpha-1771104257000" + "playwright": "1.59.0-alpha-1773608981000", + "playwright-core": "1.59.0-alpha-1773608981000" }, "bin": { "playwright-mcp": "cli.js" diff --git a/packages/playwright-mcp/tests/capabilities.spec.ts b/packages/playwright-mcp/tests/capabilities.spec.ts index b5272a0..2ab5c44 100644 --- a/packages/playwright-mcp/tests/capabilities.spec.ts +++ b/packages/playwright-mcp/tests/capabilities.spec.ts @@ -30,7 +30,6 @@ test('test snapshot tool list', async ({ client }) => { 'browser_select_option', 'browser_type', 'browser_close', - 'browser_install', 'browser_navigate_back', 'browser_navigate', 'browser_network_requests', diff --git a/packages/playwright-mcp/tests/cli.spec.ts b/packages/playwright-mcp/tests/cli.spec.ts new file mode 100644 index 0000000..21653ae --- /dev/null +++ b/packages/playwright-mcp/tests/cli.spec.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import child_process from 'child_process'; +import path from 'path'; +import { test, expect } from './fixtures'; + +const cliPath = path.resolve(__dirname, '..', 'cli.js'); + +test('install-browser --help', async () => { + const output = child_process.execSync(`node ${cliPath} install-browser --help`, { encoding: 'utf-8' }); + expect(output).toContain('install'); +}); diff --git a/packages/playwright-mcp/update-readme.js b/packages/playwright-mcp/update-readme.js index 786987d..be19096 100644 --- a/packages/playwright-mcp/update-readme.js +++ b/packages/playwright-mcp/update-readme.js @@ -20,7 +20,7 @@ const fs = require('fs') const path = require('path') const { execSync } = require('child_process'); -const { browserTools } = require('playwright/lib/mcp/browser/tools'); +const { browserTools } = require('playwright-core/lib/tools/exports'); const capabilities = /** @type {Record} */ ({ 'core-navigation': 'Core automation', diff --git a/roll.js b/roll.js index 8f94f09..d8638e0 100644 --- a/roll.js +++ b/roll.js @@ -3,7 +3,7 @@ const path = require('path'); const { execSync } = require('child_process'); function copyConfig() { - const src = path.join(__dirname, '..', 'playwright', 'packages', 'playwright', 'src', 'mcp', 'config.d.ts'); + const src = path.join(__dirname, '..', 'playwright', 'packages', 'playwright-core', 'src', 'tools', 'mcp', 'config.d.ts'); const dst = path.join(__dirname, 'packages', 'playwright-mcp', 'config.d.ts'); let content = fs.readFileSync(src, 'utf-8'); content = content.replace(