chore: merge browser and channel settings (#100)

This commit is contained in:
Pavel Feldman 2025-04-01 10:26:48 -07:00 committed by GitHub
parent 9042c03faa
commit 4f16786432
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 65 additions and 12 deletions

View File

@ -59,9 +59,25 @@ code-insiders --add-mcp '{"name":"playwright","command":"npx","args":["@playwrig
After installation, the Playwright MCP server will be available for use with your GitHub Copilot agent in VS Code. After installation, the Playwright MCP server will be available for use with your GitHub Copilot agent in VS Code.
### CLI Options
The Playwright MCP server supports the following command-line options:
- `--browser <browser>`: Browser or chrome channel to use. Possible values:
- `chrome`, `firefox`, `webkit`, `msedge`
- Chrome channels: `chrome-beta`, `chrome-canary`, `chrome-dev`
- Edge channels: `msedge-beta`, `msedge-canary`, `msedge-dev`
- Default: `chrome`
- `--cdp-endpoint <endpoint>`: CDP endpoint to connect to
- `--executable-path <path>`: Path to the browser executable
- `--headless`: Run browser in headless mode (headed by default)
- `--port <port>`: Port to listen on for SSE transport
- `--user-data-dir <path>`: Path to the user data directory
- `--vision`: Run server that uses screenshots (Aria snapshots are used by default)
### User data directory ### User data directory
Playwright MCP will launch Chrome browser with the new profile, located at Playwright MCP will launch the browser with the new profile, located at
``` ```
- `%USERPROFILE%\AppData\Local\ms-playwright\mcp-chrome-profile` on Windows - `%USERPROFILE%\AppData\Local\ms-playwright\mcp-chrome-profile` on Windows

View File

@ -20,6 +20,7 @@ import path from 'path';
import * as playwright from 'playwright'; import * as playwright from 'playwright';
export type ContextOptions = { export type ContextOptions = {
browserName?: 'chromium' | 'firefox' | 'webkit';
userDataDir: string; userDataDir: string;
launchOptions?: playwright.LaunchOptions; launchOptions?: playwright.LaunchOptions;
cdpEndpoint?: string; cdpEndpoint?: string;
@ -73,7 +74,7 @@ export class Context {
} }
async install(): Promise<string> { async install(): Promise<string> {
const channel = this._options.launchOptions?.channel || 'chrome'; const channel = this._options.launchOptions?.channel ?? this._options.browserName ?? 'chrome';
const cli = path.join(require.resolve('playwright/package.json'), '..', 'cli.js'); const cli = path.join(require.resolve('playwright/package.json'), '..', 'cli.js');
const child = fork(cli, ['install', channel], { const child = fork(cli, ['install', channel], {
stdio: 'pipe', stdio: 'pipe',
@ -125,9 +126,11 @@ export class Context {
private async _createPage(): Promise<{ browser?: playwright.Browser, page: playwright.Page }> { private async _createPage(): Promise<{ browser?: playwright.Browser, page: playwright.Page }> {
if (this._options.remoteEndpoint) { if (this._options.remoteEndpoint) {
const url = new URL(this._options.remoteEndpoint); const url = new URL(this._options.remoteEndpoint);
if (this._options.browserName)
url.searchParams.set('browser', this._options.browserName);
if (this._options.launchOptions) if (this._options.launchOptions)
url.searchParams.set('launch-options', JSON.stringify(this._options.launchOptions)); url.searchParams.set('launch-options', JSON.stringify(this._options.launchOptions));
const browser = await playwright.chromium.connect(String(url)); const browser = await playwright[this._options.browserName ?? 'chromium'].connect(String(url));
const page = await browser.newPage(); const page = await browser.newPage();
return { browser, page }; return { browser, page };
} }
@ -148,7 +151,8 @@ export class Context {
private async _launchPersistentContext(): Promise<playwright.BrowserContext> { private async _launchPersistentContext(): Promise<playwright.BrowserContext> {
try { try {
return await playwright.chromium.launchPersistentContext(this._options.userDataDir, this._options.launchOptions); const browserType = this._options.browserName ? playwright[this._options.browserName] : playwright.chromium;
return await browserType.launchPersistentContext(this._options.userDataDir, this._options.launchOptions);
} catch (error: any) { } catch (error: any) {
if (error.message.includes('Executable doesn\'t exist')) if (error.message.includes('Executable doesn\'t exist'))
throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`); throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);

View File

@ -65,6 +65,7 @@ const resources: Resource[] = [
]; ];
type Options = { type Options = {
browserName?: 'chromium' | 'firefox' | 'webkit';
userDataDir?: string; userDataDir?: string;
launchOptions?: LaunchOptions; launchOptions?: LaunchOptions;
cdpEndpoint?: string; cdpEndpoint?: string;
@ -80,6 +81,7 @@ export function createServer(options?: Options): Server {
version: packageJSON.version, version: packageJSON.version,
tools, tools,
resources, resources,
browserName: options?.browserName,
userDataDir: options?.userDataDir ?? '', userDataDir: options?.userDataDir ?? '',
launchOptions: options?.launchOptions, launchOptions: options?.launchOptions,
cdpEndpoint: options?.cdpEndpoint, cdpEndpoint: options?.cdpEndpoint,

View File

@ -35,21 +35,52 @@ const packageJSON = require('../package.json');
program program
.version('Version ' + packageJSON.version) .version('Version ' + packageJSON.version)
.name(packageJSON.name) .name(packageJSON.name)
.option('--browser <browser>', 'Browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.')
.option('--cdp-endpoint <endpoint>', 'CDP endpoint to connect to.')
.option('--executable-path <path>', 'Path to the browser executable.')
.option('--headless', 'Run browser in headless mode, headed by default') .option('--headless', 'Run browser in headless mode, headed by default')
.option('--port <port>', 'Port to listen on for SSE transport.')
.option('--user-data-dir <path>', 'Path to the user data directory') .option('--user-data-dir <path>', 'Path to the user data directory')
.option('--vision', 'Run server that uses screenshots (Aria snapshots are used by default)') .option('--vision', 'Run server that uses screenshots (Aria snapshots are used by default)')
.option('--port <port>', 'Port to listen on for SSE transport.')
.option('--cdp-endpoint <endpoint>', 'CDP endpoint to connect to.')
.option('--channel <channel>', 'Channel to use for browser, possible values: chrome, msedge, chromium. Default: chrome')
.option('--executable-path <path>', 'Path to the browser executable.')
.action(async options => { .action(async options => {
let browserName: 'chromium' | 'firefox' | 'webkit';
let channel: string | undefined;
switch (options.browser) {
case 'chrome':
case 'chrome-beta':
case 'chrome-canary':
case 'chrome-dev':
case 'msedge':
case 'msedge-beta':
case 'msedge-canary':
case 'msedge-dev':
browserName = 'chromium';
channel = options.browser;
break;
case 'chromium':
browserName = 'chromium';
break;
case 'firefox':
browserName = 'firefox';
break;
case 'webkit':
browserName = 'webkit';
break;
default:
browserName = 'chromium';
channel = 'chrome';
}
const launchOptions: LaunchOptions = { const launchOptions: LaunchOptions = {
headless: !!options.headless, headless: !!options.headless,
channel: options.channel ?? 'chrome', channel,
executablePath: options.executablePath, executablePath: options.executablePath,
}; };
const userDataDir = options.userDataDir ?? await createUserDataDir();
const userDataDir = options.userDataDir ?? await createUserDataDir(browserName);
const serverList = new ServerList(() => createServer({ const serverList = new ServerList(() => createServer({
browserName,
userDataDir, userDataDir,
launchOptions, launchOptions,
vision: !!options.vision, vision: !!options.vision,
@ -75,7 +106,7 @@ function setupExitWatchdog(serverList: ServerList) {
program.parse(process.argv); program.parse(process.argv);
async function createUserDataDir() { async function createUserDataDir(browserName: 'chromium' | 'firefox' | 'webkit') {
let cacheDirectory: string; let cacheDirectory: string;
if (process.platform === 'linux') if (process.platform === 'linux')
cacheDirectory = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache'); cacheDirectory = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
@ -85,7 +116,7 @@ async function createUserDataDir() {
cacheDirectory = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'); cacheDirectory = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
else else
throw new Error('Unsupported platform: ' + process.platform); throw new Error('Unsupported platform: ' + process.platform);
const result = path.join(cacheDirectory, 'ms-playwright', 'mcp-chrome-profile'); const result = path.join(cacheDirectory, 'ms-playwright', `mcp-${browserName}-profile`);
await fs.promises.mkdir(result, { recursive: true }); await fs.promises.mkdir(result, { recursive: true });
return result; return result;
} }