mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-09-14 19:45:30 +08:00
feat: make pageId required for page-scoped tools by default (#1777)
Removes `--experimental-page-id-routing` making it the default behavior. To go back to the previous behavior, pass `--pageIdRouting=false` when starting the server. --------- Co-authored-by: Alex Rudenko <alexrudenko@chromium.org> Co-authored-by: Samiya Caur <samiyac@google.com>
This commit is contained in:
committed by
GitHub
parent
ebf58f2f4a
commit
50a16fae69
@@ -663,10 +663,10 @@ The Chrome DevTools MCP server supports the following configuration option:
|
||||
- **Type:** boolean
|
||||
- **Default:** `false`
|
||||
|
||||
- **`--experimentalPageIdRouting`/ `--experimental-page-id-routing`**
|
||||
Whether to expose pageId on page-scoped tools and route requests by page ID (useful for concurrent agent sessions).
|
||||
- **`--pageIdRouting`/ `--page-id-routing`**
|
||||
Require pageId on page-scoped tools and route requests by page ID (useful for concurrent agent sessions). Use --no-page-id-routing to disable.
|
||||
- **Type:** boolean
|
||||
- **Default:** `false`
|
||||
- **Default:** `true`
|
||||
|
||||
- **`--experimentalDevtools`/ `--experimental-devtools`**
|
||||
Whether to enable automation over DevTools targets
|
||||
@@ -851,22 +851,25 @@ You can also run `npx chrome-devtools-mcp@latest --help` to see all available co
|
||||
|
||||
### Concurrent sessions
|
||||
|
||||
Most MCP clients start one Chrome DevTools MCP server per conversation. If your
|
||||
client shares a single server instance across concurrent agents or subagents,
|
||||
start the server with `--experimentalPageIdRouting`. This exposes `pageId` on
|
||||
page-scoped tools so each agent can route tool calls to the tab it is working
|
||||
with.
|
||||
Most MCP clients start one Chrome DevTools MCP server per conversation.
|
||||
By default, the server runs with `--pageIdRouting` enabled, making `pageId` a
|
||||
required parameter on page-scoped tools (such as `click`, `fill`, `navigate_page`,
|
||||
`take_snapshot`, etc.) so multiple agents or subagents sharing a server instance can
|
||||
route tool calls directly to the specific tab they are working with.
|
||||
|
||||
For `evaluate_script`, `pageId` is required by default for targeting pages, but
|
||||
becomes optional when `--categoryExtensions` is enabled so that `serviceWorkerId`
|
||||
can be specified instead to evaluate inside an extension background service worker.
|
||||
|
||||
To disable this behavior and default to the currently selected page, pass
|
||||
`--no-page-id-routing`.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"chrome-devtools": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"chrome-devtools-mcp@latest",
|
||||
"--experimentalPageIdRouting"
|
||||
]
|
||||
"args": ["-y", "chrome-devtools-mcp@latest"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-12
@@ -23,11 +23,11 @@ The CLI acts as a client to a background `chrome-devtools-mcp` daemon (uses Unix
|
||||
# Check if the daemon is running
|
||||
chrome-devtools status
|
||||
|
||||
# Navigate the current page to a URL
|
||||
chrome-devtools navigate_page "https://google.com"
|
||||
# Navigate page 1 to a URL
|
||||
chrome-devtools navigate_page 1 --url "https://google.com"
|
||||
|
||||
# Take a screenshot and save it to a file
|
||||
chrome-devtools take_screenshot --filePath screenshot.png
|
||||
# Take a screenshot of page 1 and save it to a file
|
||||
chrome-devtools take_screenshot 1 --filePath screenshot.png
|
||||
|
||||
# Stop the background daemon when finished
|
||||
chrome-devtools stop
|
||||
@@ -42,7 +42,7 @@ Thus, `--categoryExtensions` tools are currently not available in the CLI.
|
||||
chrome-devtools <tool> [arguments] [flags]
|
||||
```
|
||||
|
||||
- **Required Arguments**: Passed as positional arguments.
|
||||
- **Required Arguments**: Passed as positional arguments. Page-scoped tools require `<pageId>` as their first positional argument.
|
||||
- **Optional Arguments**: Passed as flags (e.g., `--filePath`, `--fullPage`).
|
||||
|
||||
### Examples
|
||||
@@ -51,24 +51,38 @@ chrome-devtools <tool> [arguments] [flags]
|
||||
|
||||
```sh
|
||||
chrome-devtools new_page "https://example.com"
|
||||
chrome-devtools navigate_page "https://web.dev" --type url
|
||||
chrome-devtools navigate_page 1 --url "https://web.dev"
|
||||
```
|
||||
|
||||
**Interaction:**
|
||||
|
||||
```sh
|
||||
# Click an element by its UID from a snapshot
|
||||
chrome-devtools click "element-uid-123"
|
||||
# Click an element by its UID from a snapshot on page 1
|
||||
chrome-devtools click 1 "element-uid-123"
|
||||
|
||||
# Fill a form field
|
||||
chrome-devtools fill "input-uid-456" "search query"
|
||||
# Fill a form field on page 1
|
||||
chrome-devtools fill 1 "input-uid-456" "search query"
|
||||
```
|
||||
|
||||
**Script Evaluation:**
|
||||
|
||||
- When `--categoryExtensions` and `--pageIdRouting` are enabled:
|
||||
- Target a page using `--pageId <number>`: `chrome-devtools evaluate_script "() => document.title" --pageId 1`
|
||||
- Target an extension service worker using `--serviceWorkerId <string>`: `chrome-devtools evaluate_script "() => self.registration.scope" --serviceWorkerId sw-1`
|
||||
|
||||
```sh
|
||||
# Evaluate a JavaScript expression on page 1
|
||||
chrome-devtools evaluate_script "() => document.title" --pageId 1
|
||||
|
||||
# Evaluate inside an extension service worker
|
||||
chrome-devtools evaluate_script "() => self.registration.scope" --serviceWorkerId sw-1
|
||||
```
|
||||
|
||||
**Analysis:**
|
||||
|
||||
```sh
|
||||
# Run a Lighthouse audit (defaults to navigation mode)
|
||||
chrome-devtools lighthouse_audit --mode snapshot
|
||||
# Run a Lighthouse audit on page 1 (defaults to navigation mode)
|
||||
chrome-devtools lighthouse_audit 1 --mode snapshot
|
||||
```
|
||||
|
||||
## Output format
|
||||
|
||||
+51
-16
@@ -79,6 +79,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **uid** (string) **(required)**: The uid of an element on the page from the page content snapshot
|
||||
- **dblClick** (boolean) _(optional)_: Set to true for double clicks. Default is false.
|
||||
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
|
||||
@@ -92,6 +93,7 @@
|
||||
**Parameters:**
|
||||
|
||||
- **from_uid** (string) **(required)**: The uid of the element to [`drag`](#drag)
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **to_uid** (string) **(required)**: The uid of the element to drop into
|
||||
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
|
||||
|
||||
@@ -103,6 +105,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **uid** (string) **(required)**: The uid of an element on the page from the page content snapshot
|
||||
- **value** (string) **(required)**: The value to [`fill`](#fill) in. "true" or "false" for checkboxes and toggles, "true" for radio buttons.
|
||||
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
|
||||
@@ -116,6 +119,7 @@
|
||||
**Parameters:**
|
||||
|
||||
- **elements** (array) **(required)**: Elements from snapshot to [`fill`](#fill) out.
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
|
||||
|
||||
---
|
||||
@@ -127,6 +131,7 @@
|
||||
**Parameters:**
|
||||
|
||||
- **action** (enum: "accept", "dismiss") **(required)**: Whether to dismiss or accept the dialog
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **promptText** (string) _(optional)_: Optional prompt text to enter into the dialog.
|
||||
|
||||
---
|
||||
@@ -137,6 +142,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **uid** (string) **(required)**: The uid of an element on the page from the page content snapshot
|
||||
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
|
||||
|
||||
@@ -149,6 +155,7 @@
|
||||
**Parameters:**
|
||||
|
||||
- **key** (string) **(required)**: A key or a combination (e.g., "Enter", "Control+A", "Control++", "Control+Shift+R"). Modifiers: Control, Shift, Alt, Meta
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
|
||||
|
||||
---
|
||||
@@ -159,6 +166,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **text** (string) **(required)**: The text to type
|
||||
- **submitKey** (string) _(optional)_: Optional key to press after typing. E.g., "Enter", "Tab", "Escape"
|
||||
|
||||
@@ -171,6 +179,7 @@
|
||||
**Parameters:**
|
||||
|
||||
- **filePaths** (array) **(required)**: One or more files paths to upload. File paths have to be local to the browser instance (not the MCP).
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **uid** (string) **(required)**: The uid of the file input element or an element that will open file chooser on the page from the page content snapshot
|
||||
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
|
||||
|
||||
@@ -182,6 +191,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **x** (number) **(required)**: The x coordinate
|
||||
- **y** (number) **(required)**: The y coordinate
|
||||
- **dblClick** (boolean) _(optional)_: Set to true for double clicks. Default is false.
|
||||
@@ -215,6 +225,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **handleBeforeUnload** (enum: "accept", "dismiss") _(optional)_: Whether to auto accept or beforeunload dialogs triggered by this navigation. Default is accept.
|
||||
- **ignoreCache** (boolean) _(optional)_: Whether to ignore cache on reload.
|
||||
- **initScript** (string) _(optional)_: A JavaScript script to be executed on each new document before any other scripts for the next navigation.
|
||||
@@ -254,6 +265,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **text** (array) **(required)**: Non-empty list of texts. Resolves when any value appears on the page.
|
||||
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
|
||||
|
||||
@@ -263,10 +275,11 @@
|
||||
|
||||
### `emulate`
|
||||
|
||||
**Description:** Emulates various features on the selected page.
|
||||
**Description:** Emulates various features on the target page.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **colorScheme** (enum: "dark", "light", "auto") _(optional)_: [`Emulate`](#emulate) the dark or the light mode. Set to "auto" to reset to the default.
|
||||
- **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Omit or set the rate to 1 to disable throttling
|
||||
- **extraHttpHeaders** (string) _(optional)_: Extra HTTP headers as a JSON string object, e.g. {"X-Custom": "value", "Authorization": "Bearer token"}. Headers are included into every HTTP request originating from the page and persist across navigations until cleared. Pass an empty string to clear all extra headers.
|
||||
@@ -279,11 +292,12 @@
|
||||
|
||||
### `resize_page`
|
||||
|
||||
**Description:** Resizes the selected page's window so that the page has specified dimension
|
||||
**Description:** Resizes the page's window so that the page has specified dimension
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **height** (number) **(required)**: Page height
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **width** (number) **(required)**: Page width
|
||||
|
||||
---
|
||||
@@ -298,27 +312,30 @@
|
||||
|
||||
- **insightName** (string) **(required)**: The name of the Insight you want more information on. For example: "DocumentLatency" or "LCPBreakdown"
|
||||
- **insightSetId** (string) **(required)**: The id for the specific insight set. Only use the ids given in the "Available insight sets" list.
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
|
||||
---
|
||||
|
||||
### `performance_start_trace`
|
||||
|
||||
**Description:** Start a performance trace on the selected webpage. Use to find frontend performance issues, Core Web Vitals (LCP, INP, CLS), and improve page load speed.
|
||||
**Description:** Start a performance trace on the target webpage. Use to find frontend performance issues, Core Web Vitals (LCP, INP, CLS), and improve page load speed.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **autoStop** (boolean) _(optional)_: Determines if the trace recording should be automatically stopped.
|
||||
- **filePath** (string) _(optional)_: The absolute file path, or a file path relative to the current working directory, to save the raw trace data. For example, trace.json.gz (compressed) or trace.json (uncompressed).
|
||||
- **reload** (boolean) _(optional)_: Determines if, once tracing has started, the current selected page should be automatically reloaded. Navigate the page to the right URL using the [`navigate_page`](#navigate_page) tool BEFORE starting the trace if reload or autoStop is set to true.
|
||||
- **reload** (boolean) _(optional)_: Determines if, once tracing has started, the target page should be automatically reloaded. Navigate the page to the right URL using the [`navigate_page`](#navigate_page) tool BEFORE starting the trace if reload or autoStop is set to true.
|
||||
|
||||
---
|
||||
|
||||
### `performance_stop_trace`
|
||||
|
||||
**Description:** Stop the active performance trace recording on the selected webpage.
|
||||
**Description:** Stop the active performance trace recording on the target webpage.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **filePath** (string) _(optional)_: The absolute file path, or a file path relative to the current working directory, to save the raw trace data. For example, trace.json.gz (compressed) or trace.json (uncompressed).
|
||||
|
||||
---
|
||||
@@ -331,6 +348,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **reqid** (number) _(optional)_: The reqid of the network request. If omitted returns the currently selected request in the DevTools Network panel.
|
||||
- **requestFilePath** (string) _(optional)_: The absolute or relative path to a .network-request file to save the request body to. If omitted, the body is returned inline.
|
||||
- **responseFilePath** (string) _(optional)_: The absolute or relative path to a .network-response file to save the response body to. If omitted, the body is returned inline.
|
||||
@@ -339,10 +357,11 @@
|
||||
|
||||
### `list_network_requests`
|
||||
|
||||
**Description:** Lists the most recent requests for the currently selected page since the last navigation.
|
||||
**Description:** Lists the most recent requests for the target page since the last navigation.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **includePreservedRequests** (boolean) _(optional)_: Set to true to return the preserved requests over the last 3 navigations.
|
||||
- **pageIdx** (integer) _(optional)_: Page number to return (0-based). When omitted, returns the first page.
|
||||
- **pageSize** (integer) _(optional)_: Maximum number of requests to return. When omitted, returns all requests.
|
||||
@@ -354,14 +373,15 @@
|
||||
|
||||
### `evaluate_script`
|
||||
|
||||
**Description:** Evaluate a JavaScript function inside the currently selected page. Returns the response as JSON, so returned values have to be JSON-serializable.
|
||||
**Description:** Evaluate a JavaScript function inside the target page. Returns the response as JSON, so returned values have to be JSON-serializable.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **function** (string) **(required)**: A JavaScript function declaration to be executed by the tool in the currently selected page.
|
||||
- **function** (string) **(required)**: A JavaScript function declaration to be executed by the tool in the target page.
|
||||
Example without arguments: `() => document.title` or `async () => await fetch("example.com")`.
|
||||
Example with arguments: `(el) => el.innerText`
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **args** (array) _(optional)_: An optional list of arguments to pass to the function.
|
||||
- **dialogAction** (string) _(optional)_: Handle dialogs while execution. "accept", "dismiss", or string for response of window.prompt. Defaults to accept.
|
||||
- **filePath** (string) _(optional)_: The absolute or relative path to a file to save the script output to. If omitted, the output is returned inline.
|
||||
@@ -376,6 +396,7 @@
|
||||
**Parameters:**
|
||||
|
||||
- **msgid** (number) **(required)**: The msgid of a console message on the page from the listed console messages
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
|
||||
---
|
||||
|
||||
@@ -385,6 +406,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **device** (enum: "desktop", "mobile") _(optional)_: Device to [`emulate`](#emulate).
|
||||
- **mode** (enum: "navigation", "snapshot") _(optional)_: "navigation" reloads & audits. "snapshot" analyzes current state.
|
||||
- **outputDirPath** (string) _(optional)_: Directory for reports. If omitted, uses temporary files.
|
||||
@@ -393,10 +415,11 @@
|
||||
|
||||
### `list_console_messages`
|
||||
|
||||
**Description:** List all console messages for the currently selected page since the last navigation.
|
||||
**Description:** List all console messages for the target page since the last navigation.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **includePreservedMessages** (boolean) _(optional)_: Set to true to return the preserved messages over the last 3 navigations.
|
||||
- **includeStackTraces** (boolean) _(optional)_: Set to true to include the stack trace for each message when available. Increases the response size.
|
||||
- **pageIdx** (integer) _(optional)_: Page number to return (0-based). When omitted, returns the first page.
|
||||
@@ -412,6 +435,7 @@
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **filePath** (string) _(optional)_: The absolute path, or a path relative to the current working directory, to save the screenshot to instead of attaching it to the response.
|
||||
- **format** (enum: "png", "jpeg", "webp") _(optional)_: Type of format to save the screenshot as. Default is "png"
|
||||
- **fullPage** (boolean) _(optional)_: If set to true takes a screenshot of the full page instead of the currently visible viewport. Incompatible with uid.
|
||||
@@ -422,12 +446,13 @@
|
||||
|
||||
### `take_snapshot`
|
||||
|
||||
**Description:** Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique
|
||||
**Description:** Take a text snapshot of the target page based on the a11y tree. The snapshot lists page elements along with a unique
|
||||
identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected
|
||||
in the DevTools Elements panel (if any).
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **filePath** (string) _(optional)_: The absolute path, or a path relative to the current working directory, to save the snapshot to instead of attaching it to the response.
|
||||
- **verbose** (boolean) _(optional)_: Whether to include all possible information available in the full a11y tree. Default is false.
|
||||
|
||||
@@ -435,19 +460,22 @@ in the DevTools Elements panel (if any).
|
||||
|
||||
### `screencast_start`
|
||||
|
||||
**Description:** Starts recording a screencast (video) of the selected page in specified format. (requires flag: --experimentalScreencast=true)
|
||||
**Description:** Starts recording a screencast (video) of the target page in specified format. (requires flag: --experimentalScreencast=true)
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **filePath** (string) _(optional)_: Output file path (.webm,.mp4 are supported). Uses mkdtemp to generate a unique path if not provided.
|
||||
|
||||
---
|
||||
|
||||
### `screencast_stop`
|
||||
|
||||
**Description:** Stops the active screencast recording on the selected page. (requires flag: --experimentalScreencast=true)
|
||||
**Description:** Stops the active screencast recording on the target page. (requires flag: --experimentalScreencast=true)
|
||||
|
||||
**Parameters:** None
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
|
||||
---
|
||||
|
||||
@@ -455,11 +483,12 @@ in the DevTools Elements panel (if any).
|
||||
|
||||
### `take_heapsnapshot`
|
||||
|
||||
**Description:** Capture a heap snapshot of the currently selected page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.
|
||||
**Description:** Capture a heap snapshot of the target page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to save the heapsnapshot to.
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
|
||||
---
|
||||
|
||||
@@ -682,6 +711,7 @@ in the DevTools Elements panel (if any).
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **toolName** (string) **(required)**: The name of the tool to execute
|
||||
- **params** (string) _(optional)_: The JSON-stringified parameters to pass to the tool
|
||||
|
||||
@@ -697,7 +727,9 @@ following command to the script:
|
||||
This might be helpful when the third-party developer tools return non-serializable values or when composing
|
||||
third-party developer tools with additional functionality. (requires flag: --categoryExperimentalThirdParty=true)
|
||||
|
||||
**Parameters:** None
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
|
||||
---
|
||||
|
||||
@@ -711,6 +743,7 @@ third-party developer tools with additional functionality. (requires flag: --cat
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
- **toolName** (string) **(required)**: The name of the WebMCP tool to execute
|
||||
- **input** (string) _(optional)_: The JSON-stringified parameters to pass to the WebMCP tool
|
||||
|
||||
@@ -720,7 +753,9 @@ third-party developer tools with additional functionality. (requires flag: --cat
|
||||
|
||||
**Description:** Lists all WebMCP tools the page exposes. (requires flag: --categoryExperimentalWebmcp=true)
|
||||
|
||||
**Parameters:** None
|
||||
**Parameters:**
|
||||
|
||||
- **pageId** (number) **(required)**: Targets a specific page by ID.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export class Result {
|
||||
}
|
||||
|
||||
get hasPageIdRouting(): boolean {
|
||||
return this.serverArgs.includes('--experimental-page-id-routing');
|
||||
return !this.serverArgs.includes('--no-page-id-routing');
|
||||
}
|
||||
|
||||
get remainingCalls(): CapturedFunctionCall[] {
|
||||
@@ -102,6 +102,6 @@ export interface TestScenario {
|
||||
path: string;
|
||||
htmlContent: string;
|
||||
};
|
||||
/** Extra CLI flags passed to the MCP server (e.g. '--experimental-page-id-routing'). */
|
||||
/** Extra CLI flags passed to the MCP server (e.g. '--no-page-id-routing'). */
|
||||
serverArgs?: string[];
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import assert from 'node:assert';
|
||||
import type {TestScenario} from '../eval_gemini.ts';
|
||||
|
||||
export const scenario: TestScenario = {
|
||||
serverArgs: ['--experimental-page-id-routing'],
|
||||
prompt: `Open two pages in the same isolated context "session":
|
||||
- Page 1 at data:text/html,<textarea id="ta"></textarea>
|
||||
- Page 2 at data:text/html,<h1>Other</h1>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import assert from 'node:assert';
|
||||
|
||||
import type {TestScenario} from '../eval_gemini.ts';
|
||||
|
||||
const PAGE_A_URL =
|
||||
'data:text/html,<form id="login-form"><input type="text" id="username" placeholder="Username" /><input type="password" id="password" placeholder="Password" /><button type="submit" id="submit-login">Log In</button></form>';
|
||||
const PAGE_B_URL =
|
||||
'data:text/html,<form id="feedback-form"><input type="text" id="email" placeholder="Email" /><textarea id="comments" placeholder="Comments"></textarea><button type="submit" id="submit-feedback">Send</button></form>';
|
||||
|
||||
export const scenario: TestScenario = {
|
||||
prompt: `Open two new pages in isolated contexts:
|
||||
- Page A (isolatedContext "login_ctx") at ${PAGE_A_URL}
|
||||
- Page B (isolatedContext "feedback_ctx") at ${PAGE_B_URL}
|
||||
|
||||
Take a snapshot of both pages. Then, perform the following actions individually in the exact order specified below:
|
||||
1. Fill "admin" into the Username input on Page A.
|
||||
2. Fill "user@example.com" into the Email input on Page B.
|
||||
3. Fill "secret123" into the Password input on Page A.
|
||||
4. Fill "Great tools!" into the Comments textarea on Page B.
|
||||
|
||||
Finally, submit both forms by clicking the submit buttons on Page A and Page B.`,
|
||||
maxTurns: 15,
|
||||
expectations: result => {
|
||||
const newPages = result.calls.filter(c => c.name === 'new_page');
|
||||
assert.strictEqual(newPages.length, 2, 'Should open 2 pages');
|
||||
const snapshots = result.calls.filter(c => c.name === 'take_snapshot');
|
||||
assert.ok(snapshots.length >= 2, 'Should snapshot both pages');
|
||||
|
||||
const fills = result.calls.filter(c => c.name === 'fill');
|
||||
assert.strictEqual(
|
||||
fills.length,
|
||||
4,
|
||||
'Should fill 4 inputs across the forms',
|
||||
);
|
||||
|
||||
// Verify that each fill targeted the correct pageId based on its value/element
|
||||
for (const fill of fills) {
|
||||
const value = String(fill.args['value'] || '');
|
||||
if (value === 'admin' || value === 'secret123') {
|
||||
assert.strictEqual(
|
||||
fill.args['pageId'],
|
||||
2,
|
||||
`Filling '${value}' should target login page (pageId 2)`,
|
||||
);
|
||||
} else if (value === 'user@example.com' || value === 'Great tools!') {
|
||||
assert.strictEqual(
|
||||
fill.args['pageId'],
|
||||
3,
|
||||
`Filling '${value}' should target feedback page (pageId 3)`,
|
||||
);
|
||||
} else {
|
||||
assert.fail(`Unexpected fill value: ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify no select_page calls were made between the interleaved actions
|
||||
const selects = result.calls.filter(c => c.name === 'select_page');
|
||||
assert.strictEqual(
|
||||
selects.length,
|
||||
0,
|
||||
'Should not use select_page when pageId routing is active',
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import assert from 'node:assert';
|
||||
|
||||
import type {TestScenario} from '../eval_gemini.ts';
|
||||
|
||||
const PAGE_COUNTER =
|
||||
'data:text/html,<h1>Counter Page</h1><button id="inc-btn" onclick="document.getElementById(\'count\').innerText = parseInt(document.getElementById(\'count\').innerText) + 1">Increment</button><div id="count">0</div>';
|
||||
const PAGE_INPUT =
|
||||
'data:text/html,<h1>Input Page</h1><label for="val-input">Enter Counter Value:</label><input type="text" id="val-input" /><button id="submit-btn">Submit</button>';
|
||||
|
||||
export const scenario: TestScenario = {
|
||||
prompt: `Open two new pages:
|
||||
- Page A at ${PAGE_COUNTER}
|
||||
- Page B at ${PAGE_INPUT}
|
||||
|
||||
Take snapshot of both pages and then perform the following steps:
|
||||
1. Click the "Increment" button on Page A twice.
|
||||
2. Take a snapshot of Page A to read the updated counter value.
|
||||
3. On Page B, fill that exact counter value into the input field, and click the "Submit" button.`,
|
||||
maxTurns: 12,
|
||||
expectations: result => {
|
||||
const newPages = result.calls.filter(c => c.name === 'new_page');
|
||||
assert.strictEqual(newPages.length, 2, 'Should open 2 pages');
|
||||
|
||||
const clicks = result.calls.filter(c => c.name === 'click');
|
||||
assert.ok(
|
||||
clicks.length >= 3,
|
||||
'Should click increment twice and then submit',
|
||||
);
|
||||
|
||||
// First click and second click should target the increment button on Page A
|
||||
const counterClicks = clicks.filter(c => c.args['pageId'] === 2);
|
||||
assert.strictEqual(
|
||||
counterClicks.length,
|
||||
2,
|
||||
'Should click increment button on Page A exactly twice',
|
||||
);
|
||||
|
||||
// There should be a snapshot of Page A to read the value
|
||||
const snapshots = result.calls.filter(c => c.name === 'take_snapshot');
|
||||
const counterSnapshot = snapshots.find(s => s.args['pageId'] === 2);
|
||||
assert.ok(
|
||||
counterSnapshot,
|
||||
'Should snapshot Page A to read the counter value',
|
||||
);
|
||||
|
||||
// The fill and final click should target Page B
|
||||
const fills = result.calls.filter(
|
||||
c => c.name === 'fill' || c.name === 'fill_form',
|
||||
);
|
||||
assert.strictEqual(
|
||||
fills.length,
|
||||
1,
|
||||
'Should fill the input field on Page B',
|
||||
);
|
||||
assert.strictEqual(fills[0].args['pageId'], 3, 'Fill should target Page B');
|
||||
|
||||
let filledValue = '';
|
||||
if (fills[0].name === 'fill_form') {
|
||||
const elements = fills[0].args['elements'];
|
||||
assert.ok(Array.isArray(elements), 'elements should be an array');
|
||||
filledValue = elements[0]['value'];
|
||||
} else if (fills[0].name === 'fill') {
|
||||
filledValue = String(fills[0].args['value']);
|
||||
}
|
||||
|
||||
assert.strictEqual(
|
||||
filledValue,
|
||||
'2',
|
||||
'Should fill the value "2" (since we incremented twice)',
|
||||
);
|
||||
|
||||
const finalClick = clicks[clicks.length - 1];
|
||||
assert.strictEqual(
|
||||
finalClick.args['pageId'],
|
||||
3,
|
||||
'Submit click should target Page B',
|
||||
);
|
||||
|
||||
// Verify no select_page calls were made between the interleaved actions
|
||||
const selects = result.calls.filter(c => c.name === 'select_page');
|
||||
assert.strictEqual(
|
||||
selects.length,
|
||||
0,
|
||||
'Should not use select_page when pageId routing is active',
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -9,7 +9,6 @@ import assert from 'node:assert';
|
||||
import type {TestScenario} from '../eval_gemini.ts';
|
||||
|
||||
export const scenario: TestScenario = {
|
||||
serverArgs: ['--experimental-page-id-routing'],
|
||||
prompt: `Open two new pages in isolated contexts:
|
||||
- Page A (isolatedContext "contextA") at data:text/html,<button>Click A</button>
|
||||
- Page B (isolatedContext "contextB") at data:text/html,<button>Click B</button>
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
OFF_BY_DEFAULT_CATEGORIES,
|
||||
labels,
|
||||
} from '../build/src/tools/categories.js';
|
||||
import {pageIdSchema} from '../build/src/tools/ToolDefinition.js';
|
||||
import {createTools} from '../build/src/tools/tools.js';
|
||||
|
||||
const OUTPUT_PATH = './docs/tool-reference.md';
|
||||
@@ -434,7 +435,7 @@ async function generateReference(
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function getToolsAndCategories(tools: any) {
|
||||
function getToolsAndCategories(tools: any, slim = false) {
|
||||
// Convert ToolDefinitions to ToolWithAnnotations
|
||||
const toolsWithAnnotations: ToolWithAnnotations[] = tools
|
||||
.filter(tool => {
|
||||
@@ -455,8 +456,12 @@ function getToolsAndCategories(tools: any) {
|
||||
const properties: Record<string, TypeInfo> = {};
|
||||
const required: string[] = [];
|
||||
|
||||
const toolSchema = {
|
||||
...tool.schema,
|
||||
...(tool.pageScoped && !slim ? pageIdSchema : {}),
|
||||
};
|
||||
for (const [key, schema] of Object.entries(
|
||||
tool.schema as unknown as Record<string, ZodSchema>,
|
||||
toolSchema as unknown as Record<string, ZodSchema>,
|
||||
)) {
|
||||
const info = getZodTypeInfo(schema);
|
||||
properties[key] = info;
|
||||
@@ -520,7 +525,9 @@ async function generateToolDocumentation(): Promise<void> {
|
||||
|
||||
{
|
||||
const {toolsWithAnnotations, categories, sortedCategories} =
|
||||
getToolsAndCategories(createTools({slim: false} as ParsedArguments));
|
||||
getToolsAndCategories(
|
||||
createTools({slim: false, pageIdRouting: true} as ParsedArguments),
|
||||
);
|
||||
await generateReference(
|
||||
'Chrome DevTools MCP Tool Reference',
|
||||
OUTPUT_PATH,
|
||||
@@ -536,7 +543,10 @@ async function generateToolDocumentation(): Promise<void> {
|
||||
|
||||
{
|
||||
const {toolsWithAnnotations, categories, sortedCategories} =
|
||||
getToolsAndCategories(createTools({slim: true} as ParsedArguments));
|
||||
getToolsAndCategories(
|
||||
createTools({slim: true} as ParsedArguments),
|
||||
true,
|
||||
);
|
||||
await generateReference(
|
||||
'Chrome DevTools MCP Slim Tool Reference',
|
||||
SLIM_OUTPUT_PATH,
|
||||
|
||||
@@ -5,6 +5,8 @@ description: Uses Chrome DevTools MCP for accessibility (a11y) debugging and aud
|
||||
|
||||
## Core Concepts
|
||||
|
||||
**Page Targeting**: Page-scoped tools (`take_snapshot`, `list_console_messages`, `evaluate_script`, `press_key`, `take_screenshot`, `lighthouse_audit`, etc.) require a `pageId` parameter. Retrieve available page IDs using `list_pages` or from `new_page`.
|
||||
|
||||
**Accessibility Tree vs DOM**: Visually hiding an element (e.g., `CSS opacity: 0`) behaves differently for screen readers than `display: none` or `aria-hidden="true"`. The `take_snapshot` tool returns the accessibility tree of the page, which represents what assistive technologies "see", making it the most reliable source of truth for semantic structure.
|
||||
|
||||
**Reading web.dev documentation**: If you need to research specific accessibility guidelines (like `https://web.dev/articles/accessible-tap-targets`), you can append `.md.txt` to the URL (e.g., `https://web.dev/articles/accessible-tap-targets.md.txt`) to fetch the clean, raw markdown version. This is much easier to read!
|
||||
|
||||
@@ -11,9 +11,9 @@ _Note: If this is your very first time using the CLI, see [references/installati
|
||||
|
||||
## AI Workflow
|
||||
|
||||
1. **Execute**: Run tools directly (e.g., `chrome-devtools list_pages`). The background server starts implicitly; **do not** run `start`/`status`/`stop` before each use.
|
||||
2. **Inspect**: Use `take_snapshot` to get an element `<uid>`.
|
||||
3. **Act**: Use `click`, `fill`, etc. State persists across commands.
|
||||
1. **Execute**: Run tools directly. If you don't know the target page's ID, run `chrome-devtools list_pages` to find it. The background server starts implicitly; **do not** run `start`/`status`/`stop` before each use.
|
||||
2. **Inspect**: Use `chrome-devtools take_snapshot <pageId>` to get an element `<uid>`.
|
||||
3. **Act**: Use `chrome-devtools click <pageId> <uid>`, `chrome-devtools fill <pageId> <uid> <value>`, etc. State persists across commands.
|
||||
|
||||
Snapshot example:
|
||||
|
||||
@@ -44,23 +44,23 @@ chrome-devtools <tool> [arguments] [flags]
|
||||
## Input Automation (<uid> from snapshot)
|
||||
|
||||
```bash
|
||||
chrome-devtools take_snapshot # Take a text snapshot of the page to get UIDs for elements
|
||||
chrome-devtools click "id" # Clicks on the provided element
|
||||
chrome-devtools click "id" --dblClick true --includeSnapshot true # Double clicks and returns a snapshot
|
||||
chrome-devtools drag "src" "dst" # Drag an element onto another element
|
||||
chrome-devtools drag "src" "dst" --includeSnapshot true # Drag an element and return a snapshot
|
||||
chrome-devtools fill "id" "text" # Type text into an input, textarea, or select an option
|
||||
chrome-devtools fill "id" "text" --includeSnapshot true # Fill an element and return a snapshot
|
||||
chrome-devtools handle_dialog accept # Handle a browser dialog (accept/dismiss)
|
||||
chrome-devtools handle_dialog dismiss --promptText "hi" # Dismiss a dialog with prompt text
|
||||
chrome-devtools hover "id" # Hover over the provided element
|
||||
chrome-devtools hover "id" --includeSnapshot true # Hover over an element and return a snapshot
|
||||
chrome-devtools press_key "Enter" # Press a key or key combination ("Control+A", "Escape")
|
||||
chrome-devtools press_key "Control+A" --includeSnapshot true # Press a key and return a snapshot
|
||||
chrome-devtools type_text "hello" # Type text using keyboard into a focused input
|
||||
chrome-devtools type_text "hello" --submitKey "Enter" # Type text and press a submit key
|
||||
chrome-devtools upload_file "id" "file.txt" # Upload a file through a provided element
|
||||
chrome-devtools upload_file "id" "file.txt" --includeSnapshot true # Upload a file and return a snapshot
|
||||
chrome-devtools take_snapshot 1 # Take a text snapshot of the page to get UIDs for elements
|
||||
chrome-devtools click 1 "id" # Clicks on the provided element
|
||||
chrome-devtools click 1 "id" --dblClick true --includeSnapshot true # Double clicks and returns a snapshot
|
||||
chrome-devtools drag 1 "src" "dst" # Drag an element onto another element
|
||||
chrome-devtools drag 1 "src" "dst" --includeSnapshot true # Drag an element and return a snapshot
|
||||
chrome-devtools fill 1 "id" "text" # Type text into an input, textarea, or select an option
|
||||
chrome-devtools fill 1 "id" "text" --includeSnapshot true # Fill an element and return a snapshot
|
||||
chrome-devtools handle_dialog 1 accept # Handle a browser dialog (accept/dismiss)
|
||||
chrome-devtools handle_dialog 1 dismiss --promptText "hi" # Dismiss a dialog with prompt text
|
||||
chrome-devtools hover 1 "id" # Hover over the provided element
|
||||
chrome-devtools hover 1 "id" --includeSnapshot true # Hover over an element and return a snapshot
|
||||
chrome-devtools press_key 1 "Enter" # Press a key or key combination ("Control+A", "Escape")
|
||||
chrome-devtools press_key 1 "Control+A" --includeSnapshot true # Press a key and return a snapshot
|
||||
chrome-devtools type_text 1 "hello" # Type text using keyboard into a focused input
|
||||
chrome-devtools type_text 1 "hello" --submitKey "Enter" # Type text and press a submit key
|
||||
chrome-devtools upload_file 1 "id" "file.txt" # Upload a file through a provided element
|
||||
chrome-devtools upload_file 1 "id" "file.txt" --includeSnapshot true # Upload a file and return a snapshot
|
||||
```
|
||||
|
||||
## Navigation
|
||||
@@ -68,11 +68,11 @@ chrome-devtools upload_file "id" "file.txt" --includeSnapshot true # Upload a fi
|
||||
```bash
|
||||
chrome-devtools close_page 1 # Closes the page by its index
|
||||
chrome-devtools list_pages # Get a list of pages open in the browser
|
||||
chrome-devtools navigate_page --url "https://example.com" # Navigates the currently selected page to a URL
|
||||
chrome-devtools navigate_page --type "reload" --ignoreCache true # Reload page ignoring cache
|
||||
chrome-devtools navigate_page --url "https://example.com" --timeout 5000 # Navigate with a timeout
|
||||
chrome-devtools navigate_page --handleBeforeUnload "accept" # Handle before unload dialog
|
||||
chrome-devtools navigate_page --type "back" --initScript "foo()" # Navigate back and run an init script
|
||||
chrome-devtools navigate_page 1 --url "https://example.com" # Navigates the currently selected page to a URL
|
||||
chrome-devtools navigate_page 1 --type "reload" --ignoreCache true # Reload page ignoring cache
|
||||
chrome-devtools navigate_page 1 --url "https://example.com" --timeout 5000 # Navigate with a timeout
|
||||
chrome-devtools navigate_page 1 --handleBeforeUnload "accept" # Handle before unload dialog
|
||||
chrome-devtools navigate_page 1 --type "back" --initScript "foo()" # Navigate back and run an init script
|
||||
chrome-devtools new_page "https://example.com" # Creates a new page
|
||||
chrome-devtools new_page "https://example.com" --background true --timeout 5000 # Create new page in background
|
||||
chrome-devtools new_page "https://example.com" --isolatedContext "ctx" # Create new page with isolated context
|
||||
@@ -83,27 +83,27 @@ chrome-devtools select_page 1 --bringToFront true # Select a page and bring it t
|
||||
## Emulation
|
||||
|
||||
```bash
|
||||
chrome-devtools emulate --networkConditions "Offline" # Emulate network conditions
|
||||
chrome-devtools emulate --cpuThrottlingRate 4 --geolocation "0x0" # Emulate CPU throttling and geolocation
|
||||
chrome-devtools emulate --colorScheme "dark" --viewport "1920x1080" # Emulate color scheme and viewport
|
||||
chrome-devtools emulate --userAgent "Mozilla/5.0..." # Emulate user agent
|
||||
chrome-devtools resize_page 1920 1080 # Resizes the selected page's window
|
||||
chrome-devtools emulate 1 --networkConditions "Offline" # Emulate network conditions
|
||||
chrome-devtools emulate 1 --cpuThrottlingRate 4 --geolocation "0x0" # Emulate CPU throttling and geolocation
|
||||
chrome-devtools emulate 1 --colorScheme "dark" --viewport "1920x1080" # Emulate color scheme and viewport
|
||||
chrome-devtools emulate 1 --userAgent "Mozilla/5.0..." # Emulate user agent
|
||||
chrome-devtools resize_page 1 1920 1080 # Resizes the selected page's window
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
```bash
|
||||
chrome-devtools performance_analyze_insight "1" "LCPBreakdown" # Get more details on a specific Performance Insight
|
||||
chrome-devtools performance_start_trace true false # Starts a performance trace recording (reload, autoStop)
|
||||
chrome-devtools performance_start_trace true true --filePath "t.json.gz" # Start trace and save to a file
|
||||
chrome-devtools performance_stop_trace # Stops the active performance trace
|
||||
chrome-devtools performance_stop_trace --filePath "t.json.gz" # Stop trace and save to a file
|
||||
chrome-devtools performance_analyze_insight 1 "1" "LCPBreakdown" # Get more details on a specific Performance Insight (pageId, insightSetId, insightName)
|
||||
chrome-devtools performance_start_trace 1 --reload true --autoStop false # Starts a performance trace recording (reload, autoStop)
|
||||
chrome-devtools performance_start_trace 1 --reload true --autoStop true --filePath "t.json.gz" # Start trace and save to a file
|
||||
chrome-devtools performance_stop_trace 1 # Stops the active performance trace
|
||||
chrome-devtools performance_stop_trace 1 --filePath "t.json.gz" # Stop trace and save to a file
|
||||
```
|
||||
|
||||
## Memory
|
||||
|
||||
```bash
|
||||
chrome-devtools take_heapsnapshot "./snap.heapsnapshot" # Capture a memory heap snapshot
|
||||
chrome-devtools take_heapsnapshot 1 "./snap.heapsnapshot" # Capture a memory heap snapshot
|
||||
```
|
||||
|
||||
### Memory Debugging (requires `--memoryDebugging=true`)
|
||||
@@ -125,33 +125,33 @@ chrome-devtools close_heapsnapshot "./snap.heapsnapshot" # Free memory from load
|
||||
## Network
|
||||
|
||||
```bash
|
||||
chrome-devtools get_network_request # Get the currently selected network request
|
||||
chrome-devtools get_network_request --reqid 1 --requestFilePath "req.md" # Get request by id and save to file
|
||||
chrome-devtools get_network_request --responseFilePath "res.md" # Save response body to file
|
||||
chrome-devtools list_network_requests # List all network requests
|
||||
chrome-devtools list_network_requests --pageSize 50 --pageIdx 0 # List network requests with pagination
|
||||
chrome-devtools list_network_requests --resourceTypes Fetch # Filter requests by resource type
|
||||
chrome-devtools list_network_requests --includePreservedRequests true # Include preserved requests
|
||||
chrome-devtools get_network_request 1 # Get the currently selected network request for page 1
|
||||
chrome-devtools get_network_request 1 --reqid 1 --requestFilePath "req.md" # Get request by id and save to file
|
||||
chrome-devtools get_network_request 1 --responseFilePath "res.md" # Save response body to file
|
||||
chrome-devtools list_network_requests 1 # List all network requests for page 1
|
||||
chrome-devtools list_network_requests 1 --pageSize 50 --pageIdx 0 # List network requests with pagination
|
||||
chrome-devtools list_network_requests 1 --resourceTypes Fetch # Filter requests by resource type
|
||||
chrome-devtools list_network_requests 1 --includePreservedRequests true # Include preserved requests
|
||||
```
|
||||
|
||||
## Debugging & Inspection
|
||||
|
||||
```bash
|
||||
chrome-devtools evaluate_script "() => document.title" # Evaluate a JavaScript function on the page
|
||||
chrome-devtools evaluate_script "(a) => a.innerText" --args 1_4 # Evaluate JS with UID arguments
|
||||
chrome-devtools get_console_message 1 # Gets a console message by its ID
|
||||
chrome-devtools lighthouse_audit --mode "navigation" # Run Lighthouse audit for navigation
|
||||
chrome-devtools lighthouse_audit --mode "snapshot" --device "mobile" # Run Lighthouse audit for a snapshot on mobile
|
||||
chrome-devtools lighthouse_audit --outputDirPath ./out # Run Lighthouse audit and save reports
|
||||
chrome-devtools list_console_messages # List all console messages
|
||||
chrome-devtools list_console_messages --pageSize 20 --pageIdx 1 # List console messages with pagination
|
||||
chrome-devtools list_console_messages --types error --types info # Filter console messages by type
|
||||
chrome-devtools list_console_messages --includePreservedMessages true # Include preserved messages
|
||||
chrome-devtools take_screenshot # Take a screenshot of the page viewport
|
||||
chrome-devtools take_screenshot --fullPage true --format "jpeg" --quality 80 # Take a full page screenshot as JPEG with quality
|
||||
chrome-devtools take_screenshot --uid "id" --filePath "s.png" # Take a screenshot of an element
|
||||
chrome-devtools take_snapshot # Take a text snapshot of the page from the a11y tree
|
||||
chrome-devtools take_snapshot --verbose true --filePath "s.txt" # Take a verbose snapshot and save to file
|
||||
chrome-devtools evaluate_script "() => document.title" --pageId 1 # Evaluate a JavaScript function on page 1
|
||||
chrome-devtools evaluate_script "(a) => a.innerText" --pageId 1 --args 1_4 # Evaluate JS with UID arguments on page 1
|
||||
chrome-devtools get_console_message 1 1 # Gets a console message by its ID
|
||||
chrome-devtools lighthouse_audit 1 --mode "navigation" # Run Lighthouse audit for navigation
|
||||
chrome-devtools lighthouse_audit 1 --mode "snapshot" --device "mobile" # Run Lighthouse audit for a snapshot on mobile
|
||||
chrome-devtools lighthouse_audit 1 --outputDirPath ./out # Run Lighthouse audit and save reports
|
||||
chrome-devtools list_console_messages 1 # List all console messages
|
||||
chrome-devtools list_console_messages 1 --pageSize 20 --pageIdx 1 # List console messages with pagination
|
||||
chrome-devtools list_console_messages 1 --types error --types info # Filter console messages by type
|
||||
chrome-devtools list_console_messages 1 --includePreservedMessages true # Include preserved messages
|
||||
chrome-devtools take_screenshot 1 # Take a screenshot of the page viewport
|
||||
chrome-devtools take_screenshot 1 --fullPage true --format "jpeg" --quality 80 # Take a full page screenshot as JPEG with quality
|
||||
chrome-devtools take_screenshot 1 --uid "id" --filePath "s.png" # Take a screenshot of an element
|
||||
chrome-devtools take_snapshot 1 # Take a text snapshot of the page from the a11y tree
|
||||
chrome-devtools take_snapshot 1 --verbose true --filePath "s.txt" # Take a verbose snapshot and save to file
|
||||
```
|
||||
|
||||
## Extensions
|
||||
@@ -178,13 +178,13 @@ chrome-devtools uninstall_pwa "https://example.com/" # Uninstall PWA and close w
|
||||
Experimental tools are disabled by default. Enable them with the corresponding flag during `start`.
|
||||
|
||||
```bash
|
||||
chrome-devtools click_at 100 200 # Clicks at the provided coordinates (requires --experimentalVision=true)
|
||||
chrome-devtools screencast_start --filePath "screen.mp4" # Starts a screencast recording (requires --experimentalScreencast=true and ffmpeg)
|
||||
chrome-devtools screencast_stop # Stops the active screencast
|
||||
chrome-devtools list_webmcp_tools # List all WebMCP tools (requires --categoryExperimentalWebmcp=true)
|
||||
chrome-devtools execute_webmcp_tool "tool_name" '{"arg":"val"}' # Execute a WebMCP tool (requires --categoryExperimentalWebmcp=true)
|
||||
chrome-devtools list_3p_developer_tools # List third-party developer tools (requires --categoryExperimentalThirdParty=true)
|
||||
chrome-devtools execute_3p_developer_tool "tool_name" '{"arg":"val"}' # Execute third-party developer tool (requires --categoryExperimentalThirdParty=true)
|
||||
chrome-devtools click_at 1 100 200 # Clicks at the provided coordinates on page 1 (requires --experimentalVision=true)
|
||||
chrome-devtools screencast_start 1 --filePath "screen.mp4" # Starts a screencast recording on page 1 (requires --experimentalScreencast=true and ffmpeg)
|
||||
chrome-devtools screencast_stop 1 # Stops the active screencast on page 1
|
||||
chrome-devtools list_webmcp_tools 1 # List all WebMCP tools on page 1 (requires --categoryExperimentalWebmcp=true)
|
||||
chrome-devtools execute_webmcp_tool 1 "tool_name" --input '{"arg":"val"}' # Execute a WebMCP tool on page 1 (requires --categoryExperimentalWebmcp=true)
|
||||
chrome-devtools list_3p_developer_tools 1 # List third-party developer tools on page 1 (requires --categoryExperimentalThirdParty=true)
|
||||
chrome-devtools execute_3p_developer_tool 1 "tool_name" --params '{"arg":"val"}' # Execute third-party developer tool on page 1 (requires --categoryExperimentalThirdParty=true)
|
||||
```
|
||||
|
||||
## Service Management
|
||||
|
||||
@@ -11,7 +11,8 @@ Addional tooling can be enabled by providing the following flags:
|
||||
- For extension tooling, use the `--categoryExtensions` flag.
|
||||
- For memory tooling, use the `--memoryDebugging` flag.
|
||||
|
||||
**Page selection**: Tools operate on the currently selected page. Use `list_pages` to see available pages, then `select_page` to switch context.
|
||||
**Page targeting**: Page-scoped tools require a `pageId` parameter to target a specific page. Use `list_pages` to see available pages and their IDs (e.g. `pageId: 1`), or use the ID returned when creating a page with `new_page`.
|
||||
Note: For `evaluate_script`, `pageId` is required when targeting pages. However, when `--categoryExtensions` is enabled, `pageId` is optional so you can pass `serviceWorkerId` instead to evaluate inside an extension background service worker.
|
||||
**Element interaction**: Use `take_snapshot` to get page structure with element `uid`s. Each element has a unique `uid` for interaction. If an element isn't found, take a fresh snapshot - the element may have been removed or the page changed.
|
||||
|
||||
## Workflow Patterns
|
||||
@@ -20,8 +21,8 @@ Addional tooling can be enabled by providing the following flags:
|
||||
|
||||
1. Navigate: `navigate_page` or `new_page`
|
||||
2. Wait: `wait_for` to ensure content is loaded if you know what you look for.
|
||||
3. Snapshot: `take_snapshot` to understand page structure
|
||||
4. Interact: Use element `uid`s from snapshot for `click`, `fill`, etc.
|
||||
3. Snapshot: `take_snapshot` with `pageId` to understand page structure
|
||||
4. Interact: Use element `uid`s from snapshot for `click`, `fill`, etc., passing the corresponding `pageId`.
|
||||
|
||||
### Efficient data retrieval
|
||||
|
||||
@@ -59,7 +60,7 @@ You can send multiple tool calls in parallel, but maintain correct order: naviga
|
||||
1. **Install**: Use `install_extension` with the path to the unpacked extension.
|
||||
2. **Identify**: Get the extension ID from the response or by calling `list_extensions`.
|
||||
3. **Trigger Action**: Use `trigger_extension_action` to open the popup or side panel if applicable.
|
||||
4. **Verify Service Worker**: Use `evaluate_script` with `serviceWorkerId` to check extension state or trigger background actions.
|
||||
4. **Verify Service Worker**: Use `evaluate_script` with `serviceWorkerId` (omitting `pageId` and `args`) to check extension state or trigger background actions. When evaluating in a page, pass `pageId` (omitting `serviceWorkerId`).
|
||||
5. **Verify Page Behavior**: Navigate to a page where the extension operates and use `take_snapshot` to check if content scripts injected elements or modified the page correctly.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -36,8 +36,8 @@ Follow these steps in order. Each step builds on the previous one.
|
||||
|
||||
Navigate to the page, then record a trace with reload to capture the full page load including LCP:
|
||||
|
||||
1. `navigate_page` to the target URL.
|
||||
2. `performance_start_trace` with `reload: true` and `autoStop: true`.
|
||||
1. `navigate_page` with `pageId` to the target URL.
|
||||
2. `performance_start_trace` with `pageId`, `reload: true` and `autoStop: true`.
|
||||
|
||||
The trace results will include LCP timing and available insight sets. Note the insight set IDs from the output — you'll need them in the next step.
|
||||
|
||||
@@ -50,11 +50,11 @@ Use `performance_analyze_insight` to drill into LCP-specific insights. Look for
|
||||
- **RenderBlocking** — Resources blocking the LCP element from rendering.
|
||||
- **LCPDiscovery** — Whether the LCP resource was discoverable early.
|
||||
|
||||
Call `performance_analyze_insight` with the insight set ID and the insight name from the trace results.
|
||||
Call `performance_analyze_insight` with `pageId`, the insight set ID, and the insight name from the trace results.
|
||||
|
||||
### Step 3: Identify the LCP Element
|
||||
|
||||
Use `evaluate_script` with the **"Identify LCP Element" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to reveal the LCP element's tag, resource URL, and raw timing data.
|
||||
Use `evaluate_script` (with `pageId`) and the **"Identify LCP Element" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to reveal the LCP element's tag, resource URL, and raw timing data.
|
||||
|
||||
The `url` field tells you what resource to look for in the network waterfall. If `url` is empty, the LCP element is text-based (no resource to load).
|
||||
|
||||
@@ -62,8 +62,8 @@ The `url` field tells you what resource to look for in the network waterfall. If
|
||||
|
||||
Use `list_network_requests` to see when the LCP resource loaded relative to other resources:
|
||||
|
||||
- Call `list_network_requests` filtered by `resourceTypes: ["Image", "Font"]` (adjust based on Step 3).
|
||||
- Then use `get_network_request` with the LCP resource's request ID for full details.
|
||||
- Call `list_network_requests` with `pageId` filtered by `resourceTypes: ["Image", "Font"]` (adjust based on Step 3).
|
||||
- Then use `get_network_request` with `pageId` and the LCP resource's request ID for full details.
|
||||
|
||||
**Key Checks:**
|
||||
|
||||
@@ -72,7 +72,7 @@ Use `list_network_requests` to see when the LCP resource loaded relative to othe
|
||||
|
||||
### Step 5: Inspect HTML for Common Issues
|
||||
|
||||
Use `evaluate_script` with the **"Audit Common Issues" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to check for lazy-loaded images in the viewport, missing fetchpriority, and render-blocking scripts.
|
||||
Use `evaluate_script` (with `pageId`) and the **"Audit Common Issues" snippet** found in [references/lcp-snippets.md](references/lcp-snippets.md) to check for lazy-loaded images in the viewport, missing fetchpriority, and render-blocking scripts.
|
||||
|
||||
## Optimization Strategies
|
||||
|
||||
@@ -115,7 +115,7 @@ The HTML document itself takes too long to arrive.
|
||||
|
||||
## Verifying Fixes & Emulation
|
||||
|
||||
- **Verification**: Re-run the trace (`performance_start_trace` with `reload: true`) and compare the new subpart breakdown. The bottleneck should shrink.
|
||||
- **Verification**: Re-run the trace (`performance_start_trace` with `pageId` and `reload: true`) and compare the new subpart breakdown. The bottleneck should shrink.
|
||||
- **Emulation**: Lab measurements differ from real-world experience. Use `emulate` to test under constraints:
|
||||
- `emulate` with `networkConditions: "Fast 3G"` and `cpuThrottlingRate: 4`.
|
||||
- `emulate` with `pageId`, `networkConditions: "Fast 3G"` and `cpuThrottlingRate: 4`.
|
||||
- This surfaces issues visible only on slower connections/devices.
|
||||
|
||||
@@ -20,10 +20,10 @@ This skill provides expert guidance and workflows for finding, diagnosing, and f
|
||||
|
||||
When investigating a frontend web application memory leak, utilize the `chrome-devtools-mcp` tools to interact with the application and take snapshots.
|
||||
|
||||
- Use tools like `click`, `navigate_page`, `fill`, etc., to manipulate the page into the desired state.
|
||||
- Use page-scoped tools like `click`, `navigate_page`, `fill`, etc. (specifying `pageId`) to manipulate the page into the desired state.
|
||||
- Revert the page back to the original state after interactions to see if memory is released.
|
||||
- Repeat the same user interactions 10 times to amplify the leak.
|
||||
- Use `take_heapsnapshot` to save `.heapsnapshot` files to disk at baseline, target (after actions), and final (after reverting actions) states.
|
||||
- Use `take_heapsnapshot` (with `pageId`) to save `.heapsnapshot` files to disk at baseline, target (after actions), and final (after reverting actions) states.
|
||||
|
||||
### 2. Comparing Snapshots
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ Identify other error messages from the failed tool call or the MCP initializatio
|
||||
|
||||
- `Target closed`
|
||||
- "Tool not found" (check if they are using `--slim` which only enables navigation and screenshot tools).
|
||||
- Missing `pageId`: Page-scoped tools require a `pageId` argument. Call `list_pages` to find active page IDs.
|
||||
- `ProtocolError: Network.enable timed out` or `The socket connection was closed unexpectedly`
|
||||
- `Error [ERR_MODULE_NOT_FOUND]: Cannot find module`
|
||||
- Any sandboxing or host validation errors.
|
||||
|
||||
+2
-2
@@ -242,7 +242,7 @@ export class ToolHandler {
|
||||
this.inputSchema =
|
||||
'pageScoped' in tool &&
|
||||
tool.pageScoped &&
|
||||
serverArgs.experimentalPageIdRouting &&
|
||||
serverArgs.pageIdRouting &&
|
||||
!serverArgs.slim
|
||||
? {...pageIdSchema, ...tool.schema}
|
||||
: tool.schema;
|
||||
@@ -311,7 +311,7 @@ export class ToolHandler {
|
||||
const pageId =
|
||||
typeof params.pageId === 'number' ? params.pageId : undefined;
|
||||
page =
|
||||
this.serverArgs.experimentalPageIdRouting &&
|
||||
this.serverArgs.pageIdRouting &&
|
||||
pageId !== undefined &&
|
||||
!this.serverArgs.slim
|
||||
? context.getPageById(pageId)
|
||||
|
||||
@@ -60,7 +60,6 @@ function getCliOptions() {
|
||||
// Change the defaults for the CLI.
|
||||
delete options.experimentalStructuredContent;
|
||||
delete options.experimentalInteropTools;
|
||||
delete options.experimentalPageIdRouting;
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -103,13 +102,11 @@ const y = yargs(hideBin(process.argv))
|
||||
'1. Required parameters MUST be passed as positional arguments (without flags).',
|
||||
);
|
||||
console.error(
|
||||
' - INCORRECT: chrome-devtools evaluate_script --expression "() => document.title"',
|
||||
' - INCORRECT: chrome-devtools click --pageId 1 --uid "1_2"',
|
||||
);
|
||||
console.error(' - CORRECT: chrome-devtools click 1 "1_2"');
|
||||
console.error(
|
||||
' - CORRECT: chrome-devtools evaluate_script "() => document.title"',
|
||||
);
|
||||
console.error(
|
||||
'2. Optional parameters are passed as double-dash options/flags (e.g. --pageId 1).',
|
||||
'2. Optional parameters are passed as double-dash options/flags (e.g. --dblClick true).',
|
||||
);
|
||||
console.error(
|
||||
'3. Make sure to escape quotes properly for your shell environment.',
|
||||
|
||||
+200
-16
@@ -31,6 +31,12 @@ export const commands: Commands = {
|
||||
description: 'Clicks on the provided element',
|
||||
category: 'Input automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
uid: {
|
||||
name: 'uid',
|
||||
type: 'string',
|
||||
@@ -58,6 +64,12 @@ export const commands: Commands = {
|
||||
'Clicks at the provided coordinates (requires flag: --experimentalVision=true)',
|
||||
category: 'Input automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
x: {
|
||||
name: 'x',
|
||||
type: 'number',
|
||||
@@ -144,6 +156,12 @@ export const commands: Commands = {
|
||||
description: 'Drag an element onto another element',
|
||||
category: 'Input automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
from_uid: {
|
||||
name: 'from_uid',
|
||||
type: 'string',
|
||||
@@ -166,9 +184,15 @@ export const commands: Commands = {
|
||||
},
|
||||
},
|
||||
emulate: {
|
||||
description: 'Emulates various features on the selected page.',
|
||||
description: 'Emulates various features on the target page.',
|
||||
category: 'Emulation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
networkConditions: {
|
||||
name: 'networkConditions',
|
||||
type: 'string',
|
||||
@@ -223,14 +247,21 @@ export const commands: Commands = {
|
||||
},
|
||||
evaluate_script: {
|
||||
description:
|
||||
'Evaluate a JavaScript function inside the currently selected page or service worker. Returns the response as JSON, so returned values have to be JSON-serializable.',
|
||||
'Evaluate a JavaScript function inside the target page or service worker. Returns the response as JSON, so returned values have to be JSON-serializable.',
|
||||
category: 'Debugging',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description:
|
||||
'Targets a specific page by ID. Required when not evaluating in a service worker.',
|
||||
required: false,
|
||||
},
|
||||
function: {
|
||||
name: 'function',
|
||||
type: 'string',
|
||||
description:
|
||||
'A JavaScript function declaration to be executed by the tool in the currently selected page.\nExample without arguments: `() => document.title` or `async () => await fetch("example.com")`.\nExample with arguments: `(el) => el.innerText`\n',
|
||||
'A JavaScript function declaration to be executed by the tool in the target page.\nExample without arguments: `() => document.title` or `async () => await fetch("example.com")`.\nExample with arguments: `(el) => el.innerText`\n',
|
||||
required: true,
|
||||
},
|
||||
args: {
|
||||
@@ -274,6 +305,12 @@ export const commands: Commands = {
|
||||
'Executes a tool exposed by the page. (requires flag: --categoryExperimentalThirdParty=true)',
|
||||
category: 'Third-party',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
toolName: {
|
||||
name: 'toolName',
|
||||
type: 'string',
|
||||
@@ -293,6 +330,12 @@ export const commands: Commands = {
|
||||
'Executes a WebMCP tool exposed by the page. (requires flag: --categoryExperimentalWebmcp=true)',
|
||||
category: 'WebMCP',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
toolName: {
|
||||
name: 'toolName',
|
||||
type: 'string',
|
||||
@@ -313,6 +356,12 @@ export const commands: Commands = {
|
||||
'Type text into an input, text area or select an option from a <select> element.',
|
||||
category: 'Input automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
uid: {
|
||||
name: 'uid',
|
||||
type: 'string',
|
||||
@@ -341,6 +390,12 @@ export const commands: Commands = {
|
||||
'Gets a console message by its ID. You can get all messages by calling list_console_messages.',
|
||||
category: 'Debugging',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
msgid: {
|
||||
name: 'msgid',
|
||||
type: 'number',
|
||||
@@ -651,6 +706,12 @@ export const commands: Commands = {
|
||||
'Gets a network request by an optional reqid, if omitted returns the currently selected request in the DevTools Network panel.',
|
||||
category: 'Network',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
reqid: {
|
||||
name: 'reqid',
|
||||
type: 'number',
|
||||
@@ -693,6 +754,12 @@ export const commands: Commands = {
|
||||
'If a browser dialog was opened, use this command to handle it',
|
||||
category: 'Input automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
action: {
|
||||
name: 'action',
|
||||
type: 'string',
|
||||
@@ -712,6 +779,12 @@ export const commands: Commands = {
|
||||
description: 'Hover over the provided element',
|
||||
category: 'Input automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
uid: {
|
||||
name: 'uid',
|
||||
type: 'string',
|
||||
@@ -796,6 +869,12 @@ export const commands: Commands = {
|
||||
'Get Lighthouse score and reports for accessibility, SEO, best practices, and agentic browsing. This excludes performance. For performance audits, run performance_start_trace',
|
||||
category: 'Debugging',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
mode: {
|
||||
name: 'mode',
|
||||
type: 'string',
|
||||
@@ -825,13 +904,26 @@ export const commands: Commands = {
|
||||
description:
|
||||
"Lists all third-party developer tools the page exposes for providing runtime information.\nThird-party developer tools can be called via the 'execute_3p_developer_tool()' MCP tool.\nAlternatively, third-party developer tools can be executed by calling 'evaluate_script' and adding the\nfollowing command to the script:\n`window.__dtmcp.executeTool(toolName, params)`\nThis might be helpful when the third-party developer tools return non-serializable values or when composing\nthird-party developer tools with additional functionality. (requires flag: --categoryExperimentalThirdParty=true)",
|
||||
category: 'Third-party',
|
||||
args: {},
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
list_console_messages: {
|
||||
description:
|
||||
'List all console messages for the currently selected page since the last navigation. This includes console messages originating from extensions content scripts.',
|
||||
'List all console messages for the target page since the last navigation. This includes console messages originating from extensions content scripts.',
|
||||
category: 'Debugging',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
pageSize: {
|
||||
name: 'pageSize',
|
||||
type: 'integer',
|
||||
@@ -886,9 +978,15 @@ export const commands: Commands = {
|
||||
},
|
||||
list_network_requests: {
|
||||
description:
|
||||
'Lists the most recent requests for the currently selected page since the last navigation.',
|
||||
'Lists the most recent requests for the target page since the last navigation.',
|
||||
category: 'Network',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
pageSize: {
|
||||
name: 'pageSize',
|
||||
type: 'integer',
|
||||
@@ -930,13 +1028,26 @@ export const commands: Commands = {
|
||||
description:
|
||||
'Lists all WebMCP tools the page exposes. (requires flag: --categoryExperimentalWebmcp=true)',
|
||||
category: 'WebMCP',
|
||||
args: {},
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
navigate_page: {
|
||||
description:
|
||||
'Go to a URL, or back, forward, or reload. Use project URL if not specified otherwise.',
|
||||
category: 'Navigation automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
name: 'type',
|
||||
type: 'string',
|
||||
@@ -1020,6 +1131,12 @@ export const commands: Commands = {
|
||||
'Provides more detailed information on a specific Performance Insight of an insight set that was highlighted in the results of a trace recording.',
|
||||
category: 'Performance',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
insightSetId: {
|
||||
name: 'insightSetId',
|
||||
type: 'string',
|
||||
@@ -1038,14 +1155,20 @@ export const commands: Commands = {
|
||||
},
|
||||
performance_start_trace: {
|
||||
description:
|
||||
'Start a performance trace on the selected webpage. Use to find frontend performance issues, Core Web Vitals (LCP, INP, CLS), and improve page load speed.',
|
||||
'Start a performance trace on the target webpage. Use to find frontend performance issues, Core Web Vitals (LCP, INP, CLS), and improve page load speed.',
|
||||
category: 'Performance',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
reload: {
|
||||
name: 'reload',
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Determines if, once tracing has started, the current selected page should be automatically reloaded. Navigate the page to the right URL using the navigate_page tool BEFORE starting the trace if reload or autoStop is set to true.',
|
||||
'Determines if, once tracing has started, the target page should be automatically reloaded. Navigate the page to the right URL using the navigate_page tool BEFORE starting the trace if reload or autoStop is set to true.',
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
@@ -1068,9 +1191,15 @@ export const commands: Commands = {
|
||||
},
|
||||
performance_stop_trace: {
|
||||
description:
|
||||
'Stop the active performance trace recording on the selected webpage.',
|
||||
'Stop the active performance trace recording on the target webpage.',
|
||||
category: 'Performance',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
filePath: {
|
||||
name: 'filePath',
|
||||
type: 'string',
|
||||
@@ -1085,6 +1214,12 @@ export const commands: Commands = {
|
||||
'Press a key or key combination. Use this when other input methods like fill() cannot be used (e.g., keyboard shortcuts, navigation keys, or special key combinations).',
|
||||
category: 'Input automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
key: {
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
@@ -1188,9 +1323,15 @@ export const commands: Commands = {
|
||||
},
|
||||
resize_page: {
|
||||
description:
|
||||
"Resizes the selected page's window so that the page has specified dimension",
|
||||
"Resizes the page's window so that the page has specified dimension",
|
||||
category: 'Emulation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
width: {
|
||||
name: 'width',
|
||||
type: 'number',
|
||||
@@ -1207,9 +1348,15 @@ export const commands: Commands = {
|
||||
},
|
||||
screencast_start: {
|
||||
description:
|
||||
'Starts recording a screencast (video) of the selected page in specified format. (requires flag: --experimentalScreencast=true)',
|
||||
'Starts recording a screencast (video) of the target page in specified format. (requires flag: --experimentalScreencast=true)',
|
||||
category: 'Debugging',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
filePath: {
|
||||
name: 'filePath',
|
||||
type: 'string',
|
||||
@@ -1221,9 +1368,16 @@ export const commands: Commands = {
|
||||
},
|
||||
screencast_stop: {
|
||||
description:
|
||||
'Stops the active screencast recording on the selected page. (requires flag: --experimentalScreencast=true)',
|
||||
'Stops the active screencast recording on the target page. (requires flag: --experimentalScreencast=true)',
|
||||
category: 'Debugging',
|
||||
args: {},
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
select_page: {
|
||||
description: 'Select a page as a context for future tool calls.',
|
||||
@@ -1246,9 +1400,15 @@ export const commands: Commands = {
|
||||
},
|
||||
take_heapsnapshot: {
|
||||
description:
|
||||
'Capture a heap snapshot of the currently selected page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.',
|
||||
'Capture a heap snapshot of the target page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.',
|
||||
category: 'Memory',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
filePath: {
|
||||
name: 'filePath',
|
||||
type: 'string',
|
||||
@@ -1262,6 +1422,12 @@ export const commands: Commands = {
|
||||
description: 'Take a screenshot of the page or element.',
|
||||
category: 'Debugging',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
format: {
|
||||
name: 'format',
|
||||
type: 'string',
|
||||
@@ -1303,9 +1469,15 @@ export const commands: Commands = {
|
||||
},
|
||||
take_snapshot: {
|
||||
description:
|
||||
'Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique\nidentifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected\nin the DevTools Elements panel (if any).',
|
||||
'Take a text snapshot of the target page based on the a11y tree. The snapshot lists page elements along with a unique\nidentifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected\nin the DevTools Elements panel (if any).',
|
||||
category: 'Debugging',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
verbose: {
|
||||
name: 'verbose',
|
||||
type: 'boolean',
|
||||
@@ -1339,6 +1511,12 @@ export const commands: Commands = {
|
||||
description: 'Type text using keyboard into a previously focused input',
|
||||
category: 'Input automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
text: {
|
||||
name: 'text',
|
||||
type: 'string',
|
||||
@@ -1385,6 +1563,12 @@ export const commands: Commands = {
|
||||
description: 'Upload a file through a provided element.',
|
||||
category: 'Input automation',
|
||||
args: {
|
||||
pageId: {
|
||||
name: 'pageId',
|
||||
type: 'number',
|
||||
description: 'Targets a specific page by ID.',
|
||||
required: true,
|
||||
},
|
||||
uid: {
|
||||
name: 'uid',
|
||||
type: 'string',
|
||||
|
||||
@@ -147,10 +147,11 @@ export const mcpOptions = {
|
||||
type: 'boolean',
|
||||
description: `If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.`,
|
||||
},
|
||||
experimentalPageIdRouting: {
|
||||
pageIdRouting: {
|
||||
type: 'boolean',
|
||||
describe:
|
||||
'Whether to expose pageId on page-scoped tools and route requests by page ID (useful for concurrent agent sessions).',
|
||||
'Require pageId on page-scoped tools and route requests by page ID (useful for concurrent agent sessions). Use --no-page-id-routing to disable.',
|
||||
default: true,
|
||||
},
|
||||
experimentalDevtools: {
|
||||
type: 'boolean',
|
||||
|
||||
@@ -242,11 +242,13 @@
|
||||
},
|
||||
{
|
||||
"name": "experimental_page_id_routing",
|
||||
"flagType": "boolean"
|
||||
"flagType": "boolean",
|
||||
"isDeprecated": true
|
||||
},
|
||||
{
|
||||
"name": "experimental_page_id_routing_present",
|
||||
"flagType": "boolean"
|
||||
"flagType": "boolean",
|
||||
"isDeprecated": true
|
||||
},
|
||||
{
|
||||
"name": "experimental_webmcp",
|
||||
@@ -300,6 +302,14 @@
|
||||
"name": "category_experimental_third_party",
|
||||
"flagType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "page_id_routing_present",
|
||||
"flagType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "page_id_routing",
|
||||
"flagType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "memory_debugging_present",
|
||||
"flagType": "boolean"
|
||||
|
||||
@@ -123,6 +123,10 @@
|
||||
{
|
||||
"name": "wait_for_stable_dom",
|
||||
"argType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "page_id",
|
||||
"argType": "number"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -188,7 +192,8 @@
|
||||
"args": [
|
||||
{
|
||||
"name": "page_id",
|
||||
"argType": "number"
|
||||
"argType": "number",
|
||||
"isDeprecated": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -42,7 +42,7 @@ const LIST_CONSOLE_MESSAGES_TOOL_NAME = 'list_console_messages';
|
||||
export const listConsoleMessages = definePageTool(cliArgs => {
|
||||
return {
|
||||
name: LIST_CONSOLE_MESSAGES_TOOL_NAME,
|
||||
description: `List all console messages for the currently selected page since the last navigation.${cliArgs?.categoryExtensions ? ' This includes console messages originating from extensions content scripts.' : ''}`,
|
||||
description: `List all console messages for the target page since the last navigation.${cliArgs?.categoryExtensions ? ' This includes console messages originating from extensions content scripts.' : ''}`,
|
||||
annotations: {
|
||||
category: ToolCategory.DEBUGGING,
|
||||
readOnlyHint: true,
|
||||
|
||||
@@ -47,7 +47,7 @@ const throttlingOptions: [string, ...string[]] = [
|
||||
|
||||
export const emulate = definePageTool({
|
||||
name: 'emulate',
|
||||
description: `Emulates various features on the selected page.`,
|
||||
description: `Emulates various features on the target page.`,
|
||||
annotations: {
|
||||
category: ToolCategory.EMULATION,
|
||||
readOnlyHint: false,
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const HEAP_SNAPSHOT_FILTERS: readonly [string, ...string[]] = [
|
||||
|
||||
export const takeHeapSnapshot = definePageTool({
|
||||
name: 'take_heapsnapshot',
|
||||
description: `Capture a heap snapshot of the currently selected page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.`,
|
||||
description: `Capture a heap snapshot of the target page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.`,
|
||||
annotations: {
|
||||
category: ToolCategory.MEMORY,
|
||||
readOnlyHint: false,
|
||||
|
||||
@@ -34,7 +34,7 @@ const FILTERABLE_RESOURCE_TYPES: readonly [ResourceType, ...ResourceType[]] = [
|
||||
|
||||
export const listNetworkRequests = definePageTool({
|
||||
name: 'list_network_requests',
|
||||
description: `Lists the most recent requests for the currently selected page since the last navigation.`,
|
||||
description: `Lists the most recent requests for the target page since the last navigation.`,
|
||||
annotations: {
|
||||
category: ToolCategory.NETWORK,
|
||||
readOnlyHint: true,
|
||||
|
||||
+4
-10
@@ -296,7 +296,7 @@ export const navigatePage = definePageTool(() => {
|
||||
|
||||
export const resizePage = definePageTool({
|
||||
name: 'resize_page',
|
||||
description: `Resizes the selected page's window so that the page has specified dimension`,
|
||||
description: `Resizes the page's window so that the page has specified dimension`,
|
||||
annotations: {
|
||||
category: ToolCategory.EMULATION,
|
||||
readOnlyHint: false,
|
||||
@@ -396,17 +396,11 @@ export const getTabId = definePageTool({
|
||||
readOnlyHint: true,
|
||||
conditions: ['experimentalInteropTools'],
|
||||
},
|
||||
schema: {
|
||||
pageId: zod
|
||||
.number()
|
||||
.describe(
|
||||
`The ID of the page to get the tab ID for. Call ${listPages().name} to get available pages.`,
|
||||
),
|
||||
},
|
||||
schema: {},
|
||||
blockedByDialog: false,
|
||||
verifyFilesSchema: {},
|
||||
handler: async (request, response, context) => {
|
||||
const page = context.getPageById(request.params.pageId);
|
||||
handler: async (request, response) => {
|
||||
const page = request.page;
|
||||
const tabId = (page.pptrPage as unknown as CdpPage)._tabId;
|
||||
response.setTabId(tabId);
|
||||
response.appendResponseLine(`Tab ID: ${tabId}`);
|
||||
|
||||
@@ -27,7 +27,7 @@ const filePathSchema = zod
|
||||
|
||||
export const startTrace = definePageTool({
|
||||
name: 'performance_start_trace',
|
||||
description: `Start a performance trace on the selected webpage. Use to find frontend performance issues, Core Web Vitals (LCP, INP, CLS), and improve page load speed.`,
|
||||
description: `Start a performance trace on the target webpage. Use to find frontend performance issues, Core Web Vitals (LCP, INP, CLS), and improve page load speed.`,
|
||||
annotations: {
|
||||
category: ToolCategory.PERFORMANCE,
|
||||
readOnlyHint: false,
|
||||
@@ -37,7 +37,7 @@ export const startTrace = definePageTool({
|
||||
.boolean()
|
||||
.default(true)
|
||||
.describe(
|
||||
'Determines if, once tracing has started, the current selected page should be automatically reloaded. Navigate the page to the right URL using the navigate_page tool BEFORE starting the trace if reload or autoStop is set to true.',
|
||||
'Determines if, once tracing has started, the target page should be automatically reloaded. Navigate the page to the right URL using the navigate_page tool BEFORE starting the trace if reload or autoStop is set to true.',
|
||||
),
|
||||
autoStop: zod
|
||||
.boolean()
|
||||
@@ -125,7 +125,7 @@ export const startTrace = definePageTool({
|
||||
export const stopTrace = definePageTool({
|
||||
name: 'performance_stop_trace',
|
||||
description:
|
||||
'Stop the active performance trace recording on the selected webpage.',
|
||||
'Stop the active performance trace recording on the target webpage.',
|
||||
annotations: {
|
||||
category: ToolCategory.PERFORMANCE,
|
||||
readOnlyHint: false,
|
||||
|
||||
@@ -25,7 +25,7 @@ const supportedExtensions: SupportedVideoExtension[] = ['.webm', '.mp4'];
|
||||
|
||||
export const startScreencast = definePageTool(args => ({
|
||||
name: 'screencast_start',
|
||||
description: `Starts recording a screencast (video) of the selected page in specified format.`,
|
||||
description: `Starts recording a screencast (video) of the target page in specified format.`,
|
||||
annotations: {
|
||||
category: ToolCategory.DEBUGGING,
|
||||
readOnlyHint: false,
|
||||
@@ -123,7 +123,7 @@ export const startScreencast = definePageTool(args => ({
|
||||
|
||||
export const stopScreencast = definePageTool({
|
||||
name: 'screencast_stop',
|
||||
description: 'Stops the active screencast recording on the selected page.',
|
||||
description: 'Stops the active screencast recording on the target page.',
|
||||
annotations: {
|
||||
category: ToolCategory.DEBUGGING,
|
||||
readOnlyHint: false,
|
||||
|
||||
+22
-6
@@ -17,15 +17,26 @@ export type Evaluatable = Page | Frame | WebWorker;
|
||||
export const evaluateScript = defineTool(cliArgs => {
|
||||
return {
|
||||
name: 'evaluate_script',
|
||||
description: `Evaluate a JavaScript function inside the currently selected page${cliArgs?.categoryExtensions ? ' or service worker' : ''}. Returns the response as JSON, so returned values have to be JSON-serializable.`,
|
||||
description: `Evaluate a JavaScript function inside the target page${cliArgs?.categoryExtensions ? ' or service worker' : ''}. Returns the response as JSON, so returned values have to be JSON-serializable.`,
|
||||
annotations: {
|
||||
category: ToolCategory.DEBUGGING,
|
||||
readOnlyHint: false,
|
||||
},
|
||||
schema: {
|
||||
...(cliArgs?.experimentalPageIdRouting ? pageIdSchema : {}),
|
||||
...(cliArgs?.pageIdRouting
|
||||
? cliArgs.categoryExtensions
|
||||
? {
|
||||
pageId: zod
|
||||
.number()
|
||||
.optional()
|
||||
.describe(
|
||||
'Targets a specific page by ID. Required when not evaluating in a service worker.',
|
||||
),
|
||||
}
|
||||
: pageIdSchema
|
||||
: {}),
|
||||
function: zod.string().describe(
|
||||
`A JavaScript function declaration to be executed by the tool in the currently selected page.
|
||||
`A JavaScript function declaration to be executed by the tool in the target page.
|
||||
Example without arguments: \`() => document.title\` or \`async () => await fetch("example.com")\`.
|
||||
Example with arguments: \`(el) => el.innerText\`
|
||||
`,
|
||||
@@ -114,9 +125,14 @@ Example with arguments: \`(el) => el.innerText\`
|
||||
return;
|
||||
}
|
||||
|
||||
const mcpPage = cliArgs?.experimentalPageIdRouting
|
||||
? context.getPageById(request.params.pageId)
|
||||
: context.getSelectedMcpPage();
|
||||
if (cliArgs?.categoryExtensions && cliArgs?.pageIdRouting && !pageId) {
|
||||
throw new Error('specify either a pageId or a serviceWorkerId.');
|
||||
}
|
||||
|
||||
const mcpPage =
|
||||
cliArgs?.pageIdRouting && request.params.pageId
|
||||
? context.getPageById(request.params.pageId)
|
||||
: context.getSelectedMcpPage();
|
||||
const page: Page = mcpPage.pptrPage;
|
||||
|
||||
const args: Array<JSHandle<unknown>> = [];
|
||||
|
||||
@@ -11,7 +11,7 @@ import {definePageTool, timeoutSchema} from './ToolDefinition.js';
|
||||
|
||||
export const takeSnapshot = definePageTool({
|
||||
name: 'take_snapshot',
|
||||
description: `Take a text snapshot of the currently selected page based on the a11y tree. The snapshot lists page elements along with a unique
|
||||
description: `Take a text snapshot of the target page based on the a11y tree. The snapshot lists page elements along with a unique
|
||||
identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected
|
||||
in the DevTools Elements panel (if any).`,
|
||||
annotations: {
|
||||
|
||||
@@ -32,7 +32,51 @@ describe('ToolHandler', () => {
|
||||
ClearcutLogger.resetForTesting();
|
||||
});
|
||||
|
||||
it('calls page getter for page scoped tools', async () => {
|
||||
it('calls getPageById for page scoped tools when pageId is provided', async () => {
|
||||
let handlerCalled = false;
|
||||
const tool: DefinedPageTool = {
|
||||
name: 'page_tool',
|
||||
description: 'A page scoped tool',
|
||||
annotations: {
|
||||
category: ToolCategory.INPUT,
|
||||
readOnlyHint: false,
|
||||
},
|
||||
schema: {},
|
||||
blockedByDialog: false,
|
||||
verifyFilesSchema: {},
|
||||
pageScoped: true,
|
||||
handler: async () => {
|
||||
handlerCalled = true;
|
||||
},
|
||||
};
|
||||
|
||||
const mockContext = sinon.createStubInstance(McpContext);
|
||||
const mockProcess = sinon.createStubInstance(ChildProcess);
|
||||
mockContext.browser = getMockBrowser({process: mockProcess});
|
||||
const mockPage = sinon.createStubInstance(McpPage);
|
||||
mockContext.getPageById.returns(mockPage);
|
||||
|
||||
const toolMutex = new Mutex();
|
||||
const serverArgs = parseArguments('1.0.0', ['node', 'script.js'], {
|
||||
CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true',
|
||||
});
|
||||
|
||||
const toolHandler = new ToolHandler(
|
||||
tool,
|
||||
serverArgs,
|
||||
async () => mockContext,
|
||||
toolMutex,
|
||||
);
|
||||
|
||||
assert.strictEqual(toolHandler.shouldRegister, true);
|
||||
await toolHandler.handle({pageId: 1});
|
||||
|
||||
assert.strictEqual(mockContext.getPageById.calledOnce, true);
|
||||
assert.strictEqual(mockContext.getPageById.calledWith(1), true);
|
||||
assert.strictEqual(handlerCalled, true);
|
||||
});
|
||||
|
||||
it('calls getSelectedMcpPage for page scoped tools when pageIdRouting is disabled', async () => {
|
||||
let handlerCalled = false;
|
||||
const tool: DefinedPageTool = {
|
||||
name: 'page_tool',
|
||||
@@ -57,9 +101,11 @@ describe('ToolHandler', () => {
|
||||
mockContext.getSelectedMcpPage.returns(mockPage);
|
||||
|
||||
const toolMutex = new Mutex();
|
||||
const serverArgs = parseArguments('1.0.0', ['node', 'script.js'], {
|
||||
CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true',
|
||||
});
|
||||
const serverArgs = parseArguments(
|
||||
'1.0.0',
|
||||
['node', 'script.js', '--no-page-id-routing'],
|
||||
{CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true'},
|
||||
);
|
||||
|
||||
const toolHandler = new ToolHandler(
|
||||
tool,
|
||||
|
||||
@@ -29,6 +29,7 @@ describe('cli args parsing', () => {
|
||||
allowUnrestrictedPaths: false,
|
||||
memoryDebugging: false,
|
||||
experimentalStructuredContent: false,
|
||||
pageIdRouting: true,
|
||||
};
|
||||
|
||||
it('parses with default args', async () => {
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('chrome-devtools', () => {
|
||||
`start command failed: ${startResult.stderr}`,
|
||||
);
|
||||
|
||||
const result = await runCli(['take_screenshot'], sessionId);
|
||||
const result = await runCli(['take_screenshot', '1'], sessionId);
|
||||
assert.strictEqual(
|
||||
result.status,
|
||||
0,
|
||||
@@ -75,7 +75,7 @@ describe('chrome-devtools', () => {
|
||||
it('fails to invoke list_network_requests when categoryNetwork is disabled', async () => {
|
||||
await runCli(['start', '--categoryNetwork=false'], sessionId);
|
||||
|
||||
const result = await runCli(['list_network_requests'], sessionId);
|
||||
const result = await runCli(['list_network_requests', '1'], sessionId);
|
||||
assert.strictEqual(result.status, 0);
|
||||
|
||||
assert(
|
||||
@@ -93,7 +93,7 @@ describe('chrome-devtools', () => {
|
||||
it('fails to invoke click_at when experimentalVision is disabled (default)', async () => {
|
||||
await runCli(['start'], sessionId);
|
||||
|
||||
const result = await runCli(['click_at', '100', '100'], sessionId);
|
||||
const result = await runCli(['click_at', '1', '100', '100'], sessionId);
|
||||
assert.strictEqual(result.status, 0);
|
||||
assert(
|
||||
result.stdout.includes(
|
||||
@@ -119,7 +119,7 @@ describe('chrome-devtools', () => {
|
||||
);
|
||||
|
||||
const emulateResult = await runCli(
|
||||
['emulate', '--cpuThrottlingRate', '2'],
|
||||
['emulate', '1', '--cpuThrottlingRate', '2'],
|
||||
sessionId,
|
||||
);
|
||||
assert.strictEqual(
|
||||
@@ -128,7 +128,7 @@ describe('chrome-devtools', () => {
|
||||
`emulate command failed: ${emulateResult.stderr}`,
|
||||
);
|
||||
|
||||
const result = await runCli(['performance_start_trace'], sessionId);
|
||||
const result = await runCli(['performance_start_trace', '1'], sessionId);
|
||||
assert.strictEqual(
|
||||
result.status,
|
||||
0,
|
||||
|
||||
+11
-2
@@ -244,6 +244,7 @@ describe('e2e', () => {
|
||||
const result = await client.callTool({
|
||||
name: 'take_screenshot',
|
||||
arguments: {
|
||||
pageId: 1,
|
||||
filePath: path.resolve(os.homedir(), 'test.png'),
|
||||
},
|
||||
});
|
||||
@@ -271,6 +272,7 @@ describe('e2e', () => {
|
||||
const result = await client.callTool({
|
||||
name: 'take_screenshot',
|
||||
arguments: {
|
||||
pageId: 1,
|
||||
filePath: path.join(os.tmpdir(), 'test.png'),
|
||||
},
|
||||
});
|
||||
@@ -349,7 +351,10 @@ describe('e2e', () => {
|
||||
|
||||
const result = await client.callTool({
|
||||
name: 'take_screenshot',
|
||||
arguments: {filePath: path.join(workspace, 'shot.png')},
|
||||
arguments: {
|
||||
pageId: 1,
|
||||
filePath: path.join(workspace, 'shot.png'),
|
||||
},
|
||||
});
|
||||
|
||||
// Asserted before isError so a denial reports the path it rejected
|
||||
@@ -381,7 +386,9 @@ describe('e2e', () => {
|
||||
|
||||
const snapshotResult = await client.callTool({
|
||||
name: 'take_snapshot',
|
||||
arguments: {},
|
||||
arguments: {
|
||||
pageId: 2,
|
||||
},
|
||||
});
|
||||
|
||||
const snapshotText = (snapshotResult.content as TextContent[])[0].text;
|
||||
@@ -392,6 +399,7 @@ describe('e2e', () => {
|
||||
const result = await client.callTool({
|
||||
name: 'click',
|
||||
arguments: {
|
||||
pageId: 2,
|
||||
uid,
|
||||
},
|
||||
});
|
||||
@@ -412,6 +420,7 @@ describe('e2e', () => {
|
||||
const result = await client.callTool({
|
||||
name: 'take_screenshot',
|
||||
arguments: {
|
||||
pageId: 2,
|
||||
// Use os.tmpdir() so validatePath passes on macOS/Windows before
|
||||
// reaching the dialog-blocked check.
|
||||
filePath: path.join(os.tmpdir(), 'test.png'),
|
||||
|
||||
@@ -1338,7 +1338,7 @@ describe('pages', () => {
|
||||
// @ts-expect-error _tabId is internal.
|
||||
page._tabId = 'test-tab-id';
|
||||
await getTabId.handler(
|
||||
{params: {pageId: 1}, page: context.getSelectedMcpPage()},
|
||||
{params: {}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import sinon from 'sinon';
|
||||
|
||||
import type {ParsedArguments} from '../../src/config/mcp-options.js';
|
||||
import {TextSnapshot} from '../../src/TextSnapshot.js';
|
||||
import {zod} from '../../src/third_party/index.js';
|
||||
import {installExtension} from '../../src/tools/extensions.js';
|
||||
import {evaluateScript} from '../../src/tools/script.js';
|
||||
import {WaitForHelper} from '../../src/utils/WaitForHelper.js';
|
||||
@@ -426,7 +427,7 @@ describe('script', () => {
|
||||
params: {
|
||||
function: String(() => 'test'),
|
||||
serviceWorkerId: 'example_service_worker',
|
||||
pageId: '1',
|
||||
pageId: 1,
|
||||
},
|
||||
},
|
||||
response,
|
||||
@@ -469,5 +470,41 @@ describe('script', () => {
|
||||
{categoryExtensions: true},
|
||||
);
|
||||
});
|
||||
|
||||
it('makes pageId optional in schema when categoryExtensions is true and pageIdRouting is true', () => {
|
||||
const tool = evaluateScript({
|
||||
categoryExtensions: true,
|
||||
pageIdRouting: true,
|
||||
} as ParsedArguments);
|
||||
const schema = zod.object(tool.schema);
|
||||
const validSw = schema.safeParse({
|
||||
function: '() => 1',
|
||||
serviceWorkerId: 'sw_1',
|
||||
});
|
||||
assert.strictEqual(validSw.success, true);
|
||||
|
||||
const validPage = schema.safeParse({
|
||||
function: '() => 1',
|
||||
pageId: 1,
|
||||
});
|
||||
assert.strictEqual(validPage.success, true);
|
||||
});
|
||||
|
||||
it('makes pageId required in schema when categoryExtensions is false and pageIdRouting is true', () => {
|
||||
const tool = evaluateScript({
|
||||
pageIdRouting: true,
|
||||
} as ParsedArguments);
|
||||
const schema = zod.object(tool.schema);
|
||||
const resultWithoutPageId = schema.safeParse({
|
||||
function: '() => 1',
|
||||
});
|
||||
assert.strictEqual(resultWithoutPageId.success, false);
|
||||
|
||||
const resultWithPageId = schema.safeParse({
|
||||
function: '() => 1',
|
||||
pageId: 1,
|
||||
});
|
||||
assert.strictEqual(resultWithPageId.success, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user