Imoran ai LogoImoran ai Logo

Building rpc-agent: Device Screenshots for Coding Agents

I built rpc-agent, a CLI and MCP server that lets Claude Code, Codex and Cursor screenshot a page at real device viewports. Here is how it stitches pages taller than Chromium will draw and keeps auth headers off redirects.

Published: October 30, 2026

Last updated: October 30, 2026

Line drawing of a laptop, tablet and phone behind a terminal running rpc shot, with the Claude Code, Codex and Cursor marks wired into its prompt.

Coding agents are confident about layouts they have never seen. I'd ask Claude Code to fix a cramped mobile header, it would edit the CSS, tell me it was done, and the header would still wrap on an iPhone. It had read the code. It hadn't looked at the page.

I already had a tool for looking: Responsive Point Check, a Chrome extension that puts a site in a row of device frames. But an agent can't click a toolbar button. So over two days I built rpc-agent, a CLI and an MCP server that hand the same check to Claude Code, Codex and Cursor. This is how it works and the three problems that took the most thought.

What it does

You give it a URL and some devices, and it writes PNGs. Each device gets a fresh Chromium context with the right viewport, pixel ratio and, for phones and tablets, a mobile user agent and touch. Add --bezels and you also get a copy framed in the device. The catalog of 49 devices and the bezel artwork come from the extension.

The same greeting page captured on Desktop 1440, iPad Air 11 and iPhone 15, each framed in its device with the viewport size underneath.
npx rpc-agent install
npx rpc-agent shot localhost:3000 -d iphone-15,ipad,desktop-1440 --bezels

For an agent, it's one line of setup:

claude mcp add --scope user rpc -- npx -y rpc-agent mcp

After that the agent has a screenshot tool and can check its own work before it tells me it's finished.

One capture function, two ways in

The CLI and the MCP server both call the same capture() function, so a bug fixed for one is fixed for both. The bigger split was output. In a terminal, rpc draws a banner, spinners and a results table with @clack/prompts. When stdout is a pipe, when CI is set, or with --json, it prints the same plain ok and x lines as before the terminal UI existed. Agents and scripts that parse those lines never see the decoration, and rpc mcp prints nothing but MCP messages.

A terminal run of rpc capturing three devices, with a capture summary, a results table and six saved PNG paths.

Pages taller than Chromium will draw

Chromium won't produce a screenshot taller than 16,384 device pixels. A long marketing page on an iPhone at 3x passes that quickly. The extension handled length by scrolling and stitching, which repeated the sticky header in every segment.

rpc takes clipped full-page captures instead. Each segment renders as if the viewport were the whole document, so sticky and fixed elements appear once, at the top. Segments are about 8,192 device pixels, and I stitch them in Node with a small PNG decoder on node:zlib, so there's no native dependency to install. Segment heights are picked so each seam lands on a whole pixel row even at a 2.625 pixel ratio. The e2e test captures a 21,656 px page at 3x, which comes to 64,968 rows, and checks every row against the known layout.

Slices instead of one tall image

I didn't want to hand a model a 65,000 pixel PNG. So with fullPage, the MCP tool returns the page as viewport-tall JPEG slices, each labelled like iphone-15 · slice 2/7 · y=852–1704 css px. Ten come back inline by default. If there are more, the rest go to disk and the text tells the agent which range it skipped and where the files are, so it can read them itself.

Five viewport-tall iPhone 15 slices of a long page, each labelled with its slice number and CSS pixel range, as the screenshot tool returns them.

Signed-in pages without leaking the token

Most of what I want checked sits behind a login, so v0.4.0 added auth. rpc login opens a real browser, you sign in, and rpc saves the storage state for that origin with file mode 0600. Later captures use it automatically.

Headers were the subtle part. Playwright's page.route can add an Authorization header, but its header overrides follow redirects. A 302 to another host would carry your token there. So rpc injects headers through the CDP Fetch domain, which pauses on each redirect hop separately and lets me check the origin every time. Values never appear in output, and a page that redirects to /login fails with AUTH_REQUIRED rather than handing the agent a screenshot of a sign-in form.

What it doesn't do

It's Chromium only and portrait only. Every MCP call launches a fresh browser, which costs about a second. Scroll-driven animations get captured in their scroll-top state, and login detection reads the URL path, so an app that shows a sign-in form without redirecting gets captured as is. I'd rather list those than have someone find them mid-review.

Where it leaves me

Now when an agent says the mobile header is fixed, it can show me the iPhone screenshot. rpc-agent is on npm and on GitHub under MIT. If your team runs coding agents and wants them checking their own UI work, this is the kind of tooling I build.

Frequently asked questions

What is rpc-agent?

It's an npm package that screenshots a URL at device viewports, optionally framed in a device bezel. It ships as an rpc command for the terminal and as an MCP server with screenshot, list_devices and list_sessions tools, so a coding agent can look at the layout it just built.

Which coding agents does it work with?

Any agent that speaks MCP over stdio. The README has setup for Claude Code, Codex and Cursor, and each one is a single command or a few lines of config that run npx -y rpc-agent mcp.

Can it screenshot pages behind a login?

Yes, since v0.4.0. You sign in once with rpc login and later captures of that origin reuse the saved session. Cookies and headers can also be passed per capture, and they only go to the target origin unless you allow another one. If a page redirects to a login screen, the capture fails with AUTH_REQUIRED instead of returning a screenshot of the sign-in form.

How is it different from Responsive Point Check?

Responsive Point Check is the Chrome extension I built for checking layouts by hand. rpc-agent reuses its 49 devices and bezel artwork, but it runs headless in Playwright so an agent can call it, and it emulates mobile user agents and touch, which the extension never did.

Can you build tooling like this for my team?

Yes. If your team uses coding agents and keeps finding layout bugs they should have caught, I can build the tools and MCP servers that let them check their own work. Tell me how you work and I'll suggest an approach.

More Articles

Let's Work
Together

Hand Reaching

Have a project in mind? We'd love to hear about it. Let's create something great together!

PRODUCT DEVELOPERUI ENGINEERMVP DEVELOPMENTFRONTEND DEVELOPMENTPRODUCT DEVELOPERUI ENGINEERMVP DEVELOPMENTFRONTEND DEVELOPMENTPRODUCT DEVELOPERUI ENGINEERMVP DEVELOPMENTFRONTEND DEVELOPMENT