Skip to main content
Glama

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-reach

The 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-devtools

The 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 --isolated

Any 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

--isolated

a fresh Chrome per session, temp profile

persistent

no flag

the stock profile in ~/.cache/chrome-devtools-mcp, logins kept

attach

--browserUrl http://127.0.0.1:9222 or --autoConnect

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

goal

The state to reach, in one sentence. Say what should be visible at the end.

url

Navigate here first. Omit to start from the current page.

tab_url_prefix

Act on the open tab whose URL starts with this instead of the selected tab.

values

Named strings Jev may type. Only the names reach Jev; the values go to the browser.

max_steps

Step budget, default 15.

allow_irreversible

Let Jev click through order, pay, send, delete, publish. Default false.

settle_ms

Network-quiet time after each action, default 500.

timeout_ms

Wall-clock budget, default 60000.

Statuses

status

meaning

done

Jev rates the goal reached at 0.85 or above.

likely_done

The page looks done but Jev is unsure. Verify before moving on.

needs_confirmation

The next action looks irreversible. See pending. Re-run with allow_irreversible only if the user wants it.

needs_login

A sign-in wall and no values. Pass credentials.

stuck

Nothing changed for three actions. See candidates for the top elements.

blocked

Captcha, access denied, or an error page.

max_steps, timeout

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, usage

Above 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.

  • reach has 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

TYPESAFE_API_KEY

The key. Or ~/.config/typesafe/key, or JEV_KEY_FILE.

JEV_MODEL

Model id, default jev-latest.

JEV_TIMEOUT_MS

Per-request timeout, default 15000.

JEV_REACH_LOG

1 prints one stderr line per step.

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 run

Credits

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 tools
clickC

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_messageA
Read-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_messagesA
Read-only

List all console messages for the target page since the last navigation.

Inputs: pageId (required), types, pageIdx, pageSize, +3 more.

list_network_requestsA
Read-only

Lists the most recent requests for the target page since the last navigation.

Inputs: pageId (required), pageIdx, pageSize, resourceTypes, +1 more.

list_pagesA
Read-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_insightA
Read-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_pageA
Read-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_forB
Read-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

    B3.3/5.0

    Scored across 30 tools

    Disambiguation3/5

    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.

    Naming Consistency3/5

    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.

    Tool Count2/5

    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.

    Completeness5/5

    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

    ActivitySlowing
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Connects 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
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables 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.
      1
      MIT
    • A
      license
      D
      quality
      B
      maintenance
      Connects 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.
      75
      MIT
    • A
      license
      Not graded
      quality
      A
      maintenance
      Enables 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 npm
      MIT