Skip to content

Commit 765a9d5

Browse files
committed
refactor: replace isolatedContext routing with pageId routing
Replace the isolatedContext-based page resolution with the more general pageId parameter. The isolatedContext approach only worked when agents used different browser contexts. pageId works for any multi-page scenario, uses an already-existing concept (page IDs), and does not depend on isolated contexts. - Rename isolatedContextSchema to pageIdSchema (string to number) - Replace resolvePageByContext() with resolvePageById() in McpContext - Remove per-context page tracking (#contextSelectedPage map) - Update all tool files to use pageId routing - Keep isolatedContext on new_page (browser context isolation, not routing) - Update tests to use pageId-based assertions
1 parent 8487a20 commit 765a9d5

15 files changed

Lines changed: 81 additions & 331 deletions

docs/tool-reference.md

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

3-
# Chrome DevTools MCP Tool Reference (~6719 cl100k_base tokens)
3+
# Chrome DevTools MCP Tool Reference (~7070 cl100k_base tokens)
44

55
- **[Input automation](#input-automation)** (8 tools)
66
- [`click`](#click)
@@ -70,6 +70,7 @@
7070
- **uid** (string) **(required)**: The uid of an element on the page from the page content snapshot
7171
- **value** (string) **(required)**: The value to [`fill`](#fill) in
7272
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
73+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
7374

7475
---
7576

@@ -81,6 +82,7 @@
8182

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

8587
---
8688

@@ -114,6 +116,7 @@
114116

115117
- **key** (string) **(required)**: A key or a combination (e.g., "Enter", "Control+A", "Control++", "Control+Shift+R"). Modifiers: Control, Shift, Alt, Meta
116118
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
119+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
117120

118121
---
119122

@@ -126,6 +129,7 @@
126129
- **filePath** (string) **(required)**: The local path of the file to upload
127130
- **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
128131
- **includeSnapshot** (boolean) _(optional)_: Whether to include a snapshot in the response. Default is false.
132+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
129133

130134
---
131135

@@ -158,6 +162,7 @@
158162
- **handleBeforeUnload** (enum: "accept", "decline") _(optional)_: Whether to auto accept or beforeunload dialogs triggered by this navigation. Default is accept.
159163
- **ignoreCache** (boolean) _(optional)_: Whether to ignore cache on reload.
160164
- **initScript** (string) _(optional)_: A JavaScript script to be executed on each new document before any other scripts for the next navigation.
165+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
161166
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
162167
- **type** (enum: "url", "back", "forward", "reload") _(optional)_: Navigate the page by URL, back or forward in history, or reload.
163168
- **url** (string) _(optional)_: Target URL (only type=url)
@@ -195,6 +200,7 @@
195200
**Parameters:**
196201

197202
- **text** (string) **(required)**: Text to appear on the page
203+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
198204
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
199205

200206
---
@@ -211,6 +217,7 @@
211217
- **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Set the rate to 1 to disable throttling. If omitted, throttling remains unchanged.
212218
- **geolocation** (unknown) _(optional)_: Geolocation to [`emulate`](#emulate). Set to null to clear the geolocation override.
213219
- **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.
220+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
214221
- **userAgent** (unknown) _(optional)_: User agent to [`emulate`](#emulate). Set to null to clear the user agent override.
215222
- **viewport** (unknown) _(optional)_: Viewport to [`emulate`](#emulate). Set to null to reset to the default viewport.
216223

@@ -224,6 +231,7 @@
224231

225232
- **height** (number) **(required)**: Page height
226233
- **width** (number) **(required)**: Page width
234+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
227235

228236
---
229237

@@ -249,6 +257,7 @@
249257
- **autoStop** (boolean) **(required)**: Determines if the trace recording should be automatically stopped.
250258
- **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.
251259
- **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).
260+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
252261

253262
---
254263

@@ -259,6 +268,7 @@
259268
**Parameters:**
260269

261270
- **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).
271+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
262272

263273
---
264274

@@ -309,6 +319,7 @@ so returned values have to be JSON-serializable.
309319
}`
310320

311321
- **args** (array) _(optional)_: An optional list of arguments to pass to the function.
322+
- **pageId** (number) _(optional)_: Targets a specific page by ID.
312323

313324
---
314325

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

@@ -358,6 +370,7 @@ in the DevTools Elements panel (if any).
358370
**Parameters:**
359371

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

363376
---

src/McpContext.ts

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ export class McpContext implements Context {
131131
#pages: Page[] = [];
132132
#pageToDevToolsPage = new Map<Page, Page>();
133133
#selectedPage?: Page;
134-
// Per-context selected page tracking for parallel agent support.
135-
#contextSelectedPage = new Map<string, Page>();
136134
#textSnapshot: TextSnapshot | null = null;
137135
#networkCollector: NetworkCollector;
138136
#consoleCollector: ConsoleCollector;
@@ -505,39 +503,11 @@ export class McpContext implements Context {
505503
return page;
506504
}
507505

508-
resolvePageByContext(isolatedContext?: string): Page {
509-
if (isolatedContext === undefined) {
506+
resolvePageById(pageId?: number): Page {
507+
if (pageId === undefined) {
510508
return this.getSelectedPage();
511509
}
512-
513-
// Try the per-context selected page first.
514-
const tracked = this.#contextSelectedPage.get(isolatedContext);
515-
if (tracked && !tracked.isClosed()) {
516-
return tracked;
517-
}
518-
519-
// Fall back: find any non-closed page in the context.
520-
const ctx = this.#isolatedContexts.get(isolatedContext);
521-
if (!ctx) {
522-
throw new Error(
523-
`No isolated context named "${isolatedContext}" exists. ` +
524-
`Create one first with new_page(isolatedContext: "${isolatedContext}").`,
525-
);
526-
}
527-
528-
for (const page of this.#pages) {
529-
if (
530-
!page.isClosed() &&
531-
this.#pageToIsolatedContextName.get(page) === isolatedContext
532-
) {
533-
this.#contextSelectedPage.set(isolatedContext, page);
534-
return page;
535-
}
536-
}
537-
538-
throw new Error(
539-
`No open page found in isolated context "${isolatedContext}".`,
540-
);
510+
return this.getPageById(pageId);
541511
}
542512

543513
getPageById(pageId: number): Page {
@@ -574,12 +544,6 @@ export class McpContext implements Context {
574544
void newPage.emulateFocusedPage(true).catch(error => {
575545
this.logger('Error turning on focused page emulation', error);
576546
});
577-
578-
// Track per-context selected page for parallel agent routing.
579-
const contextName = this.#pageToIsolatedContextName.get(newPage);
580-
if (contextName) {
581-
this.#contextSelectedPage.set(contextName, newPage);
582-
}
583547
}
584548

585549
#updateSelectedPageTimeouts() {

src/tools/ToolDefinition.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export type Context = Readonly<{
108108
recordedTraces(): TraceResult[];
109109
storeTraceRecording(result: TraceResult): void;
110110
getSelectedPage(): Page;
111-
resolvePageByContext(isolatedContext?: string): Page;
111+
resolvePageById(pageId?: number): Page;
112112
getDialog(): Dialog | undefined;
113113
clearDialog(): void;
114114
getPageById(pageId: number): Page;
@@ -182,16 +182,8 @@ export function defineTool<Schema extends zod.ZodRawShape>(
182182
export const CLOSE_PAGE_ERROR =
183183
'The last open page cannot be closed. It is fine to keep it open.';
184184

185-
export const isolatedContextSchema = {
186-
isolatedContext: zod
187-
.string()
188-
.optional()
189-
.describe(
190-
'The name of the isolated browser context to resolve the page from. ' +
191-
'When provided, the tool operates on the page belonging to this context ' +
192-
'instead of the globally selected page. ' +
193-
'Use this to avoid race conditions when multiple agents work in parallel.',
194-
),
185+
export const pageIdSchema = {
186+
pageId: zod.number().optional().describe('Targets a specific page by ID.'),
195187
};
196188

197189
export const timeoutSchema = {

src/tools/emulation.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import {zod, PredefinedNetworkConditions} from '../third_party/index.js';
99

1010
import {ToolCategory} from './categories.js';
11-
import {defineTool, isolatedContextSchema} from './ToolDefinition.js';
11+
import {defineTool, pageIdSchema} from './ToolDefinition.js';
1212

1313
const throttlingOptions: [string, ...string[]] = [
1414
'No emulation',
@@ -24,7 +24,7 @@ export const emulate = defineTool({
2424
readOnlyHint: false,
2525
},
2626
schema: {
27-
...isolatedContextSchema,
27+
...pageIdSchema,
2828
networkConditions: zod
2929
.enum(throttlingOptions)
3030
.optional()
@@ -105,9 +105,7 @@ export const emulate = defineTool({
105105
),
106106
},
107107
handler: async (request, _response, context) => {
108-
const page = context.resolvePageByContext(
109-
request.params.isolatedContext,
110-
);
108+
const page = context.resolvePageById(request.params.pageId);
111109
await context.emulate(request.params, page);
112110
},
113111
});

0 commit comments

Comments
 (0)