Skip to content

Commit cdda8db

Browse files
OzTamirOrKoN
authored andcommitted
feat: Support filtering network request by resource type
1 parent 22ec7ee commit cdda8db

File tree

4 files changed

+85
-34
lines changed

4 files changed

+85
-34
lines changed

src/McpResponse.ts

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,32 @@ import type {
77
ImageContent,
88
TextContent,
99
} from '@modelcontextprotocol/sdk/types.js';
10+
import type { ResourceType } from 'puppeteer-core';
1011

11-
import {formatConsoleEvent} from './formatters/consoleFormatter.js';
12+
import { formatConsoleEvent } from './formatters/consoleFormatter.js';
1213
import {
1314
getFormattedHeaderValue,
1415
getShortDescriptionForRequest,
1516
getStatusFromRequest,
1617
} from './formatters/networkFormatter.js';
17-
import {formatA11ySnapshot} from './formatters/snapshotFormatter.js';
18-
import type {McpContext} from './McpContext.js';
19-
import type {ImageContentData, Response} from './tools/ToolDefinition.js';
20-
import {paginate, type PaginationOptions} from './utils/pagination.js';
18+
import { formatA11ySnapshot } from './formatters/snapshotFormatter.js';
19+
import type { McpContext } from './McpContext.js';
20+
import type { ImageContentData, Response } from './tools/ToolDefinition.js';
21+
import { paginate, type PaginationOptions } from './utils/pagination.js';
2122

2223
export class McpResponse implements Response {
2324
#includePages = false;
2425
#includeSnapshot = false;
25-
#includeNetworkRequests = false;
2626
#attachedNetworkRequestUrl?: string;
2727
#includeConsoleData = false;
2828
#textResponseLines: string[] = [];
2929
#formattedConsoleData?: string[];
3030
#images: ImageContentData[] = [];
31-
#networkRequestsPaginationOptions?: PaginationOptions;
31+
#networkRequestsOptions?: {
32+
include: boolean;
33+
pagination?: PaginationOptions;
34+
resourceTypes?: ResourceType[];
35+
};
3236

3337
setIncludePages(value: boolean): void {
3438
this.#includePages = value;
@@ -40,17 +44,20 @@ export class McpResponse implements Response {
4044

4145
setIncludeNetworkRequests(
4246
value: boolean,
43-
options?: {pageSize?: number; pageIdx?: number},
47+
options?: { pageSize?: number; pageIdx?: number; resourceTypes?: ResourceType[] },
4448
): void {
45-
this.#includeNetworkRequests = value;
46-
if (!value || !options) {
47-
this.#networkRequestsPaginationOptions = undefined;
49+
if (!value) {
50+
this.#networkRequestsOptions = undefined;
4851
return;
4952
}
5053

51-
this.#networkRequestsPaginationOptions = {
52-
pageSize: options.pageSize,
53-
pageIdx: options.pageIdx,
54+
this.#networkRequestsOptions = {
55+
include: value,
56+
pagination: options?.pageSize || options?.pageIdx ? {
57+
pageSize: options.pageSize,
58+
pageIdx: options.pageIdx,
59+
} : undefined,
60+
resourceTypes: options?.resourceTypes,
5461
};
5562
}
5663

@@ -67,7 +74,7 @@ export class McpResponse implements Response {
6774
}
6875

6976
get includeNetworkRequests(): boolean {
70-
return this.#includeNetworkRequests;
77+
return this.#networkRequestsOptions?.include ?? false;
7178
}
7279

7380
get includeConsoleData(): boolean {
@@ -77,7 +84,7 @@ export class McpResponse implements Response {
7784
return this.#attachedNetworkRequestUrl;
7885
}
7986
get networkRequestsPageIdx(): number | undefined {
80-
return this.#networkRequestsPaginationOptions?.pageIdx;
87+
return this.#networkRequestsOptions?.pagination?.pageIdx;
8188
}
8289

8390
appendResponseLine(value: string): void {
@@ -179,25 +186,35 @@ Call browser_handle_dialog to handle it before continuing.`);
179186

180187
response.push(...this.#getIncludeNetworkRequestsData(context));
181188

182-
if (this.#includeNetworkRequests) {
183-
const requests = context.getNetworkRequests();
189+
if (this.#networkRequestsOptions?.include) {
190+
let requests = context.getNetworkRequests();
191+
192+
// Apply resource type filtering if specified
193+
if (this.#networkRequestsOptions.resourceTypes) {
194+
const normalizedTypes = new Set(this.#networkRequestsOptions.resourceTypes);
195+
requests = requests.filter(request => {
196+
const type = request.resourceType();
197+
return normalizedTypes.has(type);
198+
});
199+
}
200+
184201
response.push('## Network requests');
185202
if (requests.length) {
186203
const paginationResult = paginate(
187204
requests,
188-
this.#networkRequestsPaginationOptions,
205+
this.#networkRequestsOptions.pagination,
189206
);
190207
if (paginationResult.invalidPage) {
191208
response.push('Invalid page number provided. Showing first page.');
192209
}
193210

194-
const {startIndex, endIndex, currentPage, totalPages} =
211+
const { startIndex, endIndex, currentPage, totalPages } =
195212
paginationResult;
196213
response.push(
197214
`Showing ${startIndex + 1}-${endIndex} of ${requests.length} (Page ${currentPage + 1} of ${totalPages}).`,
198215
);
199216

200-
if (this.#networkRequestsPaginationOptions) {
217+
if (this.#networkRequestsOptions.pagination) {
201218
if (paginationResult.hasNextPage) {
202219
response.push(`Next page: ${currentPage + 1}`);
203220
}

src/tools/ToolDefinition.ts

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

7-
import type {Dialog, ElementHandle, Page} from 'puppeteer-core';
7+
import type { Dialog, ElementHandle, Page } from 'puppeteer-core';
88
import type z from 'zod';
99

10-
import type {TraceResult} from '../trace-processing/parse.js';
10+
import type { TraceResult } from '../trace-processing/parse.js';
1111

12-
import type {ToolCategories} from './categories.js';
12+
import type { ToolCategories } from './categories.js';
1313

1414
export interface ToolDefinition<Schema extends z.ZodRawShape = z.ZodRawShape> {
1515
name: string;
@@ -44,7 +44,7 @@ export interface Response {
4444
setIncludePages(value: boolean): void;
4545
setIncludeNetworkRequests(
4646
value: boolean,
47-
options?: {pageSize?: number; pageIdx?: number},
47+
options?: { pageSize?: number; pageIdx?: number; resourceTypes?: string[] },
4848
): void;
4949
setIncludeConsoleData(value: boolean): void;
5050
setIncludeSnapshot(value: boolean): void;
@@ -73,7 +73,7 @@ export type Context = Readonly<{
7373
saveTemporaryFile(
7474
data: Uint8Array<ArrayBufferLike>,
7575
mimeType: 'image/png' | 'image/jpeg',
76-
): Promise<{filename: string}>;
76+
): Promise<{ filename: string }>;
7777
waitForEventsAfterAction(action: () => Promise<unknown>): Promise<void>;
7878
}>;
7979

src/tools/network.ts

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

7+
import type { ResourceType } from 'puppeteer-core';
78
import z from 'zod';
89

9-
import {ToolCategories} from './categories.js';
10-
import {defineTool} from './ToolDefinition.js';
10+
import { ToolCategories } from './categories.js';
11+
import { defineTool } from './ToolDefinition.js';
12+
13+
export const FILTERABLE_RESOURCE_TYPES = [
14+
'document',
15+
'stylesheet',
16+
'image',
17+
'media',
18+
'font',
19+
'script',
20+
'texttrack',
21+
'xhr',
22+
'fetch',
23+
'prefetch',
24+
'eventsource',
25+
'websocket',
26+
'manifest',
27+
'signedexchange',
28+
'ping',
29+
'cspviolationreport',
30+
'preflight',
31+
'fedcm',
32+
'other',
33+
] as const satisfies readonly ResourceType[];
1134

1235
export const listNetworkRequests = defineTool({
1336
name: 'list_network_requests',
@@ -33,11 +56,18 @@ export const listNetworkRequests = defineTool({
3356
.describe(
3457
'Page number to return (0-based). When omitted, returns the first page.',
3558
),
59+
resourceType: z
60+
.array(z.enum(FILTERABLE_RESOURCE_TYPES as any))
61+
.optional()
62+
.describe(
63+
'Filter requests by resource type. When omitted, returns all requests.',
64+
),
3665
},
3766
handler: async (request, response) => {
3867
response.setIncludeNetworkRequests(true, {
3968
pageSize: request.params.pageSize,
4069
pageIdx: request.params.pageIdx,
70+
resourceTypes: request.params.resourceType,
4171
});
4272
},
4373
});

tests/utils.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55
*/
66

77
import logger from 'debug';
8-
import type {Browser} from 'puppeteer';
8+
import type { Browser } from 'puppeteer';
99
import puppeteer from 'puppeteer';
10-
import type {HTTPRequest, HTTPResponse} from 'puppeteer-core';
10+
import type { HTTPRequest, HTTPResponse } from 'puppeteer-core';
1111

12-
import {McpContext} from '../src/McpContext.js';
13-
import {McpResponse} from '../src/McpResponse.js';
12+
import { McpContext } from '../src/McpContext.js';
13+
import { McpResponse } from '../src/McpResponse.js';
1414

1515
let browser: Browser | undefined;
1616

1717
export async function withBrowser(
1818
cb: (response: McpResponse, context: McpContext) => Promise<void>,
19-
options: {debug?: boolean} = {},
19+
options: { debug?: boolean } = {},
2020
) {
21-
const {debug = false} = options;
21+
const { debug = false } = options;
2222
if (!browser) {
2323
browser = await puppeteer.launch({
2424
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH,
@@ -46,6 +46,7 @@ export function getMockRequest(
4646
method?: string;
4747
response?: HTTPResponse;
4848
failure?: HTTPRequest['failure'];
49+
resourceType?: string;
4950
} = {},
5051
): HTTPRequest {
5152
return {
@@ -61,6 +62,9 @@ export function getMockRequest(
6162
failure() {
6263
return options.failure?.() ?? null;
6364
},
65+
resourceType() {
66+
return options.resourceType ?? 'document';
67+
},
6468
headers(): Record<string, string> {
6569
return {
6670
'content-size': '10',

0 commit comments

Comments
 (0)