Skip to content

Commit d332f6b

Browse files
committed
feat(mcp): include request body in network tool output
Adds `includeBody` option to `browser_network_requests` MCP tool and `--body` flag to the `network` CLI command to include POST/PUT/PATCH request bodies in the network log output.
1 parent 7f38b61 commit d332f6b

File tree

4 files changed

+70
-3
lines changed

4 files changed

+70
-3
lines changed

packages/playwright-core/src/tools/backend/network.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const requests = defineTabTool({
2828
description: 'Returns all network requests since loading the page',
2929
inputSchema: z.object({
3030
includeStatic: z.boolean().default(false).describe('Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false.'),
31+
includeBody: z.boolean().default(false).describe('Whether to include request body. Defaults to false.'),
3132
filename: z.string().optional().describe('Filename to save the network requests to. If not provided, requests are returned as text.'),
3233
}),
3334
type: 'readOnly',
@@ -39,7 +40,7 @@ const requests = defineTabTool({
3940
for (const request of requests) {
4041
if (!params.includeStatic && !isFetch(request) && isSuccessfulResponse(request))
4142
continue;
42-
text.push(await renderRequest(request));
43+
text.push(await renderRequest(request, params.includeBody));
4344
}
4445
await response.addResult('Network', text.join('\n'), { prefix: 'network', ext: 'log', suggestedFilename: params.filename });
4546
},
@@ -71,7 +72,7 @@ export function isFetch(request: playwright.Request): boolean {
7172
return ['fetch', 'xhr'].includes(request.resourceType());
7273
}
7374

74-
export async function renderRequest(request: playwright.Request): Promise<string> {
75+
export async function renderRequest(request: playwright.Request, includeBody = false): Promise<string> {
7576
const response = request.existingResponse();
7677

7778
const result: string[] = [];
@@ -80,6 +81,11 @@ export async function renderRequest(request: playwright.Request): Promise<string
8081
result.push(`=> [${response.status()}] ${response.statusText()}`);
8182
else if (request.failure())
8283
result.push(`=> [FAILED] ${request.failure()?.errorText ?? 'Unknown error'}`);
84+
if (includeBody) {
85+
const postData = request.postData();
86+
if (postData)
87+
result.push(`\n Request body: ${postData}`);
88+
}
8389
return result.join(' ');
8490
}
8591

packages/playwright-core/src/tools/cli-daemon/commands.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,10 +743,11 @@ const networkRequests = declareCommand({
743743
args: z.object({}),
744744
options: z.object({
745745
static: z.boolean().optional().describe('Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false.'),
746+
body: z.boolean().optional().describe('Whether to include request body. Defaults to false.'),
746747
clear: z.boolean().optional().describe('Whether to clear the network list'),
747748
}),
748749
toolName: ({ clear }) => clear ? 'browser_network_clear' : 'browser_network_requests',
749-
toolParams: ({ static: includeStatic, clear }) => clear ? ({}) : ({ includeStatic }),
750+
toolParams: ({ static: includeStatic, body: includeBody, clear }) => clear ? ({}) : ({ includeStatic, includeBody }),
750751
});
751752

752753
const tracingStart = declareCommand({

tests/mcp/cli-devtools.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,26 @@ test('network --static', async ({ cli, server }) => {
6464
expect(attachments[0].data.toString()).toContain(`[GET] ${`${server.PREFIX}/`} => [200] OK`);
6565
});
6666

67+
test('network --body', async ({ cli, server }) => {
68+
server.setContent('/', `
69+
<button onclick="fetch('/api', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'value' }) })">Click me</button>
70+
`, 'text/html');
71+
server.setContent('/api', '{}', 'application/json');
72+
await cli('open', server.PREFIX);
73+
await cli('click', 'e2');
74+
75+
{
76+
const { attachments } = await cli('network');
77+
expect(attachments[0].data.toString()).not.toContain('Request body:');
78+
}
79+
80+
{
81+
const { attachments } = await cli('network', '--body');
82+
expect(attachments[0].data.toString()).toContain(`[POST] ${server.PREFIX}/api => [200] OK`);
83+
expect(attachments[0].data.toString()).toContain('Request body: {"key":"value"}');
84+
}
85+
});
86+
6787
test('network --clear', async ({ cli, server }) => {
6888
await cli('open', server.PREFIX);
6989
await cli('eval', '() => fetch("/hello-world")');

tests/mcp/network.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,43 @@ test('browser_network_requests', async ({ client, server }) => {
6060
expect(response.result).toContain(`[GET] ${`${server.PREFIX}/image.png`} => [404]`);
6161
}
6262
});
63+
64+
test('browser_network_requests includes request payload', async ({ client, server }) => {
65+
server.setContent('/', `
66+
<button onclick="fetch('/api', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'value' }) })">Click me</button>
67+
`, 'text/html');
68+
69+
server.setContent('/api', '{}', 'application/json');
70+
71+
await client.callTool({
72+
name: 'browser_navigate',
73+
arguments: {
74+
url: server.PREFIX,
75+
},
76+
});
77+
78+
await client.callTool({
79+
name: 'browser_click',
80+
arguments: {
81+
element: 'Click me button',
82+
ref: 'e2',
83+
},
84+
});
85+
86+
{
87+
const response = parseResponse(await client.callTool({
88+
name: 'browser_network_requests',
89+
}));
90+
expect(response.result).toContain(`[POST] ${server.PREFIX}/api => [200] OK`);
91+
expect(response.result).not.toContain(`Request body:`);
92+
}
93+
94+
{
95+
const response = parseResponse(await client.callTool({
96+
name: 'browser_network_requests',
97+
arguments: { includeBody: true },
98+
}));
99+
expect(response.result).toContain(`[POST] ${server.PREFIX}/api => [200] OK`);
100+
expect(response.result).toContain(`Request body: {"key":"value"}`);
101+
}
102+
});

0 commit comments

Comments
 (0)