Skip to content

Commit ae45cc1

Browse files
committed
chore: support structured network
1 parent 4090da2 commit ae45cc1

8 files changed

Lines changed: 476 additions & 351 deletions

File tree

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/McpResponse.ts

Lines changed: 65 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,8 @@ import {
1111
formatConsoleEventVerbose,
1212
} from './formatters/consoleFormatter.js';
1313
import {
14-
getFormattedHeaderValue,
15-
getFormattedResponseBody,
16-
getFormattedRequestBody,
17-
getShortDescriptionForRequest,
18-
getStatusFromRequest,
19-
} from './formatters/networkFormatter.js';
14+
NetworkFormatter,
15+
} from './formatters/NetworkFormatter.js';
2016
import {SnapshotFormatter} from './formatters/SnapshotFormatter.js';
2117
import type {McpContext} from './McpContext.js';
2218
import {DevTools} from './third_party/index.js';
@@ -215,22 +211,17 @@ export class McpResponse implements Response {
215211
}
216212
}
217213

218-
const bodies: {
219-
requestBody?: string;
220-
responseBody?: string;
221-
} = {};
222-
214+
let detailedNetworkRequest: NetworkFormatter | undefined;
223215
if (this.#attachedNetworkRequestId) {
224216
const request = context.getNetworkRequestById(
225217
this.#attachedNetworkRequestId,
226218
);
227-
228-
bodies.requestBody = await getFormattedRequestBody(request);
229-
230-
const response = request.response();
231-
if (response) {
232-
bodies.responseBody = await getFormattedResponseBody(response);
233-
}
219+
const formatter = await NetworkFormatter.from(request, {
220+
requestId: this.#attachedNetworkRequestId,
221+
requestIdResolver: (req) => context.getNetworkRequestStableId(req),
222+
fetchData: true,
223+
});
224+
detailedNetworkRequest = formatter;
234225
}
235226

236227
let consoleData: ConsoleMessageData | undefined;
@@ -341,25 +332,61 @@ export class McpResponse implements Response {
341332
).filter(item => item !== null);
342333
}
343334

335+
let networkRequests: NetworkFormatter[] | undefined;
336+
if (this.#networkRequestsOptions?.include) {
337+
let requests = context.getNetworkRequests(
338+
this.#networkRequestsOptions?.includePreservedRequests,
339+
);
340+
341+
// Apply resource type filtering if specified
342+
if (this.#networkRequestsOptions.resourceTypes?.length) {
343+
const normalizedTypes = new Set(
344+
this.#networkRequestsOptions.resourceTypes,
345+
);
346+
requests = requests.filter(request => {
347+
const type = request.resourceType();
348+
return normalizedTypes.has(type);
349+
});
350+
}
351+
352+
if (requests.length) {
353+
const data = this.#dataWithPagination(
354+
requests,
355+
this.#networkRequestsOptions.pagination,
356+
);
357+
358+
networkRequests = await Promise.all(
359+
data.items.map(request =>
360+
NetworkFormatter.from(request, {
361+
requestId: context.getNetworkRequestStableId(request),
362+
selectedInDevToolsUI:
363+
context.getNetworkRequestStableId(request) ===
364+
this.#networkRequestsOptions?.networkRequestIdInDevToolsUI,
365+
fetchData: false,
366+
}),
367+
),
368+
);
369+
}
370+
}
371+
344372
return this.format(toolName, context, {
345-
bodies,
346373
consoleData,
347374
consoleListData,
348375
snapshot,
376+
detailedNetworkRequest,
377+
networkRequests,
349378
});
350379
}
351380

352381
format(
353382
toolName: string,
354383
context: McpContext,
355384
data: {
356-
bodies: {
357-
requestBody?: string;
358-
responseBody?: string;
359-
};
360385
consoleData: ConsoleMessageData | undefined;
361386
consoleListData: ConsoleMessageData[] | undefined;
362387
snapshot: SnapshotFormatter | string | undefined;
388+
detailedNetworkRequest?: NetworkFormatter;
389+
networkRequests?: NetworkFormatter[];
363390
},
364391
): {content: Array<TextContent | ImageContent>; structuredContent: object} {
365392
const response = [`# ${toolName} response`];
@@ -407,6 +434,8 @@ Call ${handleDialog.name} to handle it before continuing.`);
407434
snapshot?: object;
408435
snapshotFilePath?: string;
409436
tabId?: string;
437+
networkRequest?: object;
438+
networkRequests?: object[];
410439
} = {};
411440

412441
if (this.#tabId) {
@@ -424,7 +453,11 @@ Call ${handleDialog.name} to handle it before continuing.`);
424453
}
425454
}
426455

427-
response.push(...this.#formatNetworkRequestData(context, data.bodies));
456+
if (data.detailedNetworkRequest) {
457+
response.push(data.detailedNetworkRequest.toStringDetailed());
458+
structuredContent.networkRequest =
459+
data.detailedNetworkRequest.toJSONDetailed();
460+
}
428461
response.push(...this.#formatConsoleData(context, data.consoleData));
429462

430463
if (this.#networkRequestsOptions?.include) {
@@ -445,20 +478,17 @@ Call ${handleDialog.name} to handle it before continuing.`);
445478

446479
response.push('## Network requests');
447480
if (requests.length) {
448-
const data = this.#dataWithPagination(
481+
const paginationData = this.#dataWithPagination(
449482
requests,
450483
this.#networkRequestsOptions.pagination,
451484
);
452-
response.push(...data.info);
453-
for (const request of data.items) {
454-
response.push(
455-
getShortDescriptionForRequest(
456-
request,
457-
context.getNetworkRequestStableId(request),
458-
context.getNetworkRequestStableId(request) ===
459-
this.#networkRequestsOptions?.networkRequestIdInDevToolsUI,
460-
),
461-
);
485+
response.push(...paginationData.info);
486+
if (data.networkRequests) {
487+
structuredContent.networkRequests = [];
488+
for (const formatter of data.networkRequests) {
489+
response.push(formatter.toString());
490+
structuredContent.networkRequests.push(formatter.toJSON());
491+
}
462492
}
463493
} else {
464494
response.push('No requests found.');
@@ -539,65 +569,6 @@ Call ${handleDialog.name} to handle it before continuing.`);
539569
return response;
540570
}
541571

542-
#formatNetworkRequestData(
543-
context: McpContext,
544-
data: {
545-
requestBody?: string;
546-
responseBody?: string;
547-
},
548-
): string[] {
549-
const response: string[] = [];
550-
const id = this.#attachedNetworkRequestId;
551-
if (!id) {
552-
return response;
553-
}
554-
555-
const httpRequest = context.getNetworkRequestById(id);
556-
response.push(`## Request ${httpRequest.url()}`);
557-
response.push(`Status: ${getStatusFromRequest(httpRequest)}`);
558-
response.push(`### Request Headers`);
559-
for (const line of getFormattedHeaderValue(httpRequest.headers())) {
560-
response.push(line);
561-
}
562-
563-
if (data.requestBody) {
564-
response.push(`### Request Body`);
565-
response.push(data.requestBody);
566-
}
567-
568-
const httpResponse = httpRequest.response();
569-
if (httpResponse) {
570-
response.push(`### Response Headers`);
571-
for (const line of getFormattedHeaderValue(httpResponse.headers())) {
572-
response.push(line);
573-
}
574-
}
575-
576-
if (data.responseBody) {
577-
response.push(`### Response Body`);
578-
response.push(data.responseBody);
579-
}
580-
581-
const httpFailure = httpRequest.failure();
582-
if (httpFailure) {
583-
response.push(`### Request failed with`);
584-
response.push(httpFailure.errorText);
585-
}
586-
587-
const redirectChain = httpRequest.redirectChain();
588-
if (redirectChain.length) {
589-
response.push(`### Redirect chain`);
590-
let indent = 0;
591-
for (const request of redirectChain.reverse()) {
592-
response.push(
593-
`${' '.repeat(indent)}${getShortDescriptionForRequest(request, context.getNetworkRequestStableId(request))}`,
594-
);
595-
indent++;
596-
}
597-
}
598-
return response;
599-
}
600-
601572
resetResponseLineForTesting() {
602573
this.#textResponseLines = [];
603574
}

0 commit comments

Comments
 (0)