Skip to content

Commit c3e21d3

Browse files
committed
feat: make pageId routing always-on and fix waitForEventsAfterAction page isolation
Remove the experimental-page-id-routing gate so pageId is always exposed on page-scoped tools. This prevents race conditions in multi-agent workflows where concurrent select_page calls can cause tools to operate on the wrong page. Key changes: - Remove experimentalPageIdRouting gate from schema injection and page resolution in index.ts - Add explicit page parameter to waitForEventsAfterAction so it uses the correct page's emulation settings instead of the global selected page - Update all page-scoped tool handlers (input, pages, script) to pass the explicit page to waitForEventsAfterAction - Unify evaluate_script's pageId handling with the standard pattern - Deprecate --experimental-page-id-routing CLI flag (now always enabled) - Add E2E tests for pageId routing, race condition simulation, and per-page emulation isolation
1 parent b1684c6 commit c3e21d3

11 files changed

Lines changed: 719 additions & 90 deletions

docs/tool-reference.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!-- AUTO GENERATED DO NOT EDIT - run 'npm run gen' to update-->
22

3-
# Chrome DevTools MCP Tool Reference (~6940 cl100k_base tokens)
3+
# Chrome DevTools MCP Tool Reference (~8190 cl100k_base tokens)
44

55
- **[Input automation](#input-automation)** (9 tools)
66
- [`click`](#click)
@@ -193,7 +193,7 @@
193193

194194
### `select_page`
195195

196-
**Description:** Select a page as a context for future tool calls.
196+
**Description:** Select a page as a context for future tool calls. For multi-agent workflows, prefer passing pageId directly to each tool instead of using [`select_page`](#select_page).
197197

198198
**Parameters:**
199199

@@ -333,6 +333,7 @@ so returned values have to be JSON-serializable.
333333
}`
334334

335335
- **args** (array) _(optional)_: An optional list of arguments to pass to the function.
336+
- **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.
336337

337338
---
338339

src/McpContext.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -851,9 +851,11 @@ export class McpContext implements Context {
851851

852852
waitForEventsAfterAction(
853853
action: () => Promise<unknown>,
854-
options?: {timeout?: number},
854+
options?: {timeout?: number; page?: McpPage},
855855
): Promise<void> {
856-
const page = this.#getSelectedMcpPage();
856+
const page = options?.page
857+
? this.#getMcpPage(options.page.pptrPage)
858+
: this.#getSelectedMcpPage();
857859
const cpuMultiplier = page.cpuThrottlingRate;
858860
const networkMultiplier = getNetworkMultiplierFromString(
859861
page.networkConditions,

src/bin/chrome-devtools-mcp-cli-options.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ export const cliOptions = {
150150
experimentalPageIdRouting: {
151151
type: 'boolean',
152152
describe:
153-
'Whether to expose pageId on page-scoped tools and route requests by page ID.',
153+
'(Deprecated, now always enabled) pageId is always exposed on page-scoped tools.',
154154
hidden: true,
155155
},
156156
experimentalDevtools: {

src/bin/cliDefinitions.ts

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ export const commands: Commands = {
4747
'Whether to include a snapshot in the response. Default is false.',
4848
required: false,
4949
},
50+
pageId: {
51+
name: 'pageId',
52+
type: 'number',
53+
description:
54+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
55+
required: false,
56+
},
5057
},
5158
},
5259
close_page: {
@@ -86,6 +93,13 @@ export const commands: Commands = {
8693
'Whether to include a snapshot in the response. Default is false.',
8794
required: false,
8895
},
96+
pageId: {
97+
name: 'pageId',
98+
type: 'number',
99+
description:
100+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
101+
required: false,
102+
},
89103
},
90104
},
91105
emulate: {
@@ -135,6 +149,13 @@ export const commands: Commands = {
135149
"Emulate device viewports '<width>x<height>x<devicePixelRatio>[,mobile][,touch][,landscape]'. 'touch' and 'mobile' to emulate mobile devices. 'landscape' to emulate landscape mode.",
136150
required: false,
137151
},
152+
pageId: {
153+
name: 'pageId',
154+
type: 'number',
155+
description:
156+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
157+
required: false,
158+
},
138159
},
139160
},
140161
evaluate_script: {
@@ -155,6 +176,13 @@ export const commands: Commands = {
155176
description: 'An optional list of arguments to pass to the function.',
156177
required: false,
157178
},
179+
pageId: {
180+
name: 'pageId',
181+
type: 'number',
182+
description:
183+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
184+
required: false,
185+
},
158186
},
159187
},
160188
fill: {
@@ -182,6 +210,13 @@ export const commands: Commands = {
182210
'Whether to include a snapshot in the response. Default is false.',
183211
required: false,
184212
},
213+
pageId: {
214+
name: 'pageId',
215+
type: 'number',
216+
description:
217+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
218+
required: false,
219+
},
185220
},
186221
},
187222
get_console_message: {
@@ -196,6 +231,13 @@ export const commands: Commands = {
196231
'The msgid of a console message on the page from the listed console messages',
197232
required: true,
198233
},
234+
pageId: {
235+
name: 'pageId',
236+
type: 'number',
237+
description:
238+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
239+
required: false,
240+
},
199241
},
200242
},
201243
get_network_request: {
@@ -224,6 +266,13 @@ export const commands: Commands = {
224266
'The absolute or relative path to save the response body to. If omitted, the body is returned inline.',
225267
required: false,
226268
},
269+
pageId: {
270+
name: 'pageId',
271+
type: 'number',
272+
description:
273+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
274+
required: false,
275+
},
227276
},
228277
},
229278
handle_dialog: {
@@ -244,6 +293,13 @@ export const commands: Commands = {
244293
description: 'Optional prompt text to enter into the dialog.',
245294
required: false,
246295
},
296+
pageId: {
297+
name: 'pageId',
298+
type: 'number',
299+
description:
300+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
301+
required: false,
302+
},
247303
},
248304
},
249305
hover: {
@@ -264,6 +320,13 @@ export const commands: Commands = {
264320
'Whether to include a snapshot in the response. Default is false.',
265321
required: false,
266322
},
323+
pageId: {
324+
name: 'pageId',
325+
type: 'number',
326+
description:
327+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
328+
required: false,
329+
},
267330
},
268331
},
269332
lighthouse_audit: {
@@ -294,6 +357,13 @@ export const commands: Commands = {
294357
description: 'Directory for reports. If omitted, uses temporary files.',
295358
required: false,
296359
},
360+
pageId: {
361+
name: 'pageId',
362+
type: 'number',
363+
description:
364+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
365+
required: false,
366+
},
297367
},
298368
},
299369
list_console_messages: {
@@ -330,6 +400,13 @@ export const commands: Commands = {
330400
required: false,
331401
default: false,
332402
},
403+
pageId: {
404+
name: 'pageId',
405+
type: 'number',
406+
description:
407+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
408+
required: false,
409+
},
333410
},
334411
},
335412
list_network_requests: {
@@ -366,12 +443,27 @@ export const commands: Commands = {
366443
required: false,
367444
default: false,
368445
},
446+
pageId: {
447+
name: 'pageId',
448+
type: 'number',
449+
description:
450+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
451+
required: false,
452+
},
369453
},
370454
},
371455
list_pages: {
372456
description: 'Get a list of pages open in the browser.',
373457
category: 'Navigation automation',
374-
args: {},
458+
args: {
459+
pageId: {
460+
name: 'pageId',
461+
type: 'number',
462+
description:
463+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
464+
required: false,
465+
},
466+
},
375467
},
376468
navigate_page: {
377469
description:
@@ -420,6 +512,13 @@ export const commands: Commands = {
420512
'Maximum wait time in milliseconds. If set to 0, the default timeout will be used.',
421513
required: false,
422514
},
515+
pageId: {
516+
name: 'pageId',
517+
type: 'number',
518+
description:
519+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
520+
required: false,
521+
},
423522
},
424523
},
425524
new_page: {
@@ -475,6 +574,13 @@ export const commands: Commands = {
475574
'The name of the Insight you want more information on. For example: "DocumentLatency" or "LCPBreakdown"',
476575
required: true,
477576
},
577+
pageId: {
578+
name: 'pageId',
579+
type: 'number',
580+
description:
581+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
582+
required: false,
583+
},
478584
},
479585
},
480586
performance_start_trace: {
@@ -505,6 +611,13 @@ export const commands: Commands = {
505611
'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).',
506612
required: false,
507613
},
614+
pageId: {
615+
name: 'pageId',
616+
type: 'number',
617+
description:
618+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
619+
required: false,
620+
},
508621
},
509622
},
510623
performance_stop_trace: {
@@ -519,6 +632,13 @@ export const commands: Commands = {
519632
'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).',
520633
required: false,
521634
},
635+
pageId: {
636+
name: 'pageId',
637+
type: 'number',
638+
description:
639+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
640+
required: false,
641+
},
522642
},
523643
},
524644
press_key: {
@@ -540,6 +660,13 @@ export const commands: Commands = {
540660
'Whether to include a snapshot in the response. Default is false.',
541661
required: false,
542662
},
663+
pageId: {
664+
name: 'pageId',
665+
type: 'number',
666+
description:
667+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
668+
required: false,
669+
},
543670
},
544671
},
545672
resize_page: {
@@ -559,10 +686,18 @@ export const commands: Commands = {
559686
description: 'Page height',
560687
required: true,
561688
},
689+
pageId: {
690+
name: 'pageId',
691+
type: 'number',
692+
description:
693+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
694+
required: false,
695+
},
562696
},
563697
},
564698
select_page: {
565-
description: 'Select a page as a context for future tool calls.',
699+
description:
700+
'Select a page as a context for future tool calls. For multi-agent workflows, prefer passing pageId directly to each tool instead of using select_page.',
566701
category: 'Navigation automation',
567702
args: {
568703
pageId: {
@@ -592,6 +727,13 @@ export const commands: Commands = {
592727
'A path to a .heapsnapshot file to save the heapsnapshot to.',
593728
required: true,
594729
},
730+
pageId: {
731+
name: 'pageId',
732+
type: 'number',
733+
description:
734+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
735+
required: false,
736+
},
595737
},
596738
},
597739
take_screenshot: {
@@ -635,6 +777,13 @@ export const commands: Commands = {
635777
'The absolute path, or a path relative to the current working directory, to save the screenshot to instead of attaching it to the response.',
636778
required: false,
637779
},
780+
pageId: {
781+
name: 'pageId',
782+
type: 'number',
783+
description:
784+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
785+
required: false,
786+
},
638787
},
639788
},
640789
take_snapshot: {
@@ -656,6 +805,13 @@ export const commands: Commands = {
656805
'The absolute path, or a path relative to the current working directory, to save the snapshot to instead of attaching it to the response.',
657806
required: false,
658807
},
808+
pageId: {
809+
name: 'pageId',
810+
type: 'number',
811+
description:
812+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
813+
required: false,
814+
},
659815
},
660816
},
661817
type_text: {
@@ -675,6 +831,13 @@ export const commands: Commands = {
675831
'Optional key to press after typing. E.g., "Enter", "Tab", "Escape"',
676832
required: false,
677833
},
834+
pageId: {
835+
name: 'pageId',
836+
type: 'number',
837+
description:
838+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
839+
required: false,
840+
},
678841
},
679842
},
680843
upload_file: {
@@ -701,6 +864,13 @@ export const commands: Commands = {
701864
'Whether to include a snapshot in the response. Default is false.',
702865
required: false,
703866
},
867+
pageId: {
868+
name: 'pageId',
869+
type: 'number',
870+
description:
871+
'Targets a specific page by ID. Use list_pages to get available page IDs. If omitted, operates on the most recently selected page.',
872+
required: false,
873+
},
704874
},
705875
},
706876
} as const;

0 commit comments

Comments
 (0)