Skip to main content
Glama

jev-mcp

jev-mcp is an MCP server that hands the browser agent jev-ultrafast to Claude Code and Claude Desktop. You give it a start URL and your goals in plain sentences, and it operates the page on its own in your real, already logged-in Chrome. A TypeSafe decision model (Jev) picks every step, and a separate text model writes the values that get typed into fields.

Requirements

  • Python 3.12 or newer

  • uv

  • Google Chrome, driven through browser-harness

  • A TypeSafe API key (TYPESAFE_API_KEY). Without it no run starts at all, because nothing can choose an action, not even a click or a scroll.

  • A text model key. Without it the agent can still click, scroll, navigate and pick from dropdowns, but it cannot type, so forms and search fields stay empty.

  • git and access to GitHub during install. jev-ultrafast is not published on PyPI, so it is installed straight from its repository, pinned to the exact commit this server was tested against.

Related MCP server: Browser Tools for Claude Code

Setup

git clone https://github.com/SAITS-Swiss-AI-Tech-Services/jev-mcp.git
cd jev-mcp
uv sync

Put the keys into ~/.config/jev-mcp/env. The format is NAME=VALUE, one entry per line. Empty lines and lines starting with # are skipped, a leading export is stripped, quotes around a value are removed, and a trailing comment is cut off. The file is read only when it is a regular file of at most 256 KiB.

TYPESAFE_API_KEY=...
MOONSHOT_API_KEY=...

Claude Desktop inherits no shell, which is exactly why this file exists. Claude Code does inherit the shell, so keys already exported there are found as well.

The server looks for the text model key by provider first and by source second. It tries TEXT_MODEL_API_KEY, then Kimi (MOONSHOT_API_KEY, KIMI_API_KEY), then DEEPSEEK_API_KEY, then OPENROUTER_API_KEY, and inside each of those steps the process environment before the file. The first hit wins and decides the provider, including base URL and model name. For TYPESAFE_API_KEY there is only one variable, and there the environment simply beats the file.

Register the server in Claude Code. Use the absolute path to uv:

claude mcp add jev-browser --scope user -- /absolute/path/to/uv run --directory /absolute/path/to/jev-mcp jev-mcp

Register it in Claude Desktop by adding the same command to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "jev-browser": {
      "command": "/absolute/path/to/uv",
      "args": ["run", "--directory", "/absolute/path/to/jev-mcp", "jev-mcp"]
    }
  }
}

Both entries need absolute paths, because Claude Desktop does not inherit your shell environment and therefore does not find uv on the PATH. command -v uv prints the path. Restart both clients afterwards, they only load new servers on start.

Finally, allow Chrome to be driven. Once: open chrome://inspect/#remote-debugging and tick "Allow remote debugging for this browser instance". After that, Chrome asks once per browser session whether a connection may be made. See Troubleshooting for the reliable way to answer it.

The three tools

browser_task(url, goals, max_actions, time_budget_s, allow_domains, dry_run)

Operates a site autonomously: it opens url and pursues goals, deciding every click, selection and keystroke without asking.

Parameter

Default

Limits

url

required

at most 2048 characters

goals

required

a sentence or a list of them, at most 20 entries of 2000 characters each

max_actions

25

1 to 60, the library's own ceiling

time_budget_s

120

1 to 900, wall clock

allow_domains

none

at most 50 entries of 253 characters each

dry_run

false

observes the page, fetches the first decision, executes nothing

The result reports the status, the final URL, the page title, every executed step, the duration, the actions and model calls used, the reason it stopped and, for a dry run, the step it would have taken next.

browser_status()

Read-only diagnosis, and free: it opens no browser, loads no page and calls no model. It says which keys were found and where they came from, which text model would type, whether the browser-harness daemon is running and whether a Chrome is connected to it, and which operations are blocked as a result. Call it before the first run of a session and whenever one of the other two tools fails.

browser_read(url, time_budget_s, allow_domains, text_limit)

Opens one page and returns its visible text plus the table of operable elements, without clicking, typing or selecting anything.

Parameter

Default

Limits

url

required

at most 2048 characters

time_budget_s

30

1 to 900

allow_domains

none

at most 50 entries

text_limit

4000

200 to 20000 characters

The answer says how long the text was before truncation, and it lists at most 120 elements. A redirect onto another registrable domain is reported, and nothing from that page is returned.

Limits, stated up front

The agent underneath is an MVP, and these gaps are real:

  • It cannot see into iframes, and it cannot see into shadow DOM.

  • It cannot upload files.

  • It cannot follow pop-up tabs.

  • It only observes the visible viewport. It scrolls, but it never reads what is not rendered.

  • Only one run can be in flight at a time. A second call is refused immediately with not_started instead of being queued, because a run writes process-wide values into os.environ and two runs would overwrite each other.

  • A call blocks until the run ends.

  • The agent works inside your real, logged-in Chrome profile. Whatever it clicks happens in your sessions, for real. Each run opens its own background window and closes it afterwards. It does not use a background tab, because Chrome 153 answers no command sent to a background tab created over the DevTools protocol, while a background window works and leaves your own window alone.

  • Result messages are in German. The tool descriptions, which the model reads, are in English.

  • Do not put credentials into goals. The goal text comes back verbatim in the result, and so does every value the agent typed into a field. Nothing of that is masked, on purpose: the typed text is the most important record of what the agent actually did. Password, file and hidden fields are excluded from observation, so nothing is typed there, but a one-time code or a customer number in an ordinary text field is not covered by that and will appear in the result.

  • Logging in, paying, ordering and submitting forms on banking or payment sites are out of scope for this project.

The safeguard: domain fidelity instead of a blocklist

There is no blocklist. That was a deliberate decision: the agent only ever runs on request, so there are no forbidden domains. Instead a run remembers the registrable domain of its start URL. If a click or a redirect would take it to a different registrable domain, it stops and reports where it wanted to go, rather than acting there. Subdomains of the same registrable domain count as the same domain.

The check runs at two moments. Before a navigation it uses the address the next step would open, and that is the check that actually protects you, because in a logged-in profile the loaded page is already the damage: it has seen cookies, run scripts and sent requests. After every load it uses the address the browser actually ended up on, which catches redirects, window.location from a script and clicks the agent did not recognise as navigation. A block at that second moment means the damage has already happened, and the run aborts instead of continuing.

When the module cannot read its own input reliably, it blocks. An unreadable start URL, a host with characters outside the permitted set, an invalid port, a numeric address that does not parse: all of those end in a block, never in a pass.

What it explicitly does not do:

  • It does not judge content. What an allowed page whispers to the agent is invisible to it, and page content can steer the decision model.

  • It knows no confirmation prompt and no step limit. Those live in the runner.

  • Before a click it can only read a target address from a real link (a[href]). A <button>, a form submission or a click that only a script turns into navigation contributes nothing, and there the check first bites after the load.

Known weakness with hosting suffixes: the module does not use the Public Suffix List, which would be a dependency. It applies the rule "the last two labels", extended by a built-in set of multi-part suffixes that covers the common country suffixes and the common hosting suffixes such as github.io and vercel.app. A multi-part suffix that is missing from that set, blogspot.de for example, is read too loosely, and two unrelated sites under the same provider then count as the same domain. The opposite error, a three-label host whose middle label happens to look like a suffix, makes the check too strict, and the agent stops although it would have been allowed.

The check can be relaxed in four places: allow_domains per call, allow_domains globally in ~/.config/jev-mcp/policy.toml, the switch enforce_domain_lock = false in the same file, and allow_unbound for runs that are meant to start without a domain binding.

Choosing the text model

Measured on 2026-09-20 through a real jev_ultrafast.model.field_text() call, filling the field "Where from?" in a flight search. All four runs answered correctly ("Zurich"):

Model

reasoning=low

reasoning=none

kimi-k3

5855 ms

5619 ms

kimi-k2.7-code-highspeed

1156 ms

1138 ms

K3 costs five times as much latency, and it costs it per typed field. K3 is the default anyway, by explicit decision. Switching is one line in ~/.config/jev-mcp/env, and the provider stays the same:

TEXT_MODEL=kimi-k2.7-code-highspeed

Troubleshooting

Call browser_status first. It names the missing key, the missing daemon or the missing Chrome connection and says what is blocked because of it, which is faster than guessing.

If it reports that the browser-harness daemon is not running, or that it runs but no Chrome is connected to it, browser-harness --doctor diagnoses install, daemon and browser state.

Chrome asks for approval once per browser session. The reliable way to answer it: bring a normal Chrome window to the front, not in full screen, then run the command below and click Allow in the dialog. It waits without a time limit.

cd /absolute/path/to/jev-mcp && echo 'print(page_info())' | uv run browser-harness

browser-harness mac-approve can click the dialog for you on macOS, but only when Chrome's interface is in English: it matches the English dialog text. It also needs the Accessibility permission for your terminal.

If a run is refused with not_started and a note about concurrency, another run is still in flight. Wait for it and start again.

Development

uv run pytest
uv run ruff check .

461 tests pass as of 2026-09-21. The suite runs without a browser and without network access, against test doubles and against contract tests that pin every assumption about jev-ultrafast to its installed source. An end-to-end run against real Chrome 153 through the server as a separate stdio process passed on 2026-09-21.

Built on

jev-mcp is a thin, guarded layer. The actual work is done by these projects:

Project

What it does here

License

jev-ultrafast by Browser Use

The browser agent itself: the observe, decide, act loop

MIT

browser-harness by Browser Use

Connects to Chrome over the DevTools protocol and keeps the session

MIT

TypeSafe Jev (docs)

The decision model that picks every action and target

commercial API

Model Context Protocol

The protocol Claude Code and Claude Desktop speak to this server

see repository

MCP Python SDK

Server implementation and stdio transport

MIT

uv by Astral

Environment and dependency management

see repository

WHATWG URL Standard

The reference for how URLs are read, so the safeguard reads them the way Chrome does

CC BY 4.0

The text that gets typed into fields comes from an OpenAI-compatible model of your choice. The server knows Moonshot Kimi, DeepSeek and OpenRouter by name and works with any other endpoint through TEXT_MODEL_API_KEY and TEXT_MODEL_BASE_URL.

Related: typesafe-mcp (MIT) exposes the same Jev model as a general judgment tool, typed answers with probabilities, for any MCP client.

Security

This server drives a real, logged-in browser, and its only safeguard is the domain check described above. If you find a way around it, please report it privately to hello@saits.ai instead of opening a public issue.

License

MIT, see LICENSE. Copyright (c) 2026 R. Schröder GmbH (SAITS - Swiss AI Tech Services).

Available Tools

3 tools
browser_readA

Open one page in the user's real, logged-in Chrome and return its visible text plus the table of elements that can be operated. This tool clicks nothing, types nothing and selects nothing. Opening the page is itself a request, though: it opens a background tab and navigates to url with the user's cookies, so a URL that acts on GET will act, for example a logout, an unsubscribe, a confirmation link or a one-click action. Right for reading or checking a page, for seeing what a page offers before starting browser_task, and for content behind a login the user already has. Wrong when anything has to be clicked or filled in (use browser_task), and wasteful for public pages that need no login (use a web fetch or search tool). Redirects are followed but verified: landing on another registrable domain is reported, and none of that page's text, elements, title or address is returned; widen that with allow_domains. The underlying library observes at most 6000 characters of visible text and cuts a longer page before text_limit (default 4000) applies, so the answer reports how long the observed text was and warns when it reached that ceiling. Labels and field values are capped at 200 characters and option lists at 50 entries per element, and the answer says when it capped something. Same limits as the agent: no iframes, no shadow DOM, no file uploads, no pop-up tabs, and only the visible viewport is observed, so content further down the page can be missing. Only one run can be in flight at a time.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes
text_limitNo
allow_domainsNo
time_budget_sNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It does this exceptionally well: it explains GET side effects with user cookies, redirect verification and cross-domain blocking, text length capping, element/field caps, unsupported iframes/shadow DOM/popups, viewport-only observation, and the single-flight concurrency constraint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every sentence earns its place. It front-loads the core purpose and output, then organizes guidance into usage recommendations, redirect behavior, limits, and concurrency. No structured schema information is repeated, and there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, an output schema already exists, and no annotations are present, the description is operationally complete. It covers side effects, explicit use cases, alternative tool routing, domain verification, truncation behavior, environment limitations, and concurrency. An agent has enough to know when and how to invoke it safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It adds real meaning to url by explaining cookie-aware navigation and GET side effects, to text_limit through the 4000-character default ceiling, and to allow_domains by showing it widens redirect acceptance. time_budget_s is not explained in detail, but its title and default are fairly self-explanatory, and the core parameters receive substantial semantic grounding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Open one page in the user's real, logged-in Chrome and return its visible text plus the table of elements that can be operated.' It clearly distinguishes itself from browser_task by stating it clicks, types, and selects nothing, and its role as a read-oriented tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use the tool: for reading or checking pages, for seeing what a page offers before starting browser_task, and for content behind the user's login. It also states when not to use it: when clicks or fills are required (use browser_task) and for public pages (use a web fetch or search tool). This gives the agent concrete routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_statusA

Report whether a browser run can work at all, before spending time on one. Says which API keys are present and where they came from, which text model would type into fields, whether the browser-harness daemon is running and whether a Chrome is connected to it, and which operations are therefore blocked. Read-only and free: it opens no browser, loads no page, calls no model and costs nothing. Call it when browser_task or browser_read fails, before the first run of a session, or when the user asks whether the browser agent is ready. It says nothing about any particular website.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully carries behavioral disclosure. It clearly states the tool is read-only, free, opens no browser, loads no page, calls no model, and costs nothing. It also discloses the kind of state it inspects, which operations are blocked, and that it reports on readiness rather than site-specific behavior, giving the agent a complete operational picture.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The main point is front-loaded in the first sentence, and every subsequent clause adds distinct information about safety, prerequisites, or limitations. Though longer than typical descriptions, the density is high and there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers when to call, what it checks, what it avoids doing, and what it does not report. Given that an output schema exists and there are no parameters, nothing needed for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the schema fully defines the call shape and the baseline is 4. The description reinforces that no arguments are needed by describing the tool as a global status check rather than a parameterized query.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: it reports whether a browser run can work at all. It enumerates concrete outputs (API key presence and provenance, active text model, daemon and Chrome state, blocked operations) and explicitly distinguishes itself from the siblings by noting it opens no browser, loads no page, and calls no model.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit trigger conditions: call it when browser_task or browser_read fails, before the first run of a session, or when the user asks whether the browser agent is ready. It also sets an exclusion boundary by stating it says nothing about any particular website, which prevents misuse.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_taskA

Operate a website autonomously in the user's real, logged-in Chrome: open url, then pursue goals written as plain sentences, deciding every click, selection and keystroke without asking. Right for multi-step work on one site: filling a form, walking to a page that is only reachable by clicking, searching inside a site, reading something behind a session the user already has. Wrong for reading one page (use browser_read), for public pages that need no login (use a web fetch or search tool), and for logging in, paying, ordering or submitting anything on a banking or payment site. Never put credentials into goals: the goals and every typed value come back in the result. The run stays on the registrable domain of url and stops when a step would leave it; widen that with allow_domains. Bounded by max_actions (default 25, values above 60 are rejected) and time_budget_s (default 120 s, values above 900 s are rejected); the call blocks until the run ends, and only one run can be in flight at a time, including any run still finishing after its budget ran out. Set dry_run to get the first planned step without executing anything. The underlying agent cannot see into iframes or shadow DOM, cannot do file uploads, cannot follow pop-up tabs, and only observes the visible viewport, so it scrolls but never reads what is not rendered. The result reports the status, the final URL, every executed step and the reason it stopped.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes
goalsYes
dry_runNo
max_actionsNo
allow_domainsNo
time_budget_sNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

There are no annotations, so the description carries the full burden, and it does so thoroughly. It discloses that the call blocks until the run ends, that only one run may be in flight, that the tool runs in the user's real logged-in Chrome, that credentials surface in results, and that the agent cannot see iframes, shadow DOM, pop-ups, or off-screen content. It also states the stop behavior, domain confinement, and result contents.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but every sentence earns its place for a high-risk autonomous browser tool. It front-loads the core behavior and use cases, then sequences security warnings, constraints, limitations, and result reporting. There is no redundant or filler content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with six parameters, no annotations, an output schema, and substantial side effects, the description is remarkably complete. It covers usage, constraints, concurrency, blocking, security risks, technical limitations, and return-value contents. An agent has everything needed to decide whether to invoke it and how to set its parameters correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must explain the parameters, and it explains all six. It defines `url`, `goals` as plain sentences with a security warning, `allow_domains` as domain widening, `max_actions` and `time_budget_s` with defaults and rejected bounds, and `dry_run` as planning without execution. No parameter is left to guesswork.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb-resource pairing: 'Operate a website autonomously in the user's real, logged-in Chrome,' then names the core inputs `url` and `goals`. It clearly differentiates from sibling tools by naming browser_read as the wrong choice for reading a single page. An agent can understand exactly what this tool does and what distinguishes it from nearby alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit positive use cases ('multi-step work on one site: filling a form, walking to a page that is only reachable by clicking') and explicit wrong-use cases with named alternatives ('use browser_read', 'use a web fetch or search tool'). It also adds hard exclusions for logging in, paying, or ordering on banking/payment sites and warns against putting credentials into `goals`. This is exemplary routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.1.0
    • First observedbrowser_read
    • First observedbrowser_status
    • First observedbrowser_task

TDQS

A4.9/5.0

Scored across 3 tools

Disambiguation5/5

The three tools are sharply delineated: browser_task performs autonomous multi-step interaction, browser_read is a read-only page fetch, and browser_status is a diagnostic readiness check. Both browser tools explicitly cross-reference when the other should be used, removing any ambiguity.

Naming Consistency4/5

All tools share the browser_ prefix, making the family instantly recognizable. However, browser_read uses a verb while browser_task and browser_status use nouns, so the pattern is not perfectly uniform.

Tool Count5/5

Three tools is a well-scoped set for the server's purpose: one for autonomous interaction, one for passive reading, and one for environment status. No tool feels redundant or extraneous.

Completeness5/5

The tool surface covers the full lifecycle of a browser automation session: verify readiness, read a single page, and execute multi-step tasks. Limitations are documented rather than missing functionality, and no obvious dead ends remain for the stated domain.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    A
    maintenance
    Enables controlling a real Chrome browser from MCP hosts like Claude, with extension-based or CDP fallback, supporting tabs, navigation, interaction, and page reading tools.
    40
    1,043 npm
    6
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Lets Claude Code drive your own Chrome browser with human-like clicks, drags, and typing. It uses your real sessions and logins, and pauses for your confirmation on anything irreversible.
    8
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI agents to delegate complex web browsing goals to a real Chrome instance driven by Jev, completing tasks end-to-end in ~300ms per decision and returning only the final result.
    11
    1
    MIT