Compare commits

...

2 Commits

Author SHA1 Message Date
Pavel Feldman
4c6d66d04e chore: roll Playwright to 1.59.0-alpha-1773608981000 (#1461)
## Summary
- Roll playwright and playwright-core to `1.59.0-alpha-1773451864000`
- Fix `install-browser` CLI command registration (was calling
`parseAsync` before registering the subcommand)
- Add `selector` parameter to accessibility snapshot documentation
- Add CLAUDE.md
2026-03-16 09:40:52 -07:00
Yury Semikhatsky
a6baddb044 feat(extension): inject public key into release zip to preserve Web Store extension ID (#1462)
The public key is hardcoded in vite.config.mts. When
SET_EXTENSION_PUBLIC_KEY_IN_MANIFEST is set, the vite build transforms
the copied manifest.json to include the key field, ensuring the packed
extension shares the same Chrome extension ID as the Web Store listing.

Fixes #1452
2026-03-16 09:38:48 -07:00
14 changed files with 136 additions and 61 deletions

View File

@@ -135,6 +135,8 @@ jobs:
- name: Build extension
working-directory: ./packages/extension
run: npm run build
env:
SET_EXTENSION_PUBLIC_KEY_IN_MANIFEST: 1
- name: Get extension version
id: get-version
working-directory: ./packages/extension

31
CLAUDE.md Normal file
View File

@@ -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 <changed-files>
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
- <describe the change very! briefly>
Fixes https://github.com/microsoft/playwright/issues/39562
EOF
)"
```
Never add Co-Authored-By agents in commit message.
Branch naming for issue fixes: `fix-<issue-number>`

View File

@@ -386,8 +386,6 @@ Playwright MCP server supports following arguments. They can be provided in the
| --proxy-server <proxy> | specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"<br>*env* `PLAYWRIGHT_MCP_PROXY_SERVER` |
| --sandbox | enable the sandbox for all process types that are normally not sandboxed.<br>*env* `PLAYWRIGHT_MCP_SANDBOX` |
| --save-session | Whether to save the Playwright MCP session into the output directory.<br>*env* `PLAYWRIGHT_MCP_SAVE_SESSION` |
| --save-trace | Whether to save the Playwright Trace of the session into the output directory.<br>*env* `PLAYWRIGHT_MCP_SAVE_TRACE` |
| --save-video <size> | Whether to save the video of the session into the output directory. For example "--save-video=800x600"<br>*env* `PLAYWRIGHT_MCP_SAVE_VIDEO` |
| --secrets <path> | path to a file containing secrets in the dotenv format<br>*env* `PLAYWRIGHT_MCP_SECRETS` |
| --shared-browser-context | reuse the same browser context between all connected HTTP clients.<br>*env* `PLAYWRIGHT_MCP_SHARED_BROWSER_CONTEXT` |
| --snapshot-mode <mode> | when taking snapshots for responses, specifies the mode to use. Can be "incremental", "full", or "none". Default is incremental.<br>*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**
<!-- NOTE: This has been generated via update-readme.js -->
@@ -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**
<!-- NOTE: This has been generated via update-readme.js -->
@@ -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**
<!-- NOTE: This has been generated via update-readme.js -->
@@ -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**
<!-- NOTE: This has been generated via update-readme.js -->
@@ -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) => {
<details>
<summary><b>Browser installation</b></summary>
<!-- NOTE: This has been generated via update-readme.js -->
- **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**
</details>
<details>
@@ -1048,6 +1040,15 @@ http.createServer(async (req, res) => {
<!-- NOTE: This has been generated via update-readme.js -->
- **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**
<!-- NOTE: This has been generated via update-readme.js -->
- **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**
<!-- NOTE: This has been generated via update-readme.js -->
@@ -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**
<!-- NOTE: This has been generated via update-readme.js -->
@@ -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**

28
package-lock.json generated
View File

@@ -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"

View File

@@ -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"
}
}

View File

@@ -19,6 +19,10 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteStaticCopy } from 'vite-plugin-static-copy';
// Public key matching the Chrome Web Store listing — used to fix the extension ID across installs.
// Set SET_EXTENSION_PUBLIC_KEY_IN_MANIFEST=1 in release builds to inject it into the manifest.
const extensionPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwRsUUO4mmbCi4JpmrIoIw31iVW9+xUJRZ6nSzya17PQkaUPDxe1IpgM+vpd/xB6mJWlJSyE1Lj95c0sbomGfVY1M0zUeKbaRVcAb+/a6m59gNR+ubFlmTX0nK9/8fE2FpRB9D+4N5jyeIPQuASW/0oswI2/ijK7hH5NTRX8gWc/ROMSgUj7rKhTAgBrICt/NsStgDPsxRTPPJnhJ/ViJtM1P5KsSYswE987DPoFnpmkFpq8g1ae0eYbQfXy55ieaacC4QWyJPj3daU2kMfBQw7MXnnk0H/WDxouMOIHnd8MlQxpEMqAihj7KpuONH+MUhuj9HEQo4df6bSaIuQ0b4QIDAQAB';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
@@ -31,7 +35,14 @@ export default defineConfig({
},
{
src: '../../manifest.json',
dest: '.'
dest: '.',
...(!!process.env.SET_EXTENSION_PUBLIC_KEY_IN_MANIFEST ? {
transform: (content: string) => {
const manifest = JSON.parse(content);
manifest.key = extensionPublicKey;
return JSON.stringify(manifest, null, 2);
}
} : {})
}
]
})

View File

@@ -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);

View File

@@ -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;
};
/**

View File

@@ -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 };

View File

@@ -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"

View File

@@ -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',

View File

@@ -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');
});

View File

@@ -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<string, string>} */ ({
'core-navigation': 'Core automation',

View File

@@ -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(