Skip to content

Commit 50f92b0

Browse files
committed
Merge branch 'main' into feat/save-to-file
2 parents 7fc55a6 + 9af9431 commit 50f92b0

File tree

5 files changed

+67
-36
lines changed

5 files changed

+67
-36
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ You can also run `npx chrome-devtools-mcp@latest --help` to see all available co
314314
data directory:
315315

316316
- Linux / MacOS: `$HOME/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
317-
- Window: `%HOMEPATH%/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
317+
- Windows: `%HOMEPATH%/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
318318

319319
The user data directory is not cleared between runs and shared across
320320
all instances of `chrome-devtools-mcp`. Set the `isolated` option to `true`

src/McpContext.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ function getNetworkMultiplierFromString(condition: string | null): number {
5858
return 1;
5959
}
6060

61+
function getExtensionFromMimeType(mimeType: string) {
62+
switch (mimeType) {
63+
case 'image/png':
64+
return 'png';
65+
case 'image/jpeg':
66+
return 'jpeg';
67+
case 'image/webp':
68+
return 'webp';
69+
}
70+
throw new Error(`No mapping for Mime type ${mimeType}.`);
71+
}
72+
6173
export class McpContext implements Context {
6274
browser: Browser;
6375
logger: Debugger;
@@ -346,18 +358,29 @@ export class McpContext implements Context {
346358
const dir = await fs.mkdtemp(
347359
path.join(os.tmpdir(), 'chrome-devtools-mcp-'),
348360
);
349-
const ext =
350-
mimeType === 'image/png'
351-
? 'png'
352-
: mimeType === 'image/jpeg'
353-
? 'jpg'
354-
: 'webp';
355-
const filename = path.join(dir, `screenshot.${ext}`);
361+
362+
const filename = path.join(
363+
dir,
364+
`screenshot.${getExtensionFromMimeType(mimeType)}`,
365+
);
356366
await fs.writeFile(filename, data);
357367
return {filename};
358368
} catch (err) {
359369
this.logger(err);
360-
throw new Error('Could not save a screenshot to a file');
370+
throw new Error('Could not save a screenshot to a file', {cause: err});
371+
}
372+
}
373+
async saveFile(
374+
data: Uint8Array<ArrayBufferLike>,
375+
filename: string,
376+
): Promise<{filename: string}> {
377+
try {
378+
const filePath = path.resolve(filename);
379+
await fs.writeFile(filePath, data);
380+
return {filename};
381+
} catch (err) {
382+
this.logger(err);
383+
throw new Error('Could not save a screenshot to a file', {cause: err});
361384
}
362385
}
363386

src/McpResponse.ts

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -210,30 +210,12 @@ Call ${handleDialog.name} to handle it before continuing.`);
210210

211211
response.push('## Network requests');
212212
if (requests.length) {
213-
const paginationResult = paginate(
213+
const data = this.#dataWithPagination(
214214
requests,
215215
this.#networkRequestsOptions.pagination,
216216
);
217-
if (paginationResult.invalidPage) {
218-
response.push('Invalid page number provided. Showing first page.');
219-
}
220-
221-
const {startIndex, endIndex, currentPage, totalPages} =
222-
paginationResult;
223-
response.push(
224-
`Showing ${startIndex + 1}-${endIndex} of ${requests.length} (Page ${currentPage + 1} of ${totalPages}).`,
225-
);
226-
227-
if (this.#networkRequestsOptions.pagination) {
228-
if (paginationResult.hasNextPage) {
229-
response.push(`Next page: ${currentPage + 1}`);
230-
}
231-
if (paginationResult.hasPreviousPage) {
232-
response.push(`Previous page: ${currentPage - 1}`);
233-
}
234-
}
235-
236-
for (const request of paginationResult.items) {
217+
response.push(...data.info);
218+
for (const request of data.items) {
237219
response.push(getShortDescriptionForRequest(request));
238220
}
239221
} else {
@@ -264,6 +246,32 @@ Call ${handleDialog.name} to handle it before continuing.`);
264246
return [text, ...images];
265247
}
266248

249+
#dataWithPagination<T>(data: T[], pagination?: PaginationOptions) {
250+
const response = [];
251+
const paginationResult = paginate<T>(data, pagination);
252+
if (paginationResult.invalidPage) {
253+
response.push('Invalid page number provided. Showing first page.');
254+
}
255+
256+
const {startIndex, endIndex, currentPage, totalPages} = paginationResult;
257+
response.push(
258+
`Showing ${startIndex + 1}-${endIndex} of ${data.length} (Page ${currentPage + 1} of ${totalPages}).`,
259+
);
260+
if (pagination) {
261+
if (paginationResult.hasNextPage) {
262+
response.push(`Next page: ${currentPage + 1}`);
263+
}
264+
if (paginationResult.hasPreviousPage) {
265+
response.push(`Previous page: ${currentPage - 1}`);
266+
}
267+
}
268+
269+
return {
270+
info: response,
271+
items: paginationResult.items,
272+
};
273+
}
274+
267275
#getIncludeNetworkRequestsData(context: McpContext): string[] {
268276
const response: string[] = [];
269277
const url = this.#attachedNetworkRequestUrl;

src/tools/ToolDefinition.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ export type Context = Readonly<{
7575
data: Uint8Array<ArrayBufferLike>,
7676
mimeType: 'image/png' | 'image/jpeg' | 'image/webp',
7777
): Promise<{filename: string}>;
78+
saveFile(
79+
data: Uint8Array<ArrayBufferLike>,
80+
filename: string,
81+
): Promise<{filename: string}>;
7882
waitForEventsAfterAction(action: () => Promise<unknown>): Promise<void>;
7983
createTextSnapshot(): Promise<void>;
8084
getTextSnapshot(): TextSnapshot | null;

src/tools/screenshot.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import {writeFile} from 'node:fs/promises';
8-
97
import type {ElementHandle, Page} from 'puppeteer-core';
108
import z from 'zod';
119

@@ -85,10 +83,8 @@ export const screenshot = defineTool({
8583
}
8684

8785
if (request.params.filePath) {
88-
await writeFile(request.params.filePath, screenshot);
89-
response.appendResponseLine(
90-
`Saved screenshot to ${request.params.filePath}.`,
91-
);
86+
const file = await context.saveFile(screenshot, request.params.filePath);
87+
response.appendResponseLine(`Saved screenshot to ${file.filename}.`);
9288
} else if (screenshot.length >= 2_000_000) {
9389
const {filename} = await context.saveTemporaryFile(
9490
screenshot,

0 commit comments

Comments
 (0)