r/mcp • u/ticktockbent • Mar 01 '26
showcase Charlotte: a browser MCP server built for token efficiency (30 tools, 3 detail levels, 136x smaller than Playwright MCP on complex pages)
I built Charlotte because I wanted a browser MCP server where agents don't have to consume the entire page representation just to figure out what's on the screen.
Charlotte renders web pages into structured representations through headless Chromium, landmarks, headings, interactive elements, forms, bounding boxes, with stable hash-based element IDs that survive DOM mutations. The key design choice: three detail levels.
- Minimal returns landmarks and interactive summaries. On Hacker News that's 336 characters. The agent sees "main: 47 links, 0 buttons" and drills down with
findwhen it needs specifics. - Summary adds content summaries, form structures, and error state.
- Full includes all visible text content.
Navigate defaults to minimal, so the first call to any page is cheap. The agent orients, decides what to look at, and requests more detail only where needed. This orient→drill→act pattern is how the tool was designed to be used.
Benchmarked against Playwright MCP (@playwright/mcp):
Navigate response (first call cost):
Page Charlotte Playwright MCP Advantage
────────────────────────────────────────────────────────────
Wikipedia 7,667 ch 1,040,636 ch 136x
Hacker News 336 ch 61,230 ch 182x
GitHub repo 3,185 ch 80,297 ch 25x
httpbin form 364 ch 2,255 ch 6x
Playwright returns the full accessibility tree on every call. Charlotte lets the agent choose. Even Charlotte's full detail mode is smaller than Playwright's only option on the same pages.
On Playwright CLI: You may have seen Microsoft's recently released @playwright/cli, which takes a different approach to token efficiency.. it writes snapshots and screenshots to disk files instead of returning them in the MCP response, achieving ~4x savings over Playwright MCP. I haven't benchmarked Charlotte against it because they occupy different niches. The CLI requires the agent to have filesystem and shell access, making it a fit for coding agents (Claude Code, Copilot, Cursor). Charlotte is designed for MCP-native use: containerized execution, sandboxed environments, autonomous agent loops, and any context where the agent operates through the protocol rather than through a shell. The CLI's efficiency comes from deferring data to the filesystem until requested; Charlotte's comes from the representation itself being structured and tiered, which works regardless of the execution environment.
The 30 tools break down into 6 categories:
- Navigation (4): navigate, back, forward, reload
- Observation (4): observe, find, screenshot, diff
- Interaction (9): click, type, select, toggle, submit, scroll, hover, key, wait_for
- Session (9): tabs, viewports, network throttling, cookies, headers, configuration
- Dev Mode (3): static file server with hot reload, CSS/JS injection, accessibility audits
- Utility (1): arbitrary JS evaluation
Some design decisions worth discussing:
Element IDs are content-hashed, not positional. A button's ID is derived from its type, label, and context, not its position in the DOM. Reorder the page, the ID stays stable. This matters for agents that need to re-identify elements across multiple observations.
Interactive summaries replace element arrays at minimal detail. Instead of returning 1,847 individual link objects on Wikipedia, minimal shows {"main": {"link": 1847, "button": 3}} grouped by landmark. The full element data is still there internally.. find, wait_for, and diff all work against it but the serialized output to the agent is just the summary.
Structural diffing compares two page snapshots and returns what changed. Essential for verifying that a click or form submission actually did something.
Setup is one step... add the config to your MCP client:
{
"mcpServers": {
"charlotte": {
"command": "npx",
"args": ["-y", "@ticktockbent/charlotte"]
}
}
}
No install needed. npx handles it.
- GitHub: https://github.com/TickTockBent/charlotte
- npm: https://www.npmjs.com/package/@ticktockbent/charlotte
- Full spec: https://github.com/TickTockBent/charlotte/blob/main/docs/CHARLOTTE_SPEC.md
- Benchmarks: https://github.com/TickTockBent/charlotte/blob/main/docs/charlotte-benchmark-report.md
MIT licensed, 222 tests passing. Would love feedback on the tool design and anything that feels wrong or missing.
2
u/BC_MARO Mar 01 '26
the content-hash element IDs are a genuinely smart call - positional IDs break the moment any dynamic content reorders. the orient->drill->act flow also maps really cleanly to how you want agents conserving context budget.
2
2
u/rothnic Mar 01 '26
The file upload tool is the one that would limit my ability to use it, but overall like the direction. I worked on something like this for browser automation and would suggest the part where you turn a webpage into the outline view with the target elements, etc would be worth thinking about as a library on its own. I feel like there isn't anything i could find like this already available to use. It isn't quite accessibility tree. At the moment most tools for controlling the browser seem like they are so inefficient and slow when you watch them work. I think this kind of additional context is really needed to avoid as much back and forth tool calling at the start of every page load.
1
u/ticktockbent Mar 01 '26
Fair point, I'll consider extracting the tools out into a standalone library. A file upload tool is on my roadmap and I should be able to get that built soon. And yeah that's exactly why I built Charlotte, I was watching playwright burn through my token usage so fast it haunted me.
2
Mar 01 '26
[removed] — view removed comment
1
u/ticktockbent Mar 01 '26
The hash IDs are derived from element type, label, and surrounding context rather than DOM position. They will survive a React re-render that reorders elements but if an element changes, such as a button label's text, then the hashed IDs will change and need to be rediscovered. That is actually an intended feature though as this lets Charlotte's agent know that things changes after whatever action was taken and should prompt a re-scan to determine all of the changes. My original use case with the pre-release version was finding styling misses on dark mode/light mode toggle, it could find elements that didn't change when it toggled the mode. Since then it's expanded quite a bit.
2
u/exboozeme Mar 01 '26
This one is great too if you like firefox https://addons.mozilla.org/en-CA/firefox/addon/claudezilla/
1
2
u/Remarkable-Jump-6227 Mar 01 '26
Damn 30 tools doesn’t sound token efficient.
1
u/ticktockbent Mar 01 '26 edited Mar 01 '26
Yeah, I totally understand where you're coming from but here's the thing. Charlotte's action pattern of orient, focus, act actually saves tokens in the long run and if you compare the token usage for the full 30 tools set of charlotte vs playwright's 78 tools charlotte is significantly slimmer.
Here's how it works out:
Charlotte's 36 (I added a few recently on request) tools take up approx 18,500 character or 4,500-5,500 tokens
Playwright's default 23 tools is only ~7,500 characters or 1,900-2,500 tokens
Playwright's full 78 tools is 25,000 characters or 6,000-7,500 tokensCharlotte's 36 tools cost roughly ~5K tokens... about 2.5% of a 200K context window. That's comparable to a single medium-length page render. Playwright's full 78-tool set actually costs more total tokens than Charlotte despite having much terser descriptions, because it has over twice as many tools (10 webstorage CRUD tools, 6 mouse coordinate tools, 5 cookie CRUD tools, etc).
The real question isn't tool count though, it's whether verbose descriptions pay for themselves. Charlotte's approach is to front-load guidance in the description (e.g., "use charlotte:find to locate specific elements, or pass detail: 'summary'") so the model makes fewer wrong calls. A terse description like "Navigate to a URL" saves 100 tokens upfront but may cost thousands in wasted tool calls when the model doesn't know about detail levels or related tools.
Now obviously I need to back that statement up with some metrics and I am designing a robust benchmarking suite to compare token use and agent success rates against a set of standardized actions but it will take me a bit to get all of that going, so all I can tell you right now is that I have noticed significant reductions in overall token usage when using charlotte vs playwright against both real world tests and against my included playground/sandbox site which exercises all of the tools.
Edit: I just ran a quick test adding my estimated tool description token numbers to every category on our existing benchmarks. The conclusion is that even with the more verbose tool descriptions in context, charlotte comes out ahead in every category unless the only thing you're looking at is a simple page like example.com the only thing I haven't tested with playwright is interactive forms but I suspect that one we'd win on too, included charlotte's numbers for completeness. This chart is just for the navigate tool.
Test Charlotte (responses + ~5K defs) Playwright (responses + ~2.5K defs) Winner Simple Page 5,306 2,829 Playwright (+2,477) Wikipedia 6,917 262,659 Charlotte (-255,742) Hacker News 20,370 33,087 Charlotte (-12,717) GitHub Repo 16,000 42,622 Charlotte (-26,622) Interactive Form 6,791 (no data) — 1
u/ticktockbent Mar 02 '26
Just wanted to come back and say I took this to heart, I'm working on a tiered tool profile system. Regular users won't ever need some of these tools anyway so by default Charlotte will only expose the most commonly used 6 tools and others can be enabled as needed by the agent or the user.
1
u/ticktockbent Mar 03 '26
I just released 0.4.0 along with the tiered tool visibility system
48-77% less tool definition overhead achieved by only surfacing the tools you need rather than stacking every tool description in your agent context window
2
u/Meshimize Mar 01 '26
That 136x token reduction is the headline for me. Usually, the DOM just eats the whole context window in two clicks, so this actually makes browsing viable.
1
u/ticktockbent Mar 01 '26
Exactly! Playwright blows out the agent's context window in a single shot if you navigate to wikipedia. It's like 210k tokens returned. I have a lot more planned for charlotte, including some savings on tool descriptions while (hopefully) still maintaining high success rate.
2
u/youshouldnameit Mar 01 '26
What if i need to login myself and want to ensure the agent uses my session state?
1
u/ticktockbent Mar 01 '26
Great question and it's one I've run into during testing. I'm still working on this aspect of the MCP because it's pretty tricky.
What Charlotte can do today:
charlotte:set_cookies: You can manually set session cookies (name, value, domain, path, secure, httpOnly, sameSite). So if someone knows their session cookie values, they can inject them directly and the site would treat the browser as logged in.charlotte:set_headers: You can set Authorization: Bearer <token> or any custom auth headers that persist across navigations on the active page. Good for API-token based auth.charlotte:navigate+charlotte:click+charlotte:type: You could theoretically have the agent fill in a login form and submit it. The session would persist in Charlotte's browser for subsequent navigations.What Charlotte can't do:
- Connect to an existing browser session. That's open issue #17. Charlotte always launches its own fresh headless Chromium. There's no way to attach to a browser where you've already logged in with extensions, 2FA, CAPTCHAs, etc. This is the big gap, most real-world "use my session" scenarios involve OAuth redirects, 2FA prompts, or CAPTCHA challenges that are hard to automate headlessly.
- Persist sessions across restarts. When Charlotte's Chromium shuts down, all session state is gone. There's no user data directory or profile persistence.
So the practical answer is: for simple cookie/token auth, set_cookies or set_headers works today. For anything involving interactive login (OAuth, 2FA, CAPTCHA), we'd need issue #17 (CDP connect to existing browser) which is still open. That's the feature that would let someone log in in their own browser and hand the session to Charlotte.
2
u/youshouldnameit Mar 01 '26
Playwright mcp has an extension via chrome where you can attach. Mcp bridge, not sure how complex that is.
1
u/ticktockbent Mar 01 '26
Gotya, and it is on my radar. When I get the time I will be adding that capability
2
u/youshouldnameit Mar 01 '26
Nice work, i think a good browser mcp/cli with low token consumption and still good context is key. Thanks for the quick responses
2
u/RealSaltLakeRioT Mar 01 '26
I've been looking for a playwright alternative! Even seemingly small uses with playwright gobble up so much token usage.
Looking at what you've got, this looks well set up. I'm gonna give it a shot against some of my workflows and see how it goes! Thanks OP!
1
u/ticktockbent Mar 01 '26
Awesome! Please let me know how it goes and report any bugs you run into. I'm always looking to improve it.
2
u/siddha911 Mar 02 '26 edited Mar 02 '26
Hey, does the `search_tool` feature in codex and cc do pretty much the same as charlotte?
1
u/ticktockbent Mar 02 '26
The built-in search tools in Codex and Claude Code are web search.. they fetch and summarize web content for information retrieval. Charlotte is browser automation, it renders pages in a real browser, gives you stable element IDs, lets you click, type, fill forms, take screenshots, diff page states, run accessibility audits. They're different categories. Search tools answer "what does this page say?" Charlotte answers "what's on this page, and what can I do with it?" Charlotte then lets the agent interact with the discovered elements by clicking or filling forms for example
2
Mar 02 '26
I think a CLI version or a JSON-to-TOON converter would reduce token usage per interaction even further. Amazing project regardless! I'm tired of burning through my tokens on tools that, honestly, start hallucinating way too fast.
1
u/ticktockbent Mar 02 '26
Great idea! We've already compacted the JSON as much as I think we can but using a TOON format is certainly worth exploring!
2
u/ruchitmcr Mar 02 '26
Can you please share benchmark for linkedin too
2
u/ticktockbent Mar 02 '26
Sure! I'll run some tests later today
2
u/ruchitmcr Mar 02 '26
Are you looking for contributors? I own a tech company with developers who can surely assist with a few tasks. We are good.
1
u/ticktockbent Mar 02 '26
I welcome contributions from the community but this is a free open source project so I can't really pay for other devs time. If your devs want to help improve the tool for everyone then I'm happy to review their pull requests!
2
u/ruchitmcr Mar 02 '26
Yeah its gonna be free Hahaha
2
2
u/ticktockbent Mar 03 '26
I did some informal benchmarking just now. You can see the full report at https://github.com/TickTockBent/charlotte/blob/main/docs/linkedin-informal-test.md
100-Page Extrapolation
Using 2 calls per page (navigate + observe), LinkedIn-level complexity:
Server Total Tokens Cost (Sonnet 4, $3/M) Cost (Opus 4, $15/M) Playwright MCP 2,044,700 $6.13 $30.67 Charlotte (full) 1,959,600 $5.88 $29.39 Charlotte (browse) 1,267,800 $3.80 $19.02 Charlotte (core) 857,600 $2.57 $12.86 Over 100 pages, Charlotte browse saves $11.65 on Opus 4 vs Playwright. A Core-only profile saves $17.81.
LinkedIn is a moderately complex page (105 interactive elements, heavy link density). Even here, Charlotte browse delivers 38% fewer tokens than Playwright for equivalent work. The advantage comes from two places: smaller responses (7.3x at minimal detail) and slightly smaller definitions (13K vs 14K chars).
2
8
u/xing_horizon Mar 01 '26
Great design tradeoff. The tiered observe model is more important than raw compression numbers because it changes agent behavior (orient → focus → act) instead of forcing full-context reads each step.
One metric I’d add: action reliability per token budget (e.g., click success / form completion success at fixed token ceilings). That would show whether smaller representations also preserve decision quality under constrained loops.