diff --git a/README.md b/README.md index 5c44896..a88fab1 100644 --- a/README.md +++ b/README.md @@ -180,52 +180,65 @@ Playwright MCP server supports following arguments. They can be provided in the ``` > npx @playwright/mcp@latest --help - --allowed-origins semicolon-separated list of origins to allow the - browser to request. Default is to allow all. - --blocked-origins semicolon-separated list of origins to block the - browser from requesting. Blocklist is evaluated - before allowlist. If used without the allowlist, - requests not matching the blocklist are still - allowed. - --block-service-workers block service workers - --browser browser or chrome channel to use, possible - values: chrome, firefox, webkit, msedge. - --caps comma-separated list of additional capabilities - to enable, possible values: vision, pdf. - --cdp-endpoint CDP endpoint to connect to. - --config path to the configuration file. - --device device to emulate, for example: "iPhone 15" - --executable-path path to the browser executable. - --extension Connect to a running browser instance - (Edge/Chrome only). Requires the "Playwright MCP - Bridge" browser extension to be installed. - --headless run browser in headless mode, headed by default - --host host to bind server to. Default is localhost. Use - 0.0.0.0 to bind to all interfaces. - --ignore-https-errors ignore https errors - --isolated keep the browser profile in memory, do not save - it to disk. - --image-responses whether to send image responses to the client. - Can be "allow" or "omit", Defaults to "allow". - --no-sandbox disable the sandbox for all process types that - are normally sandboxed. - --output-dir path to the directory for output files. - --port port to listen on for SSE transport. - --proxy-bypass comma-separated domains to bypass proxy, for - example ".com,chromium.org,.domain.com" - --proxy-server specify proxy server, for example - "http://myproxy:3128" or "socks5://myproxy:8080" - --save-session Whether to save the Playwright MCP session into - the output directory. - --save-trace Whether to save the Playwright Trace of the - session into the output directory. - --storage-state path to the storage state file for isolated - sessions. - --user-agent specify user agent string - --user-data-dir path to the user data directory. If not - specified, a temporary directory will be created. - --viewport-size specify browser viewport size in pixels, for - example "1280, 720" + --allowed-origins semicolon-separated list of origins to allow + the browser to request. Default is to allow + all. + --blocked-origins semicolon-separated list of origins to block + the browser from requesting. Blocklist is + evaluated before allowlist. If used without + the allowlist, requests not matching the + blocklist are still allowed. + --block-service-workers block service workers + --browser browser or chrome channel to use, possible + values: chrome, firefox, webkit, msedge. + --caps comma-separated list of additional + capabilities to enable, possible values: + vision, pdf. + --cdp-endpoint CDP endpoint to connect to. + --cdp-header CDP headers to send with the connect request, + multiple can be specified. + --config path to the configuration file. + --device device to emulate, for example: "iPhone 15" + --executable-path path to the browser executable. + --extension Connect to a running browser instance + (Edge/Chrome only). Requires the "Playwright + MCP Bridge" browser extension to be installed. + --headless run browser in headless mode, headed by + default + --host host to bind server to. Default is localhost. + Use 0.0.0.0 to bind to all interfaces. + --ignore-https-errors ignore https errors + --isolated keep the browser profile in memory, do not + save it to disk. + --image-responses whether to send image responses to the client. + Can be "allow" or "omit", Defaults to "allow". + --no-sandbox disable the sandbox for all process types that + are normally sandboxed. + --output-dir path to the directory for output files. + --port port to listen on for SSE transport. + --proxy-bypass comma-separated domains to bypass proxy, for + example ".com,chromium.org,.domain.com" + --proxy-server specify proxy server, for example + "http://myproxy:3128" or + "socks5://myproxy:8080" + --save-session Whether to save the Playwright MCP session + into the output directory. + --save-trace Whether to save the Playwright Trace of the + session into the output directory. + --secrets path to a file containing secrets in the + dotenv format + --storage-state path to the storage state file for isolated + sessions. + --timeout-action specify action timeout in milliseconds, + defaults to 5000ms + --timeout-navigation specify navigation timeout in milliseconds, + defaults to 60000ms + --user-agent specify user agent string + --user-data-dir path to the user data directory. If not + specified, a temporary directory will be + created. + --viewport-size specify browser viewport size in pixels, for + example "1280, 720" ``` @@ -442,6 +455,7 @@ http.createServer(async (req, res) => { - `ref` (string): Exact target element reference from the page snapshot - `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 - Read-only: **false** diff --git a/cli.js b/cli.js index 9f9991e..46689fe 100755 --- a/cli.js +++ b/cli.js @@ -15,4 +15,10 @@ * limitations under the License. */ -require('playwright/lib/mcp/program'); +const { program } = require('playwright-core/lib/utilsBundle'); +const { decorateCommand } = require('playwright/lib/mcp/program'); + +const packageJSON = require('./package.json'); +const p = program.version('Version ' + packageJSON.version).name('Playwright MCP'); +decorateCommand(p, packageJSON.version) +void program.parseAsync(process.argv); diff --git a/config.d.ts b/config.d.ts index af43236..eb70776 100644 --- a/config.d.ts +++ b/config.d.ts @@ -59,6 +59,11 @@ export type Config = { */ cdpEndpoint?: string; + /** + * CDP headers to send with the connect request. + */ + cdpHeaders?: Record; + /** * Remote endpoint to connect to an existing Playwright server. */ @@ -95,6 +100,13 @@ export type Config = { */ saveTrace?: boolean; + /** + * Secrets are used to prevent LLM from getting sensitive data while + * automating scenarios such as authentication. + * Prefer the browser.contextOptions.storageState over secrets file as a more secure alternative. + */ + secrets?: Record; + /** * The directory to save output files. */ @@ -112,6 +124,18 @@ export type Config = { blockedOrigins?: string[]; }; + timeouts?: { + /* + * Configures default action timeout: https://playwright.dev/docs/api/class-page#page-set-default-timeout. Defaults to 5000ms. + */ + action?: number; + + /* + * Configures default navigation timeout: https://playwright.dev/docs/api/class-page#page-set-default-navigation-timeout. Defaults to 60000ms. + */ + navigation?: number; + }; + /** * Whether to send image responses to the client. Can be "allow", "omit", or "auto". Defaults to "auto", which sends images if the client can display them. */ diff --git a/package-lock.json b/package-lock.json index 0a45439..24b7283 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,16 +9,17 @@ "version": "0.0.36", "license": "Apache-2.0", "dependencies": { - "playwright": "1.56.0-alpha-1756945786000", - "zod-to-json-schema": "^3.24.6" + "playwright": "1.56.0-alpha-1757090131000", + "playwright-core": "1.56.0-alpha-1757090131000" }, "bin": { "mcp-server-playwright": "cli.js" }, "devDependencies": { "@modelcontextprotocol/sdk": "^1.17.5", - "@playwright/test": "1.56.0-alpha-1756945786000", - "@types/node": "^24.3.0" + "@playwright/test": "1.56.0-alpha-1757090131000", + "@types/node": "^24.3.0", + "zod-to-json-schema": "^3.24.6" }, "engines": { "node": ">=18" @@ -49,13 +50,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.56.0-alpha-1756945786000", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.0-alpha-1756945786000.tgz", - "integrity": "sha512-I4+J2BnzDO2KLMhafHmVw8gPzWA0R4niu+tDHwD+YaJzaxj4lH/nxSvqSRZJ5lCEBrt/zbDbEyzNpBdqBy4/Wg==", + "version": "1.56.0-alpha-1757090131000", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.0-alpha-1757090131000.tgz", + "integrity": "sha512-DVyN8ScyBMPasnrcIN7RjltZl7YkJlFZcz9otXe4TwUMo6JYt98MMmcsGXWbKCe+sk4KsgUNx3gYZtAWRBfIzw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.56.0-alpha-1756945786000" + "playwright": "1.56.0-alpha-1757090131000" }, "bin": { "playwright": "cli.js" @@ -65,9 +66,9 @@ } }, "node_modules/@types/node": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", - "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", + "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", "dev": true, "license": "MIT", "dependencies": { @@ -824,12 +825,12 @@ } }, "node_modules/playwright": { - "version": "1.56.0-alpha-1756945786000", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.0-alpha-1756945786000.tgz", - "integrity": "sha512-Y07i9m/hZOQ8lLb4XDYAO24V1+qy+OCgxsW66fSlJJ064p8N4KjwKL5OJpVZofhf3EP8vp0adGePjBOjSwhVeA==", + "version": "1.56.0-alpha-1757090131000", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.0-alpha-1757090131000.tgz", + "integrity": "sha512-3G5jTso8FKB8iiha0aRsZy7T/oanxL7Hx/D3oK3FzM0K3fS0eUXPetzQk8elYz/ZwNPCpfYvXbzGzdaRiBKLFw==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.0-alpha-1756945786000" + "playwright-core": "1.56.0-alpha-1757090131000" }, "bin": { "playwright": "cli.js" @@ -842,9 +843,9 @@ } }, "node_modules/playwright-core": { - "version": "1.56.0-alpha-1756945786000", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.0-alpha-1756945786000.tgz", - "integrity": "sha512-692W77VRoV2hq1bNwrZXsRBVOY8z5DDjV5jacB5det4v8rUcvOAGTqNUhoV17acP4mfbItM34VOPYOMtlJxjXg==", + "version": "1.56.0-alpha-1757090131000", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.0-alpha-1757090131000.tgz", + "integrity": "sha512-kj4hYkv79zIqUjepNun3homv1h9jkn4CHUNyfV8tJotj6X1ypmIiox/smLm7Lcjg9wLexk4N/Sxx1mglrXlQeg==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -904,19 +905,36 @@ } }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", "dev": true, "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", - "iconv-lite": "0.6.3", + "iconv-lite": "0.7.0", "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/router": { @@ -1205,9 +1223,10 @@ "license": "ISC" }, "node_modules/zod": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -1217,6 +1236,7 @@ "version": "3.24.6", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, "license": "ISC", "peerDependencies": { "zod": "^3.24.1" diff --git a/package.json b/package.json index 3c54e95..54fc7ad 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,12 @@ "scripts": { "lint": "npm run update-readme", "update-readme": "node update-readme.js", + "docker-build": "docker build --no-cache -t playwright-mcp-dev:latest .", "test": "playwright test", "ctest": "playwright test --project=chrome", "ftest": "playwright test --project=firefox", "wtest": "playwright test --project=webkit", + "dtest": "MCP_IN_DOCKER=1 playwright test --project=chromium-docker", "npm-publish": "npm run clean && npm run build && npm run test && npm publish" }, "exports": { @@ -31,15 +33,16 @@ } }, "dependencies": { - "playwright": "1.56.0-alpha-1756945786000", - "zod-to-json-schema": "^3.24.6" + "playwright": "1.56.0-alpha-1757090131000", + "playwright-core": "1.56.0-alpha-1757090131000" }, "bin": { "mcp-server-playwright": "cli.js" }, "devDependencies": { "@modelcontextprotocol/sdk": "^1.17.5", - "@playwright/test": "1.56.0-alpha-1756945786000", - "@types/node": "^24.3.0" + "@playwright/test": "1.56.0-alpha-1757090131000", + "@types/node": "^24.3.0", + "zod-to-json-schema": "^3.24.6" } } diff --git a/tests/click.spec.ts b/tests/click.spec.ts index 862bbf1..c256c3e 100644 --- a/tests/click.spec.ts +++ b/tests/click.spec.ts @@ -22,9 +22,12 @@ test('browser_click', async ({ client, server, mcpBrowser }) => { `, 'text/html'); - await client.callTool({ + expect(await client.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX }, + })).toHaveResponse({ + code: `await page.goto('${server.PREFIX}');`, + pageState: expect.stringContaining(`- button \"Submit\" [ref=e2]`), }); expect(await client.callTool({ diff --git a/update-readme.js b/update-readme.js index 0d086cc..f2f6718 100644 --- a/update-readme.js +++ b/update-readme.js @@ -34,6 +34,10 @@ const capabilities = { const toolsByCapability = Object.fromEntries(Object.entries(capabilities).map(([capability, title]) => [title, allTools.filter(tool => tool.capability === capability).sort((a, b) => a.schema.name.localeCompare(b.schema.name))])); +/** + * @param {any} tool + * @returns {string[]} + */ function formatToolForReadme(tool) { const lines = /** @type {string[]} */ ([]); lines.push(``);