From 7bd535c3fdca41c7ddf781510b391cab1b714edd Mon Sep 17 00:00:00 2001 From: Piotr Paulski Date: Fri, 17 Oct 2025 13:40:27 +0000 Subject: [PATCH 1/3] refactor: bundle zod together with modelcontextprotocol/sdk --- src/third_party/modelcontextprotocol-sdk/index.ts | 1 + src/tools/ToolDefinition.ts | 2 +- src/tools/console.ts | 3 ++- src/tools/emulation.ts | 3 ++- src/tools/input.ts | 2 +- src/tools/network.ts | 3 ++- src/tools/pages.ts | 3 +-- src/tools/performance.ts | 2 +- src/tools/screenshot.ts | 3 ++- src/tools/script.ts | 3 ++- src/tools/snapshot.ts | 3 ++- 11 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/third_party/modelcontextprotocol-sdk/index.ts b/src/third_party/modelcontextprotocol-sdk/index.ts index 3c3d05bf..e0c3e990 100644 --- a/src/third_party/modelcontextprotocol-sdk/index.ts +++ b/src/third_party/modelcontextprotocol-sdk/index.ts @@ -12,3 +12,4 @@ export { type ImageContent, type TextContent, } from '@modelcontextprotocol/sdk/types.js'; +export {default as z} from 'zod'; diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index f5cb454b..9304e09a 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -5,9 +5,9 @@ */ import type {Dialog, ElementHandle, Page} from 'puppeteer-core'; -import z from 'zod'; import type {TextSnapshotNode} from '../McpContext.js'; +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import type {TraceResult} from '../trace-processing/parse.js'; import type {PaginationOptions} from '../utils/types.js'; diff --git a/src/tools/console.ts b/src/tools/console.ts index 5b049111..0b873fed 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -5,7 +5,8 @@ */ import type {ConsoleMessageType} from 'puppeteer-core'; -import z from 'zod'; + +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/emulation.ts b/src/tools/emulation.ts index 92b949e9..a3684155 100644 --- a/src/tools/emulation.ts +++ b/src/tools/emulation.ts @@ -5,7 +5,8 @@ */ import {PredefinedNetworkConditions} from 'puppeteer-core'; -import z from 'zod'; + +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/input.ts b/src/tools/input.ts index 02bb8a0f..a9169599 100644 --- a/src/tools/input.ts +++ b/src/tools/input.ts @@ -5,9 +5,9 @@ */ import type {ElementHandle} from 'puppeteer-core'; -import z from 'zod'; import type {McpContext, TextSnapshotNode} from '../McpContext.js'; +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/network.ts b/src/tools/network.ts index 23b2c1f6..f8a33f27 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -5,7 +5,8 @@ */ import type {ResourceType} from 'puppeteer-core'; -import z from 'zod'; + +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/pages.ts b/src/tools/pages.ts index 7643881d..923ac8be 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -4,9 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import z from 'zod'; - import {logger} from '../logger.js'; +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {CLOSE_PAGE_ERROR, defineTool, timeoutSchema} from './ToolDefinition.js'; diff --git a/src/tools/performance.ts b/src/tools/performance.ts index 9a6a3b95..0ca11b2f 100644 --- a/src/tools/performance.ts +++ b/src/tools/performance.ts @@ -5,9 +5,9 @@ */ import type {Page} from 'puppeteer-core'; -import z from 'zod'; import {logger} from '../logger.js'; +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import type {InsightName} from '../trace-processing/parse.js'; import { getInsightOutput, diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index 901a880d..baf07534 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -5,7 +5,8 @@ */ import type {ElementHandle, Page} from 'puppeteer-core'; -import z from 'zod'; + +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/script.ts b/src/tools/script.ts index be6349d1..c8f7ee50 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -4,7 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ import type {JSHandle} from 'puppeteer-core'; -import z from 'zod'; + +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; diff --git a/src/tools/snapshot.ts b/src/tools/snapshot.ts index 75ab1d0e..8c74db0e 100644 --- a/src/tools/snapshot.ts +++ b/src/tools/snapshot.ts @@ -5,7 +5,8 @@ */ import {Locator} from 'puppeteer-core'; -import z from 'zod'; + +import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool, timeoutSchema} from './ToolDefinition.js'; From 82163cfb0dbd7b40c0d2fff3a432d4bf60c5ca7c Mon Sep 17 00:00:00 2001 From: Piotr Paulski Date: Fri, 17 Oct 2025 13:48:25 +0000 Subject: [PATCH 2/3] Remove bundled dependency. --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index a54543a6..f31e95be 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,7 @@ "core-js": "3.46.0", "debug": "4.4.3", "puppeteer-core": "^24.24.1", - "yargs": "18.0.0", - "zod": "^3.25.76" + "yargs": "18.0.0" }, "devDependencies": { "@eslint/js": "^9.35.0", From 1881258c5b99d9f4a7e31ead66d60ff86e3c5478 Mon Sep 17 00:00:00 2001 From: Piotr Paulski Date: Fri, 17 Oct 2025 16:53:01 +0000 Subject: [PATCH 3/3] Update zod imports for better readability. --- .../modelcontextprotocol-sdk/index.ts | 2 +- src/tools/ToolDefinition.ts | 14 ++++++---- src/tools/console.ts | 10 +++---- src/tools/emulation.ts | 6 ++-- src/tools/input.ts | 28 +++++++++---------- src/tools/network.ts | 12 ++++---- src/tools/pages.ts | 20 ++++++------- src/tools/performance.ts | 8 +++--- src/tools/screenshot.ts | 12 ++++---- src/tools/script.ts | 10 +++---- src/tools/snapshot.ts | 6 ++-- 11 files changed, 65 insertions(+), 63 deletions(-) diff --git a/src/third_party/modelcontextprotocol-sdk/index.ts b/src/third_party/modelcontextprotocol-sdk/index.ts index e0c3e990..6366490f 100644 --- a/src/third_party/modelcontextprotocol-sdk/index.ts +++ b/src/third_party/modelcontextprotocol-sdk/index.ts @@ -12,4 +12,4 @@ export { type ImageContent, type TextContent, } from '@modelcontextprotocol/sdk/types.js'; -export {default as z} from 'zod'; +export {z as zod} from 'zod'; diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 9304e09a..7c3b379f 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -7,13 +7,15 @@ import type {Dialog, ElementHandle, Page} from 'puppeteer-core'; import type {TextSnapshotNode} from '../McpContext.js'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import type {TraceResult} from '../trace-processing/parse.js'; import type {PaginationOptions} from '../utils/types.js'; import type {ToolCategories} from './categories.js'; -export interface ToolDefinition { +export interface ToolDefinition< + Schema extends zod.ZodRawShape = zod.ZodRawShape, +> { name: string; description: string; annotations: { @@ -32,8 +34,8 @@ export interface ToolDefinition { ) => Promise; } -export interface Request { - params: z.objectOutputType; +export interface Request { + params: zod.objectOutputType; } export interface ImageContentData { @@ -92,7 +94,7 @@ export type Context = Readonly<{ waitForEventsAfterAction(action: () => Promise): Promise; }>; -export function defineTool( +export function defineTool( definition: ToolDefinition, ) { return definition; @@ -102,7 +104,7 @@ export const CLOSE_PAGE_ERROR = 'The last open page cannot be closed. It is fine to keep it open.'; export const timeoutSchema = { - timeout: z + timeout: zod .number() .int() .optional() diff --git a/src/tools/console.ts b/src/tools/console.ts index 0b873fed..91ac20d4 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -6,7 +6,7 @@ import type {ConsoleMessageType} from 'puppeteer-core'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; @@ -45,7 +45,7 @@ export const consoleTool = defineTool({ readOnlyHint: true, }, schema: { - pageSize: z + pageSize: zod .number() .int() .positive() @@ -53,7 +53,7 @@ export const consoleTool = defineTool({ .describe( 'Maximum number of messages to return. When omitted, returns all requests.', ), - pageIdx: z + pageIdx: zod .number() .int() .min(0) @@ -61,8 +61,8 @@ export const consoleTool = defineTool({ .describe( 'Page number to return (0-based). When omitted, returns the first page.', ), - types: z - .array(z.enum(FILTERABLE_MESSAGE_TYPES)) + 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.', diff --git a/src/tools/emulation.ts b/src/tools/emulation.ts index a3684155..d65ac5e8 100644 --- a/src/tools/emulation.ts +++ b/src/tools/emulation.ts @@ -6,7 +6,7 @@ import {PredefinedNetworkConditions} from 'puppeteer-core'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; @@ -25,7 +25,7 @@ export const emulateNetwork = defineTool({ readOnlyHint: false, }, schema: { - throttlingOption: z + throttlingOption: zod .enum(throttlingOptions) .describe( `The network throttling option to emulate. Available throttling options are: ${throttlingOptions.join(', ')}. Set to "No emulation" to disable. Set to "Offline" to simulate offline network conditions.`, @@ -71,7 +71,7 @@ export const emulateCpu = defineTool({ readOnlyHint: false, }, schema: { - throttlingRate: z + throttlingRate: zod .number() .min(1) .max(20) diff --git a/src/tools/input.ts b/src/tools/input.ts index a9169599..ffb1cbb7 100644 --- a/src/tools/input.ts +++ b/src/tools/input.ts @@ -7,7 +7,7 @@ import type {ElementHandle} from 'puppeteer-core'; import type {McpContext, TextSnapshotNode} from '../McpContext.js'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; @@ -20,12 +20,12 @@ export const click = defineTool({ readOnlyHint: false, }, schema: { - uid: z + uid: zod .string() .describe( 'The uid of an element on the page from the page content snapshot', ), - dblClick: z + dblClick: zod .boolean() .optional() .describe('Set to true for double clicks. Default is false.'), @@ -59,7 +59,7 @@ export const hover = defineTool({ readOnlyHint: false, }, schema: { - uid: z + uid: zod .string() .describe( 'The uid of an element on the page from the page content snapshot', @@ -143,12 +143,12 @@ export const fill = defineTool({ readOnlyHint: false, }, schema: { - uid: z + uid: zod .string() .describe( 'The uid of an element on the page from the page content snapshot', ), - value: z.string().describe('The value to fill in'), + value: zod.string().describe('The value to fill in'), }, handler: async (request, response, context) => { await context.waitForEventsAfterAction(async () => { @@ -171,8 +171,8 @@ export const drag = defineTool({ readOnlyHint: false, }, schema: { - from_uid: z.string().describe('The uid of the element to drag'), - to_uid: z.string().describe('The uid of the element to drop into'), + 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'), }, handler: async (request, response, context) => { const fromHandle = await context.getElementByUid(request.params.from_uid); @@ -200,11 +200,11 @@ export const fillForm = defineTool({ readOnlyHint: false, }, schema: { - elements: z + elements: zod .array( - z.object({ - uid: z.string().describe('The uid of the element to fill out'), - value: z.string().describe('Value for the element'), + zod.object({ + uid: zod.string().describe('The uid of the element to fill out'), + value: zod.string().describe('Value for the element'), }), ) .describe('Elements from snapshot to fill out.'), @@ -232,12 +232,12 @@ export const uploadFile = defineTool({ readOnlyHint: false, }, schema: { - uid: z + uid: zod .string() .describe( 'The uid of the file input element or an element that will open file chooser on the page from the page content snapshot', ), - filePath: z.string().describe('The local path of the file to upload'), + filePath: zod.string().describe('The local path of the file to upload'), }, handler: async (request, response, context) => { const {uid, filePath} = request.params; diff --git a/src/tools/network.ts b/src/tools/network.ts index f8a33f27..0dbc7f7e 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -6,7 +6,7 @@ import type {ResourceType} from 'puppeteer-core'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; @@ -41,7 +41,7 @@ export const listNetworkRequests = defineTool({ readOnlyHint: true, }, schema: { - pageSize: z + pageSize: zod .number() .int() .positive() @@ -49,7 +49,7 @@ export const listNetworkRequests = defineTool({ .describe( 'Maximum number of requests to return. When omitted, returns all requests.', ), - pageIdx: z + pageIdx: zod .number() .int() .min(0) @@ -57,8 +57,8 @@ export const listNetworkRequests = defineTool({ .describe( 'Page number to return (0-based). When omitted, returns the first page.', ), - resourceTypes: z - .array(z.enum(FILTERABLE_RESOURCE_TYPES)) + 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.', @@ -81,7 +81,7 @@ export const getNetworkRequest = defineTool({ readOnlyHint: true, }, schema: { - reqid: z + reqid: zod .number() .describe( 'The reqid of a request on the page from the listed network requests', diff --git a/src/tools/pages.ts b/src/tools/pages.ts index 923ac8be..ac6756d2 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -5,7 +5,7 @@ */ import {logger} from '../logger.js'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {CLOSE_PAGE_ERROR, defineTool, timeoutSchema} from './ToolDefinition.js'; @@ -31,7 +31,7 @@ export const selectPage = defineTool({ readOnlyHint: true, }, schema: { - pageIdx: z + pageIdx: zod .number() .describe( 'The index of the page to select. Call list_pages to list pages.', @@ -53,7 +53,7 @@ export const closePage = defineTool({ readOnlyHint: false, }, schema: { - pageIdx: z + pageIdx: zod .number() .describe( 'The index of the page to close. Call list_pages to list pages.', @@ -81,7 +81,7 @@ export const newPage = defineTool({ readOnlyHint: false, }, schema: { - url: z.string().describe('URL to load in a new page.'), + url: zod.string().describe('URL to load in a new page.'), ...timeoutSchema, }, handler: async (request, response, context) => { @@ -105,7 +105,7 @@ export const navigatePage = defineTool({ readOnlyHint: false, }, schema: { - url: z.string().describe('URL to navigate the page to'), + url: zod.string().describe('URL to navigate the page to'), ...timeoutSchema, }, handler: async (request, response, context) => { @@ -129,7 +129,7 @@ export const navigatePageHistory = defineTool({ readOnlyHint: false, }, schema: { - navigate: z + navigate: zod .enum(['back', 'forward']) .describe( 'Whether to navigate back or navigate forward in the selected pages history', @@ -166,8 +166,8 @@ export const resizePage = defineTool({ readOnlyHint: false, }, schema: { - width: z.number().describe('Page width'), - height: z.number().describe('Page height'), + width: zod.number().describe('Page width'), + height: zod.number().describe('Page height'), }, handler: async (request, response, context) => { const page = context.getSelectedPage(); @@ -190,10 +190,10 @@ export const handleDialog = defineTool({ readOnlyHint: false, }, schema: { - action: z + action: zod .enum(['accept', 'dismiss']) .describe('Whether to dismiss or accept the dialog'), - promptText: z + promptText: zod .string() .optional() .describe('Optional prompt text to enter into the dialog.'), diff --git a/src/tools/performance.ts b/src/tools/performance.ts index 0ca11b2f..6c172fbb 100644 --- a/src/tools/performance.ts +++ b/src/tools/performance.ts @@ -7,7 +7,7 @@ import type {Page} from 'puppeteer-core'; import {logger} from '../logger.js'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import type {InsightName} from '../trace-processing/parse.js'; import { getInsightOutput, @@ -29,12 +29,12 @@ export const startTrace = defineTool({ readOnlyHint: true, }, schema: { - reload: z + reload: zod .boolean() .describe( 'Determines if, once tracing has started, the page should be automatically reloaded.', ), - autoStop: z + autoStop: zod .boolean() .describe( 'Determines if the trace recording should be automatically stopped.', @@ -128,7 +128,7 @@ export const analyzeInsight = defineTool({ readOnlyHint: true, }, schema: { - insightName: z + insightName: zod .string() .describe( 'The name of the Insight you want more information on. For example: "DocumentLatency" or "LCPBreakdown"', diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index baf07534..991fbd2b 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -6,7 +6,7 @@ import type {ElementHandle, Page} from 'puppeteer-core'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; @@ -19,11 +19,11 @@ export const screenshot = defineTool({ readOnlyHint: true, }, schema: { - format: z + format: zod .enum(['png', 'jpeg', 'webp']) .default('png') .describe('Type of format to save the screenshot as. Default is "png"'), - quality: z + quality: zod .number() .min(0) .max(100) @@ -31,19 +31,19 @@ export const screenshot = defineTool({ .describe( 'Compression quality for JPEG and WebP formats (0-100). Higher values mean better quality but larger file sizes. Ignored for PNG format.', ), - uid: z + uid: zod .string() .optional() .describe( 'The uid of an element on the page from the page content snapshot. If omitted takes a pages screenshot.', ), - fullPage: z + fullPage: zod .boolean() .optional() .describe( 'If set to true takes a screenshot of the full page instead of the currently visible viewport. Incompatible with uid.', ), - filePath: z + filePath: zod .string() .optional() .describe( diff --git a/src/tools/script.ts b/src/tools/script.ts index c8f7ee50..beaef94b 100644 --- a/src/tools/script.ts +++ b/src/tools/script.ts @@ -5,7 +5,7 @@ */ import type {JSHandle} from 'puppeteer-core'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; @@ -19,7 +19,7 @@ so returned values have to JSON-serializable.`, readOnlyHint: false, }, schema: { - function: z.string().describe( + function: zod.string().describe( `A JavaScript function declaration to be executed by the tool in the currently selected page. Example without arguments: \`() => { return document.title @@ -31,10 +31,10 @@ Example with arguments: \`(el) => { }\` `, ), - args: z + args: zod .array( - z.object({ - uid: z + zod.object({ + uid: zod .string() .describe( 'The uid of an element on the page from the page content snapshot', diff --git a/src/tools/snapshot.ts b/src/tools/snapshot.ts index 8c74db0e..fb774f78 100644 --- a/src/tools/snapshot.ts +++ b/src/tools/snapshot.ts @@ -6,7 +6,7 @@ import {Locator} from 'puppeteer-core'; -import {z} from '../third_party/modelcontextprotocol-sdk/index.js'; +import {zod} from '../third_party/modelcontextprotocol-sdk/index.js'; import {ToolCategories} from './categories.js'; import {defineTool, timeoutSchema} from './ToolDefinition.js'; @@ -20,7 +20,7 @@ identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over readOnlyHint: true, }, schema: { - verbose: z + verbose: zod .boolean() .optional() .describe( @@ -40,7 +40,7 @@ export const waitFor = defineTool({ readOnlyHint: true, }, schema: { - text: z.string().describe('Text to appear on the page'), + text: zod.string().describe('Text to appear on the page'), ...timeoutSchema, }, handler: async (request, response, context) => {