<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Emad Omar</title>
    <description>The latest articles on DEV Community by Emad Omar (@emad_omar_5311e0e328be24c).</description>
    <link>https://dev.to/emad_omar_5311e0e328be24c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3838199%2F1dc5d09d-1f93-4a04-bca0-697c981810a5.jpg</url>
      <title>DEV Community: Emad Omar</title>
      <link>https://dev.to/emad_omar_5311e0e328be24c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emad_omar_5311e0e328be24c"/>
    <language>en</language>
    <item>
      <title>agent-browser or mare-browser? Here is how I actually choose.</title>
      <dc:creator>Emad Omar</dc:creator>
      <pubDate>Wed, 17 Jun 2026 11:18:00 +0000</pubDate>
      <link>https://dev.to/emad_omar_5311e0e328be24c/agent-browser-or-mare-browser-here-is-how-i-actually-choose-4l97</link>
      <guid>https://dev.to/emad_omar_5311e0e328be24c/agent-browser-or-mare-browser-here-is-how-i-actually-choose-4l97</guid>
      <description>&lt;h1&gt;
  
  
  agent-browser or mare-browser? Here's how I actually choose.
&lt;/h1&gt;

&lt;p&gt;If you're building web apps with an AI agent, you hit the same wall pretty fast: the model can write the code, but it can't &lt;em&gt;see&lt;/em&gt; what the code does in a real browser. It can't click the button, watch the login fail, read the console error, or check what the API actually returned. It's coding blind.&lt;/p&gt;

&lt;p&gt;That's what browser automation fixes. You give the agent a real browser it can drive — open a page, fill a form, click around, read the DOM, watch the network — so the loop stops being "AI writes code, you test it, you describe the bug, AI guesses" and becomes "AI writes code, AI tests it, AI reads the failure, AI fixes it." For both developing and testing, that's the difference between an assistant and an actual pair.&lt;/p&gt;

&lt;p&gt;The way you wire that up is an &lt;strong&gt;MCP server&lt;/strong&gt; — a small program that exposes browser actions as tools your agent can call. Both tools in this post are exactly that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/vercel-labs/agent-browser" rel="noopener noreferrer"&gt;agent-browser&lt;/a&gt;&lt;/strong&gt; is Vercel's browser automation tool for agents — a fast native CLI with a huge feature set and an MCP mode. Think "do almost anything in a browser, anywhere."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/emadklenka/mare_browser_mcp" rel="noopener noreferrer"&gt;mare-browser-mcp&lt;/a&gt;&lt;/strong&gt; is my lean, debugging-first MCP server built on Playwright. Think "give the agent the same DevTools signals I'd look at myself."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So which do you install? Someone asked me last week: "Vercel's agent-browser has 36,000 stars. Why are you still working on your little browser MCP?"&lt;/p&gt;

&lt;p&gt;Fair question. And the honest answer is more interesting than "mine is better," because it isn't. They're not even really the same kind of tool.&lt;/p&gt;

&lt;p&gt;So instead of pretending there's a winner, let me tell you how I actually decide which one to reach for. Because I use both.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, let's be honest about agent-browser
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/vercel-labs/agent-browser" rel="noopener noreferrer"&gt;agent-browser&lt;/a&gt; is excellent. I'm not going to do the thing where I damn a competitor with faint praise. It's a native Rust CLI, backed by Vercel, with a hundred-plus contributors and a feature list that's genuinely intimidating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An accessibility-tree snapshot with stable refs (&lt;code&gt;@e1&lt;/code&gt;, &lt;code&gt;@e2&lt;/code&gt;) — which is the &lt;em&gt;correct&lt;/em&gt; way to let an LLM point at elements&lt;/li&gt;
&lt;li&gt;Semantic locators (&lt;code&gt;find role&lt;/code&gt;, &lt;code&gt;find label&lt;/code&gt;, &lt;code&gt;find testid&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Tabs, frames, dialogs, HAR recording, traces, profiler&lt;/li&gt;
&lt;li&gt;React DevTools introspection and Web Vitals&lt;/li&gt;
&lt;li&gt;Network mocking, cookies, auth vault, an MCP server with tool profiles&lt;/li&gt;
&lt;li&gt;iOS Simulator support and a stack of cloud browser providers (Browserbase, Kernel, and friends)&lt;/li&gt;
&lt;li&gt;Visual + snapshot diffing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you handed me a blank slate and said "pick the most capable general-purpose browser automation tool for agents," I'd point at agent-browser and not feel conflicted about it.&lt;/p&gt;

&lt;p&gt;So why does &lt;a href="https://github.com/emadklenka/mare_browser_mcp" rel="noopener noreferrer"&gt;mare-browser-mcp&lt;/a&gt; exist?&lt;/p&gt;

&lt;h2&gt;
  
  
  Because breadth and fit are different things
&lt;/h2&gt;

&lt;p&gt;mare is small on purpose. It's a Playwright-based MCP server with about a dozen tools, and the whole thing is bent toward one job: &lt;strong&gt;debugging a web app you're actively building, with an AI agent sitting next to you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's it. That's the niche. It doesn't do iOS. It doesn't do cloud browsers. It doesn't do HAR files. And it's never going to out-feature a Vercel project — I'm one person, and I'm not trying to.&lt;/p&gt;

&lt;p&gt;What it does have is a different center of gravity. The flagship tool is &lt;code&gt;browser_debug&lt;/code&gt;, which returns the console, the network requests (with payloads, response bodies, status codes, and timing), the current URL, the title, and any dialogs — &lt;em&gt;in a single call&lt;/em&gt;. When you're debugging your own app, that one call is basically the whole game. (I wrote a &lt;a href="https://github.com/emadklenka/mare_browser_mcp" rel="noopener noreferrer"&gt;whole separate piece&lt;/a&gt; about why telemetry beats screenshots, so I won't rehash it here.)&lt;/p&gt;

&lt;p&gt;In agent-browser, that same information is spread across &lt;code&gt;console&lt;/code&gt;, &lt;code&gt;errors&lt;/code&gt;, &lt;code&gt;network requests&lt;/code&gt;, and &lt;code&gt;network request &amp;lt;id&amp;gt;&lt;/code&gt;. Nothing wrong with that — it's a more granular, more general design. But for &lt;em&gt;my&lt;/em&gt; loop, one call beats four.&lt;/p&gt;

&lt;h2&gt;
  
  
  So here's the actual decision
&lt;/h2&gt;

&lt;p&gt;I stopped thinking of it as "which tool is better" and started thinking about what I'm actually doing that hour.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reach for agent-browser when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need a browser somewhere that isn't your laptop — CI, serverless, a cloud provider&lt;/li&gt;
&lt;li&gt;You're testing on real mobile Safari or emulated devices&lt;/li&gt;
&lt;li&gt;You're doing broad automation: many tabs, frame juggling, recorded traces, visual regression diffs&lt;/li&gt;
&lt;li&gt;You want React fiber introspection or Web Vitals out of the box&lt;/li&gt;
&lt;li&gt;You want a mature, well-staffed tool with a release cadence and a big community behind it&lt;/li&gt;
&lt;li&gt;You're scraping or automating across many sites and want stealth, proxies, domain allowlists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reach for mare when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building a web app and debugging it locally with an agent&lt;/li&gt;
&lt;li&gt;The bug is "the button does nothing" and the real answer is a &lt;code&gt;401&lt;/code&gt; with the wrong field name in the request body&lt;/li&gt;
&lt;li&gt;You want the model looking at network payloads and console errors, not screenshots&lt;/li&gt;
&lt;li&gt;You want a tiny, readable tool surface the model won't get lost in&lt;/li&gt;
&lt;li&gt;You log in once by hand (it runs headed by default) and then let the agent work in your real session&lt;/li&gt;
&lt;li&gt;You want to crack open &lt;code&gt;browser_eval&lt;/code&gt; and poke at &lt;code&gt;window&lt;/code&gt; state or computed styles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice these lists barely overlap. That's the point. One is a Swiss Army knife. The other is a screwdriver I ground down to fit one screw I turn forty times a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest gap I just closed
&lt;/h2&gt;

&lt;p&gt;For a while there was one place mare was genuinely &lt;em&gt;worse&lt;/em&gt;, not just smaller — and it's worth admitting because it's the kind of bug that quietly lies to you.&lt;/p&gt;

&lt;p&gt;mare also has ref-based snapshots (&lt;code&gt;browser_snapshot&lt;/code&gt; gives you &lt;code&gt;e1&lt;/code&gt;, &lt;code&gt;e2&lt;/code&gt;, ...), but until recently those refs resolved through a generated CSS path like &lt;code&gt;#list &amp;gt; li:nth-of-type(3) &amp;gt; button&lt;/code&gt;. Think of it as directions: "the third house on the second street." If the page re-rendered between the snapshot and the click — a row inserted, a list re-sorted, React reconciling — that "third house" could now be a &lt;em&gt;different&lt;/em&gt; house. And mare would click it. Confidently. Without telling you.&lt;/p&gt;

&lt;p&gt;For a debugging tool, that's the worst possible failure: it doesn't error, it misleads.&lt;/p&gt;

&lt;p&gt;I just rewrote it so each ref is pinned to the exact element it was captured on (via a unique attribute on the node itself). Now &lt;code&gt;click e3&lt;/code&gt; either hits &lt;em&gt;that&lt;/em&gt; element or fails loudly and tells you to re-snapshot. It can't silently drift anymore. agent-browser already got this right with its ref system; mare does now too.&lt;/p&gt;

&lt;p&gt;I'm telling you this partly because it's a real improvement, and partly because I think "here's where my tool was wrong and here's how I fixed it" is more trustworthy than a feature table.&lt;/p&gt;

&lt;h2&gt;
  
  
  You're allowed to use both
&lt;/h2&gt;

&lt;p&gt;This isn't a cage match. They're both MCP servers. They can both be registered with the same agent at the same time. I genuinely run mare for the inner loop of building something — the tight write-test-read-fix cycle on localhost — and I'd reach for agent-browser the moment the job grows past my own machine.&lt;/p&gt;

&lt;p&gt;If you want the most capable, most supported, broadest browser tool for agents, use agent-browser. That's the easy, correct default and I'll be the first to recommend it.&lt;/p&gt;

&lt;p&gt;If you specifically want a small, debugging-first MCP that hands your agent the same DevTools signals you'd look at yourself — and you're mostly working on your own app on your own machine — that's the gap mare was built for.&lt;/p&gt;

&lt;p&gt;Pick the screwdriver or pick the Swiss Army knife. Just don't pick based on the star count.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try mare
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/emadklenka/mare_browser_mcp
&lt;span class="nb"&gt;cd &lt;/span&gt;mare_browser_mcp
pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;npx playwright &lt;span class="nb"&gt;install &lt;/span&gt;chromium
pnpm run setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's free. If it saves you an afternoon of squinting at screenshots, &lt;a href="https://buymeacoffee.com/emadomar" rel="noopener noreferrer"&gt;buy me a coffee&lt;/a&gt;. And if you try both tools and disagree with where I drew the line, I'd honestly like to hear it.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>webdev</category>
      <category>playwright</category>
    </item>
    <item>
      <title>Your AI Doesn't Need Screenshots. It Needs DevTools.</title>
      <dc:creator>Emad Omar</dc:creator>
      <pubDate>Sat, 28 Mar 2026 22:04:34 +0000</pubDate>
      <link>https://dev.to/emad_omar_5311e0e328be24c/your-ai-doesnt-need-screenshots-it-needs-devtools-3lg1</link>
      <guid>https://dev.to/emad_omar_5311e0e328be24c/your-ai-doesnt-need-screenshots-it-needs-devtools-3lg1</guid>
      <description>&lt;h1&gt;
  
  
  Your AI Doesn't Need Screenshots. It Needs DevTools.
&lt;/h1&gt;

&lt;p&gt;Most AI coding agents are still debugging web apps in the dumbest possible way.&lt;/p&gt;

&lt;p&gt;They ask for a screenshot.&lt;/p&gt;

&lt;p&gt;Meanwhile the real answer is usually sitting in the browser console, the Network tab, the request payload, or the response body.&lt;/p&gt;

&lt;p&gt;That is not really an AI problem. It is a tooling problem.&lt;/p&gt;

&lt;p&gt;I got tired of watching agents guess from screenshots while I had DevTools open right next to them, showing the exact reason something failed. So I built &lt;a href="https://github.com/emadklenka/mare_browser_mcp" rel="noopener noreferrer"&gt;&lt;code&gt;mare-browser-mcp&lt;/code&gt;&lt;/a&gt;, a browser MCP designed less like "remote control for a webpage" and more like "give the model the same debugging signals I actually use."&lt;/p&gt;

&lt;p&gt;That changed the loop from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI writes code -&amp;gt; I test it -&amp;gt; I describe the bug -&amp;gt; AI guesses a fix -&amp;gt; repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI writes code -&amp;gt; AI tests it -&amp;gt; AI reads the failure -&amp;gt; AI fixes it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That difference matters more than people think.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Screenshot Trap
&lt;/h2&gt;

&lt;p&gt;Most browser MCPs start from the same assumption: if the model can see the page, it can debug the app.&lt;/p&gt;

&lt;p&gt;That sounds reasonable until you try to debug anything real.&lt;/p&gt;

&lt;p&gt;A screenshot does not tell the model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;that login failed with a &lt;code&gt;401&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;that the frontend sent &lt;code&gt;user_email&lt;/code&gt; instead of &lt;code&gt;email&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;that a button click triggered a JS exception&lt;/li&gt;
&lt;li&gt;that the UI is fine but the API returned invalid JSON&lt;/li&gt;
&lt;li&gt;that the missing data is actually below the fold in a scrollable grid&lt;/li&gt;
&lt;li&gt;that the context menu only appears on right-click&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A screenshot is expensive, unstructured, and often the least useful artifact in the whole debugging flow.&lt;/p&gt;

&lt;p&gt;Language models are good at reasoning over structured text. We keep handing them pixels and asking them to guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Wanted Instead
&lt;/h2&gt;

&lt;p&gt;I wanted a browser MCP that makes the model think more like a developer with DevTools open.&lt;/p&gt;

&lt;p&gt;That means a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It should read console logs and page errors directly&lt;/li&gt;
&lt;li&gt;It should see network requests, payloads, response bodies, and timing&lt;/li&gt;
&lt;li&gt;It should batch actions instead of making one tiny tool call per click&lt;/li&gt;
&lt;li&gt;It should query the DOM as structured data&lt;/li&gt;
&lt;li&gt;It should handle hover, drag, right-click, and nested scroll containers&lt;/li&gt;
&lt;li&gt;It should only use screenshots when the problem is actually visual&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the core idea behind &lt;code&gt;mare-browser-mcp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is a lean MCP server built with Playwright and the MCP SDK, and the whole design is biased toward structured debugging data first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Tool I Wish More Browser MCPs Had
&lt;/h2&gt;

&lt;p&gt;The most important tool in the server is &lt;code&gt;browser_debug&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One call returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;current URL&lt;/li&gt;
&lt;li&gt;page title&lt;/li&gt;
&lt;li&gt;console logs&lt;/li&gt;
&lt;li&gt;page errors&lt;/li&gt;
&lt;li&gt;dialogs&lt;/li&gt;
&lt;li&gt;network request history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the network history is the useful part. The server captures things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;method&lt;/li&gt;
&lt;li&gt;URL&lt;/li&gt;
&lt;li&gt;parsed query params&lt;/li&gt;
&lt;li&gt;request headers&lt;/li&gt;
&lt;li&gt;request body&lt;/li&gt;
&lt;li&gt;status code&lt;/li&gt;
&lt;li&gt;JSON response body&lt;/li&gt;
&lt;li&gt;duration in milliseconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of the model seeing "submit button clicked and page still looks wrong," it can see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://app.example.com/api/session"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requestBody"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"secret"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"responseBody"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"missing field: email"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a completely different debugging experience.&lt;/p&gt;

&lt;p&gt;At that point the model is not guessing anymore. It can actually reason from evidence.&lt;/p&gt;

&lt;p&gt;There are a few guardrails in the implementation too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;static assets are skipped&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OPTIONS&lt;/code&gt; preflights are ignored&lt;/li&gt;
&lt;li&gt;bodies are capped at 4 KB&lt;/li&gt;
&lt;li&gt;auth headers are masked&lt;/li&gt;
&lt;li&gt;cookies are reduced to &lt;code&gt;"[present]"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;failed requests are logged with the failure reason&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the data stays useful without turning into junk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batching Matters More Than It Sounds
&lt;/h2&gt;

&lt;p&gt;Another thing that slows agents down is one-action-per-call design.&lt;/p&gt;

&lt;p&gt;Click. Wait. Screenshot. Fill. Wait. Screenshot.&lt;/p&gt;

&lt;p&gt;That is not just annoying. It makes the whole debugging loop slower and dumber.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;browser_act&lt;/code&gt; fixes that by accepting an ordered list of actions in one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"secret"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"button[type=submit]"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current action set covers a lot of what real apps actually need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;click&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hover&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;drag&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clicklink&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fill&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;select&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;keypress&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;waitfor&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scrollto&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wait&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clearconsole&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the model is not stuck the moment the UI uses hover states, resize handles, sortable grids, or context menus.&lt;/p&gt;

&lt;p&gt;And yes, right-click works through &lt;code&gt;click&lt;/code&gt; with &lt;code&gt;button: "right"&lt;/code&gt;, which turns out to matter more often than you might expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured DOM Queries Beat Giant Dumps
&lt;/h2&gt;

&lt;p&gt;Another thing I did not want was a browser tool that just vomits the whole DOM back at the model.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;browser_query&lt;/code&gt; is useful because it lets the model ask narrower questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many rows are there?&lt;/li&gt;
&lt;li&gt;Is this button visible?&lt;/li&gt;
&lt;li&gt;What text do these badges have?&lt;/li&gt;
&lt;li&gt;Is this input disabled?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".ag-row"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"count_only"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"all"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"visible_only"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".badge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"className"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"visible"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The supported fields are deliberately practical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;text&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;value&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;visible&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;disabled&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;className&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;href&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;innerHTML&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That keeps the output small enough to reason about and avoids wasting context on useless noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Screenshots Are Still There. They Just Aren't First.
&lt;/h2&gt;

&lt;p&gt;I did keep &lt;code&gt;browser_screenshot&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But I made the tool description explicitly say it should be the last resort, not the default.&lt;/p&gt;

&lt;p&gt;That sounds minor, but it really changes how an agent behaves. If the tool surface itself tells the model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use &lt;code&gt;browser_debug&lt;/code&gt; first&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;browser_query&lt;/code&gt; second&lt;/li&gt;
&lt;li&gt;use screenshots only for visual problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then the model starts spending its attention on data instead of appearance.&lt;/p&gt;

&lt;p&gt;That is the behavior I wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Apps Need More Than Page Scroll
&lt;/h2&gt;

&lt;p&gt;One of the easiest ways browser tools fall apart is scrolling.&lt;/p&gt;

&lt;p&gt;Scrolling the page is not enough for modern apps. A lot of important UI is inside:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data grid viewports&lt;/li&gt;
&lt;li&gt;chat panels&lt;/li&gt;
&lt;li&gt;sidebars&lt;/li&gt;
&lt;li&gt;overflow containers&lt;/li&gt;
&lt;li&gt;modals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;browser_scroll&lt;/code&gt; can scroll the page, scroll an element into view, or scroll inside a specific container.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".ag-body-viewport"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"down"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pixels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And because it returns scroll position data, the model can tell where it is instead of just flailing around.&lt;/p&gt;

&lt;h2&gt;
  
  
  There Is Also an Escape Hatch
&lt;/h2&gt;

&lt;p&gt;Even with all of that, fixed tools never cover every case.&lt;/p&gt;

&lt;p&gt;So there is &lt;code&gt;browser_eval&lt;/code&gt;, which runs JavaScript in the page context.&lt;/p&gt;

&lt;p&gt;That is useful for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reading computed styles&lt;/li&gt;
&lt;li&gt;inspecting app state on &lt;code&gt;window&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;appending input text without clearing it&lt;/li&gt;
&lt;li&gt;simulating more custom interactions&lt;/li&gt;
&lt;li&gt;calling &lt;code&gt;fetch()&lt;/code&gt; directly&lt;/li&gt;
&lt;li&gt;checking CSS visibility details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do not think the escape hatch should be the first thing the model reaches for. But it absolutely should exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like in Practice
&lt;/h2&gt;

&lt;p&gt;A realistic flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. browser_navigate({ url: "https://myapp.com", clear_logs: true })

2. browser_act({
     commands: [
       { action: "fill", selector: "#email", value: "user@example.com" },
       { action: "fill", selector: "#password", value: "secret" },
       { action: "click", selector: "button[type=submit]" }
     ]
   })

3. browser_wait_for_network({ url_pattern: "/api/session", method: "POST" })

4. browser_debug({ console_types: ["error"], last_n: 20 })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there the model can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether the request fired&lt;/li&gt;
&lt;li&gt;what payload got sent&lt;/li&gt;
&lt;li&gt;what response came back&lt;/li&gt;
&lt;li&gt;whether the browser threw an error&lt;/li&gt;
&lt;li&gt;whether the failure is frontend, backend, or both&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the kind of loop where an agent becomes genuinely useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Think This Matters
&lt;/h2&gt;

&lt;p&gt;I think a lot of people are measuring browser MCPs by the wrong thing.&lt;/p&gt;

&lt;p&gt;The question is not "can the AI click buttons?"&lt;/p&gt;

&lt;p&gt;The real question is: when something breaks, can the AI see the same evidence a good developer would look at?&lt;/p&gt;

&lt;p&gt;If the answer is yes, the quality of debugging jumps fast.&lt;/p&gt;

&lt;p&gt;If the answer is no, you end up with a very expensive screenshot viewer.&lt;/p&gt;

&lt;p&gt;That is why I built &lt;code&gt;mare-browser-mcp&lt;/code&gt; the way I did. Not to make agents better at looking at pages, but to make them better at understanding what happened in the browser.&lt;/p&gt;

&lt;p&gt;Not screenshots first. Telemetry first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/emadklenka/mare_browser_mcp
&lt;span class="nb"&gt;cd &lt;/span&gt;mare_browser_mcp
pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;npx playwright &lt;span class="nb"&gt;install &lt;/span&gt;chromium
pnpm run setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also supports global install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;-g&lt;/span&gt; mare-browser-mcp
npx playwright &lt;span class="nb"&gt;install &lt;/span&gt;chromium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repo has setup instructions for Claude Code and OpenCode, and the current server exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;browser_navigate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser_act&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser_debug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser_query&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser_screenshot&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser_eval&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser_scroll&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser_restart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser_upload&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser_wait_for_network&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building web apps with AI, that combination is much closer to giving your agent DevTools than giving it a screenshot and hoping for the best.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>claude</category>
      <category>playwright</category>
      <category>javascript</category>
    </item>
    <item>
      <title>claude</title>
      <dc:creator>Emad Omar</dc:creator>
      <pubDate>Sat, 28 Mar 2026 20:38:00 +0000</pubDate>
      <link>https://dev.to/emad_omar_5311e0e328be24c/claude-2m7d</link>
      <guid>https://dev.to/emad_omar_5311e0e328be24c/claude-2m7d</guid>
      <description></description>
    </item>
    <item>
      <title>Give Claude Your Browser Console — It Debugs Like a Real Developer</title>
      <dc:creator>Emad Omar</dc:creator>
      <pubDate>Sun, 22 Mar 2026 10:20:20 +0000</pubDate>
      <link>https://dev.to/emad_omar_5311e0e328be24c/give-claude-your-browser-console-it-debugs-like-a-real-developer-nmn</link>
      <guid>https://dev.to/emad_omar_5311e0e328be24c/give-claude-your-browser-console-it-debugs-like-a-real-developer-nmn</guid>
      <description>&lt;h1&gt;
  
  
  Give Claude Your Browser Console — It Debugs Like a Real Developer
&lt;/h1&gt;

&lt;p&gt;You know that moment when something breaks in your web app and you open DevTools, check the console, scan the network tab, find the failing API call, read the JSON response — and finally figure out what went wrong?&lt;/p&gt;

&lt;p&gt;That's exactly what mare-browser-mcp gives Claude.&lt;/p&gt;

&lt;p&gt;Not a screenshot. Not a DOM dump. The actual console errors, the actual network requests, the actual JSON responses from your API. Claude reads them the same way you do.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Other Browser MCP Tools
&lt;/h2&gt;

&lt;p&gt;Most browser MCP servers give the LLM one move: take a screenshot.&lt;/p&gt;

&lt;p&gt;Screenshots are pixels. Claude has to guess what's happening from an image. It can't see a 401 response. It can't read a JS stack trace. It can't tell if an API returned &lt;code&gt;{ error: "session expired" }&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's not debugging. That's guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What mare-browser-mcp Does Differently
&lt;/h2&gt;

&lt;p&gt;One tool does the heavy lifting: &lt;code&gt;browser_debug&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Call it after anything goes wrong and you get back:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current URL and page title&lt;/li&gt;
&lt;li&gt;All console logs (errors, warnings, everything)&lt;/li&gt;
&lt;li&gt;All network requests with &lt;strong&gt;status codes and full JSON response bodies&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"current_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://myapp.com/dashboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dashboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Uncaught TypeError: Cannot read properties of undefined (reading 'user')"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/session"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"token expired"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude reads that and knows exactly what happened — token expired, session failed, JS crashed because user was undefined. Same conclusion you'd reach in 30 seconds of DevTools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Toolkit
&lt;/h2&gt;

&lt;p&gt;8 tools, each with a clear job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;browser_navigate&lt;/code&gt; — go to a URL, optionally clear logs before starting&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_act&lt;/code&gt; — click, fill, keypress, scroll — batch multiple steps in one call&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_debug&lt;/code&gt; — &lt;strong&gt;start here when something breaks&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_query&lt;/code&gt; — read DOM elements without a screenshot&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_eval&lt;/code&gt; — run any JavaScript in the page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_scroll&lt;/code&gt; — scroll by pixels or to a specific element&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_wait_for_network&lt;/code&gt; — wait for a specific API call to complete&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_screenshot&lt;/code&gt; — last resort only&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real Debugging Session
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;browser_navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://myapp.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;clear_logs: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;browser_act&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;action: &lt;/span&gt;&lt;span class="s2"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;selector: &lt;/span&gt;&lt;span class="s2"&gt;"#email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;action: &lt;/span&gt;&lt;span class="s2"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;selector: &lt;/span&gt;&lt;span class="s2"&gt;"#password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="s2"&gt;"secret"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;action: &lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;selector: &lt;/span&gt;&lt;span class="s2"&gt;"button[type=submit]"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;browser_wait_for_network&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;url_pattern: &lt;/span&gt;&lt;span class="s2"&gt;"/api/session"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;method: &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;browser_debug&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;console_types: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;sees&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"token expired"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;

&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="no"&gt;Figures&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="no"&gt;No&lt;/span&gt; &lt;span class="n"&gt;back&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;forth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="no"&gt;No&lt;/span&gt; &lt;span class="n"&gt;screenshots&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the workflow. Claude acts, waits for the network, reads the debug output, understands what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install in 5 Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/emadklenka/mare_browser_mcp
&lt;span class="nb"&gt;cd &lt;/span&gt;mare_browser_mcp
pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;npx playwright &lt;span class="nb"&gt;install &lt;/span&gt;chromium
pnpm run setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pnpm run setup&lt;/code&gt; registers the MCP with Claude Code automatically — no manual path config.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's Free
&lt;/h2&gt;

&lt;p&gt;MIT license. Use it however you want.&lt;/p&gt;

&lt;p&gt;If it saves you debugging time → &lt;a href="https://buymeacoffee.com/emadomar" rel="noopener noreferrer"&gt;https://buymeacoffee.com/emadomar&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub → &lt;a href="https://github.com/emadklenka/mare_browser_mcp" rel="noopener noreferrer"&gt;https://github.com/emadklenka/mare_browser_mcp&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Built this because I was tired of Claude guessing from screenshots. Now it debugs the way I do.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>claude</category>
      <category>playwright</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
