mirror of
https://github.com/microsoft/playwright-mcp.git
synced 2025-07-27 00:52:27 +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
@ -16,7 +16,7 @@ A Model Context Protocol (MCP) server that provides browser automation capabilit
|
|||||||
- General-purpose browser interaction for agents
|
- General-purpose browser interaction for agents
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
// Generate using?:
|
// Generate using:
|
||||||
node utils/generate_links.js
|
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
|
- `--cdp-endpoint <endpoint>`: CDP endpoint to connect to
|
||||||
- `--executable-path <path>`: Path to the browser executable
|
- `--executable-path <path>`: Path to the browser executable
|
||||||
- `--headless`: Run browser in headless mode (headed by default)
|
- `--headless`: Run browser in headless mode (headed by default)
|
||||||
|
- `--device`: Emulate mobile device
|
||||||
- `--user-data-dir <path>`: Path to the user data directory
|
- `--user-data-dir <path>`: Path to the user data directory
|
||||||
- `--port <port>`: Port to listen on for SSE transport
|
- `--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.
|
- `--host <host>`: Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.
|
||||||
@ -105,6 +106,7 @@ The Playwright MCP server can be configured using a JSON configuration file. Her
|
|||||||
userDataDir?: string;
|
userDataDir?: string;
|
||||||
|
|
||||||
// Browser launch options (see Playwright docs)
|
// Browser launch options (see Playwright docs)
|
||||||
|
// @see https://playwright.dev/docs/api/class-browsertype#browser-type-launch
|
||||||
launchOptions?: {
|
launchOptions?: {
|
||||||
channel?: string; // Browser channel (e.g. 'chrome')
|
channel?: string; // Browser channel (e.g. 'chrome')
|
||||||
headless?: boolean; // Run in headless mode
|
headless?: boolean; // Run in headless mode
|
||||||
@ -113,6 +115,7 @@ The Playwright MCP server can be configured using a JSON configuration file. Her
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Browser context options
|
// Browser context options
|
||||||
|
// @see https://playwright.dev/docs/api/class-browser#browser-new-context
|
||||||
contextOptions?: {
|
contextOptions?: {
|
||||||
viewport?: { width: number, height: number };
|
viewport?: { width: number, height: number };
|
||||||
// ... other Playwright context options
|
// ... other Playwright context options
|
||||||
@ -166,8 +169,6 @@ npx @playwright/mcp@latest --config path/to/config.json
|
|||||||
|
|
||||||
### Running on Linux
|
### 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,
|
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.
|
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 net from 'net';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { devices } from 'playwright';
|
||||||
|
|
||||||
import { sanitizeForFilePath } from './tools/utils';
|
import { sanitizeForFilePath } from './tools/utils';
|
||||||
|
|
||||||
import type { Config, ToolCapability } from '../config';
|
import type { Config, ToolCapability } from '../config';
|
||||||
import type { LaunchOptions } from 'playwright';
|
import type { BrowserContextOptions, LaunchOptions } from 'playwright';
|
||||||
|
|
||||||
export type CLIOptions = {
|
export type CLIOptions = {
|
||||||
browser?: string;
|
browser?: string;
|
||||||
@ -30,6 +31,7 @@ export type CLIOptions = {
|
|||||||
cdpEndpoint?: string;
|
cdpEndpoint?: string;
|
||||||
executablePath?: string;
|
executablePath?: string;
|
||||||
headless?: boolean;
|
headless?: boolean;
|
||||||
|
device?: string;
|
||||||
userDataDir?: string;
|
userDataDir?: string;
|
||||||
port?: number;
|
port?: number;
|
||||||
host?: string;
|
host?: string;
|
||||||
@ -93,11 +95,14 @@ export async function configFromCLIOptions(cliOptions: CLIOptions): Promise<Conf
|
|||||||
if (browserName === 'chromium')
|
if (browserName === 'chromium')
|
||||||
(launchOptions as any).webSocketPort = await findFreePort();
|
(launchOptions as any).webSocketPort = await findFreePort();
|
||||||
|
|
||||||
|
const contextOptions: BrowserContextOptions | undefined = cliOptions.device ? devices[cliOptions.device] : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
browser: {
|
browser: {
|
||||||
browserName,
|
browserName,
|
||||||
userDataDir: cliOptions.userDataDir ?? await createUserDataDir({ browserName, channel }),
|
userDataDir: cliOptions.userDataDir ?? await createUserDataDir({ browserName, channel }),
|
||||||
launchOptions,
|
launchOptions,
|
||||||
|
contextOptions,
|
||||||
cdpEndpoint: cliOptions.cdpEndpoint,
|
cdpEndpoint: cliOptions.cdpEndpoint,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
@ -306,7 +306,7 @@ ${code.join('\n')}
|
|||||||
async function launchPersistentContext(browserConfig: Config['browser']): Promise<playwright.BrowserContext> {
|
async function launchPersistentContext(browserConfig: Config['browser']): Promise<playwright.BrowserContext> {
|
||||||
try {
|
try {
|
||||||
const browserType = browserConfig?.browserName ? playwright[browserConfig.browserName] : playwright.chromium;
|
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) {
|
} 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.`);
|
||||||
|
@ -33,6 +33,7 @@ program
|
|||||||
.option('--cdp-endpoint <endpoint>', 'CDP endpoint to connect to.')
|
.option('--cdp-endpoint <endpoint>', 'CDP endpoint to connect to.')
|
||||||
.option('--executable-path <path>', 'Path to the browser executable.')
|
.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('--device <device>', 'Device to emulate, for example: "iPhone 15"')
|
||||||
.option('--user-data-dir <path>', 'Path to the user data directory')
|
.option('--user-data-dir <path>', 'Path to the user data directory')
|
||||||
.option('--port <port>', 'Port to listen on for SSE transport.')
|
.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.')
|
.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