From 7be0c8872e3e064b2a5f316a2e7367e87c4385ca Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 13 May 2025 14:40:03 -0700 Subject: [PATCH] feat(args): allow configuring proxy, UA, viewport, https errors (#410) --- README.md | 22 ++++++++++++-------- src/config.ts | 56 +++++++++++++++++++++++++++++++++++++++++--------- src/program.ts | 38 +++++++++++++++++++--------------- 3 files changed, 82 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index a74d3ae..966c7c8 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,9 @@ Follow the MCP install [guide](https://modelcontextprotocol.io/quickstart/user), Playwright MCP server supports following arguments. They can be provided in the JSON configuration above, as a part of the `"args"` list: +- `--allowed-origins `: Semicolon-separated list of origins to allow the browser to request. Default is to allow all. Origins matching both `--allowed-origins` and `--blocked-origins` will be blocked. +- `--blocked-origins `: Semicolon-separated list of origins to block the browser to request. Origins matching both `--allowed-origins` and `--blocked-origins` will be blocked. +- `--block-service-workers`: Block service workers - `--browser `: Browser or chrome channel to use. Possible values: - `chrome`, `firefox`, `webkit`, `msedge` - Chrome channels: `chrome-beta`, `chrome-canary`, `chrome-dev` @@ -117,18 +120,21 @@ Playwright MCP server supports following arguments. They can be provided in the - Default: `chrome` - `--caps `: Comma-separated list of capabilities to enable, possible values: tabs, pdf, history, wait, files, install. Default is all. - `--cdp-endpoint `: CDP endpoint to connect to -- `--isolated`: Keep the browser profile in memory, do not save it to disk +- `--config `: Path to the configuration file +- `--device`: Emulate mobile device - `--executable-path `: Path to the browser executable - `--headless`: Run browser in headless mode (headed by default) -- `--device`: Emulate mobile device -- `--user-data-dir `: Path to the user data directory -- `--port `: Port to listen on for SSE transport - `--host `: Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces. -- `--allowed-origins `: Semicolon-separated list of origins to allow the browser to request. Default is to allow all. Origins matching both `--allowed-origins` and `--blocked-origins` will be blocked. -- `--blocked-origins `: Semicolon-separated list of origins to block the browser to request. Origins matching both `--allowed-origins` and `--blocked-origins` will be blocked. -- `--vision`: Run server that uses screenshots (Aria snapshots are used by default) +- `--ignore-https-errors`: Ignore https errors +- `--isolated`: Keep the browser profile in memory, do not save it to disk - `--output-dir`: Directory for output files -- `--config `: Path to the configuration file +- `--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 `: Proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"' +- `--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" +- `--vision`: Run server that uses screenshots (Aria snapshots are used by default) ### User profile diff --git a/src/config.ts b/src/config.ts index 3b90f81..ae25b3e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -25,23 +25,29 @@ import type { BrowserContextOptions, LaunchOptions } from 'playwright'; import { sanitizeForFilePath } from './tools/utils.js'; export type CLIOptions = { + allowedOrigins?: string[]; + blockedOrigins?: string[]; + blockServiceWorkers?: boolean; browser?: string; caps?: string; cdpEndpoint?: string; - isolated?: boolean; + config?: string; + device?: string; executablePath?: string; headless?: boolean; - device?: string; - userDataDir?: string; - storageState?: string; - port?: number; host?: string; - vision?: boolean; - config?: string; - allowedOrigins?: string[]; - blockedOrigins?: string[]; - outputDir?: string; + ignoreHttpsErrors?: boolean; + isolated?: boolean; noImageResponses?: boolean; + outputDir?: string; + port?: number; + proxyBypass?: string; + proxyServer?: string; + storageState?: string; + userAgent?: string; + userDataDir?: string; + viewportSize?: string; + vision?: boolean; }; const defaultConfig: Config = { @@ -94,6 +100,7 @@ export async function configFromCLIOptions(cliOptions: CLIOptions): Promise +n); + if (isNaN(width) || isNaN(height)) + throw new Error('bad values'); + contextOptions.viewport = { width, height }; + } catch (e) { + throw new Error('Invalid viewport size format: use "width,height", for example --viewport-size="800,600"'); + } + } + + if (cliOptions.ignoreHttpsErrors) + contextOptions.ignoreHTTPSErrors = true; + + if (cliOptions.blockServiceWorkers) + contextOptions.serviceWorkers = 'block'; + return { browser: { browserName, diff --git a/src/program.ts b/src/program.ts index 6c9cefd..a1ffd73 100644 --- a/src/program.ts +++ b/src/program.ts @@ -25,24 +25,30 @@ import { packageJSON } from './context.js'; program .version('Version ' + packageJSON.version) .name(packageJSON.name) - .option('--browser ', 'Browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.') - .option('--caps ', 'Comma-separated list of capabilities to enable, possible values: tabs, pdf, history, wait, files, install. Default is all.') + .option('--browser ', 'browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.') + .option('--allowed-origins ', 'semicolon-separated list of origins to allow the browser to request. Default is to allow all.', semicolonSeparatedList) + .option('--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.', semicolonSeparatedList) + .option('--block-service-workers', 'block service workers') + .option('--caps ', 'comma-separated list of capabilities to enable, possible values: tabs, pdf, history, wait, files, install. Default is all.') .option('--cdp-endpoint ', 'CDP endpoint to connect to.') - .option('--isolated', 'Keep the browser profile in memory, do not save it to disk.') - .option('--storage-state ', 'Path to the storage state file for isolated sessions.') - .option('--executable-path ', 'Path to the browser executable.') - .option('--headless', 'Run browser in headless mode, headed by default') - .option('--device ', 'Device to emulate, for example: "iPhone 15"') - .option('--user-data-dir ', 'Path to the user data directory. If not specified, a temporary directory will be created.') - .option('--in-memory', 'Use in-memory storage for user data directory.') - .option('--port ', 'Port to listen on for SSE transport.') - .option('--host ', 'Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.') - .option('--allowed-origins ', 'Semicolon-separated list of origins to allow the browser to request. Default is to allow all.', semicolonSeparatedList) - .option('--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.', semicolonSeparatedList) + .option('--config ', 'path to the configuration file.') + .option('--device ', 'device to emulate, for example: "iPhone 15"') + .option('--executable-path ', 'path to the browser executable.') + .option('--headless', 'run browser in headless mode, headed by default') + .option('--host ', 'host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.') + .option('--in-memory', 'use in-memory storage for user data directory.') + .option('--ignore-https-errors', 'ignore https errors') + .option('--isolated', 'keep the browser profile in memory, do not save it to disk.') + .option('--no-image-responses', 'do not send image responses to the client.') + .option('--output-dir ', 'path to the directory for output files.') + .option('--port ', 'port to listen on for SSE transport.') + .option('--proxy-bypass ', 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"') + .option('--proxy-server ', 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"') + .option('--storage-state ', 'path to the storage state file for isolated sessions.') + .option('--user-agent ', 'specify user agent string') + .option('--user-data-dir ', 'path to the user data directory. If not specified, a temporary directory will be created.') + .option('--viewport-size ', 'specify browser viewport size in pixels, for example "1280, 720"') .option('--vision', 'Run server that uses screenshots (Aria snapshots are used by default)') - .option('--no-image-responses', 'Do not send image responses to the client.') - .option('--output-dir ', 'Path to the directory for output files.') - .option('--config ', 'Path to the configuration file.') .action(async options => { const config = await resolveConfig(options); const connectionList: Connection[] = [];