diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 7c3b379f..fbe94047 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {Dialog, ElementHandle, Page} from 'puppeteer-core'; +import type {ConsoleMessageType, Dialog, ElementHandle, Page, ResourceType} from 'puppeteer-core'; import type {TextSnapshotNode} from '../McpContext.js'; import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; @@ -115,3 +115,109 @@ export const timeoutSchema = { return value && value <= 0 ? undefined : value; }), }; + +export const snapshotSchema = { + verbose: zod + .boolean() + .optional() + .describe( + 'Whether to include all possible information available in the full a11y tree. Default is false.', + ), +}; + +const FILTERABLE_MESSAGE_TYPES: readonly [ + ConsoleMessageType, + ...ConsoleMessageType[], +] = [ + 'log', + 'debug', + 'info', + 'error', + 'warn', + 'dir', + 'dirxml', + 'table', + 'trace', + 'clear', + 'startGroup', + 'startGroupCollapsed', + 'endGroup', + 'assert', + 'profile', + 'profileEnd', + 'count', + 'timeEnd', + 'verbose', + ] + +export const consoleMessagesSchema = { + pageSize: zod + .number() + .int() + .positive() + .optional() + .describe( + 'Maximum number of messages to return. When omitted, returns all requests.', + ), + pageIdx: zod + .number() + .int() + .min(0) + .optional() + .describe( + 'Page number to return (0-based). When omitted, returns the first page.', + ), + types: zod + .array(zod.enum(FILTERABLE_MESSAGE_TYPES)) + .optional() + .describe( + 'Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages.', + ), +}; + +const FILTERABLE_RESOURCE_TYPES: readonly [ResourceType, ...ResourceType[]] = [ + 'document', + 'stylesheet', + 'image', + 'media', + 'font', + 'script', + 'texttrack', + 'xhr', + 'fetch', + 'prefetch', + 'eventsource', + 'websocket', + 'manifest', + 'signedexchange', + 'ping', + 'cspviolationreport', + 'preflight', + 'fedcm', + 'other', +]; + +export const networkRequestsSchema = { + pageSize: zod + .number() + .int() + .positive() + .optional() + .describe( + 'Maximum number of requests to return. When omitted, returns all requests.', + ), + pageIdx: zod + .number() + .int() + .min(0) + .optional() + .describe( + 'Page number to return (0-based). When omitted, returns the first page.', + ), + resourceTypes: zod + .array(zod.enum(FILTERABLE_RESOURCE_TYPES)) + .optional() + .describe( + 'Filter requests to only return requests of the specified resource types. When omitted or empty, returns all requests.', + ), +} \ No newline at end of file diff --git a/src/tools/console.ts b/src/tools/console.ts index 91ac20d4..b53ccb2b 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -4,37 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {ConsoleMessageType} from 'puppeteer-core'; - -import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; - import {ToolCategories} from './categories.js'; -import {defineTool} from './ToolDefinition.js'; - -const FILTERABLE_MESSAGE_TYPES: readonly [ - ConsoleMessageType, - ...ConsoleMessageType[], -] = [ - 'log', - 'debug', - 'info', - 'error', - 'warn', - 'dir', - 'dirxml', - 'table', - 'trace', - 'clear', - 'startGroup', - 'startGroupCollapsed', - 'endGroup', - 'assert', - 'profile', - 'profileEnd', - 'count', - 'timeEnd', - 'verbose', -]; +import {consoleMessagesSchema, defineTool} from './ToolDefinition.js'; export const consoleTool = defineTool({ name: 'list_console_messages', @@ -45,28 +16,7 @@ export const consoleTool = defineTool({ readOnlyHint: true, }, schema: { - pageSize: zod - .number() - .int() - .positive() - .optional() - .describe( - 'Maximum number of messages to return. When omitted, returns all requests.', - ), - pageIdx: zod - .number() - .int() - .min(0) - .optional() - .describe( - 'Page number to return (0-based). When omitted, returns the first page.', - ), - types: zod - .array(zod.enum(FILTERABLE_MESSAGE_TYPES)) - .optional() - .describe( - 'Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages.', - ), + ...consoleMessagesSchema, }, handler: async (request, response) => { response.setIncludeConsoleData(true, { diff --git a/src/tools/input.ts b/src/tools/input.ts index ffb1cbb7..1ead726a 100644 --- a/src/tools/input.ts +++ b/src/tools/input.ts @@ -10,7 +10,7 @@ import type {McpContext, TextSnapshotNode} from '../McpContext.js'; import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; -import {defineTool} from './ToolDefinition.js'; +import {defineTool, snapshotSchema} from './ToolDefinition.js'; export const click = defineTool({ name: 'click', @@ -29,6 +29,9 @@ export const click = defineTool({ .boolean() .optional() .describe('Set to true for double clicks. Default is false.'), + snapshot: zod.object({ + ...snapshotSchema, + }).optional().describe('Options for the snapshot included in the response'), }, handler: async (request, response, context) => { const uid = request.params.uid; @@ -44,7 +47,7 @@ export const click = defineTool({ ? `Successfully double clicked on the element` : `Successfully clicked on the element`, ); - response.setIncludeSnapshot(true); + response.setIncludeSnapshot(true, request.params.snapshot?.verbose ?? false); } finally { void handle.dispose(); } @@ -64,6 +67,9 @@ export const hover = defineTool({ .describe( 'The uid of an element on the page from the page content snapshot', ), + snapshot: zod.object({ + ...snapshotSchema, + }).optional().describe('Options for the snapshot included in the response'), }, handler: async (request, response, context) => { const uid = request.params.uid; @@ -73,7 +79,7 @@ export const hover = defineTool({ await handle.asLocator().hover(); }); response.appendResponseLine(`Successfully hovered over the element`); - response.setIncludeSnapshot(true); + response.setIncludeSnapshot(true, request.params.snapshot?.verbose ?? false); } finally { void handle.dispose(); } @@ -149,6 +155,9 @@ export const fill = defineTool({ 'The uid of an element on the page from the page content snapshot', ), value: zod.string().describe('The value to fill in'), + snapshot: zod.object({ + ...snapshotSchema, + }).optional().describe('Options for the snapshot included in the response'), }, handler: async (request, response, context) => { await context.waitForEventsAfterAction(async () => { @@ -159,7 +168,7 @@ export const fill = defineTool({ ); }); response.appendResponseLine(`Successfully filled out the element`); - response.setIncludeSnapshot(true); + response.setIncludeSnapshot(true, request.params.snapshot?.verbose ?? false); }, }); @@ -173,6 +182,9 @@ export const drag = defineTool({ schema: { from_uid: zod.string().describe('The uid of the element to drag'), to_uid: zod.string().describe('The uid of the element to drop into'), + snapshot: zod.object({ + ...snapshotSchema, + }).optional().describe('Options for the snapshot included in the response'), }, handler: async (request, response, context) => { const fromHandle = await context.getElementByUid(request.params.from_uid); @@ -184,7 +196,7 @@ export const drag = defineTool({ await toHandle.drop(fromHandle); }); response.appendResponseLine(`Successfully dragged an element`); - response.setIncludeSnapshot(true); + response.setIncludeSnapshot(true, request.params.snapshot?.verbose ?? false); } finally { void fromHandle.dispose(); void toHandle.dispose(); @@ -208,6 +220,9 @@ export const fillForm = defineTool({ }), ) .describe('Elements from snapshot to fill out.'), + snapshot: zod.object({ + ...snapshotSchema, + }).optional().describe('Options for the snapshot included in the response'), }, handler: async (request, response, context) => { for (const element of request.params.elements) { @@ -220,7 +235,7 @@ export const fillForm = defineTool({ }); } response.appendResponseLine(`Successfully filled out the form`); - response.setIncludeSnapshot(true); + response.setIncludeSnapshot(true, request.params.snapshot?.verbose ?? false); }, }); @@ -238,6 +253,9 @@ export const uploadFile = defineTool({ 'The uid of the file input element or an element that will open file chooser on the page from the page content snapshot', ), filePath: zod.string().describe('The local path of the file to upload'), + snapshot: zod.object({ + ...snapshotSchema, + }).optional().describe('Options for the snapshot included in the response'), }, handler: async (request, response, context) => { const {uid, filePath} = request.params; @@ -264,7 +282,7 @@ export const uploadFile = defineTool({ ); } } - response.setIncludeSnapshot(true); + response.setIncludeSnapshot(true, request.params.snapshot?.verbose ?? false); response.appendResponseLine(`File uploaded from ${filePath}.`); } finally { void handle.dispose(); diff --git a/src/tools/network.ts b/src/tools/network.ts index 0dbc7f7e..f32e4429 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -4,34 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type {ResourceType} from 'puppeteer-core'; - import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; -import {defineTool} from './ToolDefinition.js'; - -const FILTERABLE_RESOURCE_TYPES: readonly [ResourceType, ...ResourceType[]] = [ - 'document', - 'stylesheet', - 'image', - 'media', - 'font', - 'script', - 'texttrack', - 'xhr', - 'fetch', - 'prefetch', - 'eventsource', - 'websocket', - 'manifest', - 'signedexchange', - 'ping', - 'cspviolationreport', - 'preflight', - 'fedcm', - 'other', -]; +import {defineTool, networkRequestsSchema} from './ToolDefinition.js'; export const listNetworkRequests = defineTool({ name: 'list_network_requests', @@ -41,28 +17,7 @@ export const listNetworkRequests = defineTool({ readOnlyHint: true, }, schema: { - pageSize: zod - .number() - .int() - .positive() - .optional() - .describe( - 'Maximum number of requests to return. When omitted, returns all requests.', - ), - pageIdx: zod - .number() - .int() - .min(0) - .optional() - .describe( - 'Page number to return (0-based). When omitted, returns the first page.', - ), - resourceTypes: zod - .array(zod.enum(FILTERABLE_RESOURCE_TYPES)) - .optional() - .describe( - 'Filter requests to only return requests of the specified resource types. When omitted or empty, returns all requests.', - ), + ...networkRequestsSchema, }, handler: async (request, response) => { response.setIncludeNetworkRequests(true, { diff --git a/src/tools/snapshot.ts b/src/tools/snapshot.ts index fb774f78..995044d6 100644 --- a/src/tools/snapshot.ts +++ b/src/tools/snapshot.ts @@ -9,7 +9,7 @@ import {Locator} from 'puppeteer-core'; import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; -import {defineTool, timeoutSchema} from './ToolDefinition.js'; +import {defineTool, snapshotSchema, timeoutSchema} from './ToolDefinition.js'; export const takeSnapshot = defineTool({ name: 'take_snapshot', @@ -20,12 +20,7 @@ identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over readOnlyHint: true, }, schema: { - verbose: zod - .boolean() - .optional() - .describe( - 'Whether to include all possible information available in the full a11y tree. Default is false.', - ), + ...snapshotSchema, }, handler: async (request, response) => { response.setIncludeSnapshot(true, request.params.verbose ?? false); @@ -41,6 +36,9 @@ export const waitFor = defineTool({ }, schema: { text: zod.string().describe('Text to appear on the page'), + snapshot: zod.object({ + ...snapshotSchema, + }).optional().describe('Options for the snapshot included in the response'), ...timeoutSchema, }, handler: async (request, response, context) => { @@ -64,6 +62,6 @@ export const waitFor = defineTool({ `Element with text "${request.params.text}" found.`, ); - response.setIncludeSnapshot(true); + response.setIncludeSnapshot(true, request.params.snapshot?.verbose ?? false); }, });