· Updated · Tutorials · By Simon Chiu

Chrome DevTools MCP Screenshots: Options and Limits

How take_screenshot in Chrome DevTools MCP works, tested on v1.10.1. Formats, full page, element uids, the 2 MB inline limit, resize flags and a script.

Line illustration of the RenderScreenshot mascot taking a snapshot from a browser developer tools panel

To take a screenshot with Chrome DevTools MCP, the agent calls take_screenshot with a pageId. Add fullPage: true for the whole page, uid for one element from take_snapshot, format (png, jpeg or webp) and quality, or filePath to write it to disk. The result comes back as an inline image unless it is 2,000,000 bytes or more, in which case the server saves it to a temporary file and returns only the path.

We tested every option below on chrome-devtools-mcp 1.10.1 with a script that drives the server over MCP, and we report what came back: sizes, dimensions, and the cases where you do not get an image. This post is part of our guide to screenshots for AI agents.

Tested with: chrome-devtools-mcp 1.10.1, @modelcontextprotocol/sdk 1.32.1, Google Chrome 155.0.8059.40, Node 22.18.0, macOS, 2026-10-15. Server flags: --headless --isolated --viewport 1280x800. Test pages: https://example.com and the Wikipedia article "Web browser". One run per setting; byte sizes vary with page content.

Set up the server

The server's README gives this config for any MCP client. Pin a version so a release does not change behavior under you:

{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": ["-y", "[email protected]", "--headless", "--isolated", "--viewport", "1280x800"]
    }
  }
}

In Claude Code, the same thing from the terminal:

claude mcp add chrome-devtools -- npx -y [email protected] --headless --isolated --viewport 1280x800

What the flags do:

Flag Effect Why you want it
--headless No browser window Servers, CI, and agents you do not watch
--isolated Temporary profile, deleted on exit No cookies or logins leak in from your real profile
--viewport 1280x800 Initial viewport size (max 3840x2160 in headless) Predictable screenshot size
--no-usage-statistics Opts out of Google's usage statistics Collection is on by default
--slim Exposes only 3 tools: navigate, evaluate, screenshot Smaller tool list for the model; the slim screenshot tool takes no options

Requirements from the README: a Node.js LTS release, current stable Chrome, and npm.

The take_screenshot parameters

From the tool schema the server reports over MCP (version 1.10.1):

Parameter Type Default Notes
pageId number required From list_pages or new_page
format png / jpeg / webp png Can be changed at launch with --screenshotFormat
quality 0 to 100 Puppeteer default JPEG and WebP only; ignored for PNG. Launch default via --screenshotQuality
fullPage boolean false Not allowed together with uid
uid string none Element id from take_snapshot
filePath string none Save to disk instead of returning the image

Two related tools matter for screenshots: take_snapshot returns the accessibility tree with a uid for each element, and emulate changes viewport, device pixel ratio, mobile mode and color scheme.

What came back: measured results

All captures at 1280x800, default server flags:

Page Capture Returned Bytes Pixels
example.com Viewport, PNG Inline image 105,454 1280x800
example.com Viewport, JPEG q80 Inline image 59,166 1280x800
example.com Viewport, WebP q80 Inline image 36,008 1280x800
example.com Full page, PNG Inline image 105,454 1280x800 (page is shorter than the viewport)
Wikipedia article Viewport, PNG Inline image 334,490 1280x800
Wikipedia article Viewport, JPEG q80 Inline image 150,073 1280x800
Wikipedia article Viewport, WebP q80 Inline image 98,750 1280x800
Wikipedia article Full page, PNG File path only 2,581,239 1280x7529
Wikipedia article Full page, JPEG q80 Inline image 1,426,616 1280x7529
Wikipedia article Full page, WebP q80 Inline image 950,824 1280x7529
Wikipedia article Element (uid of the title heading), PNG Inline image 4,541 600x40

What this tells you:

  • WebP at quality 80 was about a third of the PNG size on both pages, and JPEG about half. For an agent, the bytes travel with every later turn of the conversation, so this matters.
  • Full-page PNG of a long article crossed the 2 MB limit and came back as a path to a file in the OS temp folder. The model got text, not a picture. The same capture as WebP stayed inline.
  • Short pages make fullPage a no-op. example.com returned the same 1280x800 image either way.
  • An element capture is tiny. 4.5 KB for a heading. If the agent needs one table or chart, a uid capture costs a fraction of a viewport.

Where the 2 MB rule comes from

It is in the tool's source (build/src/tools/screenshot.js in 1.10.1): if filePath is set, the image is saved there; otherwise, if the encoded screenshot is 2,000,000 bytes or more, it is saved with saveTemporaryFile and the response says Saved screenshot to <path>; otherwise it is attached inline. So an agent asking for a long full-page PNG gets a path it usually cannot open.

Recipes

Viewport, full page and format

Ask in plain language and the model fills in the parameters, or call the tool directly from a script:

{ "name": "take_screenshot", "arguments": { "pageId": 2, "fullPage": true, "format": "webp", "quality": 80 } }

Prefer WebP or JPEG for anything taller than one screen. Keep PNG for small element captures, where size does not matter and sharp edges do.

One element by uid

take_screenshot does not take a CSS selector. It takes a uid from the accessibility snapshot. The flow is two calls:

  1. take_snapshot returns lines such as uid=1_0 RootWebArea "Example Domain" and, for each element, its role, name and uid.
  2. take_screenshot with { "pageId": 2, "uid": "<uid of the element>" }.

Watch the snapshot size. For example.com it was 1,235 characters. For the Wikipedia article it was 143,012 characters, which the model must read before it can pick a uid. The tool description itself says to prefer a snapshot over a screenshot, which is right for acting on a page and expensive for long pages. Our token comparison of screenshots, DOM and accessibility trees measures that trade-off on 26 pages.

Mobile size, retina and dark mode

emulate takes a viewport string <width>x<height>x<devicePixelRatio> plus optional mobile, touch and landscape, and a colorScheme of dark, light or auto:

{ "name": "emulate", "arguments": { "pageId": 2, "viewport": "390x844x3,mobile,touch", "colorScheme": "dark" } }

The next take_screenshot returned a 1170x2532 PNG (390x844 at device pixel ratio 3). That is a sharp image, and also 868,728 bytes for the Wikipedia article. If a vision model reads it, it will likely downscale it; Claude's standard tier, for example, caps the long edge at 1568 px. Use ratio 1 unless you are checking icon sharpness.

Keep images small for the model

Three launch flags shape every screenshot without the model having to ask:

npx -y [email protected] --headless --isolated \
  --screenshotFormat webp --screenshotQuality 80 \
  --screenshotMaxWidth 1024 --screenshotMaxHeight 4000

With --screenshotMaxWidth 1024 --screenshotMaxHeight 4000, our viewport captures came back at 1024x640, and the Wikipedia full page came back at 680x4000 and inline (1,048,692 bytes as PNG, 346,408 as WebP) instead of as a file path. The server's help text says it directly: image tokens scale with dimensions, not encoded bytes. The max-size flags are what reduce context cost; format only reduces bytes.

The trade-off: a 680 px wide full page is hard to read. For reading, use the snapshot or text. For seeing, use viewport and element captures.

Save to a file

Pass filePath and the tool writes the image instead of returning it:

{ "name": "take_screenshot", "arguments": { "pageId": 2, "filePath": "/tmp/shots/home.png" } }

The response is text: Took a screenshot of the current page's viewport. Saved screenshot to <path>. Where the server may write depends on your client. Per the server's help, when the client does not share MCP roots, file-writing tools are limited to the OS temp directory unless you add --workspace <dir>.

Drive it from a script

You do not need a chat client to use the server. This script starts it over stdio with the official MCP SDK, opens a page and saves three captures. It is a clean way to test flags before you hand the server to an agent.

// capture.mjs — drive Chrome DevTools MCP from a script and save screenshots to disk.
// Setup: npm i [email protected] @modelcontextprotocol/[email protected]
// Run:   node capture.mjs https://example.com
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { mkdir, writeFile } from 'node:fs/promises';

const url = process.argv[2] ?? 'https://example.com';
const transport = new StdioClientTransport({
  command: 'npx',
  args: ['[email protected]', '--headless', '--isolated', '--no-usage-statistics', '--viewport', '1280x800'],
  stderr: 'ignore',
});
const client = new Client({ name: 'capture-script', version: '1.0.0' });
await client.connect(transport);

async function call(name, args) {
  const result = await client.callTool({ name, arguments: args });
  if (result.isError) throw new Error(`${name} failed: ${result.content.map((c) => c.text).join(' ')}`);
  return result;
}

async function save(result, file) {
  const image = result.content.find((c) => c.type === 'image');
  if (!image) {
    // Images of 2,000,000 bytes or more come back as a file path, not inline data.
    console.log(`${file}: not inline -> ${result.content.map((c) => c.text).join(' ')}`);
    return;
  }
  const bytes = Buffer.from(image.data, 'base64');
  await writeFile(file, bytes);
  console.log(`${file}: ${image.mimeType}, ${bytes.length} bytes`);
}

try {
  await mkdir('shots', { recursive: true });
  await call('new_page', { url });
  const pages = (await call('list_pages', {})).content[0].text;
  const pageId = Number([...pages.matchAll(/^(\d+):/gm)].at(-1)[1]);

  await save(await call('take_screenshot', { pageId, format: 'webp', quality: 80 }), 'shots/viewport.webp');
  await save(await call('take_screenshot', { pageId, fullPage: true, format: 'jpeg', quality: 80 }), 'shots/full.jpeg');

  await call('emulate', { pageId, viewport: '390x844x3,mobile,touch', colorScheme: 'dark' });
  await save(await call('take_screenshot', { pageId, format: 'png' }), 'shots/mobile-dark.png');
} finally {
  await client.close();
}

Output on the Wikipedia article (2026-10-15):

shots/viewport.webp: image/webp, 98752 bytes
shots/full.jpeg: image/jpeg, 1426616 bytes
shots/mobile-dark.png: image/png, 868728 bytes

The pageId parsing reads the list_pages text output, which lists pages as <id>: <title> (<url>) lines in this version, for example 2: Example Domain (https://example.com/) [selected]. If a future release changes that text, start the server with --experimentalStructuredContent and read the structured result instead.

Troubleshooting

Symptom Cause Fix
Response says "Saved screenshot to /tmp/..." and no image Screenshot was 2,000,000 bytes or more Use format: "webp" or "jpeg", or launch with --screenshotMaxWidth/Height
Error: Providing both "uid" and "fullPage" is not allowed. Both parameters set Element captures are never full page; drop one
Element screenshot shows the wrong element Stale uid from an older snapshot Call take_snapshot again after any navigation or DOM change
Model cannot find the element's uid Snapshot is huge on long pages Navigate to an anchor or ask for a viewport capture first
Full-page capture is unreadable in the model's answer The model downscaled a tall image Capture viewport-sized parts or one element
filePath write is refused Client does not share roots; writes limited to temp dir Add --workspace <dir> or write under the temp dir
Screenshots include your logged-in state Server uses your persistent profile Launch with --isolated
Tool asks for pageId Page-ID routing is on by default Call list_pages and pass the id
Cookie banner covers the page No built-in banner blocking Click it away with the input tools, or use an API with banner blocking
No browser window appears --headless is set Expected; remove it to watch the agent work

When a local MCP server is the wrong tool

Chrome DevTools MCP is excellent while you develop: it sees your localhost, your console and your network panel. It is a poor fit when screenshots are a product feature or run unattended, because Chrome runs on your machine, there is no banner or ad blocking, the 2 MB rule turns long captures into file paths, and nothing is cached or shared.

The same capture as one HTTP request to RenderScreenshot (we are the vendor; this is our API):

curl -sS -X POST https://api.renderscreenshot.com/v1/screenshot \
  -H "Authorization: Bearer $RS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://en.wikipedia.org/wiki/Web_browser",
    "capture": { "mode": "full_page", "scroll": { "max_height": 4000 } },
    "output": { "format": "webp", "quality": 80 },
    "block": { "cookie_banners": true, "ads": true }
  }' -o page.webp

The response body is always the image, and the X-Cache-URL header gives a public link to it. See capture options for full-page settings and screenshot API for AI agents for tool definitions you can give a model instead of an MCP server. Get a free API key to try the same page.

Related in this topic