Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/puppeteer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,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
Expand Down
49 changes: 39 additions & 10 deletions src/puppeteer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import {
} from "@modelcontextprotocol/sdk/types.js";
import puppeteer, { Browser, Page } from "puppeteer";

const DEFAULT_VIEWPORT_WIDTH = 800;
const DEFAULT_VIEWPORT_HEIGHT = 600;

// Define the tools once to avoid repetition
const TOOLS: Tool[] = [
{
Expand All @@ -23,7 +26,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"],
Expand All @@ -37,8 +40,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})` },
encoded: { type: "boolean", description: "If true, capture the screenshot as a base64-encoded data URI (as text) instead of binary image content. Default false." },
},
required: ["name"],
Expand Down Expand Up @@ -109,6 +112,7 @@ let page: Page | null;
const consoleLogs: string[] = [];
const screenshots = new Map<string, string>();
let previousLaunchOptions: any = null;
let storedViewport: { width: number; height: number } | null = null;

async function ensureBrowser({ launchOptions, allowDangerous }: any) {

Expand Down Expand Up @@ -159,13 +163,26 @@ 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
));
);

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];

if (storedViewport) {
await page.setViewport(storedViewport);
}

page.on("console", (msg) => {
const logEntry = `[${msg.type()}] ${msg.text()}`;
consoleLogs.push(logEntry);
Expand Down Expand Up @@ -227,10 +244,18 @@ async function handleToolCall(name: string, args: any): Promise<CallToolResult>
};

case "puppeteer_screenshot": {
const width = args.width ?? 800;
const height = args.height ?? 600;
let previousViewport = null;
const encoded = args.encoded ?? false;
await page.setViewport({ width, height });

// 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" }) :
Expand All @@ -251,11 +276,15 @@ async function handleToolCall(name: string, args: any): Promise<CallToolResult>
method: "notifications/resources/list_changed",
});

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,
encoded ? ({
type: "text",
Expand Down Expand Up @@ -486,4 +515,4 @@ runServer().catch(console.error);
process.stdin.on("close", () => {
console.error("Puppeteer MCP Server closed");
server.close();
});
});