If you let Claude Code, Codex or Cursor drive a browser, a big part of your token bill is page snapshots your agent reads over and over. I measured it, then built Deputy to cut it: https://razin.ai/deputy?ref=devto
Disclosure: Deputy is my product, so read this as a builder's notes. The method and raw numbers are public and you can rerun them.
Where the tokens go
Playwright MCP gives your agent an accessibility snapshot of the page. That snapshot goes into the conversation, and every later call to the model reads the whole conversation again.
I ran one task on an admin dashboard with 400 orders: filter to status Refunded, placed on or after 2026-08-01, count the matches. Claude Sonnet in Claude Code, medians of 3 runs, every run passed.
Context per model call with Playwright MCP, in thousands of tokens:
19.2, 21.1, 22.1, 44.6, 45.3, 45.5, 46.3
The jump from 22 to 44 is one snapshot of the dashboard: 22,556 tokens. Every call after it pays for it again.
What Deputy does instead
Deputy is a Chrome extension plus a small local MCP server. Your agent asks Deputy for the page and gets the page's forms back as typed tools: each form is one call with named, typed fields, plus a short list of things to click. One call fills the whole form.
Same task, same model:
| Playwright MCP | Deputy | |
|---|---|---|
| agent turns | 10 | 3 |
| Claude tokens | 245,250 | 72,632 |
| page data sent to the agent | 46,640 chars | 7,269 chars |
| total cost | $0.189 | $0.072 |
Against Vercel's agent-browser, all 12 runs passed:
| agent-browser | Deputy | |
|---|---|---|
| dashboard: turns / tokens | 11 / 271,172 | 3 / 72,756 |
| 7-field booking form: turns / tokens | 7 / 155,200 | 3 / 68,683 |
Part of agent-browser's count is the usage guide its docs tell the agent to load first.
It runs in your own Chrome
Fewer tokens was the first reason. The second is that Deputy works inside the Chrome you already use:
- The agent is logged in where you are. No separate browser profile, no logging in again.
- No
debuggerpermission, so no "controlled by automated test software" bar. - Anything that submits a form stops and asks you on the page first.
- The MCP server is local and listens on 127.0.0.1 only.
Try it in two minutes
- Add the extension from the Chrome Web Store (link on https://razin.ai/deputy?ref=devto).
- Connect your agent. For Claude Code:
claude mcp add deputy -- npx -y deputy-mcp
Codex, Cursor, Gemini CLI, Copilot CLI, VS Code and others have one-line setups on the same page. Needs Node.js 20 or later.
Then open a page and tell your agent what to do there.
Free and Pro
Free, no key: 10 tools. Open and read pages, fill forms in one call, click and type. No model of ours involved, nothing sent to us.
Pro, $9.99 a month or $95.99 a year: all 27 tools (tabs, scrolling, uploads, tables, dialogs, console, network, screenshots), plus our hosted model reading pages and screenshots for your agent, up to 5,000 answers a month. Your agent gets a one-sentence answer back instead of spending its own tokens reading the page. Cancel any time, full refund within 14 days.
Where it doesn't win
- On small pages the gap shrinks. A booking form against Playwright MCP was 107,392 vs 68,551 tokens, not 3x. A short Wikipedia lookup was about equal.
- Typed tools need real forms. Of 8 sites I swept (DuckDuckGo, Wikipedia, GitHub search, HN login, npm, MDN, a W3C form demo, Stack Overflow), 7 gave typed tools on first load. Wikipedia's appearance settings still get tool names from field ids.
- Clicks and keys are synthetic events, so heavy editors like Google Docs may ignore them.
- If you only test your own app in CI, Playwright itself (the test runner) is still the right tool. If you stay on Playwright MCP,
--snapshot-mode incrementalhelps.
Links
- Deputy, install and pricing: https://razin.ai/deputy?ref=devto
- Measurements and method: https://github.com/zraisan/deputy/blob/master/docs/measurements.md
- Open-source version: https://github.com/zraisan/deputy
Start on Free and watch what your own token counts do: https://razin.ai/deputy?ref=devto. If you rerun the numbers and get something different, tell me in the comments.
Top comments (6)
Per call. I ran it with
claude -p --output-format jsonand read the context size of every model call, which is where the 19.2, 21.1, 22.1, 44.6 list comes from. So the snapshot shows up as a step you can point at, not just a bigger total.On caching: Claude Code already prompt-caches, so the repeated snapshot is mostly billed as cache reads, which are cheap per token. It still fills the context window though, and on longer tasks that's what hurts first. On a short Wikipedia lookup, 202,310 of 218,135 tokens were cache reads.
Routing it to a smaller model is pretty much what Deputy Pro does: our hosted model reads the page or a screenshot and hands your agent a one sentence answer, so the big model never sees the page. Free skips the model and sends typed tools instead of the snapshot. Both are on razin.ai/deputy?ref=devto
Love that you actually measured it instead of guessing. The deeper catch: the context-build step (browsing, snapshots) is usually the least-attributed cost in an agent loop, because it hides inside a tool call. Routing that step to a smaller model and caching the snapshot per page turns a recurring 22k-token bill into a one-time cost. Do you attribute that spend per tool call, or only see it in the aggregate?
Each snapshot is billed on the turn it's taken, then again on each later call while it stays in the history. Your 22,556 tokens per page is the first bill; the later calls are the rest. Do you drop the snapshot after the step that used it?
The 22k token jump on the dashboard matches what happens whenever an accessibility tree serializes large tabular views. A table with 400 rows generates thousands of repeating cell nodes and aria wrappers that get dumped into context, and every subsequent turn pays to read those same rows again.
The mitigation that held up best in my browser harnesses is bounding the accessibility snapshot to the visible viewport and collapsing repeating table bodies into sample rows with a row count. If the agent needs to inspect a specific record, it queries by row index or filter instead of ingesting the entire table. Converting forms into typed tools cuts turns on input workflows, but bounding the DOM walker is what stops the context floor from exploding on data-dense pages.
same thing showed up on the data side for us. the biggest part of one tool's cost was the output schema the server inferred from sample rows, and flat rows made it a lot smaller. so shrinking what comes back before it enters the context seems to matter more than anything you do to the prompt
Some comments may only be visible to logged-in visitors. Sign in to view all comments.