🇨🇳 中文

Claude Code Screenshot MCP Setup: Frontend Debugging 2026

I measured four ways to hand a page to Claude Code. The a11y snapshot cost 10,220 tokens. A targeted script cost 65 and actually found the bug. Setup, receipts, and the trap in fullPage.

Bruce

Claude CodeMCPBrowser AutomationFrontendDeveloper Tools

2099  Words

2026-07-21


Claude Code screenshot MCP setup for frontend debugging: token costs measured across snapshot, screenshot, and targeted script

My blog gets 8% of its traffic from mobile. For a site about developer tooling that number is low but not absurd, so it sat in a dashboard for months without me looking at it.

When I finally looked, it took one script and 79 tokens to find a table rendering 427 pixels wide inside a 390-pixel viewport, plus 35 tap targets smaller than the 44-pixel minimum.

That is the good ending. The actual session started badly: I did what the official tool description tells you to do, called take_snapshot, spent 10,220 tokens, and learned nothing about the bug — because an accessibility tree cannot tell you that an element is 427 pixels wide.

This is the writeup of that detour. It covers setting up Claude Code screenshot MCP access properly, what four different ways of handing a page to the model actually cost, the trap hiding inside fullPage, and the four-step loop I use now.

Version anchor. Measured on 2026-07-21 against chrome-devtools-mcp 1.6.0 (released 2026-07-14) and Claude Code with a 1M-token context. Both MCP servers ship breaking changes often — Playwright MCP is still pre-1.0 after 16 months — so treat the token figures as a shape, not a constant.

What Four Ways of Reading a Page Actually Cost

I pointed all four at the same URL — one of my own long-form articles — at a 1280x800 viewport, and wrote each result to disk so I could measure it without polluting the context I was measuring.

MethodTokensWhat it can answer
take_snapshot (a11y tree)10,220Structure and clickable elements. No styles.
take_screenshot (viewport)1,866How it looks. No exact values.
take_screenshot (fullPage)303Nothing — see the next section.
evaluate_script (targeted)65Exactly the values you asked for.

The snapshot costs 5.5x more than a screenshot and 157x more than a targeted script. That ordering surprised me, because both vendors tell you to prefer the snapshot.

Chrome DevTools MCP says it in the tool description itself: “Prefer taking a snapshot over taking a screenshot.” Playwright MCP’s README makes it a design goal — structured accessibility snapshots, “bypassing the need for screenshots or visually-tuned models.”

They are not wrong. They are answering a different question.

The advice is about acting, not looking

A snapshot assigns every element a uid. That is what makes click(uid) and fill(uid, text) possible. A screenshot gives you pixels you cannot address, which is why Playwright MCP’s own screenshot tool warns: “You can’t perform actions based on the screenshot, use browser_snapshot for actions.”

So the rule “prefer snapshot” is correct — for driving a page.

Frontend debugging is not driving a page. When I ask why is this table overflowing on mobile, I need a number: the element’s width, its container’s width, the computed max-width. A snapshot has none of those. Neither does a screenshot. You can see that something is too wide; you cannot read 427px off a picture.

Both defaults dump the whole page. The winning move is to ask one question:

() => {
  const de = document.documentElement;
  const wide = [];
  document.querySelectorAll('pre,table,img,iframe').forEach(el => {
    const r = el.getBoundingClientRect();
    if (r.width > de.clientWidth + 1) wide.push({ tag: el.tagName, w: Math.round(r.width) });
  });
  return { viewport: de.clientWidth, overflow: de.scrollWidth > de.clientWidth, wide };
}

That returned {"viewport":390,"scrollWidth":390,"horizontalOverflow":false,"wideElements":1,"wideSample":[{"tag":"TABLE","w":427}]} — 79 tokens, and it names the offending element.

Google’s own design principles for the server say the same thing in the abstract: “Return semantic summaries. ‘LCP was 3.2s’ is better than 50k lines of JSON.” Targeted scripting is that principle applied to CSS.

flowchart LR
    Q["Question about
the page"] --> D{"What do you
need back?"} D -->|"A number or
computed value"| S["evaluate_script
65-95 tokens"] D -->|"An element to
click or fill"| N["take_snapshot
10,220 tokens"] D -->|"A visual
judgement"| P["take_screenshot + filePath
1,866 tokens"] S --> F["Fix"] N --> F P --> F classDef cheap fill:#0f766e,stroke:#134e4a,color:#ffffff classDef mid fill:#b45309,stroke:#78350f,color:#ffffff classDef exp fill:#9f1239,stroke:#4c0519,color:#ffffff classDef plain fill:#1e293b,stroke:#0f172a,color:#e2e8f0 class S cheap class P mid class N exp class Q,D,F plain

The fullPage Trap

Look again at that table. The full-page screenshot was the cheapest image at 303 tokens. That number is a lie, and the mechanism is worth understanding because it fails silently.

The capture measured 2544 x 27358 pixels. Claude downscales any image whose long edge exceeds 1568 pixels, preserving aspect ratio. Do the arithmetic: 1568 / 27358 = 0.057, so the image arrives as 145 x 1568 — a strip of paper 145 pixels wide.

It is cheap because it has been destroyed. The model receives something it cannot read, does not error, and confidently tells you the page looks fine.

CaptureNativeAfter the 1568px clampReadable?
Viewport2560 x 14581568 x 893Yes
Mobile viewport1170 x 2532724 x 1568Yes
fullPage2544 x 27358145 x 1568No

For long pages, take several viewport screenshots at known scroll positions, or pass a uid to capture one element. Never reach for fullPage on an article-length page and assume you have seen it.

Setting It Up

Two servers are worth installing. They are not competitors so much as different instruments.

Chrome DevTools MCP — Google’s, currently 1.6.0, 52 tools, 47k stars. Reach for it when you need Lighthouse audits, performance traces with LCP/INP/CLS insights, heap snapshots, or extension debugging.

claude mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest

Playwright MCP — Microsoft’s, currently 0.0.78, 24 tools by default and 69 with every capability enabled. Reach for it when you need Firefox or WebKit, form filling, cookie and storage control, or test assertions.

claude mcp add playwright npx @playwright/mcp@latest

Verify both with /mcp. Node 20.19+ is required by Chrome DevTools MCP; if Playwright MCP reports missing browsers, run npx playwright install chromium.

If you also maintain my earlier comparison of five browser automation tools in your bookmarks, note that its install snippets name two npm packages that do not exist. The commands above are the correct ones, and I have since fixed that page.

Three things that cost me time

filePath only writes inside your workspace. Passing an absolute path to /tmp returns Access denied: path ... is not within any of the configured workspace roots. Write into the repo and clean up after, or the call simply fails.

resize_page does not give you a mobile viewport. I resized to 390x844, then had the page report its own width: it came back 1504. A headed Chrome window has a minimum size, and resize_page resizes the window. For real device dimensions you need emulation:

emulate(viewport: "390x844x3,mobile,touch")

After that the page reported 390 and the overflow bug appeared. Everything I found on mobile depended on this one distinction.

Headed Chrome steals focus on macOS. Every CDP command — including read-only ones like take_screenshot and list_pages — pulls the browser in front of your editor. It is issue #1254, 25 upvotes, and the documented workarounds are worse than the problem. Run headless unless you specifically need to watch.

For connecting to your already-logged-in Chrome instead of a fresh profile, I wrote that up separately in the Chrome DevTools MCP connection guide — that piece covers the port 9222 and --user-data-dir mechanics this one deliberately skips.

The Loop I Use Now

Four steps. The ordering is the whole point: the cheap, precise call comes first, and pixels come last.

flowchart TD
    A["1 - Reproduce
navigate + emulate device"] --> B["2 - Interrogate
evaluate_script returns numbers"] B --> C["3 - Patch
edit the CSS in your repo"] C --> D["4 - Confirm
reload + screenshot with filePath"] D --> E{"Fixed?"} E -->|"No"| B E -->|"Yes"| G["Done"] classDef step fill:#1e3a5f,stroke:#0f172a,color:#e2e8f0 classDef check fill:#7c2d12,stroke:#431407,color:#ffffff classDef done fill:#14532d,stroke:#052e16,color:#ffffff class A,B,C,D step class E check class G done

1. Reproduce. Navigate, then emulate the device. Skipping emulation is how you spend an hour failing to reproduce a bug that only exists below 400 pixels.

2. Interrogate. Write a script that returns the specific numbers in question. Overflowing elements, tap targets under 44px, computed styles on one selector. This is the step that replaces both the snapshot and the screenshot, and it is where the 100x saving lives.

3. Patch. Fix the CSS in your repo, not in the browser. Changes made through the browser evaporate on reload and tempt you into declaring victory.

4. Confirm. Reload, then take one viewport screenshot with filePath set. This is the single moment where pixels earn their cost — a script can tell you the width is now 390, but only your eyes can tell you the fix did not wreck the spacing.

Keep a Screenshot From Bricking Your Session

This is the failure mode most worth guarding against, because it does not degrade — it ends the session.

Anthropic’s vision API rejects any image with a dimension over 8000 pixels. That ceiling drops to 2000 pixels once a request carries more than 20 images. Cross it and you get:

API Error: 400 messages.7.content.2.image.source.base64.data:
At least one of the image dimensions exceed max allowed size: 8000 pixels

The cruel part is that the oversized image is now in your conversation history, so every subsequent request fails the same way. claude-code issue #2939 has been open since July 2025 — a year — with 62 upvotes. One reporter puts it plainly: “it corrupts the Claude Code session.”

Chrome DevTools MCP maintainer @OrKoN explains why writing to a file is not a guaranteed escape:

“because it polluted its request history with an image that is too large and does not recover from that. I think even file path might not work if it tried to load the image from file into its context window.”

And the reason no server-side annotation saves you is written into Anthropic’s own MCP docs, twice: the maxResultSizeChars annotation “has no effect on tools that return image content; for those, raising MAX_MCP_OUTPUT_TOKENS is the only option.” Every text-heavy MCP server has an escape hatch. Screenshots do not.

Since v1.3.0 you can cap the image at the source, which is the fix I now run by default:

{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": [
        "-y", "chrome-devtools-mcp@latest",
        "--screenshot-format=jpeg",
        "--screenshot-max-width=2000",
        "--screenshot-max-height=2000"
      ]
    }
  }
}

One caveat from the pull request that shipped it: the cap is per call. It prevents the next disaster; it cannot un-poison a session that already has an oversized image in it. When you hit the error, start a new session — there is no other recovery.

Where This Advice Stops

Targeted scripting requires you to know what to ask. On a page you have never seen, you have no selectors and no hypothesis, and one snapshot or screenshot to build a mental model is money well spent. The rule is to stop dumping the page after you have oriented, not before.

Some questions are irreducibly visual. Does the font render correctly, is anything overlapping, does the hierarchy read at a glance — no script answers those. If your question contains the word “looks,” take the screenshot.

And there is a bigger objection I should represent honestly, because I made it myself in an earlier piece arguing MCP is the wrong default: a resident MCP server costs tool-schema tokens on every request whether you use it or not. Microsoft’s own README now concedes the point, recommending CLI-based skills over MCP for coding agents because they “avoid loading large tool schemas and verbose accessibility trees into the model context.”

That is a real argument, and it is why I run the browser MCP only during debugging sessions rather than leaving it on. For repeatable automation that runs on a schedule, the three-stage compression pattern beats anything in this article. This one is about the exploratory phase — the part where you genuinely do not yet know what is broken.

Bottom Line

Stop asking the browser to describe the whole page.

The snapshot-versus-screenshot debate hides the fact that both are page dumps, and for CSS and layout work both are the wrong default. Ask a specific question, get specific numbers back, and spend a screenshot only to confirm the fix with your own eyes.

For me that was the difference between 10,220 tokens and no answer, and 79 tokens and a named element. The bug, incidentally, is now fixed — the table scrolls inside its own container, which is where an overflowing table should have lived from the start.

Comments

Join the discussion — requires a GitHub account