-
Notifications
You must be signed in to change notification settings - Fork 4.7k
mcp tool design
Reference for anyone building on this server rather than just using it. The MCP server page says what the tools are; this says why they return what they return, and carries the measurements behind each decision. If you are building a server of your own rather than reading about ours, how to build an MCP server is the general version of the same argument.
Moved out of what is now the MCP server page on 2026-09-03: it was 82 of 227 lines, sitting between the tool list and everything else, on a page whose job is to get somebody browsing in two commands.
browser_click_at takes viewport coordinates instead of a selector, moves the
pointer there rather than teleporting, optionally holds before releasing, and
returns a screenshot of what happened - for a slider, a canvas-drawn challenge,
or a press-and-hold.
browser_snapshot returns the title, the url and the visible interactive
elements rather than the accessibility tree: one country <select> on a real
sign-up page contributes about two hundred <option> nodes, which fill the
character budget before the form does.
Each element comes with a selector when one can reach it, and that string goes
to browser_click or browser_type verbatim. It is built to match exactly one
element, which the obvious selector often does not: measured across 958 elements
on real pages, 88% could be addressed but only 48% unambiguously, and Playwright
acts on the first match without complaining, so aiming at the third of five
identical links quietly hit the first. Elements no selector can reach carry at
instead, the centre coordinates, for browser_click_at.
When a click does not land, the error says what stopped it rather than only that
it timed out: an element covering the target is named, with its id, its class and
its text, because the next move is to deal with that thing and not to retry. If
the page has replaced getBoundingClientRect so that nothing can be measured,
the snapshot reports unmeasurable with a count instead of an empty list, so a
tampered page cannot be mistaken for a page with no controls.
When an element has none of those, the selector falls back to data-testid and
then to a unique aria-label, which together took coverage from 90.5% of
elements to 97.9%. It stops there: a text-based selector is not stable, and a
handle that sometimes points elsewhere is the thing this exists to remove. What
is left carries at and nothing else.
A link addressed by its href has no separate href field, because the selector
already holds it: a[href='/cart'] says where the link goes as plainly as the
field did, and not repeating it is what kept this affordable (+47% of the
payload with the repetition, +15% without). A link addressed by its id keeps its
href, having nothing duplicated.
browser_read_html returns the page's markup instead of a flat list, for when
the structure is what matters: a form and its labels, a table, what a control is
wired to. The browser decides what is actually painted, on a clone of the
document so the live page is never written to, and the markup is then reduced to
what is worth reading. Measured on real pages, 9.6 MB of markup became 293 KB
with every one of the 1,453 interactive elements still present. mode
is form (the interactive surface and the text explaining it), text (the prose
alone) or full (the structure, with the noise and the attribute soup gone).
Tool names mirror the Microsoft Playwright MCP, so prompts written for it work here too.
The MCP server page states the four rungs. This is what they cost and what they bought.
This is a pattern check on the obvious road, not a sandbox. That sentence used
to end "and it is not described as one", which was false of the only text a
model actually reads: the instructions block said browser_evaluate will not act on the page and the tool docstring said It will not act. Measured 2026-09-04,
thirteen of fifteen ordinary acting expressions passed the guard, requestSubmit
among them - the modern spelling of the one call that was refused. Both texts now
say what the code does and add the sentence that matters, which is that a script
slipping past the check is a bug to report rather than a licence to use it. A
model told the door is locked has no reason to avoid the handle.
What makes the refusal reasonable is the rest of the ladder:
measured on the same task, the same model went from 14 steps with two
browser_evaluate calls - one of them setting a <select> from script - to 8
steps with none, three runs out of three.
The snapshot carries the state as well as the shape: checked for a checkbox or
radio, value for a select, alongside the text. That half matters as much as
the tools. The run above reached for script to READ the form back before it ever
reached for script to write it, and a gap in what a caller can see is answered
with evaluate just as surely as a gap in what it can do.
- OpenAI Operator alternatives
- Open-source Operator-style agents
- Is OpenAI Operator still available?
- OpenAI Operator vs Claude computer use
- browser-use alternatives
- Choosing an AI browser agent
- Open-source AI browser agents
- Open-source computer-use agents
- What is an AI web agent?
- AI browser agents vs traditional scraping
- Cloud browser infrastructure for AI agents, explained
- Browserbase alternatives
- Firecrawl vs an AI browser agent
- Skyvern alternatives
- Stagehand vs browser-use
- Project Mariner is gone: what replaced it
- Manus alternatives
- Gemini computer use vs Claude computer use
- AIHawk, reviewed honestly by its own wiki
- AI browser vs AI browser agent: which one do you want?
- AI browser agent vs RPA: which one fits the job
- AI browser agent vs n8n, Zapier and Make
- Vercel agent-browser alternatives, compared honestly
- What is an agentic browser? Definition and the two kinds
- Open-source agentic browsers: the three layers, compared
- Choosing an MCP server for browser automation: four axes
- Stealth MCP servers compared: Camoufox, nodriver, Patchright
- Playwright MCP alternatives, and the three you don't need
- Autonomous browser agents: the four rungs of autonomy
- What is actually free in the AI browser agent stack
- browser-use on GitHub: what the repo actually gives you
- Playwright MCP vs Chrome DevTools MCP: different jobs
- How to choose among MCP servers: a map by category
- Which MCP servers are worth adding to Claude Code
- MCP on GitHub: finding servers and judging them fast
- MCP vs an API: the decision, and what the wrapper costs
- MCP alternatives: when the protocol is the wrong shape
- Why does my AI agent get blocked?
- The timing signal AI agents give off
- Agent retry loops trip rate limits, not fingerprints
- Claude computer use detected as a bot
- browser-use getting blocked: what you can and cannot change
- Playwright MCP session blocked: four causes, four fixes
- Playwright MCP and captchas: what actually gets you past
- Cloudflare and a browser MCP server: what is being read
- Can an AI agent solve a captcha? The honest answer
- Getting an AI agent to fill out forms
- Which model to use with AIHawk
- Browser problem or model problem?
- Running AIHawk's browser from Claude Code
- Extracting data to a CSV with an AI agent
- Monitoring a page for changes with an AI agent
- Running AIHawk's browser from Claude Desktop
- Running AIHawk's browser from Cursor
- Using an AI agent to hunt for apartments
- Getting website data into Google Sheets with an AI agent
- Using an AI agent to download invoices from portals
- AI agents for web research
- Using an AI agent to test your own website
- Running AIHawk's browser from Cline
- Posting to social media with an AI agent
- Posting to Facebook with an AI agent
- Posting to Instagram with an AI agent
- Posting to X with an AI agent
- Automating LinkedIn posts: read this first
- Appointment bots: what they are and what an agent can legitimately do
- Track prices across sites with an AI agent
- Build a lead list with an AI browser agent
- Run an AI browser agent on a schedule
- AI browser agent with a local LLM: what changes
- Should you log your AI agent into your accounts?
- How to write a task an AI browser agent can follow
- Move data between two web apps with an AI agent
- The MCP server
- How the tools are shaped, and why
- Playwright MCP vs the Playwright CLI: which fits when
- Playwright MCP: browser is already in use, and the fix
- Playwright MCP best practices: four decisions that matter
- Playwright MCP with a proxy, and the three leaks it leaves
- A browser MCP server in GitHub Copilot: setup and limits
- Using a browser MCP server for web scraping: the pattern
- Which LLM for browser automation: the four properties
- How to build a browser agent, and what to take instead
- Getting an AI agent to log into a website: three routes
- MCP tools, resources and prompts: who controls each
- How many MCP tools is too many? The context arithmetic
- How to build an MCP server: the decisions, not the scaffold
- Local or remote MCP server: what changes, and what does not
- Writing an MCP client in Python: the thirty-line version
- Self-hosted AI agent: what one actually costs to run
- How long an AI browser agent takes per step, measured
- Text, HTML, snapshot or screenshot: what the agent should read
- Giving an AI browser agent a stopping condition
- Keeping an AI browser agent out of destructive actions
- Why did the AI agent click the wrong thing
- When the page changes under the AI agent
- Running one AI agent task across a list of sites
- Seeing a page as it appears in another country
- Getting data out of a dashboard with no export button
- Two browsers in one session: main and support
- Finding the dead links on a site with an AI agent
- Filling a CRM record from a company's website
- One form submission per spreadsheet row, with an AI agent
- Dated screenshots of a page as evidence
- Checking order and delivery status with an AI agent
- Reading a PDF that opens inside the browser
- Summarising a long page or thread with an AI agent
- Collecting every image on a page with its caption
- Collecting event and course listings with an AI agent
- Cancelling a subscription with an AI agent
- What an AI agent can and cannot do inside an iframe
- Shadow DOM and an AI agent: you can click it, you cannot read it
- What a page snapshot costs, per control
- Native selects and the ones that only look like selects
- Clicking by selector or by coordinates
- How long the agent waits before it gives up
- What a second browser costs
- Uploading a file with an AI agent, and why this one cannot
- Watching the agent work, and when it is worth it
- When not to use an AI browser agent
- Agent or script: deciding once instead of every time
- Using the keyboard instead of the mouse
- Secrets in an agent task: where they end up
- What an agent run should log
- Deduplicating what an AI agent collects
- Normalising values across sites
- Validating an AI agent's output
- Reading a table with an AI agent
- Driving a site's own search and filters
- The task works headed and fails headless