diff --git a/README.md b/README.md index e0429202b..78c37c182 100644 --- a/README.md +++ b/README.md @@ -247,11 +247,10 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles - [`hover`](docs/tool-reference.md#hover) - [`press_key`](docs/tool-reference.md#press_key) - [`upload_file`](docs/tool-reference.md#upload_file) -- **Navigation automation** (7 tools) +- **Navigation automation** (6 tools) - [`close_page`](docs/tool-reference.md#close_page) - [`list_pages`](docs/tool-reference.md#list_pages) - [`navigate_page`](docs/tool-reference.md#navigate_page) - - [`navigate_page_history`](docs/tool-reference.md#navigate_page_history) - [`new_page`](docs/tool-reference.md#new_page) - [`select_page`](docs/tool-reference.md#select_page) - [`wait_for`](docs/tool-reference.md#wait_for) diff --git a/docs/tool-reference.md b/docs/tool-reference.md index 4872226c9..a956e1ece 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -11,11 +11,10 @@ - [`hover`](#hover) - [`press_key`](#press_key) - [`upload_file`](#upload_file) -- **[Navigation automation](#navigation-automation)** (7 tools) +- **[Navigation automation](#navigation-automation)** (6 tools) - [`close_page`](#close_page) - [`list_pages`](#list_pages) - [`navigate_page`](#navigate_page) - - [`navigate_page_history`](#navigate_page_history) - [`new_page`](#new_page) - [`select_page`](#select_page) - [`wait_for`](#wait_for) @@ -151,18 +150,8 @@ **Parameters:** - **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used. -- **url** (string) **(required)**: URL to navigate the page to - ---- - -### `navigate_page_history` - -**Description:** Navigates the currently selected page. - -**Parameters:** - -- **navigate** (enum: "back", "forward") **(required)**: Whether to navigate back or navigate forward in the selected pages history -- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used. +- **type** (enum: "url", "back", "forward", "reload") _(optional)_: Navigate the page by URL, back or forward in history, or reload. +- **url** (string) _(optional)_: Target URL (only type=url) --- diff --git a/src/tools/pages.ts b/src/tools/pages.ts index 0dbaa39b7..d907ffad4 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -105,35 +105,13 @@ export const navigatePage = defineTool({ readOnlyHint: false, }, schema: { - url: zod.string().describe('URL to navigate the page to'), - ...timeoutSchema, - }, - handler: async (request, response, context) => { - const page = context.getSelectedPage(); - - await context.waitForEventsAfterAction(async () => { - await page.goto(request.params.url, { - timeout: request.params.timeout, - }); - }); - - response.setIncludePages(true); - }, -}); - -export const navigatePageHistory = defineTool({ - name: 'navigate_page_history', - description: `Navigates the currently selected page.`, - annotations: { - category: ToolCategory.NAVIGATION, - readOnlyHint: false, - }, - schema: { - navigate: zod - .enum(['back', 'forward']) + type: zod + .enum(['url', 'back', 'forward', 'reload']) + .optional() .describe( - 'Whether to navigate back or navigate forward in the selected pages history', + 'Navigate the page by URL, back or forward in history, or reload.', ), + url: zod.string().optional().describe('Target URL (only type=url)'), ...timeoutSchema, }, handler: async (request, response, context) => { @@ -142,18 +120,68 @@ export const navigatePageHistory = defineTool({ timeout: request.params.timeout, }; - try { - if (request.params.navigate === 'back') { - await page.goBack(options); - } else { - await page.goForward(options); - } - } catch (error) { - response.appendResponseLine( - `Unable to navigate ${request.params.navigate} in currently selected page. ${error.message}`, - ); + if (!request.params.type && !request.params.url) { + throw new Error('Either URL or a type is required.'); + } + + if (!request.params.type) { + request.params.type = 'url'; } + await context.waitForEventsAfterAction(async () => { + switch (request.params.type) { + case 'url': + if (!request.params.url) { + throw new Error('A URL is required for navigation of type=url.'); + } + try { + await page.goto(request.params.url, options); + response.appendResponseLine( + `Successfully navigated to ${request.params.url}.`, + ); + } catch (error) { + response.appendResponseLine( + `Unable to navigate in the selected page: ${error.message}.`, + ); + } + break; + case 'back': + try { + await page.goBack(options); + response.appendResponseLine( + `Successfully navigated back to ${page.url()}.`, + ); + } catch (error) { + response.appendResponseLine( + `Unable to navigate back in the selected page: ${error.message}.`, + ); + } + break; + case 'forward': + try { + await page.goForward(options); + response.appendResponseLine( + `Successfully navigated forward to ${page.url()}.`, + ); + } catch (error) { + response.appendResponseLine( + `Unable to navigate forward in the selected page: ${error.message}.`, + ); + } + break; + case 'reload': + try { + await page.reload(options); + response.appendResponseLine(`Successfully reloaded the page.`); + } catch (error) { + response.appendResponseLine( + `Unable to reload the selected page: ${error.message}.`, + ); + } + break; + } + }); + response.setIncludePages(true); }, }); diff --git a/tests/tools/pages.test.ts b/tests/tools/pages.test.ts index 43dfd918e..cf482ae89 100644 --- a/tests/tools/pages.test.ts +++ b/tests/tools/pages.test.ts @@ -14,7 +14,6 @@ import { closePage, selectPage, navigatePage, - navigatePageHistory, resizePage, handleDialog, } from '../../src/tools/pages.js'; @@ -29,7 +28,7 @@ describe('pages', () => { }); }); }); - describe('browser_new_page', () => { + describe('new_page', () => { it('create a page', async () => { await withBrowser(async (response, context) => { assert.strictEqual(context.getSelectedPageIdx(), 0); @@ -43,7 +42,7 @@ describe('pages', () => { }); }); }); - describe('browser_close_page', () => { + describe('close_page', () => { it('closes a page', async () => { await withBrowser(async (response, context) => { const page = await context.newPage(); @@ -67,7 +66,7 @@ describe('pages', () => { }); }); }); - describe('browser_select_page', () => { + describe('select_page', () => { it('selects a page', async () => { await withBrowser(async (response, context) => { await context.newPage(); @@ -78,7 +77,7 @@ describe('pages', () => { }); }); }); - describe('browser_navigate_page', () => { + describe('navigate_page', () => { it('navigates to correct page', async () => { await withBrowser(async (response, context) => { await navigatePage.handler( @@ -118,17 +117,11 @@ describe('pages', () => { } }); }); - }); - describe('browser_navigate_page_history', () => { it('go back', async () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); await page.goto('data:text/html,
Hello MCP
'); - await navigatePageHistory.handler( - {params: {navigate: 'back'}}, - response, - context, - ); + await navigatePage.handler({params: {type: 'back'}}, response, context); assert.equal( await page.evaluate(() => document.location.href), @@ -142,8 +135,8 @@ describe('pages', () => { const page = context.getSelectedPage(); await page.goto('data:text/html,
Hello MCP
'); await page.goBack(); - await navigatePageHistory.handler( - {params: {navigate: 'forward'}}, + await navigatePage.handler( + {params: {type: 'forward'}}, response, context, ); @@ -155,10 +148,27 @@ describe('pages', () => { assert.ok(response.includePages); }); }); + it('reload', async () => { + await withBrowser(async (response, context) => { + const page = context.getSelectedPage(); + await page.goto('data:text/html,
Hello MCP
'); + await navigatePage.handler( + {params: {type: 'reload'}}, + response, + context, + ); + + assert.equal( + await page.evaluate(() => document.location.href), + 'data:text/html,
Hello MCP
', + ); + assert.ok(response.includePages); + }); + }); it('go forward with error', async () => { await withBrowser(async (response, context) => { - await navigatePageHistory.handler( - {params: {navigate: 'forward'}}, + await navigatePage.handler( + {params: {type: 'forward'}}, response, context, ); @@ -166,31 +176,25 @@ describe('pages', () => { assert.ok( response.responseLines .at(0) - ?.startsWith( - 'Unable to navigate forward in currently selected page.', - ), + ?.startsWith('Unable to navigate forward in the selected page:'), ); assert.ok(response.includePages); }); }); it('go back with error', async () => { await withBrowser(async (response, context) => { - await navigatePageHistory.handler( - {params: {navigate: 'back'}}, - response, - context, - ); + await navigatePage.handler({params: {type: 'back'}}, response, context); assert.ok( response.responseLines .at(0) - ?.startsWith('Unable to navigate back in currently selected page.'), + ?.startsWith('Unable to navigate back in the selected page:'), ); assert.ok(response.includePages); }); }); }); - describe('browser_resize', () => { + describe('resize', () => { it('create a page', async () => { await withBrowser(async (response, context) => { assert.strictEqual(context.getSelectedPageIdx(), 0);