From 702a9b9008e71ba1023c270ddf5101c8c882216c Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Wed, 20 May 2026 12:46:28 +0200 Subject: [PATCH] feat: make pageId routing always-on by default Rename --experimental-page-id-routing to --page-id-routing and enable it by default. Users can opt out with --no-page-id-routing if token overhead is unacceptable or routing does not work as expected. - Rename serverArgs.experimentalPageIdRouting to pageIdRouting and set default: true; describe text updated to reflect opt-out semantics. - Update ToolHandler.ts schema injection and page resolution to use the new flag name. - Drop the now-redundant delete of pageIdRouting from chrome-devtools-cli start options (was previously removing experimentalPageIdRouting). - Update evaluate_script to use cliArgs.pageIdRouting and guard getPageById with a request.params.pageId check so passing the flag without a pageId no longer throws (mirrors the guard used in ToolHandler.ts). - Improve pageIdSchema description: explain list_pages and selected-page fallback so the field is self-documenting. - Fix generate-docs.ts to inject pageIdSchema for page-scoped tools so tool-reference.md surfaces pageId on every page-scoped tool; pass a slim flag so slim docs stay unchanged. - Update eval scenarios to rely on the new default instead of passing --experimental-page-id-routing; update the example flag in the TestScenario JSDoc accordingly. - Fix pageId type in script.test.ts (string -> number). - Regenerated docs/tool-reference.md, README options section, and src/telemetry/{flag_usage_metrics,tool_call_metrics}.json. - Add pageIdRouting/page-id-routing to the cli.test.ts default args fixture to reflect the new default. --- README.md | 5 +- docs/tool-reference.md | 40 +++- .../page_focus_keyboard_test.ts | 1 - .../eval_scenarios/page_id_routing_test.ts | 1 - scripts/generate-docs.ts | 14 +- src/ToolHandler.ts | 4 +- src/bin/chrome-devtools-cli-options.ts | 189 +++++++++++++++++- src/bin/chrome-devtools-mcp-cli-options.ts | 5 +- src/bin/chrome-devtools.ts | 1 - src/telemetry/flag_usage_metrics.json | 14 +- src/telemetry/tool_call_metrics.json | 4 + src/tools/script.ts | 9 +- tests/cli.test.ts | 2 + tests/tools/script.test.ts | 2 +- 14 files changed, 266 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index b17b72451..73da3190b 100644 --- a/README.md +++ b/README.md @@ -596,9 +596,10 @@ The Chrome DevTools MCP server supports the following configuration option: If enabled, ignores errors relative to self-signed and expired certificates. Use with caution. - **Type:** boolean -- **`--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`** + Expose 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:** `true` - **`--experimentalDevtools`/ `--experimental-devtools`** Whether to enable automation over DevTools targets diff --git a/docs/tool-reference.md b/docs/tool-reference.md index 747193452..fb6feeaa6 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -66,6 +66,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. @@ -79,6 +80,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. @@ -90,6 +92,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. @@ -103,6 +106,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. --- @@ -114,6 +118,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. --- @@ -124,6 +129,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. @@ -136,6 +142,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. --- @@ -146,6 +153,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" @@ -158,6 +166,7 @@ **Parameters:** - **filePath** (string) **(required)**: The local path of the file to upload +- **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. @@ -169,6 +178,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. @@ -202,6 +212,7 @@ **Parameters:** +- **pageId** (number) **(required)**: Targets a specific page by ID. - **handleBeforeUnload** (enum: "accept", "decline") _(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. @@ -241,6 +252,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. @@ -254,6 +266,7 @@ **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. @@ -271,6 +284,7 @@ **Parameters:** - **height** (number) **(required)**: Page height +- **pageId** (number) **(required)**: Targets a specific page by ID. - **width** (number) **(required)**: Page width --- @@ -285,6 +299,7 @@ - **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. --- @@ -294,6 +309,7 @@ **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. @@ -306,6 +322,7 @@ **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). --- @@ -318,6 +335,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. @@ -330,6 +348,7 @@ **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. @@ -369,6 +388,7 @@ so returned values have to be JSON-serializable. **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. --- @@ -378,6 +398,7 @@ so returned values have to be JSON-serializable. **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. @@ -390,6 +411,7 @@ so returned values have to be JSON-serializable. **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. - **pageIdx** (integer) _(optional)_: Page number to return (0-based). When omitted, returns the first page. - **pageSize** (integer) _(optional)_: Maximum number of messages to return. When omitted, returns all messages. @@ -403,6 +425,7 @@ so returned values have to be JSON-serializable. **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. @@ -419,6 +442,7 @@ 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. @@ -430,6 +454,7 @@ in the DevTools Elements panel (if any). **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. --- @@ -438,7 +463,9 @@ in the DevTools Elements panel (if any). **Description:** Stops the active screencast recording on the selected page. (requires flag: --experimentalScreencast=true) -**Parameters:** None +**Parameters:** + +- **pageId** (number) **(required)**: Targets a specific page by ID. --- @@ -451,6 +478,7 @@ in the DevTools Elements panel (if any). **Parameters:** - **filePath** (string) **(required)**: A path to a .heapsnapshot file to save the heapsnapshot to. +- **pageId** (number) **(required)**: Targets a specific page by ID. --- @@ -564,6 +592,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 @@ -579,7 +608,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. --- @@ -593,6 +624,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 @@ -602,6 +634,8 @@ 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. --- diff --git a/scripts/eval_scenarios/page_focus_keyboard_test.ts b/scripts/eval_scenarios/page_focus_keyboard_test.ts index f56c7c629..9812b8686 100644 --- a/scripts/eval_scenarios/page_focus_keyboard_test.ts +++ b/scripts/eval_scenarios/page_focus_keyboard_test.ts @@ -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, - Page 2 at data:text/html,

Other

diff --git a/scripts/eval_scenarios/page_id_routing_test.ts b/scripts/eval_scenarios/page_id_routing_test.ts index a65c9e839..864c6bf63 100644 --- a/scripts/eval_scenarios/page_id_routing_test.ts +++ b/scripts/eval_scenarios/page_id_routing_test.ts @@ -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, - Page B (isolatedContext "contextB") at data:text/html, diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index 77dc37b8c..36b64cdc3 100644 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -16,6 +16,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 = {}; const required: string[] = []; + const toolSchema = { + ...tool.schema, + ...(tool.pageScoped && !slim ? pageIdSchema : {}), + }; for (const [key, schema] of Object.entries( - tool.schema as unknown as Record, + toolSchema as unknown as Record, )) { const info = getZodTypeInfo(schema); properties[key] = info; @@ -536,7 +541,10 @@ async function generateToolDocumentation(): Promise { { 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, diff --git a/src/ToolHandler.ts b/src/ToolHandler.ts index c9bf56d9f..b177e6af3 100644 --- a/src/ToolHandler.ts +++ b/src/ToolHandler.ts @@ -161,7 +161,7 @@ export class ToolHandler { this.inputSchema = 'pageScoped' in tool && tool.pageScoped && - serverArgs.experimentalPageIdRouting && + serverArgs.pageIdRouting && !serverArgs.slim ? {...tool.schema, ...pageIdSchema} : tool.schema; @@ -224,7 +224,7 @@ export class ToolHandler { const pageId = typeof params.pageId === 'number' ? params.pageId : undefined; const page = - this.serverArgs.experimentalPageIdRouting && + this.serverArgs.pageIdRouting && pageId !== undefined && !this.serverArgs.slim ? context.getPageById(pageId) diff --git a/src/bin/chrome-devtools-cli-options.ts b/src/bin/chrome-devtools-cli-options.ts index af92dccd5..86bfbcf0d 100644 --- a/src/bin/chrome-devtools-cli-options.ts +++ b/src/bin/chrome-devtools-cli-options.ts @@ -47,6 +47,12 @@ export const commands: Commands = { 'Whether to include a snapshot in the response. Default is false.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, click_at: { @@ -79,6 +85,12 @@ export const commands: Commands = { 'Whether to include a snapshot in the response. Default is false.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, close_page: { @@ -118,6 +130,12 @@ export const commands: Commands = { 'Whether to include a snapshot in the response. Default is false.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, emulate: { @@ -174,6 +192,12 @@ export const commands: Commands = { '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.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, evaluate_script: { @@ -208,6 +232,12 @@ export const commands: Commands = { 'Handle dialogs while execution. "accept", "dismiss", or string for response of window.prompt. Defaults to accept.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, execute_3p_developer_tool: { @@ -227,6 +257,12 @@ export const commands: Commands = { description: 'The JSON-stringified parameters to pass to the tool', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, execute_webmcp_tool: { @@ -247,6 +283,12 @@ export const commands: Commands = { 'The JSON-stringified parameters to pass to the WebMCP tool', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, fill: { @@ -275,6 +317,12 @@ export const commands: Commands = { 'Whether to include a snapshot in the response. Default is false.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, get_console_message: { @@ -289,6 +337,12 @@ export const commands: Commands = { 'The msgid of a console message on the page from the listed console messages', required: true, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, get_heapsnapshot_class_nodes: { @@ -417,6 +471,12 @@ export const commands: Commands = { 'The absolute or relative path to a .network-response file to save the response body to. If omitted, the body is returned inline.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, handle_dialog: { @@ -437,6 +497,12 @@ export const commands: Commands = { description: 'Optional prompt text to enter into the dialog.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, hover: { @@ -457,6 +523,12 @@ export const commands: Commands = { 'Whether to include a snapshot in the response. Default is false.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, install_extension: { @@ -500,13 +572,26 @@ export const commands: Commands = { description: 'Directory for reports. If omitted, uses temporary files.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, list_3p_developer_tools: { description: "Lists all third-party developer tools the page exposes for providing runtime information.\n Third-party developer tools can be called via the 'execute_3p_developer_tool()' MCP tool.\n Alternatively, third-party developer tools can be executed by calling 'evaluate_script' and adding the\n following command to the script:\n 'window.__dtmcp.executeTool(toolName, params)'\n This might be helpful when the third-party developer tools return non-serializable values or when composing\n third-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: @@ -542,6 +627,12 @@ export const commands: Commands = { required: false, default: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, list_extensions: { @@ -584,6 +675,12 @@ export const commands: Commands = { required: false, default: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, list_pages: { @@ -595,7 +692,14 @@ 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: @@ -644,6 +748,12 @@ export const commands: Commands = { 'Maximum wait time in milliseconds. If set to 0, the default timeout will be used.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, new_page: { @@ -699,6 +809,12 @@ export const commands: Commands = { 'The name of the Insight you want more information on. For example: "DocumentLatency" or "LCPBreakdown"', required: true, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, performance_start_trace: { @@ -729,6 +845,12 @@ export const commands: Commands = { '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).', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, performance_stop_trace: { @@ -743,6 +865,12 @@ export const commands: Commands = { '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).', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, press_key: { @@ -764,6 +892,12 @@ export const commands: Commands = { 'Whether to include a snapshot in the response. Default is false.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, reload_extension: { @@ -796,6 +930,12 @@ export const commands: Commands = { description: 'Page height', required: true, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, screencast_start: { @@ -810,13 +950,26 @@ export const commands: Commands = { 'Output file path (.webm,.mp4 are supported). Uses mkdtemp to generate a unique path if not provided.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, screencast_stop: { description: 'Stops the active screencast recording on the selected 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.', @@ -849,6 +1002,12 @@ export const commands: Commands = { 'A path to a .heapsnapshot file to save the heapsnapshot to.', required: true, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, take_screenshot: { @@ -892,6 +1051,12 @@ export const commands: Commands = { 'The absolute path, or a path relative to the current working directory, to save the screenshot to instead of attaching it to the response.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, take_snapshot: { @@ -913,6 +1078,12 @@ export const commands: Commands = { 'The absolute path, or a path relative to the current working directory, to save the snapshot to instead of attaching it to the response.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, trigger_extension_action: { @@ -945,6 +1116,12 @@ export const commands: Commands = { 'Optional key to press after typing. E.g., "Enter", "Tab", "Escape"', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, uninstall_extension: { @@ -984,6 +1161,12 @@ export const commands: Commands = { 'Whether to include a snapshot in the response. Default is false.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: 'Targets a specific page by ID.', + required: true, + }, }, }, } as const; diff --git a/src/bin/chrome-devtools-mcp-cli-options.ts b/src/bin/chrome-devtools-mcp-cli-options.ts index f510744d3..f10b5edf3 100644 --- a/src/bin/chrome-devtools-mcp-cli-options.ts +++ b/src/bin/chrome-devtools-mcp-cli-options.ts @@ -147,10 +147,11 @@ export const cliOptions = { 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).', + 'Expose 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', diff --git a/src/bin/chrome-devtools.ts b/src/bin/chrome-devtools.ts index cab71cb3a..64166c890 100644 --- a/src/bin/chrome-devtools.ts +++ b/src/bin/chrome-devtools.ts @@ -49,7 +49,6 @@ delete startCliOptions.viewport; // Change the defaults for the CLI. delete startCliOptions.experimentalStructuredContent; delete startCliOptions.experimentalInteropTools; -delete startCliOptions.experimentalPageIdRouting; if (!('default' in cliOptions.headless)) { throw new Error('headless cli option unexpectedly does not have a default'); } diff --git a/src/telemetry/flag_usage_metrics.json b/src/telemetry/flag_usage_metrics.json index 9982b1838..33091b138 100644 --- a/src/telemetry/flag_usage_metrics.json +++ b/src/telemetry/flag_usage_metrics.json @@ -238,11 +238,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", @@ -295,5 +297,13 @@ { "name": "category_experimental_third_party", "flagType": "boolean" + }, + { + "name": "page_id_routing_present", + "flagType": "boolean" + }, + { + "name": "page_id_routing", + "flagType": "boolean" } ] diff --git a/src/telemetry/tool_call_metrics.json b/src/telemetry/tool_call_metrics.json index 597bfa7a3..201867d3d 100644 --- a/src/telemetry/tool_call_metrics.json +++ b/src/telemetry/tool_call_metrics.json @@ -119,6 +119,10 @@ { "name": "file_path_length", "argType": "number" + }, + { + "name": "page_id", + "argType": "number" } ] }, diff --git a/src/tools/script.ts b/src/tools/script.ts index 0d5bb9ec8..6b060583f 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -58,7 +58,7 @@ Example with arguments: \`(el) => { .describe( 'Handle dialogs while execution. "accept", "dismiss", or string for response of window.prompt. Defaults to accept.', ), - ...(cliArgs?.experimentalPageIdRouting ? pageIdSchema : {}), + ...(cliArgs?.pageIdRouting ? pageIdSchema : {}), ...(cliArgs?.categoryExtensions ? { serviceWorkerId: zod @@ -109,9 +109,10 @@ Example with arguments: \`(el) => { return; } - const mcpPage = cliArgs?.experimentalPageIdRouting - ? context.getPageById(request.params.pageId) - : context.getSelectedMcpPage(); + const mcpPage = + cliArgs?.pageIdRouting && request.params.pageId + ? context.getPageById(request.params.pageId) + : context.getSelectedMcpPage(); const page: Page = mcpPage.pptrPage; const args: Array> = []; diff --git a/tests/cli.test.ts b/tests/cli.test.ts index 05f2d3b06..3ca595277 100644 --- a/tests/cli.test.ts +++ b/tests/cli.test.ts @@ -29,6 +29,8 @@ describe('cli args parsing', () => { usageStatistics: true, 'redact-network-headers': false, redactNetworkHeaders: false, + 'page-id-routing': true, + pageIdRouting: true, }; it('parses with default args', async () => { diff --git a/tests/tools/script.test.ts b/tests/tools/script.test.ts index 2718deb21..7b1657ae4 100644 --- a/tests/tools/script.test.ts +++ b/tests/tools/script.test.ts @@ -370,7 +370,7 @@ describe('script', () => { params: { function: String(() => 'test'), serviceWorkerId: 'example_service_worker', - pageId: '1', + pageId: 1, }, }, response,