Skip to content

Commit 795d63c

Browse files
committed
feat: first can use
1 parent 4a87702 commit 795d63c

4 files changed

Lines changed: 93 additions & 11 deletions

File tree

src/McpContext.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ interface McpContextOptions {
5858
experimentalDevToolsDebugging: boolean;
5959
// Whether all page-like targets are exposed as pages.
6060
experimentalIncludeAllPages?: boolean;
61+
// Custom headers to add to all network requests made by the browser.
62+
headers?: Record<string, string>;
6163
}
6264

6365
const DEFAULT_TIMEOUT = 5_000;
@@ -104,6 +106,9 @@ export class McpContext implements Context {
104106
#textSnapshot: TextSnapshot | null = null;
105107
#networkCollector: NetworkCollector;
106108
#consoleCollector: ConsoleCollector;
109+
110+
// Custom headers to add to all network requests made by the browser.
111+
#headers?: Record<string, string>;
107112

108113
#isRunningTrace = false;
109114
#networkConditionsMap = new WeakMap<Page, string>();
@@ -127,6 +132,7 @@ export class McpContext implements Context {
127132
this.logger = logger;
128133
this.#locatorClass = locatorClass;
129134
this.#options = options;
135+
this.#headers = options.headers;
130136

131137
this.#networkCollector = new NetworkCollector(this.browser);
132138

@@ -153,9 +159,42 @@ export class McpContext implements Context {
153159

154160
async #init() {
155161
const pages = await this.createPagesSnapshot();
156-
await this.#networkCollector.init(pages);
162+
await this.#networkCollector.init(pages, this.#headers);
157163
await this.#consoleCollector.init(pages);
158164
}
165+
166+
/**
167+
* Gets the custom headers to add to all network requests.
168+
*/
169+
getHeaders(): Record<string, string> | undefined {
170+
return this.#headers;
171+
}
172+
173+
/**
174+
* Sets custom headers to add to all network requests.
175+
*/
176+
setHeaders(headers: Record<string, string> | undefined): void {
177+
this.#headers = headers;
178+
// Update all existing pages with the new headers
179+
for (const page of this.#pages) {
180+
this.#applyHeadersToPage(page, headers);
181+
}
182+
}
183+
184+
/**
185+
* Applies custom headers to a specific page.
186+
*/
187+
async #applyHeadersToPage(page: Page, headers: Record<string, string> | undefined): Promise<void> {
188+
try {
189+
if (headers) {
190+
await page.setExtraHTTPHeaders(headers);
191+
} else {
192+
await page.setExtraHTTPHeaders({});
193+
}
194+
} catch (error) {
195+
this.logger('Error applying headers to page:', error);
196+
}
197+
}
159198

160199
dispose() {
161200
this.#networkCollector.dispose();

src/PageCollector.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -349,20 +349,41 @@ class PageIssueSubscriber {
349349
}
350350

351351
export class NetworkCollector extends PageCollector<HTTPRequest> {
352+
#headers?: Record<string, string>;
353+
352354
constructor(
353355
browser: Browser,
354356
listeners: (
355357
collector: (item: HTTPRequest) => void,
356-
) => ListenerMap<PageEvents> = collect => {
357-
return {
358-
request: req => {
359-
collect(req);
360-
},
361-
} as ListenerMap;
362-
},
358+
) => ListenerMap<PageEvents> = collect => ({
359+
request: req => collect(req),
360+
} as ListenerMap),
363361
) {
364362
super(browser, listeners);
365363
}
364+
365+
override async init(pages: Page[], headers?: Record<string, string>): Promise<void> {
366+
this.#headers = headers;
367+
for (const page of pages) {
368+
await this.#applyHeadersToPage(page);
369+
}
370+
await super.init(pages);
371+
}
372+
373+
override addPage(page: Page): void {
374+
super.addPage(page);
375+
void this.#applyHeadersToPage(page);
376+
}
377+
378+
async #applyHeadersToPage(page: Page): Promise<void> {
379+
if (this.#headers) {
380+
try {
381+
await page.setExtraHTTPHeaders(this.#headers);
382+
} catch (error) {
383+
logger('Error applying headers to page:', error);
384+
}
385+
}
386+
}
366387
override splitAfterNavigation(page: Page) {
367388
const navigations = this.storage.get(page) ?? [];
368389
if (!navigations) {

src/cli.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,27 @@ export const cliOptions = {
8787
}
8888
},
8989
},
90+
headers: {
91+
type: 'string',
92+
description:
93+
'Custom headers to add to all network requests made by the browser in JSON format (e.g., \'{"Authorization":"Bearer token","User-Agent":"Custom"}\').',
94+
coerce: (val: string | undefined) => {
95+
if (!val) {
96+
return;
97+
}
98+
try {
99+
const parsed = JSON.parse(val);
100+
if (typeof parsed !== 'object' || Array.isArray(parsed)) {
101+
throw new Error('Headers must be a JSON object');
102+
}
103+
return parsed as Record<string, string>;
104+
} catch (error) {
105+
throw new Error(
106+
`Invalid JSON for headers: ${(error as Error).message}`,
107+
);
108+
}
109+
},
110+
},
90111
headless: {
91112
type: 'boolean',
92113
description: 'Whether to run in headless (no UI) mode.',

src/main.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ async function getContext(): Promise<McpContext> {
8585

8686
if (context?.browser !== browser) {
8787
context = await McpContext.from(browser, logger, {
88-
experimentalDevToolsDebugging: devtools,
89-
experimentalIncludeAllPages: args.experimentalIncludeAllPages,
90-
});
88+
experimentalDevToolsDebugging: devtools,
89+
experimentalIncludeAllPages: args.experimentalIncludeAllPages,
90+
headers: args.headers,
91+
});
9192
}
9293
return context;
9394
}

0 commit comments

Comments
 (0)