🪿 goshawk
A browser in the agent's hand. 9 tools. One verdict per action. Zero guessing.
Navigate, read, click, fill, log in, scrape. Pure Go, single binary, no Node, no Python, no Docker.
Built on Chrome DevTools Protocol + MCP. Works with Claude Code, Cursor, OpenCode, Pi, anything that speaks MCP.

go install github.com/dondai1234/goshawk/v4/cmd/goshawk@latest
Install · The 9 tools · Self-diagnosing verdicts · Comparison · Token cost · What goshawk can't do
Why goshawk
Most browser MCP servers give the agent a raw page snapshot and let it figure out what happened. The agent burns 3 to 5 calls investigating: did the click work? is the page blank? what went wrong? goshawk closes that loop.
- 🔬 Self-diagnosing verdicts: every action returns a confidence-scored verdict. When something goes wrong, the verdict explains why and what to do next. "did you mean Sign in?" when you type "Sgn in". "errors visible: Email is required. Fix and retry" when a form fails. Zero extra tool calls, no LLM in the loop, pure heuristics.
- 🪶 9 tools, ~3.4K tokens: dense a11y-tree snapshots (ref-lines, not aria dumps), intent-first actions, one verdict per call. More capability than tools shipping 6K+ tokens.
- 🦅 Intent-first:
act intent="Sign in" finds the button. act fields={"Username":"john","Password":"hunter2"} fills a whole form in one call. Name what you want, not how to find it.
- 🛡️ Self-healing refs: refs from
see/find survive re-renders. When React re-creates a node with the same role+name, goshawk auto-heals the ref. No stale-selector failures.
- 🍪 Cookie banners auto-dismissed: on every navigate. Consent redirects auto-recovered. The a11y tree comes back clean, not cluttered with "Accept cookies" overlays.
- 📄 Blank page detection: when a page loads empty (slow SPA, rate-limit, JS not hydrated), the verdict says
BLANK PAGE with what to try. One call, not five.
- 🔐 One-call login:
login username= password= handles single-step and multi-step, verifies the resulting state, detects 2FA, SSO redirects, remember-me, forgot-password. Reports state, not HTTP status.
- 🎭 Named profiles: isolated cookies, auth, storage. Switch identities in one call. Logins survive restarts with persistent profiles.
- 🦿 Single Go binary: 14 MB. No Node runtime, no Python venv, no Docker.
go install and done.
goshawk is for the agent. You install it once; the agent drives the browser through 9 tools.
🔬 Self-diagnosing verdicts
The feature that sets goshawk apart. Three layers, zero extra tool calls:
Layer 1: "Did you mean?"
When act intent="Sgn in" finds no exact match, goshawk returns the 3 closest matches by Levenshtein distance:
no element named "Sgn in" found; did you mean: "Sign in" (button, r4), "Sign in to GitHub" (heading, r2)?
The agent retries with the right name. No see + find + see investigation loop.
Layer 2: "What blocked it?"
When an action fires but confidence is uncertain or likely, goshawk scans the page for visible error messages, HTML5 validation failures, and blocking modals:
verdict: [uncertain] no visible effect
errors visible: "Email is required". Fix those fields and retry
modal "Login Required" opened - call see to inspect
Layer 3: "What now?"
Each diagnostic is an actionable suggestion, not just a description. The agent knows exactly what to do next without another call.
Pure heuristics. No LLM in the loop. No extra API calls. Works across React, Vue, Angular, vanilla.
🎯 Confidence-scored verdicts
Every act returns a verdict with a confidence tag:
Navigation is always confirmed. Bot challenges are always uncertain. No more guessing whether a click landed.
Fill an entire form in one call:
act fields={"Username":"john","Password":"hunter2","Remember me":"true","Country":"United States"}
Each label is resolved to a form field (a11y name + DOM fallback). The type is auto-detected: text, checkbox, radio, select, slider, file. The right action is performed for each. One re-snapshot, validation errors reported.
🔐 One-call login
login username="john" password="hunter2" url="https://example.com/login"
Detects the username + password + submit fields, fills them, submits, and verifies the resulting state. Handles:
- Single-step (username + password on one page)
- Multi-step (username first, then password appears)
- 2FA (stops and reports what's needed)
- SSO redirects (reports the domain)
- Remember-me checkbox (reports, doesn't auto-toggle)
- Forgot-password link (reports it)
- OAuth buttons (reports, doesn't auto-click)
Verifies state, not HTTP status. A silent failure is reported, not hidden.
🛡️ Self-healing refs
Refs from see and find are stable across re-renders. When a framework re-creates a DOM node with the same role + name (React key change, Vue v-if toggle), goshawk searches the current tree and auto-heals the ref. No stale-selector failures mid-flow.
Refs are per-tab and cleared on navigation. They survive re-renders within a page.
🍪 Cookie banners and consent redirects
Cookie/consent banners are auto-dismissed on every navigate. The a11y tree comes back clean, not cluttered with overlay elements.
If a site redirects to a full-page consent statement (common for EU users), goshawk detects the redirect, tries broader dismissal, and re-navigates to the original URL. If it can't recover, the verdict says CONSENT REDIRECT: original -> current with what to try.
📄 Blank page detection
When a page loads but renders empty (slow SPA hydration, rate-limiting, JS error), goshawk detects it and reports clearly:
BLANK PAGE: no interactive elements or headings detected.
The page may be a slow SPA still loading, may require JS execution,
or may have a rendering issue. Use js to check or see in a moment.
Before: the agent sees interactive: 0, calls see, then find, then see full, then js. 4 to 5 wasted calls. After: one call, the verdict explains what happened.
waitSelector= on nav gives a native waitForSelector for slow SPAs: wait for #dashboard to render before returning the orientation.
🎭 Named profiles
Isolated browser identities. Different logins, cookies, storage. Switch in one call.
session mode=profile action=create name="work"
session mode=profile action=switch name="work"
session mode=profile action=list
session mode=profile action=export # dump cookies+storage as JSON
session mode=profile action=import data="..." # restore from JSON
By default, goshawk persists to <os config dir>/goshawk so logins and cookies survive server restarts. The agent doesn't re-login each run. Use --no-persist for throwaway sessions.
🚀 Install
go install github.com/dondai1234/goshawk/v4/cmd/goshawk@latest
Then add to your MCP client config:
{
"mcpServers": {
"goshawk": {
"command": "goshawk",
"args": ["mcp"]
}
}
}
Requires Chrome or Chromium installed. goshawk connects via CDP.
CLI flags
📊 Comparison
🪙 Token cost
Most browser MCP servers cost 5 to 8K tokens just to exist. goshawk's 9 tools cost ~3.4K tokens at tools/list (measured from the live MCP JSON response, 13623 chars). The connect-time instructions (~66 tokens) are injected once at handshake, not repeated every turn.
A nav orientation on a typical page returns ~60 to 80 tokens. A see refs on a page with 10 interactive elements returns ~40 tokens. Dense by design: ref-lines, not aria dumps.
🔧 Things to know first
🚧 What goshawk can't do