Browser Task Runner
Provides browser automation and batch task execution by attaching to a Brave browser over the Chrome DevTools Protocol, enabling navigation, page reading, form filling, clicking, multi-step plans, and review-gated batch workflows within an existing logged-in session.
Provides browser automation and batch task execution by attaching to or launching Google Chrome over the Chrome DevTools Protocol, enabling navigation, page reading, form filling, clicking, multi-step plans, and review-gated batch workflows within an existing logged-in session.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Browser Task Runnerharvest pending reviews, dry-run verdicts, then apply after I approve"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Browser Task Runner
Run repetitive admin work in a web browser, in batches, with a person reviewing every decision before anything is changed. Or just ask your AI assistant to do something on a site, and let it line up the steps and carry them out in one go.
Browser Task Runner is a local tool with two halves that share one engine:
Batch skills. For high-volume, rules-based queues (moderating reviews, triaging tickets, approving submissions). Items are read without changing anything, a person or an AI session decides a verdict for each, and only then are the decisions applied, with a dry run first, a staged rollout, a rate limit, a circuit breaker, an append-only audit log and a check afterwards that each change landed.
General browser control. Navigate, read, fill and click on any allowed site, from any MCP client. A plan of many steps runs in one call. Irreversible actions stop and ask for confirmation, and password, card and ID fields are never typed into.
Everything runs on your machine, in a browser you are already logged into. There is no hosted service and no API key.
Copyright (c) 2026 Manish Verma. MIT License.
How it works
+------------------------------+
MCP client | mcp/server.mjs (stdio) |
(Claude Code, <----> | batch tools + browser tools |
Claude Desktop, +--------------+---------------+
others) |
v
bridge/*.js CLI ----> +------------------------------+ audit log
| engine/ (shared) | ---> logs/*.jsonl
| skills, templating, rate | runs in
| limit, circuit breaker, | ---> exchange/runs/
| rollout, verify, safety |
+------+---------------+-------+
| |
CDP (puppeteer-core) bridge server :8777
| | (job queue)
v v
+-----------------------------------------+
| Your Chromium-based browser |
| (Chrome, Brave, Edge, Chromium) |
| requests run in a tab on the target |
| site, with your existing session |
| - or via the optional extension - |
+-----------------------------------------+The batch flow for one skill:
list_queue ──► harvest ──► (person / AI writes verdicts) ──► submit_verdicts
read-only read-only using the skill's ruleset validated
│
verify ◄── act(live) ◄── person approves the plan ◄── act(dry run)
re-reads capped by shown every request default
each item staged rollout it would sendTwo front ends drive the same engine:
MCP server (
mcp/server.mjs). Connects to your browser over the Chrome DevTools Protocol, either by attaching to one you started with remote debugging or by launching one. Requests run inside a tab on the target site, so they carry your logged-in session. This is the main way to use it.Browser extension + bridge (optional). An unpacked Manifest V3 extension that long-polls a small local server (
bridge/server.js, port 8777) for jobs and runs them in your browser. Useful if you prefer not to start the browser with remote debugging. The bridge also serves a live dashboard at http://127.0.0.1:8777/.
Related MCP server: webpilot
Requirements
Node.js 22.12.0 or newer
A Chromium-based browser: Chrome, Chromium, Brave or Edge
git clone <this repo> browser-task-runner
cd browser-task-runner
npm install
npm test # static checks, engine, browser and MCP testsQuick start: the demo, end to end
The repo includes a tiny local admin app, a fake product-review queue, so you can try everything safely. Its state lives in memory and resets when it restarts.
1. Start the demo app
node demo/server.js # http://127.0.0.1:87902. Start a browser for automation, with remote debugging on and a separate profile, so your everyday profile is never exposed:
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 --user-data-dir="$HOME/.btr-chrome-profile"
# Linux
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.btr-chrome-profile"
# Windows (PowerShell)
& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
--remote-debugging-port=9222 --user-data-dir="$env:USERPROFILE\.btr-chrome-profile"Log in to any site you want the tool to work on in this window. Anything that can reach port 9222 can control this browser, so keep it on 127.0.0.1 and close it when you are done.
3. Add the MCP server to your client (see the next section).
4. Ask. For example:
Use browser-task-runner. List the pending items in the demo review queue, harvest them, read the skill's ruleset, and propose a verdict for each. Show me the verdicts, then do a dry run.
Review what it proposes, then:
Looks right. Approve it and run it live.
Only now does the assistant call approve_live, which returns a
single-use approval bound to that exact plan, valid for 15 minutes, and
pass it to act with dry_run: false. A dry run on its own never
unlocks a live run.
The first live batch is capped at 1 item, the next at 3, then up to the batch size, so a mistake in a skill or a verdict shows up on one item rather than a hundred. Then:
Verify the run and show me the audit log.
Open http://127.0.0.1:8790/ to see the result.
The same flow without an AI client
node bridge/queue.js demo-review-queue --out=exchange/demo/ids.json # or use demo/ids_to_harvest.json
node bridge/harvest.js demo-review-queue demo/ids_to_harvest.json # prints a run_id and the items
node bridge/act.js <run_id> --verdicts=demo/verdicts.example.json # dry run
node bridge/approve.js <run_id> # after reading the plan: prints an approval token
node bridge/act.js <run_id> --live --approval=<token> # first live batch (1 item)
node bridge/approve.js <run_id> && node bridge/act.js <run_id> --live --approval=<token> # next batch (3 items) ...
node bridge/verify.js <run_id>The command-line tools use the extension by default
(npm run bridge, extension loaded). To use the CDP connection instead:
BTR_TRANSPORT=cdp BTR_BROWSER_URL=http://127.0.0.1:9222 node bridge/harvest.js ....
For a site that needs no login, such as the demo, BTR_TRANSPORT=node
uses plain Node requests with no browser at all.
Use with Claude Code, Claude Desktop or any MCP client
The server speaks MCP over stdio. Run it from a clone
(node /path/to/browser-task-runner/mcp/server.mjs) or through the
package bin (npx browser-task-runner-mcp once installed).
Claude Code
claude mcp add browser-task-runner \
--env BTR_BROWSER_URL=http://127.0.0.1:9222 \
--env BTR_ALLOW_HOSTS=admin.example.com \
-- node /path/to/browser-task-runner/mcp/server.mjsClaude Desktop (claude_desktop_config.json), or any client that
uses the same mcpServers format:
{
"mcpServers": {
"browser-task-runner": {
"command": "node",
"args": ["/path/to/browser-task-runner/mcp/server.mjs"],
"env": {
"BTR_BROWSER_URL": "http://127.0.0.1:9222",
"BTR_ALLOW_HOSTS": "admin.example.com"
}
}
}
}Configuration
Variable | Meaning |
| Attach to a running browser, e.g. |
| Or attach by its WebSocket endpoint |
| Or attach through a profile's |
| Or launch this browser executable ( |
| Extra hosts the tools may touch, comma separated. |
| Batch transport: |
| Where runs, logs and the bridge token are written (default: the repo folder) |
| Set to |
Without any browser setting the batch tools still work over plain Node requests (fine for the demo), and the browser tools are not offered.
Just ask
With a browser attached, you can describe a task in plain language and
the assistant chains the tools itself. run_plan lets it send many steps
in one call, so a ten-step task is one or two round trips rather than ten.
"Find the open tickets assigned to me on helpdesk.example.com and list
their titles and dates."
navigate to the ticket list, snapshot to see the filters, then one
run_plan: select_option (assignee), click (Apply filter),
wait_for (results), extract with
{"title": "td.title", "date": "td.date"}. Read-only, so nothing needs
confirmation.
"Fill in the contact form on the demo app: name Test User, topic Bug
report, and say the export button does nothing."
One run_plan: navigate to /contact, fill x2, select_option,
click "Send message". The click submits a form, so the plan stops with
needs_confirmation and a description. The assistant shows you what it
is about to send; after you say yes it repeats that click with
confirm: true, then extracts the confirmation text.
"Moderate the demo review queue using its ruleset."
describe_skill, list_queue, harvest, get_harvest, decides
verdicts, submit_verdicts, act (dry run), shows you the plan, calls
approve_live only after you agree, then act with dry_run: false
and the approval, in staged batches, then verify and read_audit_log.
If a page asks for a password, card number or ID number, the tools refuse to type it and the assistant should ask you to enter it yourself. Values already in such fields are redacted from everything the tools read.
Tool reference
Batch skills
Tool | What it does | Changes the site? |
| Installed skills, their site and verdicts | No |
| Config, ruleset and notes | No |
| Ids waiting in the queue (if the skill defines a list) | No |
| Reads each item, extracts fields, starts a run | No |
| The harvested items | No |
| Validates verdicts against the skill's | No |
| Dry run: lists every request. Live: needs an approval; sends the requests within the caps | Only when |
| Single-use approval for the last dry-run plan, 15 min. Call only after the user approves the plan | No |
| Counts and state of a run | No |
| The append-only audit log | No |
| Re-reads acted items and checks the expected result | No |
Browser control
Tool | What it does |
| Tabs |
| Go somewhere in the current tab (refused if the current tab is on a disallowed host; use |
| Text outline of the page; interactive elements get uids like |
| PNG of the tab, with sensitive fields masked |
| Element uid by visible text or label |
| Click; irreversible clicks need |
| Replace a field's value |
| Type into the focused element |
| Key press; Enter in a form, or Enter/Space on an irreversible-looking button or link, needs |
| Choose a select option |
| Hover |
| Wait for the page |
| Structured data: |
| Read-only queries; sensitive field values come back as |
| Only offered with |
| Attach a local file to a file input |
| Recent requests and console messages |
| Many of the above in one call |
A target is any of uid (from snapshot or find), selector (CSS),
or text (visible text or label, with an optional role of button,
link or textbox).
run_plan steps look like {"tool": "fill", "args": {"text": "Email", "value": "a@b.test"}}. An argument can reuse an earlier result:
{"tool": "click", "args": {"uid": {"use": "step3.uid"}}}.
Safety model
There are two kinds of control. Enforced controls are checked in code
on every path that can reach a site: the MCP tools, the command-line
tools and the extension. Approval controls stop and wait for a yes.
The tool can make the request for that yes unavoidable, but it cannot
tell whether a person actually gave it: an MCP client could pass
confirm: true or call approve_live on its own. So the user should run
it with a client that shows tool calls and asks before running them.
Enforced in code
Control | Applies to |
Host allow-list: localhost plus | All browser tools (the selftest fails if a page tool is not wrapped); skill |
No redirect is followed unless it stays on the skill's origin and an allowed host, and the destination is visible (node transport). In the browser (CDP and extension), redirects are never followed and fail the request ( | Batch read, list, act and verify |
Same-origin action URLs; item values are URI-encoded into paths | Batch act |
Harvest, list and verify only send GET requests | Batch |
Dry run is the default; a live act needs an approval token that is single use, expires after 15 minutes, and is bound to the run, its verdict hash and the dry-run plan. Changing the verdicts voids it | Batch act |
Staged rollout and batch cap; a batch with an error does not advance the stage | Batch act |
One live act per skill at a time, across processes (lock file; a stale lock from a dead process is cleared) | Batch act |
Fresh read before every write: identity guard, stale-content check, precondition | Batch act |
Escalation verdicts (empty action list) are never acted on | Batch act |
Rate limit; circuit breaker after N consecutive errors | Batch harvest and act |
Processed index written before the next item, so a re-run never repeats a write | Batch act |
Sensitive fields are classified by one function, run in the page before every read, write, target lookup and screenshot, so the read and write rules cannot differ: | All browser tools |
Typing into a sensitive field is refused, including single key presses while it has focus |
|
A sensitive field's value is never returned: it is replaced by |
|
Screenshots mask every sensitive field during capture (characters hidden by an injected style, and an opaque box drawn over each field, including selects and textareas); the masking is removed afterwards, also on error |
|
| MCP and |
Bridge: per-install token on every endpoint except | Bridge and extension |
Extension: refuses a non-GET request not marked as a write by the engine | Extension |
Audit logs: | Everything |
The test suite includes a safety matrix (test/matrix.test.js) that takes
the tool list from the MCP server's registry and checks every browser tool
on a disallowed page, on a focused sensitive field, and (for tools that can
move the page) after moving off the allow-list. A new tool fails the test
until it is given a row.
Needs a person to approve
Irreversible browser actions (form submits; clicks whose text reads like submit, send, pay, buy, order, delete, publish, post, confirm, approve, reject, transfer and similar; Enter inside a form field; Enter or Space on a button or link that looks irreversible) and every
evaluatecall returnneeds_confirmation. The client must show the user what will happen and repeat the call withconfirm: trueonly after a clear yes.run_planstops at such a step. Detection is by pattern, so it errs towards asking; an action that does not look irreversible will not be stopped.Live batch runs need
approve_live. The client must show the user the dry-run plan (plan_summary) and call it only after the user agrees.Verdicts are judgements made by a person or an AI session. The engine checks their form, not whether they are right. Read the dry run.
Known limits
Rules for "irreversible" and "sensitive field" are heuristics. A sensitive value shown as ordinary page text (not in a form field) is not recognised: read tools return it and screenshots show it.
Screenshot masking covers fields in the main document; fields inside iframes or closed shadow roots are not masked.
The extension enforces its own host list but cannot check an approval token; it relies on the bridge token so that only the local engine can send it work.
Sites that answer a successful POST with a redirect need
"acceptRedirect": trueon that action (the redirect is still not followed).It does not solve CAPTCHAs or handle logins or MFA; log in yourself. It does not store cookies or passwords.
Use it on systems you are authorised to operate, and follow each site's terms of use.
The optional extension
Open
chrome://extensions(orbrave://extensions,edge://extensions), turn on Developer mode, click Load unpacked and select this folder.Start the bridge:
npm run bridge, then checkcurl -s http://127.0.0.1:8777/health. On first run it creates a random pairing token in.btr-token(gitignored, readable only by you) and prints its location. On Windows, file mode 600 is not enforced: the token file inherits the folder's permissions, so keep the data directory inside your own user profile.Pair: open the extension popup, paste the contents of
.btr-tokeninto "pairing token", add any extra hosts, and click Save. Every bridge endpoint except/healthrejects requests without the token, and requests from web pages are refused. The live dashboard is athttp://127.0.0.1:8777/#token=<token>(the popup links to it). To pin the bridge to one extension, setBTR_EXTENSION_ORIGIN=chrome-extension://<extension id>.Add each site you want the extension to work on to
host_permissionsinmanifest.json(for example"https://admin.example.com/*"), and reload the extension. By default it only has access to the bridge and the demo app.
The popup shows bridge status and the latest activity, and links to the
live dashboard. To start the bridge automatically at login, see
bridge/autostart/ (a launchd template for macOS and a scheduled-task
script for Windows).
Write your own skill
A skill is one folder and one line:
src/skills/<your-skill>/skill.json what to read, what to send, per verdict
src/skills/<your-skill>/ruleset.md how to decide verdicts (for the reviewer)
src/skills/<your-skill>/NOTES.md what you confirmed about the site
src/skills/registry.json add "<your-skill>"The engine never changes. See SKILL-AUTHORING.md
for the full format and a checklist, and src/skills/demo-review-queue/
for a working example. Add the site's host to BTR_ALLOW_HOSTS, run
node bridge/selftest.js to validate the skill, and always start with a
dry run.
Project layout
engine/ shared engine: skills.js, index.js (runs), browser.js (browser tools),
transports.js, cdp.js
mcp/ MCP server (stdio)
bridge/ local bridge server, dashboard, command-line tools, selftest
src/ extension service worker and popup; skills/
demo/ demo admin app, sample ids and verdicts
test/ engine, browser and MCP testsSee SCOPE.md for the design and NOTICE.md for third-party notices.
The browser connection and tool set are adapted from
chrome-devtools-mcp
(Apache-2.0); it is not a runtime dependency.
This server cannot be deployed
Maintenance
Related MCP Connectors
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
A paid remote MCP for AI agent browser approval MCP, built to return verdicts, receipts, usage logs,
- PomeradoOAuthai.pomerado
MCPs that do tasks on any website. Search flights, book, shop, check accounts and fill in forms.
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceHosted remote MCP for AI agent browser approval. Provides structured tools for page approval workflows, session management, and audit receipts.-
- AlicenseNot gradedqualityAmaintenanceEnables MCP clients such as Claude Desktop, Claude Code and IDE agents to call a tool that drives a real Playwright browser to complete web tasks, with sensitive actions automatically refused when no human is available to approve them.MIT
- AlicenseNot gradedqualityBmaintenanceEnables an MCP client to drive a real, already-logged-in Firefox session through a WebExtension rather than a WebDriver connection, so tabs, cookies and logins work as-is without relaunching the browser under remote-control flags. Exposes tools for tab listing and selection, navigation, accessibility-style snapshots, clicking, filling, typing, key presses, file uploads, screenshots and waiting, with per-origin permission grants and opt-in script evaluation.MIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP agents to drive the user's already-open Chrome profile through a Manifest V3 extension and loopback bridge, listing and controlling existing tabs, capturing screenshots, and sending clicks, typing, drags and key holds via an independent on-page cursor without moving the system mouse. It also supports form filling, console and network inspection, read-only or confirmed JavaScript execution, file and PDF downloads, and game-style screenshot/action loops with snapshot recovery and an emergency stop.MIT