screenshot
Capture web page screenshots to visually verify frontend changes, check responsive layouts, or debug CSS issues. Supports custom viewports, device presets, and element-specific captures.
Instructions
Take a browser screenshot of a URL. Returns the image file path. Use this to visually verify frontend changes, check responsive layouts, or debug CSS issues. The returned file path can be viewed with the Read tool.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | URL to screenshot (http://, https://, or file://) | |
| width | No | Viewport width in pixels (default: 1440) | |
| height | No | Viewport height in pixels (default: 900) | |
| fullPage | No | Capture full scrollable page (default: true) | |
| selector | No | CSS selector to screenshot a specific element instead of the full page | |
| device | No | Device preset: "mobile" (375x812), "tablet" (768x1024), "desktop" (1440x900), "4k" (3840x2160), or a Puppeteer device name like "iPhone 15 Pro" | |
| darkMode | No | Emulate dark mode via prefers-color-scheme: dark | |
| waitFor | No | Milliseconds to wait after load (default: 2000), or a CSS selector to wait for | |
| output | No | Custom output file path (default: auto-generated in /tmp/browsershot-mcp/) |
Implementation Reference
- src/index.js:43-124 (handler)The `takeScreenshot` function implements the core logic for the "screenshot" tool, using Puppeteer to navigate, emulate devices/dark mode, and capture a full page or element screenshot.
async function takeScreenshot({ url, width = 1440, height = 900, fullPage = true, selector = null, deviceScaleFactor = 2, waitFor = 2000, darkMode = false, device = null, output = null, }) { const browser = await getBrowser(); const page = await browser.newPage(); try { // Device emulation (mobile, tablet) if (device) { const devices = puppeteer.KnownDevices || puppeteer.devices; const deviceDesc = devices[device]; if (deviceDesc) { await page.emulate(deviceDesc); } else { // Fallback presets const presets = { mobile: { width: 375, height: 812, deviceScaleFactor: 3, isMobile: true, hasTouch: true }, tablet: { width: 768, height: 1024, deviceScaleFactor: 2, isMobile: true, hasTouch: true }, desktop: { width: 1440, height: 900, deviceScaleFactor: 2, isMobile: false, hasTouch: false }, "4k": { width: 3840, height: 2160, deviceScaleFactor: 1, isMobile: false, hasTouch: false }, }; const p = presets[device.toLowerCase()]; if (p) { await page.setViewport(p); } else { await page.setViewport({ width, height, deviceScaleFactor }); } } } else { await page.setViewport({ width, height, deviceScaleFactor }); } // Dark mode preference if (darkMode) { await page.emulateMediaFeatures([ { name: "prefers-color-scheme", value: "dark" }, ]); } // Navigate await page.goto(url, { waitUntil: "networkidle2", timeout: 30000 }); // Wait for content to settle if (typeof waitFor === "number") { await new Promise((r) => setTimeout(r, waitFor)); } else if (typeof waitFor === "string") { // CSS selector to wait for await page.waitForSelector(waitFor, { timeout: 10000 }); } // Screenshot options const screenshotOpts = { fullPage }; if (selector) { const el = await page.$(selector); if (!el) throw new Error(`Selector "${selector}" not found on page`); screenshotOpts.fullPage = false; // Element screenshot const filename = `element_${Date.now()}.png`; const filepath = output || join(SCREENSHOT_DIR, filename); await el.screenshot({ path: filepath }); return filepath; } const filename = `page_${Date.now()}.png`; const filepath = output || join(SCREENSHOT_DIR, filename); screenshotOpts.path = filepath; await page.screenshot(screenshotOpts); return filepath; } finally { await page.close(); } } - src/index.js:190-245 (registration)Tool registration for the "screenshot" tool in the `ListToolsRequestSchema` handler.
{ name: "screenshot", description: "Take a browser screenshot of a URL. Returns the image file path. " + "Use this to visually verify frontend changes, check responsive layouts, " + "or debug CSS issues. The returned file path can be viewed with the Read tool.", inputSchema: { type: "object", properties: { url: { type: "string", description: "URL to screenshot (http://, https://, or file://)", }, width: { type: "number", description: "Viewport width in pixels (default: 1440)", default: 1440, }, height: { type: "number", description: "Viewport height in pixels (default: 900)", default: 900, }, fullPage: { type: "boolean", description: "Capture full scrollable page (default: true)", default: true, }, selector: { type: "string", description: "CSS selector to screenshot a specific element instead of the full page", }, device: { type: "string", description: 'Device preset: "mobile" (375x812), "tablet" (768x1024), "desktop" (1440x900), "4k" (3840x2160), or a Puppeteer device name like "iPhone 15 Pro"', }, darkMode: { type: "boolean", description: "Emulate dark mode via prefers-color-scheme: dark", default: false, }, waitFor: { type: ["number", "string"], description: "Milliseconds to wait after load (default: 2000), or a CSS selector to wait for", default: 2000, }, output: { type: "string", description: "Custom output file path (default: auto-generated in /tmp/browsershot-mcp/)", }, }, required: ["url"], }, }, - src/index.js:292-310 (handler)The request handler switch case for "screenshot" which calls `takeScreenshot` and returns the image as base64 for the MCP protocol.
case "screenshot": { const filepath = await takeScreenshot(args); // Read the image and return as base64 for Claude to see const imageData = readFileSync(filepath); const base64 = imageData.toString("base64"); return { content: [ { type: "image", data: base64, mimeType: "image/png", }, { type: "text", text: `Screenshot saved: ${filepath}\nViewport: ${args.width || 1440}x${args.height || 900}${args.device ? ` (${args.device})` : ""}${args.darkMode ? " [dark mode]" : ""}`, }, ], }; }