From 394fbd2de9061aca3f4ab81256801c5a611d2ebd Mon Sep 17 00:00:00 2001 From: bcfmtolgahan Date: Tue, 31 Mar 2026 14:57:50 +0300 Subject: [PATCH 1/3] feat: make pageId routing always-on Remove the --experimental-page-id-routing gate so pageId is always exposed on page-scoped tools. Update evaluate_script which had its own duplicate gate. Fix docs generator to include pageId for page-scoped tools. Regenerate cliDefinitions.ts and tool-reference.md. - Remove experimentalPageIdRouting conditions from schema injection and page resolution in index.ts - Mark --experimental-page-id-routing CLI flag as deprecated - Improve pageIdSchema description to be more helpful - Remove evaluate_script's own experimentalPageIdRouting gate; also fixes a latent bug where getPageById(undefined) was called when the flag was on but pageId was absent - Fix pageId type in script.test.ts (string -> number) - Fix generate-docs.ts to inject pageIdSchema for page-scoped tools so tool-reference.md accurately reflects available parameters --- docs/tool-reference.md | 27 +++- scripts/generate-docs.ts | 14 +- src/bin/chrome-devtools-mcp-cli-options.ts | 2 +- src/bin/cliDefinitions.ts | 161 +++++++++++++++++++++ src/index.ts | 9 +- src/tools/ToolDefinition.ts | 7 +- src/tools/script.ts | 4 +- tests/tools/script.test.ts | 2 +- 8 files changed, 210 insertions(+), 16 deletions(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index bdc0c6ed6..92c020862 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -1,6 +1,6 @@ -# Chrome DevTools MCP Tool Reference (~6949 cl100k_base tokens) +# Chrome DevTools MCP Tool Reference (~8124 cl100k_base tokens) - **[Input automation](#input-automation)** (9 tools) - [`click`](#click) @@ -49,6 +49,7 @@ - **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. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -61,6 +62,7 @@ - **from_uid** (string) **(required)**: The uid of the element to [`drag`](#drag) - **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. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -73,6 +75,7 @@ - **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 - **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -84,6 +87,7 @@ - **elements** (array) **(required)**: Elements from snapshot to [`fill`](#fill) out. - **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -94,6 +98,7 @@ **Parameters:** - **action** (enum: "accept", "dismiss") **(required)**: Whether to dismiss or accept the dialog +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **promptText** (string) _(optional)_: Optional prompt text to enter into the dialog. --- @@ -106,6 +111,7 @@ - **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. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -117,6 +123,7 @@ - **key** (string) **(required)**: A key or a combination (e.g., "Enter", "Control+A", "Control++", "Control+Shift+R"). Modifiers: Control, Shift, Alt, Meta - **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -127,6 +134,7 @@ **Parameters:** - **text** (string) **(required)**: The text to type +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **submitKey** (string) _(optional)_: Optional key to press after typing. E.g., "Enter", "Tab", "Escape" --- @@ -140,6 +148,7 @@ - **filePath** (string) **(required)**: The local path of the file to upload - **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. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -172,6 +181,7 @@ - **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. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used. - **type** (enum: "url", "back", "forward", "reload") _(optional)_: Navigate the page by URL, back or forward in history, or reload. - **url** (string) _(optional)_: Target URL (only type=url) @@ -209,6 +219,7 @@ **Parameters:** - **text** (array) **(required)**: Non-empty list of texts. Resolves when any value appears on the page. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used. --- @@ -225,6 +236,7 @@ - **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Omit or set the rate to 1 to disable throttling - **geolocation** (string) _(optional)_: Geolocation (`<latitude>x<longitude>`) to [`emulate`](#emulate). Latitude between -90 and 90. Longitude between -180 and 180. Omit clear the geolocation override. - **networkConditions** (enum: "Offline", "Slow 3G", "Fast 3G", "Slow 4G", "Fast 4G") _(optional)_: Throttle network. Omit to disable throttling. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **userAgent** (string) _(optional)_: User agent to [`emulate`](#emulate). Set to empty string to clear the user agent override. - **viewport** (string) _(optional)_: [`Emulate`](#emulate) device viewports '<width>x<height>x<devicePixelRatio>[,mobile][,touch][,landscape]'. 'touch' and 'mobile' to [`emulate`](#emulate) mobile devices. 'landscape' to [`emulate`](#emulate) landscape mode. @@ -238,6 +250,7 @@ - **height** (number) **(required)**: Page height - **width** (number) **(required)**: Page width +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -251,6 +264,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) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -262,6 +276,7 @@ - **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). +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **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. --- @@ -273,6 +288,7 @@ **Parameters:** - **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). +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -283,6 +299,7 @@ **Parameters:** - **filePath** (string) **(required)**: A path to a .heapsnapshot file to save the heapsnapshot to. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -294,6 +311,7 @@ **Parameters:** +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **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 save the request body to. If omitted, the body is returned inline. - **responseFilePath** (string) _(optional)_: The absolute or relative path to save the response body to. If omitted, the body is returned inline. @@ -307,6 +325,7 @@ **Parameters:** - **includePreservedRequests** (boolean) _(optional)_: Set to true to return the preserved requests over the last 3 navigations. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **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. - **resourceTypes** (array) _(optional)_: Filter requests to only return requests of the specified resource types. When omitted or empty, returns all requests. @@ -333,6 +352,7 @@ so returned values have to be JSON-serializable. }` - **args** (array) _(optional)_: An optional list of arguments to pass to the function. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -343,6 +363,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) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -355,6 +376,7 @@ so returned values have to be JSON-serializable. - **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. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- @@ -365,6 +387,7 @@ so returned values have to be JSON-serializable. **Parameters:** - **includePreservedMessages** (boolean) _(optional)_: Set to true to return the preserved messages over the last 3 navigations. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **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 requests. - **types** (array) _(optional)_: Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages. @@ -380,6 +403,7 @@ so returned values have to be JSON-serializable. - **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. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **quality** (number) _(optional)_: Compression quality for JPEG and WebP formats (0-100). Higher values mean better quality but larger file sizes. Ignored for PNG format. - **uid** (string) _(optional)_: The uid of an element on the page from the page content snapshot. If omitted takes a pages screenshot. @@ -394,6 +418,7 @@ in the DevTools Elements panel (if any). **Parameters:** - **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. +- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. - **verbose** (boolean) _(optional)_: Whether to include all possible information available in the full a11y tree. Default is false. --- diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index 280c0e332..9780c93db 100644 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -14,6 +14,7 @@ import {get_encoding} from 'tiktoken'; import {cliOptions} from '../build/src/bin/chrome-devtools-mcp-cli-options.js'; import type {ParsedArguments} from '../build/src/bin/chrome-devtools-mcp-cli-options.js'; import {ToolCategory, 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'; @@ -432,7 +433,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 => { @@ -447,8 +448,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; @@ -521,7 +526,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/bin/chrome-devtools-mcp-cli-options.ts b/src/bin/chrome-devtools-mcp-cli-options.ts index 80046b115..f31bf75f2 100644 --- a/src/bin/chrome-devtools-mcp-cli-options.ts +++ b/src/bin/chrome-devtools-mcp-cli-options.ts @@ -150,7 +150,7 @@ export const cliOptions = { experimentalPageIdRouting: { type: 'boolean', describe: - 'Whether to expose pageId on page-scoped tools and route requests by page ID.', + '(Deprecated, now always enabled) pageId is always exposed on page-scoped tools.', hidden: true, }, experimentalDevtools: { diff --git a/src/bin/cliDefinitions.ts b/src/bin/cliDefinitions.ts index 55aa68d45..a1a0e4b1d 100644 --- a/src/bin/cliDefinitions.ts +++ b/src/bin/cliDefinitions.ts @@ -47,6 +47,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, close_page: { @@ -86,6 +93,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, emulate: { @@ -135,6 +149,13 @@ export const commands: Commands = { "Emulate device viewports 'xx[,mobile][,touch][,landscape]'. 'touch' and 'mobile' to emulate mobile devices. 'landscape' to emulate landscape mode.", required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: + 'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, evaluate_script: { @@ -155,6 +176,13 @@ export const commands: Commands = { description: 'An optional list of arguments to pass to the function.', required: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: + 'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, fill: { @@ -182,6 +210,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, get_console_message: { @@ -196,6 +231,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, get_network_request: { @@ -224,6 +266,13 @@ export const commands: Commands = { 'The absolute or relative path 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, handle_dialog: { @@ -244,6 +293,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, hover: { @@ -264,6 +320,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, lighthouse_audit: { @@ -294,6 +357,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, list_console_messages: { @@ -330,6 +400,13 @@ export const commands: Commands = { required: false, default: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: + 'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, list_network_requests: { @@ -366,6 +443,13 @@ export const commands: Commands = { required: false, default: false, }, + pageId: { + name: 'pageId', + type: 'number', + description: + 'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, list_pages: { @@ -420,6 +504,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, new_page: { @@ -475,6 +566,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, performance_start_trace: { @@ -505,6 +603,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, performance_stop_trace: { @@ -519,6 +624,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, press_key: { @@ -540,6 +652,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, resize_page: { @@ -559,6 +678,13 @@ export const commands: Commands = { description: 'Page height', required: true, }, + pageId: { + name: 'pageId', + type: 'number', + description: + 'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, select_page: { @@ -592,6 +718,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, take_screenshot: { @@ -635,6 +768,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, take_snapshot: { @@ -656,6 +796,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, type_text: { @@ -675,6 +822,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, upload_file: { @@ -701,6 +855,13 @@ 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. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + required: false, + }, }, }, } as const; diff --git a/src/index.ts b/src/index.ts index 362f2348a..6463dc5b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -165,10 +165,7 @@ export async function createMcpServer( return; } const schema = - 'pageScoped' in tool && - tool.pageScoped && - serverArgs.experimentalPageIdRouting && - !serverArgs.slim + 'pageScoped' in tool && tool.pageScoped && !serverArgs.slim ? {...tool.schema, ...pageIdSchema} : tool.schema; @@ -193,9 +190,7 @@ export async function createMcpServer( : new McpResponse(serverArgs); if ('pageScoped' in tool && tool.pageScoped) { const page = - serverArgs.experimentalPageIdRouting && - params.pageId && - !serverArgs.slim + params.pageId && !serverArgs.slim ? context.getPageById(params.pageId) : context.getSelectedMcpPage(); response.setPage(page); diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 9a0ef4355..71ed1a774 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -297,7 +297,12 @@ export const CLOSE_PAGE_ERROR = 'The last open page cannot be closed. It is fine to keep it open.'; export const pageIdSchema = { - pageId: zod.number().optional().describe('Targets a specific page by ID.'), + pageId: zod + .number() + .optional() + .describe( + 'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.', + ), }; export const timeoutSchema = { diff --git a/src/tools/script.ts b/src/tools/script.ts index e3ff14d7d..a0c7b91b1 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -46,7 +46,7 @@ Example with arguments: \`(el) => { ) .optional() .describe(`An optional list of arguments to pass to the function.`), - ...(cliArgs?.experimentalPageIdRouting ? pageIdSchema : {}), + ...pageIdSchema, ...(cliArgs?.categoryExtensions ? { serviceWorkerId: zod @@ -81,7 +81,7 @@ Example with arguments: \`(el) => { return; } - const mcpPage = cliArgs?.experimentalPageIdRouting + const mcpPage = request.params.pageId ? context.getPageById(request.params.pageId) : context.getSelectedMcpPage(); const page: Page = mcpPage.pptrPage; diff --git a/tests/tools/script.test.ts b/tests/tools/script.test.ts index 772ebc076..0974f97e3 100644 --- a/tests/tools/script.test.ts +++ b/tests/tools/script.test.ts @@ -257,7 +257,7 @@ describe('script', () => { params: { function: String(() => 'test'), serviceWorkerId: 'example_service_worker', - pageId: '1', + pageId: 1, }, }, response, From 7d5931358121f244e3101b4e79207394c2cf7307 Mon Sep 17 00:00:00 2001 From: bcfmtolgahan Date: Wed, 1 Apr 2026 13:35:57 +0300 Subject: [PATCH 2/3] refactor: rename --experimental-page-id-routing to --page-id-routing Per reviewer feedback: drop the experimental prefix, remove hidden: true, set default to true so pageId routing is on by default. Users can opt out with --no-page-id-routing. Flag is kept as an opt-out toggle in case token overhead is unacceptable or routing does not work as expected. --- docs/tool-reference.md | 1 - src/bin/chrome-devtools-mcp-cli-options.ts | 6 +++--- src/bin/chrome-devtools.ts | 1 - src/index.ts | 7 +++++-- src/tools/script.ts | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index 92c020862..0d9631f19 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -352,7 +352,6 @@ so returned values have to be JSON-serializable. }` - **args** (array) _(optional)_: An optional list of arguments to pass to the function. -- **pageId** (number) _(optional)_: Targets a specific page by ID. Use [`list_pages`](#list_pages) to get available page IDs. If omitted, operates on the most recently selected page. --- diff --git a/src/bin/chrome-devtools-mcp-cli-options.ts b/src/bin/chrome-devtools-mcp-cli-options.ts index f31bf75f2..1250b0a41 100644 --- a/src/bin/chrome-devtools-mcp-cli-options.ts +++ b/src/bin/chrome-devtools-mcp-cli-options.ts @@ -147,11 +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: - '(Deprecated, now always enabled) pageId is always exposed on page-scoped tools.', - hidden: true, + 'Expose pageId on page-scoped tools and route requests by page ID. 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 b30f136dd..b0a161359 100644 --- a/src/bin/chrome-devtools.ts +++ b/src/bin/chrome-devtools.ts @@ -44,7 +44,6 @@ delete startCliOptions.autoConnect; delete startCliOptions.viewport; // CLI is generated based on the default tool definitions. To enable conditional // tools, they need to be enabled during CLI generation. -delete startCliOptions.experimentalPageIdRouting; delete startCliOptions.experimentalVision; delete startCliOptions.experimentalInteropTools; delete startCliOptions.experimentalScreencast; diff --git a/src/index.ts b/src/index.ts index 6463dc5b7..32a29cc30 100644 --- a/src/index.ts +++ b/src/index.ts @@ -165,7 +165,10 @@ export async function createMcpServer( return; } const schema = - 'pageScoped' in tool && tool.pageScoped && !serverArgs.slim + 'pageScoped' in tool && + tool.pageScoped && + serverArgs.pageIdRouting && + !serverArgs.slim ? {...tool.schema, ...pageIdSchema} : tool.schema; @@ -190,7 +193,7 @@ export async function createMcpServer( : new McpResponse(serverArgs); if ('pageScoped' in tool && tool.pageScoped) { const page = - params.pageId && !serverArgs.slim + serverArgs.pageIdRouting && params.pageId && !serverArgs.slim ? context.getPageById(params.pageId) : context.getSelectedMcpPage(); response.setPage(page); diff --git a/src/tools/script.ts b/src/tools/script.ts index a0c7b91b1..26d62bf62 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -46,7 +46,7 @@ Example with arguments: \`(el) => { ) .optional() .describe(`An optional list of arguments to pass to the function.`), - ...pageIdSchema, + ...(cliArgs?.pageIdRouting ? pageIdSchema : {}), ...(cliArgs?.categoryExtensions ? { serviceWorkerId: zod @@ -81,7 +81,7 @@ Example with arguments: \`(el) => { return; } - const mcpPage = request.params.pageId + const mcpPage = cliArgs?.pageIdRouting ? context.getPageById(request.params.pageId) : context.getSelectedMcpPage(); const page: Page = mcpPage.pptrPage; From e6cec43e670240df74def168bf9ef016496f3213 Mon Sep 17 00:00:00 2001 From: bcfmtolgahan Date: Wed, 1 Apr 2026 13:59:45 +0300 Subject: [PATCH 3/3] fix: guard getPageById with pageId check in evaluate_script When pageIdRouting is true but no pageId is provided, getPageById(undefined) was called which always throws. Mirror the same guard used in index.ts: only route by pageId when both the flag is on AND a pageId is present. --- src/tools/script.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/script.ts b/src/tools/script.ts index 26d62bf62..2ca1e2e4c 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -81,9 +81,10 @@ Example with arguments: \`(el) => { return; } - const mcpPage = cliArgs?.pageIdRouting - ? 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> = [];