豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit e51ba47

Browse files
authored
fix: simplify emulation and script tools (#1073)
- reduces the token usage. - makes emulation and script compatible with CLI. - documents .nullable() and .object() restrictions for the future. - the emulation tools do not have nullable anymore and undefined would clear the emulation instead. The model is thus required to provide all emulation settings at once. Closes #918
1 parent 35980fd commit e51ba47

File tree

10 files changed

+235
-183
lines changed

10 files changed

+235
-183
lines changed

CONTRIBUTING.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,10 @@ export const scenario: TestScenario = {
119119
},
120120
};
121121
```
122+
123+
## Restrictions on JSON schema
124+
125+
- no .nullable(), no .object() types.
126+
- represent complex object as a short formatted string.
127+
128+
TODO: implement eslint for schema https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1076

docs/tool-reference.md

Lines changed: 6 additions & 6 deletions
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 (~7324 cl100k_base tokens)
3+
# Chrome DevTools MCP Tool Reference (~6919 cl100k_base tokens)
44

55
- **[Input automation](#input-automation)** (9 tools)
66
- [`click`](#click)
@@ -222,11 +222,11 @@
222222
**Parameters:**
223223

224224
- **colorScheme** (enum: "dark", "light", "auto") _(optional)_: [`Emulate`](#emulate) the dark or the light mode. Set to "auto" to reset to the default.
225-
- **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Set the rate to 1 to disable throttling. If omitted, throttling remains unchanged.
226-
- **geolocation** (unknown) _(optional)_: Geolocation to [`emulate`](#emulate). Set to null to clear the geolocation override.
227-
- **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.
228-
- **userAgent** (unknown) _(optional)_: User agent to [`emulate`](#emulate). Set to null to clear the user agent override.
229-
- **viewport** (unknown) _(optional)_: Viewport to [`emulate`](#emulate). Set to null to reset to the default viewport.
225+
- **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Omit or set the rate to 1 to disable throttling
226+
- **geolocation** (string) _(optional)_: Geolocation (`&lt;latitude&gt;x&lt;longitude&gt;`) to [`emulate`](#emulate). Latitude between -90 and 90. Longitude between -180 and 180. Omit clear the geolocation override.
227+
- **networkConditions** (enum: "Offline", "Slow 3G", "Fast 3G", "Slow 4G", "Fast 4G") _(optional)_: Throttle network. Omit to disable throttling.
228+
- **userAgent** (string) _(optional)_: User agent to [`emulate`](#emulate). Set to empty string to clear the user agent override.
229+
- **viewport** (string) _(optional)_: [`Emulate`](#emulate) device viewports '&lt;width&gt;x&lt;height&gt;x&lt;devicePixelRatio&gt;[,mobile][,touch][,landscape]'. 'touch' and 'mobile' to [`emulate`](#emulate) mobile devices. 'landscape' to [`emulate`](#emulate) landscape mode.
230230

231231
---
232232

src/McpContext.ts

Lines changed: 68 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -306,115 +306,95 @@ export class McpContext implements Context {
306306

307307
async emulate(
308308
options: {
309-
networkConditions?: string | null;
310-
cpuThrottlingRate?: number | null;
311-
geolocation?: GeolocationOptions | null;
312-
userAgent?: string | null;
313-
colorScheme?: 'dark' | 'light' | 'auto' | null;
314-
viewport?: Viewport | null;
309+
networkConditions?: string;
310+
cpuThrottlingRate?: number;
311+
geolocation?: GeolocationOptions;
312+
userAgent?: string;
313+
colorScheme?: 'dark' | 'light' | 'auto';
314+
viewport?: Viewport;
315315
},
316316
targetPage?: Page,
317317
): Promise<void> {
318318
const page = targetPage ?? this.getSelectedPptrPage();
319319
const mcpPage = this.#getMcpPage(page);
320320
const newSettings: EmulationSettings = {...mcpPage.emulationSettings};
321-
let timeoutsNeedUpdate = false;
322-
323-
if (options.networkConditions !== undefined) {
324-
timeoutsNeedUpdate = true;
325-
if (
326-
options.networkConditions === null ||
327-
options.networkConditions === 'No emulation'
328-
) {
329-
await page.emulateNetworkConditions(null);
330-
delete newSettings.networkConditions;
331-
} else if (options.networkConditions === 'Offline') {
332-
await page.emulateNetworkConditions({
333-
offline: true,
334-
download: 0,
335-
upload: 0,
336-
latency: 0,
337-
});
338-
newSettings.networkConditions = 'Offline';
339-
} else if (options.networkConditions in PredefinedNetworkConditions) {
340-
const networkCondition =
341-
PredefinedNetworkConditions[
342-
options.networkConditions as keyof typeof PredefinedNetworkConditions
343-
];
344-
await page.emulateNetworkConditions(networkCondition);
345-
newSettings.networkConditions = options.networkConditions;
346-
}
347-
}
348321

349-
if (options.cpuThrottlingRate !== undefined) {
350-
timeoutsNeedUpdate = true;
351-
if (options.cpuThrottlingRate === null) {
352-
await page.emulateCPUThrottling(1);
353-
delete newSettings.cpuThrottlingRate;
354-
} else {
355-
await page.emulateCPUThrottling(options.cpuThrottlingRate);
356-
newSettings.cpuThrottlingRate = options.cpuThrottlingRate;
357-
}
322+
if (!options.networkConditions) {
323+
await page.emulateNetworkConditions(null);
324+
delete newSettings.networkConditions;
325+
} else if (options.networkConditions === 'Offline') {
326+
await page.emulateNetworkConditions({
327+
offline: true,
328+
download: 0,
329+
upload: 0,
330+
latency: 0,
331+
});
332+
newSettings.networkConditions = 'Offline';
333+
} else if (options.networkConditions in PredefinedNetworkConditions) {
334+
const networkCondition =
335+
PredefinedNetworkConditions[
336+
options.networkConditions as keyof typeof PredefinedNetworkConditions
337+
];
338+
await page.emulateNetworkConditions(networkCondition);
339+
newSettings.networkConditions = options.networkConditions;
340+
}
341+
342+
if (!options.cpuThrottlingRate) {
343+
await page.emulateCPUThrottling(1);
344+
delete newSettings.cpuThrottlingRate;
345+
} else {
346+
await page.emulateCPUThrottling(options.cpuThrottlingRate);
347+
newSettings.cpuThrottlingRate = options.cpuThrottlingRate;
358348
}
359349

360-
if (options.geolocation !== undefined) {
361-
if (options.geolocation === null) {
362-
await page.setGeolocation({latitude: 0, longitude: 0});
363-
delete newSettings.geolocation;
364-
} else {
365-
await page.setGeolocation(options.geolocation);
366-
newSettings.geolocation = options.geolocation;
367-
}
350+
if (!options.geolocation) {
351+
await page.setGeolocation({latitude: 0, longitude: 0});
352+
delete newSettings.geolocation;
353+
} else {
354+
await page.setGeolocation(options.geolocation);
355+
newSettings.geolocation = options.geolocation;
368356
}
369357

370-
if (options.userAgent !== undefined) {
371-
if (options.userAgent === null) {
372-
await page.setUserAgent({userAgent: undefined});
373-
delete newSettings.userAgent;
374-
} else {
375-
await page.setUserAgent({userAgent: options.userAgent});
376-
newSettings.userAgent = options.userAgent;
377-
}
358+
if (!options.userAgent) {
359+
await page.setUserAgent({userAgent: undefined});
360+
delete newSettings.userAgent;
361+
} else {
362+
await page.setUserAgent({userAgent: options.userAgent});
363+
newSettings.userAgent = options.userAgent;
378364
}
379365

380-
if (options.colorScheme !== undefined) {
381-
if (options.colorScheme === null || options.colorScheme === 'auto') {
382-
await page.emulateMediaFeatures([
383-
{name: 'prefers-color-scheme', value: ''},
384-
]);
385-
delete newSettings.colorScheme;
386-
} else {
387-
await page.emulateMediaFeatures([
388-
{name: 'prefers-color-scheme', value: options.colorScheme},
389-
]);
390-
newSettings.colorScheme = options.colorScheme;
391-
}
366+
if (!options.colorScheme || options.colorScheme === 'auto') {
367+
await page.emulateMediaFeatures([
368+
{name: 'prefers-color-scheme', value: ''},
369+
]);
370+
delete newSettings.colorScheme;
371+
} else {
372+
await page.emulateMediaFeatures([
373+
{name: 'prefers-color-scheme', value: options.colorScheme},
374+
]);
375+
newSettings.colorScheme = options.colorScheme;
392376
}
393377

394-
if (options.viewport !== undefined) {
395-
if (options.viewport === null) {
396-
await page.setViewport(null);
397-
delete newSettings.viewport;
398-
} else {
399-
const defaults = {
400-
deviceScaleFactor: 1,
401-
isMobile: false,
402-
hasTouch: false,
403-
isLandscape: false,
404-
};
405-
const viewport = {...defaults, ...options.viewport};
406-
await page.setViewport(viewport);
407-
newSettings.viewport = viewport;
408-
}
378+
if (!options.viewport) {
379+
await page.setViewport(null);
380+
delete newSettings.viewport;
381+
} else {
382+
const defaults = {
383+
deviceScaleFactor: 1,
384+
isMobile: false,
385+
hasTouch: false,
386+
isLandscape: false,
387+
};
388+
const viewport = {...defaults, ...options.viewport};
389+
await page.setViewport(viewport);
390+
newSettings.viewport = viewport;
409391
}
410392

411393
mcpPage.emulationSettings = Object.keys(newSettings).length
412394
? newSettings
413395
: {};
414396

415-
if (timeoutsNeedUpdate) {
416-
this.#updateSelectedPageTimeouts();
417-
}
397+
this.#updateSelectedPageTimeouts();
418398
}
419399

420400
setIsRunningPerformanceTrace(x: boolean): void {

src/tools/ToolDefinition.ts

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,12 @@ export type Context = Readonly<{
150150
restoreEmulation(page: ContextPage): Promise<void>;
151151
emulate(
152152
options: {
153-
networkConditions?: string | null;
154-
cpuThrottlingRate?: number | null;
155-
geolocation?: GeolocationOptions | null;
156-
userAgent?: string | null;
157-
colorScheme?: 'dark' | 'light' | 'auto' | null;
158-
viewport?: Viewport | null;
153+
networkConditions?: string;
154+
cpuThrottlingRate?: number;
155+
geolocation?: GeolocationOptions;
156+
userAgent?: string;
157+
colorScheme?: 'dark' | 'light' | 'auto';
158+
viewport?: Viewport;
159159
},
160160
targetPage?: Page,
161161
): Promise<void>;
@@ -310,3 +310,46 @@ export const timeoutSchema = {
310310
return value && value <= 0 ? undefined : value;
311311
}),
312312
};
313+
314+
export function viewportTransform(arg: string | undefined):
315+
| {
316+
width: number;
317+
height: number;
318+
deviceScaleFactor?: number;
319+
isMobile?: boolean;
320+
isLandscape?: boolean;
321+
hasTouch?: boolean;
322+
}
323+
| undefined {
324+
if (!arg) {
325+
return undefined;
326+
}
327+
const [dimensions, ...tags] = arg.split(',');
328+
const isMobile = tags.includes('mobile');
329+
const hasTouch = tags.includes('touch');
330+
const isLandscape = tags.includes('landscape');
331+
const [width, height, dpr] = dimensions.split('x').map(Number) as [
332+
number,
333+
number,
334+
number | undefined,
335+
];
336+
return {
337+
width,
338+
height,
339+
deviceScaleFactor: dpr,
340+
isMobile: isMobile,
341+
isLandscape: isLandscape,
342+
hasTouch: hasTouch,
343+
};
344+
}
345+
346+
export function geolocationTransform(arg: string | undefined) {
347+
if (!arg) {
348+
return undefined;
349+
}
350+
const [latitude, longitude] = arg.split('x').map(Number) as [number, number];
351+
return {
352+
latitude,
353+
longitude,
354+
};
355+
}

src/tools/emulation.ts

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

1010
import {ToolCategory} from './categories.js';
11-
import {definePageTool} from './ToolDefinition.js';
11+
import {
12+
definePageTool,
13+
geolocationTransform,
14+
viewportTransform,
15+
} from './ToolDefinition.js';
1216

1317
const throttlingOptions: [string, ...string[]] = [
14-
'No emulation',
1518
'Offline',
1619
...Object.keys(PredefinedNetworkConditions),
1720
];
@@ -27,41 +30,27 @@ export const emulate = definePageTool({
2730
networkConditions: zod
2831
.enum(throttlingOptions)
2932
.optional()
30-
.describe(
31-
`Throttle network. Set to "No emulation" to disable. If omitted, conditions remain unchanged.`,
32-
),
33+
.describe(`Throttle network. Omit to disable throttling.`),
3334
cpuThrottlingRate: zod
3435
.number()
3536
.min(1)
3637
.max(20)
3738
.optional()
3839
.describe(
39-
'Represents the CPU slowdown factor. Set the rate to 1 to disable throttling. If omitted, throttling remains unchanged.',
40+
'Represents the CPU slowdown factor. Omit or set the rate to 1 to disable throttling',
4041
),
4142
geolocation: zod
42-
.object({
43-
latitude: zod
44-
.number()
45-
.min(-90)
46-
.max(90)
47-
.describe('Latitude between -90 and 90.'),
48-
longitude: zod
49-
.number()
50-
.min(-180)
51-
.max(180)
52-
.describe('Longitude between -180 and 180.'),
53-
})
54-
.nullable()
43+
.string()
5544
.optional()
45+
.transform(geolocationTransform)
5646
.describe(
57-
'Geolocation to emulate. Set to null to clear the geolocation override.',
47+
'Geolocation (`<latitude>x<longitude>`) to emulate. Latitude between -90 and 90. Longitude between -180 and 180. Omit clear the geolocation override.',
5848
),
5949
userAgent: zod
6050
.string()
61-
.nullable()
6251
.optional()
6352
.describe(
64-
'User agent to emulate. Set to null to clear the user agent override.',
53+
'User agent to emulate. Set to empty string to clear the user agent override.',
6554
),
6655
colorScheme: zod
6756
.enum(['dark', 'light', 'auto'])
@@ -70,37 +59,11 @@ export const emulate = definePageTool({
7059
'Emulate the dark or the light mode. Set to "auto" to reset to the default.',
7160
),
7261
viewport: zod
73-
.object({
74-
width: zod.number().int().min(0).describe('Page width in pixels.'),
75-
height: zod.number().int().min(0).describe('Page height in pixels.'),
76-
deviceScaleFactor: zod
77-
.number()
78-
.min(0)
79-
.optional()
80-
.describe('Specify device scale factor (can be thought of as dpr).'),
81-
isMobile: zod
82-
.boolean()
83-
.optional()
84-
.describe(
85-
'Whether the meta viewport tag is taken into account. Defaults to false.',
86-
),
87-
hasTouch: zod
88-
.boolean()
89-
.optional()
90-
.describe(
91-
'Specifies if viewport supports touch events. This should be set to true for mobile devices.',
92-
),
93-
isLandscape: zod
94-
.boolean()
95-
.optional()
96-
.describe(
97-
'Specifies if viewport is in landscape mode. Defaults to false.',
98-
),
99-
})
100-
.nullable()
62+
.string()
10163
.optional()
64+
.transform(viewportTransform)
10265
.describe(
103-
'Viewport to emulate. Set to null to reset to the default viewport.',
66+
`Emulate device viewports '<width>x<height>x<devicePixelRatio>[,mobile][,touch][,landscape]'. 'touch' and 'mobile' to emulate mobile devices. 'landscape' to emulate landscape mode.`,
10467
),
10568
},
10669
handler: async (request, _response, context) => {

0 commit comments

Comments
 (0)