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.
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.comand 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
fullPagea 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
uidcapture 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:
take_snapshotreturns lines such asuid=1_0 RootWebArea "Example Domain"and, for each element, its role, name and uid.take_screenshotwith{ "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 guides
- Screenshot vs DOM vs accessibility tree: token costs measured on 26 pages.
- Playwright MCP screenshots: the other popular browser MCP server.
- Build an MCP server with Cloudflare Browser Rendering: host your own remote screenshot tool.
Related in this topic
- Screenshot API for AI Agents Give AI agents web vision with one HTTP tool. Tool definitions for OpenAI, Anthropic and the Vercel AI SDK, plus measured token costs per capture.
- Screenshots for AI Agents and Agent Reports Let AI agents observe rendered pages and attach screenshot evidence to their reports. Tool schema, evidence records, capture modes and token cost per mode.
- Website Screenshots for GPT and Claude Vision Pick the viewport, scale and format that vision models read best per token. Capture settings, token math for Claude and GPT, and a tested Python example.
- Screenshot vs DOM vs Accessibility Tree: Token Costs We measured what 26 real web pages cost an LLM agent as raw HTML, cleaned HTML, text, accessibility tree and screenshot. Data, script and method included.
- AI Landing Page Review with Screenshots Capture desktop, mobile and full-page screenshots of a landing page and get a scored AI review as JSON. Rubric prompt, schema and tested Node script.
- Claude Tool Use Screenshot Tool Give Claude a screenshot tool with the Anthropic Messages API. The tool returns image blocks in tool_result. Full Python file, limits and error handling.