Skip to content

Commit ceae17b

Browse files
authored
fix: make bringToFront optional in select_page (#668)
bringToFront() not only brings the tab to front in the browser but also the browser window itself. This PR fixes the issue by making the call to bringToFront() optional allowing agents to bring a tab to the user's attention if needed but not always. To mitigate the risk of selected page that is in the background being throttled, this PR implements emulation of the focused page to make background tabs run as usual.
1 parent a8aae66 commit ceae17b

File tree

4 files changed

+33
-3
lines changed

4 files changed

+33
-3
lines changed

docs/tool-reference.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@
172172

173173
**Parameters:**
174174

175-
- **pageIdx** (number) **(required)**: The index of the page to select. Call [`list_pages`](#list_pages) to list pages.
175+
- **bringToFront** (boolean) _(optional)_: Whether to focus the page and bring it to the top.
176+
- **pageIdx** (number) **(required)**: The index of the page to select. Call [`list_pages`](#list_pages) to get available pages.
176177

177178
---
178179

src/McpContext.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,16 @@ export class McpContext implements Context {
348348
const oldPage = this.#selectedPage;
349349
if (oldPage) {
350350
oldPage.off('dialog', this.#dialogHandler);
351+
void oldPage.emulateFocusedPage(false).catch(error => {
352+
this.logger('Error turning off focused page emulation', error);
353+
});
351354
}
352355
this.#selectedPage = newPage;
353356
newPage.on('dialog', this.#dialogHandler);
354357
this.#updateSelectedPageTimeouts();
358+
void newPage.emulateFocusedPage(true).catch(error => {
359+
this.logger('Error turning on focused page emulation', error);
360+
});
355361
}
356362

357363
#updateSelectedPageTimeouts() {

src/tools/pages.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,20 @@ export const selectPage = defineTool({
3434
pageIdx: zod
3535
.number()
3636
.describe(
37-
'The index of the page to select. Call list_pages to list pages.',
37+
`The index of the page to select. Call ${listPages.name} to get available pages.`,
3838
),
39+
bringToFront: zod
40+
.boolean()
41+
.optional()
42+
.describe('Whether to focus the page and bring it to the top.'),
3943
},
4044
handler: async (request, response, context) => {
4145
const page = context.getPageByIdx(request.params.pageIdx);
42-
await page.bringToFront();
4346
context.selectPage(page);
4447
response.setIncludePages(true);
48+
if (request.params.bringToFront) {
49+
await page.bringToFront();
50+
}
4551
},
4652
});
4753

tests/tools/pages.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ describe('pages', () => {
7777
assert.ok(response.includePages);
7878
});
7979
});
80+
it('selects a page and keeps it focused in the background', async () => {
81+
await withMcpContext(async (response, context) => {
82+
await context.newPage();
83+
assert.strictEqual(context.getPageByIdx(1), context.getSelectedPage());
84+
assert.strictEqual(
85+
await context.getPageByIdx(0).evaluate(() => document.hasFocus()),
86+
false,
87+
);
88+
await selectPage.handler({params: {pageIdx: 0}}, response, context);
89+
assert.strictEqual(context.getPageByIdx(0), context.getSelectedPage());
90+
assert.strictEqual(
91+
await context.getPageByIdx(0).evaluate(() => document.hasFocus()),
92+
true,
93+
);
94+
assert.ok(response.includePages);
95+
});
96+
});
8097
});
8198
describe('navigate_page', () => {
8299
it('navigates to correct page', async () => {

0 commit comments

Comments
 (0)