diff --git a/docs/tool-reference.md b/docs/tool-reference.md index 93858193b..ac93d2ab1 100644 --- a/docs/tool-reference.md +++ b/docs/tool-reference.md @@ -157,6 +157,7 @@ - **handleBeforeUnload** (enum: "accept", "decline") _(optional)_: Whether to auto accept or beforeunload dialogs triggered by this navigation. Default is accept. - **ignoreCache** (boolean) _(optional)_: Whether to ignore cache on reload. +- **initScript** (string) _(optional)_: A JavaScript script to be executed on each new document before any other scripts for the next navigation. - **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 106de86e5..7b1a6e9e0 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -133,6 +133,12 @@ export const navigatePage = defineTool({ .describe( 'Whether to auto accept or beforeunload dialogs triggered by this navigation. Default is accept.', ), + initScript: zod + .string() + .optional() + .describe( + 'A JavaScript script to be executed on each new document before any other scripts for the next navigation.', + ), ...timeoutSchema, }, handler: async (request, response, context) => { @@ -163,6 +169,15 @@ export const navigatePage = defineTool({ context.clearDialog(); } }; + + let initScriptId: string | undefined; + if (request.params.initScript) { + const {identifier} = await page.evaluateOnNewDocument( + request.params.initScript, + ); + initScriptId = identifier; + } + page.on('dialog', dialogHandler); try { @@ -224,6 +239,13 @@ export const navigatePage = defineTool({ }); } finally { page.off('dialog', dialogHandler); + if (initScriptId) { + await page + .removeScriptToEvaluateOnNewDocument(initScriptId) + .catch(error => { + logger(`Failed to remove init script`, error); + }); + } } response.setIncludePages(true); diff --git a/tests/tools/pages.test.ts b/tests/tools/pages.test.ts index da0fd13fe..b3f6767dd 100644 --- a/tests/tools/pages.test.ts +++ b/tests/tools/pages.test.ts @@ -297,6 +297,28 @@ describe('pages', () => { assert.ok(response.includePages); }); }); + it('navigates to correct page with initScript', async () => { + await withMcpContext(async (response, context) => { + await navigatePage.handler( + { + params: { + url: 'data:text/html,
Hello MCP
', + initScript: 'window.initScript = "completed"', + }, + }, + response, + context, + ); + const page = context.getSelectedPage(); + + // wait for up to 1s for the global variable to set by the initScript to exist + await page.waitForFunction("window.initScript==='completed'", { + timeout: 1000, + }); + + assert.ok(response.includePages); + }); + }); }); describe('resize', () => { it('resize the page', async () => {