From 8dd42a4644c8ebddae063c727ede91a42bfcef19 Mon Sep 17 00:00:00 2001 From: Adam Jones Date: Mon, 26 Jan 2026 14:12:10 +0000 Subject: [PATCH 1/3] Add background parameter to new_page tool Add support for opening pages in the background without bringing them to the front, similar to cmd+click behavior in Chrome. This uses Puppeteer's existing background option for browser.newPage(). Fixes #826 --- docs/tool-reference.md | 1 + src/McpContext.ts | 4 ++-- src/tools/ToolDefinition.ts | 2 +- src/tools/pages.ts | 8 +++++++- tests/tools/pages.test.ts | 24 ++++++++++++++++++++++++ 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index a8605b837..f0f901c99 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -170,6 +170,7 @@ **Parameters:** - **url** (string) **(required)**: URL to load in a new page. +- **background** (boolean) _(optional)_: Whether to open the page in the background without bringing it to the front. - **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used. --- diff --git a/src/McpContext.ts b/src/McpContext.ts index 7335a1308..a3bcddbbc 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -265,8 +265,8 @@ export class McpContext implements Context { return this.#consoleCollector.getById(this.getSelectedPage(), id); } - async newPage(): Promise { - const page = await this.browser.newPage(); + async newPage(background?: boolean): Promise { + const page = await this.browser.newPage({background}); await this.createPagesSnapshot(); this.selectPage(page); this.#networkCollector.addPage(page); diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 11b01a44b..ae68cfba0 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -110,7 +110,7 @@ export type Context = Readonly<{ getPageById(pageId: number): Page; getPageId(page: Page): number | undefined; isPageSelected(page: Page): boolean; - newPage(): Promise; + newPage(background?: boolean): Promise; closePage(pageId: number): Promise; selectPage(page: Page): void; getElementByUid(uid: string): Promise>; diff --git a/src/tools/pages.ts b/src/tools/pages.ts index 7ad9426ea..6b141e843 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -87,10 +87,16 @@ export const newPage = defineTool({ }, schema: { url: zod.string().describe('URL to load in a new page.'), + background: zod + .boolean() + .optional() + .describe( + 'Whether to open the page in the background without bringing it to the front.', + ), ...timeoutSchema, }, handler: async (request, response, context) => { - const page = await context.newPage(); + const page = await context.newPage(request.params.background); await context.waitForEventsAfterAction(async () => { await page.goto(request.params.url, { diff --git a/tests/tools/pages.test.ts b/tests/tools/pages.test.ts index d20a7a6cf..da0fd13fe 100644 --- a/tests/tools/pages.test.ts +++ b/tests/tools/pages.test.ts @@ -43,6 +43,30 @@ describe('pages', () => { assert.ok(response.includePages); }); }); + it('create a page in the background', async () => { + await withMcpContext(async (response, context) => { + const originalPage = context.getPageById(1); + assert.strictEqual(originalPage, context.getSelectedPage()); + // Ensure original page has focus + await originalPage.bringToFront(); + assert.strictEqual( + await originalPage.evaluate(() => document.hasFocus()), + true, + ); + await newPage.handler( + {params: {url: 'about:blank', background: true}}, + response, + context, + ); + // New page should be selected but original should retain focus + assert.strictEqual(context.getPageById(2), context.getSelectedPage()); + assert.strictEqual( + await originalPage.evaluate(() => document.hasFocus()), + true, + ); + assert.ok(response.includePages); + }); + }); }); describe('close_page', () => { it('closes a page', async () => { From 4b7ae2f82f02553a364c966055167e390e6c3c2a Mon Sep 17 00:00:00 2001 From: adam jones Date: Mon, 26 Jan 2026 15:10:59 +0000 Subject: [PATCH 2/3] Update src/tools/pages.ts Co-authored-by: Alex Rudenko --- src/tools/pages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/pages.ts b/src/tools/pages.ts index 6b141e843..106de86e5 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -91,7 +91,7 @@ export const newPage = defineTool({ .boolean() .optional() .describe( - 'Whether to open the page in the background without bringing it to the front.', + 'Whether to open the page in the background without bringing it to the front. Default is false (foreground).', ), ...timeoutSchema, }, From 345954ec8c3ad524307aa4d23efffa01d6526527 Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Mon, 26 Jan 2026 16:45:47 +0100 Subject: [PATCH 3/3] chore: docs --- docs/tool-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index f0f901c99..93858193b 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -170,7 +170,7 @@ **Parameters:** - **url** (string) **(required)**: URL to load in a new page. -- **background** (boolean) _(optional)_: Whether to open the page in the background without bringing it to the front. +- **background** (boolean) _(optional)_: Whether to open the page in the background without bringing it to the front. Default is false (foreground). - **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used. ---