Skip to content

Commit d35bbf9

Browse files
committed
refactor: centralize pageId routing via pageScoped annotation and McpPage wrapper
- Add pageScoped annotation to ToolDefinition; registerTool() auto-injects pageIdSchema and resolves the page centrally via resolvePageById() - defineTool() wrapper guarantees request.page is always populated, falling back to getSelectedPage() when pageId is omitted - Remove manual ...pageIdSchema spread and resolvePageById() calls from all 15 page-scoped tool handlers - Introduce McpPage class consolidating per-page state (dialog, snapshot, emulation settings, metadata) that was previously scattered across 8 Maps/WeakMaps in McpContext - Store text snapshots per-page on McpPage so parallel agents taking snapshots on different pages no longer clobber each other - Cross-page uid lookup in getElementByUid()/getAXNodeByUid() searches all McpPage instances, enabling uid resolution from any page's snapshot - Update page_id_routing eval to test per-page snapshot isolation and cross-page uid click resolution
1 parent f1db215 commit d35bbf9

18 files changed

Lines changed: 837 additions & 227 deletions

docs/tool-reference.md

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
<!-- AUTO GENERATED DO NOT EDIT - run 'npm run docs' to update-->
22

3-
# Chrome DevTools MCP Tool Reference (~7267 cl100k_base tokens)
3+
# Chrome DevTools MCP Tool Reference (~7472 cl100k_base tokens)
44

5-
- **[Input automation](#input-automation)** (8 tools)
5+
- **[Input automation](#input-automation)** (9 tools)
66
- [`click`](#click)
77
- [`drag`](#drag)
88
- [`fill`](#fill)
99
- [`fill_form`](#fill_form)
1010
- [`handle_dialog`](#handle_dialog)
1111
- [`hover`](#hover)
1212
- [`press_key`](#press_key)
13+
- [`type_text`](#type_text)
1314
- [`upload_file`](#upload_file)
1415
- **[Navigation automation](#navigation-automation)** (6 tools)
1516
- [`close_page`](#close_page)
@@ -71,7 +72,6 @@
7172
- **uid** (string) **(required)**: The uid of an element on the page from the page content snapshot
7273
- **value** (string) **(required)**: The value to [`fill`](#fill) in
7374
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
74-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
7575

7676
---
7777

@@ -83,7 +83,6 @@
8383

8484
- **elements** (array) **(required)**: Elements from snapshot to [`fill`](#fill) out.
8585
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
86-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
8786

8887
---
8988

@@ -117,7 +116,17 @@
117116

118117
- **key** (string) **(required)**: A key or a combination (e.g., "Enter", "Control+A", "Control++", "Control+Shift+R"). Modifiers: Control, Shift, Alt, Meta
119118
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
120-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
119+
120+
---
121+
122+
### `type_text`
123+
124+
**Description:** Type text using keyboard into a previously focused input
125+
126+
**Parameters:**
127+
128+
- **text** (string) **(required)**: The text to type
129+
- **submitKey** (string) _(optional)_: Optional key to press after typing. E.g., "Enter", "Tab", "Escape"
121130

122131
---
123132

@@ -130,7 +139,6 @@
130139
- **filePath** (string) **(required)**: The local path of the file to upload
131140
- **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
132141
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
133-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
134142

135143
---
136144

@@ -163,7 +171,6 @@
163171
- **handleBeforeUnload** (enum: "accept", "decline") _(optional)_: Whether to auto accept or beforeunload dialogs triggered by this navigation. Default is accept.
164172
- **ignoreCache** (boolean) _(optional)_: Whether to ignore cache on reload.
165173
- **initScript** (string) _(optional)_: A JavaScript script to be executed on each new document before any other scripts for the next navigation.
166-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
167174
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
168175
- **type** (enum: "url", "back", "forward", "reload") _(optional)_: Navigate the page by URL, back or forward in history, or reload.
169176
- **url** (string) _(optional)_: Target URL (only type=url)
@@ -201,7 +208,6 @@
201208
**Parameters:**
202209

203210
- **text** (array) **(required)**: Non-empty list of texts. Resolves when any value appears on the page.
204-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
205211
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
206212

207213
---
@@ -218,7 +224,6 @@
218224
- **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Set the rate to 1 to disable throttling. If omitted, throttling remains unchanged.
219225
- **geolocation** (unknown) _(optional)_: Geolocation to [`emulate`](#emulate). Set to null to clear the geolocation override.
220226
- **networkConditions** (enum: "No emulation", "Offline", "Slow 3G", "Fast 3G", "Slow 4G", "Fast 4G") _(optional)_: Throttle network. Set to "No emulation" to disable. If omitted, conditions remain unchanged.
221-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
222227
- **userAgent** (unknown) _(optional)_: User agent to [`emulate`](#emulate). Set to null to clear the user agent override.
223228
- **viewport** (unknown) _(optional)_: Viewport to [`emulate`](#emulate). Set to null to reset to the default viewport.
224229

@@ -232,7 +237,6 @@
232237

233238
- **height** (number) **(required)**: Page height
234239
- **width** (number) **(required)**: Page width
235-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
236240

237241
---
238242

@@ -258,7 +262,6 @@
258262
- **autoStop** (boolean) **(required)**: Determines if the trace recording should be automatically stopped.
259263
- **reload** (boolean) **(required)**: 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.
260264
- **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).
261-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
262265

263266
---
264267

@@ -269,7 +272,6 @@
269272
**Parameters:**
270273

271274
- **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).
272-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
273275

274276
---
275277

@@ -330,7 +332,6 @@ so returned values have to be JSON-serializable.
330332
}`
331333

332334
- **args** (array) _(optional)_: An optional list of arguments to pass to the function.
333-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
334335

335336
---
336337

@@ -366,7 +367,6 @@ so returned values have to be JSON-serializable.
366367
- **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.
367368
- **format** (enum: "png", "jpeg", "webp") _(optional)_: Type of format to save the screenshot as. Default is "png"
368369
- **fullPage** (boolean) _(optional)_: If set to true takes a screenshot of the full page instead of the currently visible viewport. Incompatible with uid.
369-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
370370
- **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.
371371
- **uid** (string) _(optional)_: The uid of an element on the page from the page content snapshot. If omitted takes a pages screenshot.
372372

@@ -381,7 +381,6 @@ in the DevTools Elements panel (if any).
381381
**Parameters:**
382382

383383
- **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.
384-
- **pageId** (number) _(optional)_: Targets a specific page by ID.
385384
- **verbose** (boolean) _(optional)_: Whether to include all possible information available in the full a11y tree. Default is false.
386385

387386
---

scripts/eval_scenarios/page_id_routing_test.ts

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,31 @@ import assert from 'node:assert';
99
import type {TestScenario} from '../eval_gemini.ts';
1010

1111
export const scenario: TestScenario = {
12-
prompt: `Open three new pages in isolated contexts:
13-
- Page A at data:text/html,<h1>Page A</h1>
14-
- Page B at data:text/html,<h1>Page B</h1>
15-
- Page C at data:text/html,<h1>Page C</h1>
16-
Then take screenshots of all three pages in parallel.`,
17-
maxTurns: 8,
12+
prompt: `Open two new pages in isolated contexts:
13+
- Page A (isolatedContext "contextA") at data:text/html,<button>Click A</button>
14+
- Page B (isolatedContext "contextB") at data:text/html,<button>Click B</button>
15+
Then take a snapshot of Page A, take a snapshot of Page B, and then click the button on Page A.`,
16+
maxTurns: 12,
1817
expectations: calls => {
19-
// Exactly 3 screenshot calls.
20-
const screenshots = calls.filter(c => c.name === 'take_screenshot');
21-
assert.strictEqual(screenshots.length, 3, 'Should take 3 screenshots');
22-
23-
// Each screenshot must carry a numeric pageId.
24-
for (const ss of screenshots) {
18+
// Should have 2 new_page calls with isolatedContext.
19+
const newPages = calls.filter(c => c.name === 'new_page');
20+
assert.strictEqual(newPages.length, 2, 'Should open 2 pages');
21+
for (const np of newPages) {
2522
assert.strictEqual(
26-
typeof ss.args.pageId,
27-
'number',
28-
'Screenshot should use pageId',
23+
typeof np.args.isolatedContext,
24+
'string',
25+
'new_page should use isolatedContext',
2926
);
3027
}
3128

32-
// All pageIds should be distinct (one per page).
33-
const pageIds = new Set(screenshots.map(s => s.args.pageId));
34-
assert.strictEqual(
35-
pageIds.size,
36-
3,
37-
'Each screenshot should target a different page',
38-
);
29+
// Should have at least 2 take_snapshot calls (one per page).
30+
// The model may use pageId directly or select_page before each snapshot.
31+
const snapshots = calls.filter(c => c.name === 'take_snapshot');
32+
assert.ok(snapshots.length >= 2, 'Should take at least 2 snapshots');
33+
34+
// Should have a click call (resolving uid from Page A's snapshot
35+
// even though Page B was snapshotted after).
36+
const clicks = calls.filter(c => c.name === 'click');
37+
assert.ok(clicks.length >= 1, 'Should click the button on Page A');
3938
},
4039
};

0 commit comments

Comments
 (0)