Skip to content

Commit e8020df

Browse files
committed
feat: add set_extra_http_headers tool for Network.setExtraHTTPHeaders
1 parent e6b7a09 commit e8020df

2 files changed

Lines changed: 107 additions & 0 deletions

File tree

src/tools/network.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,30 @@ export const getNetworkRequest = definePageTool({
138138
}
139139
},
140140
});
141+
142+
export const setExtraHTTPHeaders = definePageTool({
143+
name: 'set_extra_http_headers',
144+
description: `Set extra HTTP headers to be sent with every request made by the selected page. Headers persist across navigations until cleared. Pass an empty object to clear all extra headers.`,
145+
annotations: {
146+
category: ToolCategory.NETWORK,
147+
readOnlyHint: false,
148+
},
149+
schema: {
150+
headers: zod
151+
.record(zod.string(), zod.string())
152+
.describe(
153+
'Header name-value pairs to set, e.g. {"X-Custom": "value"}. Pass an empty object {} to clear all extra headers.',
154+
),
155+
},
156+
handler: async (request, response) => {
157+
await request.page.pptrPage.setExtraHTTPHeaders(request.params.headers);
158+
const headerCount = Object.keys(request.params.headers).length;
159+
if (headerCount === 0) {
160+
response.appendResponseLine('Extra HTTP headers cleared.');
161+
} else {
162+
response.appendResponseLine(
163+
`Set ${headerCount} extra HTTP header${headerCount === 1 ? '' : 's'}: ${Object.keys(request.params.headers).join(', ')}`,
164+
);
165+
}
166+
},
167+
});

tests/tools/network.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {describe, it} from 'node:test';
1010
import {
1111
getNetworkRequest,
1212
listNetworkRequests,
13+
setExtraHTTPHeaders,
1314
} from '../../src/tools/network.js';
1415
import {serverHooks} from '../server.js';
1516
import {
@@ -187,4 +188,83 @@ describe('network', () => {
187188
});
188189
});
189190
});
191+
192+
describe('set_extra_http_headers', () => {
193+
it('sets extra headers on requests', async () => {
194+
let receivedHeaders: Record<string, string> = {};
195+
server.addRoute('/headers-test', async (req, res) => {
196+
receivedHeaders = req.headers as Record<string, string>;
197+
res.writeHead(200, {'Content-Type': 'text/html'});
198+
res.end('<main>Headers Test</main>');
199+
});
200+
201+
await withMcpContext(async (response, context) => {
202+
const page = context.getSelectedPptrPage();
203+
await setExtraHTTPHeaders.handler(
204+
{
205+
params: {headers: {'X-Custom-Header': 'test-value'}},
206+
page: context.getSelectedMcpPage(),
207+
},
208+
response,
209+
context,
210+
);
211+
assert.ok(
212+
response.responseLines[0]?.includes('1 extra HTTP header'),
213+
);
214+
215+
await page.goto(server.getRoute('/headers-test'));
216+
assert.strictEqual(receivedHeaders['x-custom-header'], 'test-value');
217+
});
218+
});
219+
220+
it('clears extra headers when empty object is passed', async () => {
221+
await withMcpContext(async (response, context) => {
222+
const page = context.getSelectedPptrPage();
223+
// Set headers first.
224+
await page.setExtraHTTPHeaders({'X-To-Clear': 'value'});
225+
226+
await setExtraHTTPHeaders.handler(
227+
{
228+
params: {headers: {}},
229+
page: context.getSelectedMcpPage(),
230+
},
231+
response,
232+
context,
233+
);
234+
assert.ok(response.responseLines[0]?.includes('cleared'));
235+
});
236+
});
237+
238+
it('headers persist across navigations', async () => {
239+
const receivedHeaders: Array<Record<string, string>> = [];
240+
server.addRoute('/persist-one', async (req, res) => {
241+
receivedHeaders.push({...req.headers} as Record<string, string>);
242+
res.writeHead(200, {'Content-Type': 'text/html'});
243+
res.end('<main>Page One</main>');
244+
});
245+
server.addRoute('/persist-two', async (req, res) => {
246+
receivedHeaders.push({...req.headers} as Record<string, string>);
247+
res.writeHead(200, {'Content-Type': 'text/html'});
248+
res.end('<main>Page Two</main>');
249+
});
250+
251+
await withMcpContext(async (response, context) => {
252+
const page = context.getSelectedPptrPage();
253+
await setExtraHTTPHeaders.handler(
254+
{
255+
params: {headers: {'X-Persist': 'yes'}},
256+
page: context.getSelectedMcpPage(),
257+
},
258+
response,
259+
context,
260+
);
261+
262+
await page.goto(server.getRoute('/persist-one'));
263+
await page.goto(server.getRoute('/persist-two'));
264+
265+
assert.strictEqual(receivedHeaders[0]?.['x-persist'], 'yes');
266+
assert.strictEqual(receivedHeaders[1]?.['x-persist'], 'yes');
267+
});
268+
});
269+
});
190270
});

0 commit comments

Comments
 (0)