From 6591dad4343f2b3116e9aab25f1111d4a326882c Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 5 May 2025 08:31:52 -0700 Subject: [PATCH 1/3] Use width and height from puppeteer launch options as the default instead of hard coded values. --- src/puppeteer/README.md | 22 +++++++++++++++++ src/puppeteer/index.ts | 53 +++++++++++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/puppeteer/README.md b/src/puppeteer/README.md index f39b8a7bbd..76c680ef40 100644 --- a/src/puppeteer/README.md +++ b/src/puppeteer/README.md @@ -73,6 +73,28 @@ The server provides access to two types of resources: - Basic web interaction (navigation, clicking, form filling) - Customizable Puppeteer launch options +### Viewport Handling + +The browser viewport size can be controlled in multiple ways, with the following precedence: + +1. Tool Call Parameters (Highest Priority) + - When width/height are provided to `puppeteer_screenshot` + - When defaultViewport is set in `puppeteer_navigate` launchOptions + +2. Environment Configuration (Medium Priority) + - Via defaultViewport in PUPPETEER_LAUNCH_OPTIONS + - Example: `{ "defaultViewport": { "width": 1920, "height": 1080 } }` + +3. Default Values (Lowest Priority) + - Width: 800 pixels + - Height: 600 pixels + +The viewport settings are maintained across operations unless explicitly changed. For example, if you set the viewport via PUPPETEER_LAUNCH_OPTIONS, it will persist until either: +- A tool call provides new viewport dimensions +- The browser is restarted with different launch options + +This ensures consistent viewport behavior while allowing flexibility when needed. + ## Configuration to use Puppeteer Server ### Usage with Claude Desktop diff --git a/src/puppeteer/index.ts b/src/puppeteer/index.ts index 1849c78398..a8ce325f27 100644 --- a/src/puppeteer/index.ts +++ b/src/puppeteer/index.ts @@ -14,6 +14,10 @@ import { } from "@modelcontextprotocol/sdk/types.js"; import puppeteer, { Browser, Page } from "puppeteer"; +// Default viewport dimensions +const DEFAULT_VIEWPORT_WIDTH = 800; +const DEFAULT_VIEWPORT_HEIGHT = 600; + // Define the tools once to avoid repetition const TOOLS: Tool[] = [ { @@ -23,7 +27,7 @@ const TOOLS: Tool[] = [ type: "object", properties: { url: { type: "string", description: "URL to navigate to" }, - launchOptions: { type: "object", description: "PuppeteerJS LaunchOptions. Default null. If changed and not null, browser restarts. Example: { headless: true, args: ['--no-sandbox'] }" }, + launchOptions: { type: "object", description: "PuppeteerJS LaunchOptions. Values provided here will override any corresponding values from PUPPETEER_LAUNCH_OPTIONS environment variable. Example: { headless: true, defaultViewport: { width: 1920, height: 1080 }, args: ['--no-sandbox'] }. The defaultViewport settings will be maintained across operations." }, allowDangerous: { type: "boolean", description: "Allow dangerous LaunchOptions that reduce security. When false, dangerous args like --no-sandbox will throw errors. Default false." }, }, required: ["url"], @@ -37,8 +41,8 @@ const TOOLS: Tool[] = [ properties: { name: { type: "string", description: "Name for the screenshot" }, selector: { type: "string", description: "CSS selector for element to screenshot" }, - width: { type: "number", description: "Width in pixels (default: 800)" }, - height: { type: "number", description: "Height in pixels (default: 600)" }, + width: { type: "number", description: `Width in pixels (uses value from tool call if provided, otherwise from PUPPETEER_LAUNCH_OPTIONS if set, finally defaults to ${DEFAULT_VIEWPORT_WIDTH})` }, + height: { type: "number", description: `Height in pixels (uses value from tool call if provided, otherwise from PUPPETEER_LAUNCH_OPTIONS if set, finally defaults to ${DEFAULT_VIEWPORT_HEIGHT})` }, }, required: ["name"], }, @@ -108,6 +112,7 @@ let page: Page | null; const consoleLogs: string[] = []; const screenshots = new Map(); let previousLaunchOptions: any = null; +let storedViewport: { width: number; height: number } | null = null; async function ensureBrowser({ launchOptions, allowDangerous }: any) { @@ -158,13 +163,28 @@ async function ensureBrowser({ launchOptions, allowDangerous }: any) { if (!browser) { const npx_args = { headless: false } const docker_args = { headless: true, args: ["--no-sandbox", "--single-process", "--no-zygote"] } - browser = await puppeteer.launch(deepMerge( + const finalConfig = deepMerge( process.env.DOCKER_CONTAINER ? docker_args : npx_args, mergedConfig - )); + ); + + // Store viewport settings from launch options + if (finalConfig.defaultViewport) { + storedViewport = { + width: finalConfig.defaultViewport.width || DEFAULT_VIEWPORT_WIDTH, + height: finalConfig.defaultViewport.height || DEFAULT_VIEWPORT_HEIGHT + }; + } + + browser = await puppeteer.launch(finalConfig); const pages = await browser.pages(); page = pages[0]; + // Apply stored viewport settings + if (storedViewport) { + await page.setViewport(storedViewport); + } + page.on("console", (msg) => { const logEntry = `[${msg.type()}] ${msg.text()}`; consoleLogs.push(logEntry); @@ -226,9 +246,17 @@ async function handleToolCall(name: string, args: any): Promise }; case "puppeteer_screenshot": { - const width = args.width ?? 800; - const height = args.height ?? 600; - await page.setViewport({ width, height }); + let previousViewport = null; + + // Only change viewport if explicitly requested + if (args.width !== undefined || args.height !== undefined) { + if (storedViewport) { + previousViewport = { ...storedViewport }; + } + const width = args.width ?? (storedViewport?.width ?? DEFAULT_VIEWPORT_WIDTH); + const height = args.height ?? (storedViewport?.height ?? DEFAULT_VIEWPORT_HEIGHT); + await page.setViewport({ width, height }); + } const screenshot = await (args.selector ? (await page.$(args.selector))?.screenshot({ encoding: "base64" }) : @@ -249,11 +277,16 @@ async function handleToolCall(name: string, args: any): Promise method: "notifications/resources/list_changed", }); + // Restore previous viewport if we changed it + if (previousViewport) { + await page.setViewport(previousViewport); + } + return { content: [ { type: "text", - text: `Screenshot '${args.name}' taken at ${width}x${height}`, + text: `Screenshot '${args.name}' taken at ${page.viewport()?.width}x${page.viewport()?.height}`, } as TextContent, { type: "image", @@ -481,4 +514,4 @@ runServer().catch(console.error); process.stdin.on("close", () => { console.error("Puppeteer MCP Server closed"); server.close(); -}); \ No newline at end of file +}); From a3cda6d60f2db102f789582abf48f2940d72bf7c Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 5 May 2025 08:35:11 -0700 Subject: [PATCH 2/3] Remove unnecessary comments --- src/puppeteer/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/puppeteer/index.ts b/src/puppeteer/index.ts index a8ce325f27..8740de7f82 100644 --- a/src/puppeteer/index.ts +++ b/src/puppeteer/index.ts @@ -14,7 +14,6 @@ import { } from "@modelcontextprotocol/sdk/types.js"; import puppeteer, { Browser, Page } from "puppeteer"; -// Default viewport dimensions const DEFAULT_VIEWPORT_WIDTH = 800; const DEFAULT_VIEWPORT_HEIGHT = 600; @@ -168,7 +167,6 @@ async function ensureBrowser({ launchOptions, allowDangerous }: any) { mergedConfig ); - // Store viewport settings from launch options if (finalConfig.defaultViewport) { storedViewport = { width: finalConfig.defaultViewport.width || DEFAULT_VIEWPORT_WIDTH, @@ -180,7 +178,6 @@ async function ensureBrowser({ launchOptions, allowDangerous }: any) { const pages = await browser.pages(); page = pages[0]; - // Apply stored viewport settings if (storedViewport) { await page.setViewport(storedViewport); } @@ -277,7 +274,6 @@ async function handleToolCall(name: string, args: any): Promise method: "notifications/resources/list_changed", }); - // Restore previous viewport if we changed it if (previousViewport) { await page.setViewport(previousViewport); } From 7920fe05336aa55a4f0015f5cd628f53f53902db Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Wed, 7 May 2025 05:45:08 -0700 Subject: [PATCH 3/3] Fix merge casualty --- src/puppeteer/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/puppeteer/index.ts b/src/puppeteer/index.ts index 020e12ec9d..eb3286a106 100644 --- a/src/puppeteer/index.ts +++ b/src/puppeteer/index.ts @@ -255,6 +255,7 @@ async function handleToolCall(name: string, args: any): Promise const width = args.width ?? (storedViewport?.width ?? DEFAULT_VIEWPORT_WIDTH); const height = args.height ?? (storedViewport?.height ?? DEFAULT_VIEWPORT_HEIGHT); await page.setViewport({ width, height }); + } const screenshot = await (args.selector ? (await page.$(args.selector))?.screenshot({ encoding: "base64" }) :