jev-reach
Provides browser automation via Chrome DevTools MCP (click, fill, navigate, snapshot, screenshots, console/network inspection, performance tracing, etc.), plus a reach tool that uses Jev to walk the browser to a desired state in one call.
Standard Chrome DevTools MCP tools: list pages, open/close tabs, navigate, click, fill forms, type text, press keys, hover, drag, upload files, evaluate scripts, take snapshots/screenshots, handle dialogs, resize/emulate devices, inspect console messages and network requests.
reachtool: given a goal, URL, and optional named values, Jev picks one element/action per step to navigate (e.g., log in, dismiss banners, open settings) and returns the final page ID, status, and a step trace.Post-reach inspection: use list_console_messages, list_network_requests, take_snapshot, or take_screenshot on the same tab; the walk is recorded for those tools.
Safety & control:
reachstops before irreversible actions (orders, sends, deletes) unless explicitly allowed; supports step budgets, timeouts, and starting from a specific tab.Isolation and persistence: supports isolated/persistent/attach modes via standard chrome-devtools-mcp flags.
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., "@jev-reach Navigate to the settings page, sign in with the supplied credentials, then take a screenshot."
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.
jev-reach
chrome-devtools-mcp plus one
tool. reach walks the browser to the spot where you want to look, using
TypeSafe Jev to pick each click. Then you make one
devtools call there instead of one call per click.
Everything chrome-devtools-mcp does still works, unchanged, in the same process and the same Chrome. jev-reach registers one extra tool on top.
Why
Verifying a frontend bug usually starts with a walk: dismiss the cookie banner, log in, open Settings, expand the third row. With a plain browser MCP each of those is a full model turn, and each turn reads a page snapshot into context. The reasoning only starts once you arrive.
reach moves the walk to Jev. Jev is a decision model: it picks one element and
one action from a typed list in about 300 ms and never reads or writes free text.
Your model states the goal once, gets back a status and a step trace, and then
calls list_console_messages, list_network_requests, take_snapshot, or
take_screenshot on the same tab. The walk is recorded for those calls, because
it happened on the same page objects the stock collectors listen to.
Related MCP server: chrome-in-harness
Install
Requires Node 20.19+ or 22.12+, Chrome, and a TypeSafe API key from console.typesafe.ai.
Claude Code plugin
Two commands inside a session:
/plugin marketplace add rashedInt32/jev-reach
/plugin install jev-reach@jev-reachThe plugin runs the published package through npx -y jev-reach@<version> --isolated.
It registers a server named chrome-devtools that includes every stock devtools
tool plus reach, so remove your existing chrome-devtools-mcp entry first.
Keeping both gives you two Chromes and two copies of every tool:
claude mcp remove chrome-devtoolsThe key must reach the server. Put it in ~/.config/typesafe/key with mode 0600,
or export TYPESAFE_API_KEY in the shell that starts Claude Code.
Any MCP client
Replace your chrome-devtools-mcp entry with jev-reach and keep the flags:
claude mcp remove chrome-devtools
claude mcp add chrome-devtools -e TYPESAFE_API_KEY=your-key -- npx -y jev-reach --isolatedAny MCP client:
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "jev-reach", "--isolated"],
"env": { "TYPESAFE_API_KEY": "your-key" }
}
}
}Every chrome-devtools-mcp flag is accepted and none is required. --isolated is
only the example because it gives each agent session its own throwaway Chrome.
The other stock modes work the same way, verified with the same walk:
mode | flags | what reach walks |
isolated |
| a fresh Chrome per session, temp profile |
persistent | no flag | the stock profile in |
attach |
| your own running Chrome |
The stock rule applies: one server per persistent profile at a time. Running a
second session against the same profile fails with the stock "browser is
already running" error, which is what --isolated is for.
The key can also live in ~/.config/typesafe/key with mode 0600.
Use
reach({
goal: "Sign in with the supplied email and password so the Settings page is showing.",
url: "http://127.0.0.1:3000/login.html",
values: { email: "dev@example.test", password: "..." }
})returns (a real run against the test fixture, headless Chrome launch included):
{
"status": "done",
"page_id": 1,
"url": "http://127.0.0.1:3000/settings.html",
"title": "Settings",
"steps": [
{ "step": 1, "action": "type", "target": "input[email] \"Email\"", "confidence": 0.96, "goal": 0.02, "latency_ms": 1032 },
{ "step": 2, "action": "type", "target": "input[password] \"Password\"", "confidence": 0.95, "goal": 0.02, "latency_ms": 432 },
{ "step": 3, "action": "click", "target": "button \"Sign in\"", "confidence": 0.73, "goal": 0.03, "latency_ms": 421 },
{ "step": 4, "action": "finish", "goal": 0.92, "latency_ms": 2030, "note": "done" }
],
"usage": { "calls": 4, "input_tokens": 3831, "output_tokens": 859, "jev_ms": 3915, "total_ms": 6573 }
}Then:
list_console_messages({ pageId: 1 })The page id matters. chrome-devtools-mcp routes page-scoped tools by id, and the
id is invisible outside the process, so reach hands it back.
Console and network history follow the stock rules: buffered per navigation, newest first. Messages from pages the walk passed through are in the earlier navigation buckets, and the current page's messages are what the default call shows.
Inputs
name | meaning |
| The state to reach, in one sentence. Say what should be visible at the end. |
| Navigate here first. Omit to start from the current page. |
| Act on the open tab whose URL starts with this instead of the selected tab. |
| Named strings Jev may type. Only the names reach Jev; the values go to the browser. |
| Step budget, default 15. |
| Let Jev click through order, pay, send, delete, publish. Default false. |
| Network-quiet time after each action, default 500. |
| Wall-clock budget, default 60000. |
Statuses
status | meaning |
| Jev rates the goal reached at 0.85 or above. |
| The page looks done but Jev is unsure. Verify before moving on. |
| The next action looks irreversible. See |
| A sign-in wall and no |
| Nothing changed for three actions. See |
| Captcha, access denied, or an error page. |
| Budget spent. |
No page text is ever returned. The follow-up devtools call is where you read the page.
How it works
reach(goal)
│ warm the stock server: internal list_pages launches Chrome and attaches
│ the console and network collectors, and names the selected tab
├─ loop
│ 1. snapshot: visible, enabled interactive elements in the main frame and
│ open shadow roots, numbered, in-viewport first
│ 2. one parallel Jev call: element, action, value binding, goal reached,
│ irreversible, login wall, blocked
│ 3. act through puppeteer on the same page objects
│ 4. settle on network quiet, diff url + text + inputs
└─ status, page_id, url, title, steps, usageAbove 254 candidates the list is split, each chunk shortlisted in parallel, and the winners plus the in-viewport elements go to the main call.
Native alert dialogs are accepted and logged. confirm and prompt count as
irreversible: they are dismissed and the walk stops with needs_confirmation.
Limits
Navigation only. Jev never judges whether the bug is fixed.
Jev never generates text. Anything typed comes from
values.Irreversible detection is a model judgment plus a keyword list. It is a guard, not a security boundary.
Page text is sent to TypeSafe for each step. Fine for local dev pages; think about it for client sites.
Main frame and open shadow roots. Iframes, file upload, and drag are not handled yet.
reachhas its own mutex and does not share the stock tool mutex. Do not run it in parallel with other browser tools.chrome-devtools-mcp is pinned to an exact version because jev-reach relies on its internals. Boot fails loudly if the pin is broken.
Environment
variable | meaning |
| The key. Or |
| Model id, default |
| Per-request timeout, default 15000. |
|
|
Development
npm install
npm run build
npm test # offline: helpers and server boot
JEV_REACH_E2E=1 npm run test:e2e # launches headless Chrome; scripted Jev
JEV_REACH_E2E=1 TYPESAFE_API_KEY=... npm run test:e2e # adds a live runCredits
The describe, decide, act loop with typed statuses is ported from the wave of Jev browser projects, in particular Ying-Kai-Liao/jev-browser and Browser Use's jev-ultrafast. What is new here is running that loop inside chrome-devtools-mcp's own process, so the devtools surface, the isolation model, and the recorded console and network stay exactly as they are.
License
MIT
Available Tools
30 toolsclickC
Clicks on the provided element
Inputs: uid (required), pageId (required), dblClick, includeSnapshot.
close_pageA
Closes the page by its index. The last open page cannot be closed.
Inputs: pageId (required).
dragB
Drag an element onto another element
Inputs: pageId (required), to_uid (required), from_uid (required), includeSnapshot.
emulateB
Emulates various features on the target page.
Inputs: pageId (required), viewport, userAgent, colorScheme, +4 more.
evaluate_scriptB
Evaluate a JavaScript function inside the target page. Returns the response as JSON, so returned values have to be JSON-serializable.
Inputs: pageId (required), function (required), args, filePath, +2 more.
fillB
Type text into an input, text area or select an option from a element.
Inputs: uid (required), value (required), pageId (required), includeSnapshot.
fill_formA
Fill out multiple form elements (inputs, selects, checkboxes, radios) at once. ALWAYS prefer this tool over multiple individual 'fill' or 'click' calls when interacting with forms. It is significantly faster, more reliable, and reduces turn count. Example: Fill username, password, and check "Remember Me" in one call.
Inputs: pageId (required), elements (required), includeSnapshot.
get_console_messageARead-only
Gets a console message by its ID. You can get all messages by calling list_console_messages.
Inputs: msgid (required), pageId (required).
get_network_requestA
Gets a network request by an optional reqid, if omitted returns the currently selected request in the DevTools Network panel. Useful for inspecting request headers (including 'Cookie') and response headers (including 'Set-Cookie' and directives).
Inputs: pageId (required), reqid, requestFilePath, responseFilePath.
handle_dialogA
If a browser dialog was opened, use this command to handle it
Inputs: action (required), pageId (required), promptText.
hoverB
Hover over the provided element
Inputs: uid (required), pageId (required), includeSnapshot.
lighthouse_auditA
Get Lighthouse score and reports for accessibility, SEO, best practices, and agentic browsing. This excludes performance. For performance audits, run performance_start_trace
Inputs: pageId (required), mode, device, outputDirPath.
list_console_messagesARead-only
List all console messages for the target page since the last navigation.
Inputs: pageId (required), types, pageIdx, pageSize, +3 more.
list_network_requestsARead-only
Lists the most recent requests for the target page since the last navigation.
Inputs: pageId (required), pageIdx, pageSize, resourceTypes, +1 more.
list_pagesARead-only
Get a list of pages open in the browser.
Inputs: no named parameters.
new_pageC
Open a new tab and load a URL. Use project URL if not specified otherwise.
Inputs: url (required), timeout, background, isolatedContext.
performance_analyze_insightARead-only
Provides more detailed information on a specific Performance Insight of an insight set that was highlighted in the results of a trace recording.
Inputs: pageId (required), insightName (required), insightSetId (required).
performance_start_traceA
Start a performance trace on the target webpage. Use to find frontend performance issues, Core Web Vitals (LCP, INP, CLS), and improve page load speed.
Inputs: pageId (required), reload, autoStop, filePath.
performance_stop_traceB
Stop the active performance trace recording on the target webpage.
Inputs: pageId (required), filePath.
press_keyA
Press a key or key combination. Use this when other input methods like fill() cannot be used (e.g., keyboard shortcuts, navigation keys, or special key combinations).
Inputs: key (required), pageId (required), includeSnapshot.
reachWalk the browser to a spot with JevA
Get the browser to the exact place where you want to look, in one call. Give a goal such as 'dismiss the cookie banner, log in, and open the Billing tab'. Jev (TypeSafe's decision model) picks one element and one action per step in about 300 ms, with no page text entering your context. Then call list_console_messages, list_network_requests, take_snapshot, or take_screenshot on the same tab; the walk is recorded for them. Jev never types free text: pass anything to type in 'values' (for example {email, password}), and only the names are shown to Jev. Stops before orders, payments, sends, deletes, and publishes unless allow_irreversible is set. Acts on the currently selected page and returns its page_id; pass that as pageId to the follow-up call.
Inputs: goal (required), url, values, max_steps, +4 more.
resize_pageB
Resizes the page's window so that the page has specified dimension
Inputs: width (required), height (required), pageId (required).
select_pageARead-only
Select a page as a context for future tool calls.
Inputs: pageId (required), bringToFront.
take_heapsnapshotA
Capture a heap snapshot of the target page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.
Inputs: pageId (required), filePath (required).
take_screenshotC
Take a screenshot of the page or element.
Inputs: pageId (required), uid, format, quality, +2 more.
take_snapshotA
Take a text snapshot of the target page based on the a11y tree. The snapshot lists page elements along with a unique identifier (uid). Always use the latest snapshot. Prefer taking a snapshot over taking a screenshot. The snapshot indicates the element selected in the DevTools Elements panel (if any).
Inputs: pageId (required), verbose, filePath.
type_textA
Type text using keyboard into a previously focused input
Inputs: text (required), pageId (required), submitKey.
upload_fileC
Upload a file through a provided element.
Inputs: uid (required), pageId (required), filePaths (required), includeSnapshot.
wait_forBRead-only
Wait for the specified text to appear on the selected page.
Inputs: text (required), pageId (required), timeout.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 30 tools
Most tools have distinct targets, but fill, fill_form, type_text, and press_key overlap significantly in the input/text-entry space. The tool descriptions help, especially fill_form's explicit preference note, but an agent could still easily misselect among them.
Names are consistently lowercase snake_case, but they mix verb-first forms like list_pages and take_screenshot with domain-prefixed forms like performance_start_trace and lighthouse_audit, plus bare verbs like fill, hover, and reach. The naming is readable but not predictable enough to infer related tools consistently.
At 30 tools, the surface is over the heavy threshold and likely larger than needed. The breadth is defensible for a DevTools-style browser automation server, but several tools could be consolidated, such as fill/fill_form and the list_network_requests/get_network_request pairs.
The set covers the full browser automation and debugging lifecycle: navigation, page management, element interaction, forms, uploads, dialogs, console/network inspection, screenshots/snapshots, and performance/memory auditing. There are no obvious dead ends or missing core operations for its apparent purpose.
Maintenance
Related MCP Connectors
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
A paid remote MCP for AI agent browser DevTools MCP, built to return verdicts, receipts, usage logs,
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Live browser debugging for AI assistants — DOM, console, network via MCP.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceConnects local stdio MCP servers to an existing Chrome 144+ session, preserving the user's signed-in sessions, cookies, tabs, and extension environment without launching a second browser. It provides tab control, semantic snapshots, screenshots, pointer, keyboard, form selection, scrolling, navigation, and waiting tools.MIT
- AlicenseNot gradedqualityBmaintenanceEnables any MCP client to drive the user's real Chrome profile through a Chrome extension bridge, with tools for navigation, accessibility snapshots, ref-based interactions, console/network reading, screenshots, tab management, and restricted script evaluation.1MIT
- AlicenseDqualityBmaintenanceConnects to an already-running Chrome/Chromium instance over CDP and exposes 75 browser-automation tools over newline-delimited MCP JSON-RPC on stdin/stdout, covering navigation, forms, keyboard/mouse input, screenshots, cookies, storage, tabs and frames, assertions, and retry/batch/step composition. It does not launch a browser itself, requiring a localhost debugging port on the existing instance.75MIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP clients to drive a real browser toward stated outcomes, where the calling model supplies goals and candidate values while Jev (Typesafe System One) resolves each step's target, action, value, and done/blocked/irreversible status. Exposes tools for navigation, acting, yes/no checks, choosing among options, page reading, snapshots, screenshots, and calling page-registered WebMCP tools.1,461 npmMIT