mirror of
https://github.com/microsoft/playwright-mcp.git
synced 2025-07-26 08:32:26 +08:00
chore: add support for device (#300)
Fixes https://github.com/microsoft/playwright-mcp/issues/294
This commit is contained in:
parent
40d125f0bb
commit
3f72fe53ec
17
README.md
17
README.md
@ -16,7 +16,7 @@ A Model Context Protocol (MCP) server that provides browser automation capabilit
|
||||
- General-purpose browser interaction for agents
|
||||
|
||||
<!--
|
||||
// Generate using?:
|
||||
// Generate using:
|
||||
node utils/generate_links.js
|
||||
-->
|
||||
|
||||
@ -72,6 +72,7 @@ The Playwright MCP server supports the following command-line options:
|
||||
- `--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)
|
||||
- `--device`: Emulate mobile device
|
||||
- `--user-data-dir <path>`: Path to the user data directory
|
||||
- `--port <port>`: Port to listen on for SSE transport
|
||||
- `--host <host>`: Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.
|
||||
@ -100,27 +101,29 @@ The Playwright MCP server can be configured using a JSON configuration file. Her
|
||||
browser?: {
|
||||
// Browser type to use (chromium, firefox, or webkit)
|
||||
browserName?: 'chromium' | 'firefox' | 'webkit';
|
||||
|
||||
|
||||
// Path to user data directory for browser profile persistence
|
||||
userDataDir?: string;
|
||||
|
||||
|
||||
// Browser launch options (see Playwright docs)
|
||||
// @see https://playwright.dev/docs/api/class-browsertype#browser-type-launch
|
||||
launchOptions?: {
|
||||
channel?: string; // Browser channel (e.g. 'chrome')
|
||||
headless?: boolean; // Run in headless mode
|
||||
executablePath?: string; // Path to browser executable
|
||||
// ... other Playwright launch options
|
||||
};
|
||||
|
||||
|
||||
// Browser context options
|
||||
// @see https://playwright.dev/docs/api/class-browser#browser-new-context
|
||||
contextOptions?: {
|
||||
viewport?: { width: number, height: number };
|
||||
// ... other Playwright context options
|
||||
};
|
||||
|
||||
|
||||
// CDP endpoint for connecting to existing browser
|
||||
cdpEndpoint?: string;
|
||||
|
||||
|
||||
// Remote Playwright server endpoint
|
||||
remoteEndpoint?: string;
|
||||
},
|
||||
@ -166,8 +169,6 @@ npx @playwright/mcp@latest --config path/to/config.json
|
||||
|
||||
### Running on Linux
|
||||
|
||||
When running headless without DISPLAY, pass `--headless` command line argument.
|
||||
|
||||
When running headed browser on system w/o display or from worker processes of the IDEs,
|
||||
run the MCP server from environment with the DISPLAY and pass the `--port` flag to enable SSE transport.
|
||||
|
||||
|
@ -18,11 +18,12 @@ import fs from 'fs';
|
||||
import net from 'net';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { devices } from 'playwright';
|
||||
|
||||
import { sanitizeForFilePath } from './tools/utils';
|
||||
|
||||
import type { Config, ToolCapability } from '../config';
|
||||
import type { LaunchOptions } from 'playwright';
|
||||
import type { BrowserContextOptions, LaunchOptions } from 'playwright';
|
||||
|
||||
export type CLIOptions = {
|
||||
browser?: string;
|
||||
@ -30,6 +31,7 @@ export type CLIOptions = {
|
||||
cdpEndpoint?: string;
|
||||
executablePath?: string;
|
||||
headless?: boolean;
|
||||
device?: string;
|
||||
userDataDir?: string;
|
||||
port?: number;
|
||||
host?: string;
|
||||
@ -93,11 +95,14 @@ export async function configFromCLIOptions(cliOptions: CLIOptions): Promise<Conf
|
||||
if (browserName === 'chromium')
|
||||
(launchOptions as any).webSocketPort = await findFreePort();
|
||||
|
||||
const contextOptions: BrowserContextOptions | undefined = cliOptions.device ? devices[cliOptions.device] : undefined;
|
||||
|
||||
return {
|
||||
browser: {
|
||||
browserName,
|
||||
userDataDir: cliOptions.userDataDir ?? await createUserDataDir({ browserName, channel }),
|
||||
launchOptions,
|
||||
contextOptions,
|
||||
cdpEndpoint: cliOptions.cdpEndpoint,
|
||||
},
|
||||
server: {
|
||||
|
@ -306,7 +306,7 @@ ${code.join('\n')}
|
||||
async function launchPersistentContext(browserConfig: Config['browser']): Promise<playwright.BrowserContext> {
|
||||
try {
|
||||
const browserType = browserConfig?.browserName ? playwright[browserConfig.browserName] : playwright.chromium;
|
||||
return await browserType.launchPersistentContext(browserConfig?.userDataDir || '', browserConfig?.launchOptions);
|
||||
return await browserType.launchPersistentContext(browserConfig?.userDataDir || '', { ...browserConfig?.launchOptions, ...browserConfig?.contextOptions });
|
||||
} catch (error: any) {
|
||||
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.`);
|
||||
|
@ -33,6 +33,7 @@ program
|
||||
.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('--device <device>', 'Device to emulate, for example: "iPhone 15"')
|
||||
.option('--user-data-dir <path>', 'Path to the user data directory')
|
||||
.option('--port <port>', 'Port to listen on for SSE transport.')
|
||||
.option('--host <host>', 'Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.')
|
||||
|
43
tests/device.spec.ts
Normal file
43
tests/device.spec.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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 { test, expect } from './fixtures';
|
||||
|
||||
test('browser_take_screenshot (viewport)', async ({ startClient, server }) => {
|
||||
const client = await startClient({
|
||||
args: ['--device', 'iPhone 15'],
|
||||
});
|
||||
|
||||
server.route('/', (req, res) => {
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end(`
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body></body>
|
||||
<script>
|
||||
document.body.textContent = window.innerWidth + "x" + window.innerHeight;
|
||||
</script>
|
||||
`);
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: {
|
||||
url: server.PREFIX,
|
||||
},
|
||||
})).toContainTextContent(`393x659`);
|
||||
});
|
6
utils/generate_links.js
Normal file
6
utils/generate_links.js
Normal file
@ -0,0 +1,6 @@
|
||||
const config = JSON.stringify({ name: 'playwright', command: 'npx', args: ["@playwright/mcp@latest"] });
|
||||
const urlForWebsites = `vscode:mcp/install?${encodeURIComponent(config)}`;
|
||||
// Github markdown does not allow linking to `vscode:` directly, so you can use our redirect:
|
||||
const urlForGithub = `https://insiders.vscode.dev/redirect?url=${encodeURIComponent(urlForWebsites)}`;
|
||||
|
||||
console.log(urlForGithub);
|
Loading…
x
Reference in New Issue
Block a user