Browser automation where an LLM plans and Jev (Typesafe System One) decides. Library, CLI and MCP server.
# Add to your Claude Code skills
git clone https://github.com/Ying-Kai-Liao/jev-browserGuides for using ai agents skills like jev-browser.
Last scanned: 9/21/2026
{
"issues": [],
"status": "PASSED",
"scannedAt": "2026-09-21T09:37:56.042Z",
"npmAuditRan": true,
"pipAuditRan": true,
"promptInjectionRan": true
}See how jev-browser compares with popular alternatives.
jev-browser is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by Ying-Kai-Liao. Browser automation where an LLM plans and Jev (Typesafe System One) decides. Library, CLI and MCP server. It has 123 GitHub stars.
Yes. jev-browser passed SkillsLLM's automated security scan — a dependency vulnerability audit plus prompt-injection heuristics — with no high-severity issues. You can read the full report in the Security Report section on this page.
Clone the repository with "git clone https://github.com/Ying-Kai-Liao/jev-browser" and add it to your Claude Code skills directory (see the Installation section above).
jev-browser is primarily written in JavaScript. It is open-source under Ying-Kai-Liao on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh jev-browser against similar tools.
No comments yet. Be the first to share your thoughts!
From $49 · 30 days · terms & refunds
⚠️ Third-Party Software Notice
This skill is third-party open-source software developed and hosted independently on GitHub. SkillsLLM is an informational directory and does not control or maintain the underlying repository.
Any security checks, ratings, or warnings displayed by SkillsLLM are automated and limited in scope. They do not constitute a security certification or guarantee that the software is safe, error-free, or free from malicious code, vulnerabilities, compromised dependencies, or prompt-injection risks.
Review the source code, permissions, dependencies, and configuration before installing or running any third-party skill. Use is at your own risk. To the maximum extent permitted by applicable law, SkillsLLM is not liable for losses arising from third-party software.
Automated security scan — not a certification.
Browser automation where an LLM plans and Jev decides.
Unofficial project, not affiliated with TypeSafe. It calls the TypeSafe System One API with your own API key.
The calling LLM (Claude, via MCP) says what outcome it wants, one step at a time, and hands over any text to type. For each round of a step, code describes the page. Then one ~300 ms Typesafe System One request asks Jev several questions at once: which element, which action, which value, and is the step done / blocked / showing an error / about to do something irreversible. Playwright performs the action. The LLM never reads page snapshots unless it chooses to take over.
Claude ── browser_do("Log in", {email, password}) ──▶ jev-browser
│ loop until done / stuck / needs confirmation
│ 1. settle (network + DOM quiet)
│ 2. describe (elements, labels, state, visible text, diff, counts)
│ 3. Jev (done? error? irreversible? tool? target? value?)
│ 4. act (Playwright)
Claude ◀── { status: "done", url, actions[], done_score } ─┘
https://github.com/user-attachments/assets/2e688df9-4985-4854-8ebe-ba97c9d13d68
With WebMCP, a page offers its own tools. Jev
chooses between those and the page's buttons, and fills each argument by picking one of the
caller's values (text: values.g1 → "milk"), never by writing text:
https://github.com/user-attachments/assets/fe309fb8-aada-4187-a91e-f18493a39526
Jev only answers with probability distributions: yes/no (noul), pick one option (choice)
or a rating (score). It never writes text. So everything free-form comes from the caller as
candidates, and code turns disagreement or low confidence into a status the LLM can act on.
42 tasks in 16 categories on live sites (see RESULTS.md):
likely_done) and verifying a sort.Works on: forms, native and custom dropdowns, checkboxes and radios (including styled replacements), dynamic loading, modals, JS dialogs, hover, right-click, drag and drop, key presses, file upload, iframes, shadow DOM, new tabs, pages with 2,000+ elements (two-stage selection), and non-English UIs.
Known limits, so write steps around them:
browser_check or browser_snapshot./plugin install jev-browser --marketplace Ying-Kai-Liao/jev-browser
On older Claude Code versions, add the marketplace first:
/plugin marketplace add Ying-Kai-Liao/jev-browser
/plugin install jev-browser@jev-browser
Claude Code then asks for your TypeSafe API key, which goes to its secure credential store. It also offers two optional settings: show the browser window, and a persistent profile directory so that logins survive restarts. The plugin runs the MCP server below, pinned to the same version, and adds a skill that teaches Claude how to write steps and handle each status. The first browser call downloads Chromium for Playwright if it is missing (one time, about 30 s).
Data: see Privacy. Page content goes to the TypeSafe API; passwords and other
secrets you pass in values don't.
npx playwright install chromium # once (the server also does this on first use)
claude mcp add jev-browser -e TYPESAFE_API_KEY=your-key -- npx -y -p jev-browser jev-browser-mcp
Any MCP client works the same way: command npx, args -y -p jev-browser jev-browser-mcp,
env TYPESAFE_API_KEY. Add JEV_BROWSER_HEADED=1 to watch it work.
git clone https://github.com/Ying-Kai-Liao/jev-browser && cd jev-browser
npm install
npm run setup # downloads Chromium for Playwright
cp .env.example .env # add TYPESAFE_API_KEY
npm test # offline tests (no network, no key)
npm run test:e2e # MCP server end to end (network + key)
claude mcp add jev-browser -- node /absolute/path/to/jev-browser/bin/jev-browser-mcp.mjs
From a source checkout the server reads TYPESAFE_API_KEY from the repo's .env.
| tool | purpose |
|---|---|
browser_open(url) |
navigate and wait for the page to settle |
browser_do(goal, values?, max_actions?, allow_irreversible?, explain?) |
work toward one outcome; returns a status |
browser_check(question) |
yes/no about the page → p_yes |
browser_choose(question, options) |
pick among given options → distribution |
browser_snapshot(diff?, interactive?, filter?, urls?, max_chars?) |
compact numbered element list, for taking over; the options below narrow it |
browser_act(action, element, value?, key?, destination?, accept_dialog?) |
act on an element directly, no model; confirm/prompt dialogs are dismissed unless accept_dialog |
browser_read(question, max_chars?) |
the page text that helps answer a question; Jev drops the rest |
browser_screenshot(full_page?) |
PNG image |
browser_close() |
end the session |
browser_webmcp_tools() |
list the WebMCP tools the page registered |
browser_webmcp_call(name, arguments?) |
run one of the page's WebMCP tools; no clicking, no Jev call |
A page that supports WebMCP registers tools for
agents with document.modelContext.registerTool(...) (or navigator.modelContext.provideContext
in earlier drafts). An init script records them before the page's scripts run, in every browser —
where Chromium has no native modelContext, a minimal one is installed so feature-detecting pages
still register. browser_open lists their names as webmcp_tools.
In browser_do, Jev uses them. Each round, the page's tools are offered next to click, type
and the rest, with a yes/no "can one of the page's tools do the next step?" that decides between
them. The same request fills every tool's arguments: each is one of your values or, for an
enum, one of its options (a list, a priority), because Jev never writes text. The page's own
execute runs, its result goes into the step's history for the "done?" judgement, and the action
shows as webmcp tool add_todo {"text":"values.todo"} => "…". A tool the site does not mark
readOnlyHint is guarded like a click: if it looks irreversible the step stops with
needs_confirmation.
To call a tool yourself instead, browser_webmcp_tools gives each schema and
browser_webmcp_call runs it directly, with no Jev call. Main frame only. Tool descriptions and results come from the site:
untrusted, and results are capped at 20k chars. A tool not marked readOnlyHint may have side
effects, so the caller should treat it like allow_irreversible.
browser_readbrowser_snapshot is the take-over-and-act surface: numbered elements, plus the text in the
viewport. That is the right trade for acting, and it is already small (99–6,900 tokens on
every page measured, because elements are capped at 400 and the text at 2,500 chars). It is a
poor way to read, though — a Wikipedia article is ~179k chars of text, so a snapshot shows
about 1.4% of it and the rest costs a scroll-and-snapshot loop.
browser_read(question) is the understand-the-content surface. Code extracts the whole
document's text and cuts it at headings and sections; Jev rates every block against the
question in batches of 40; code returns the blocks that help, in document order, inside a
character budget. Jev ranks, it never writes — the text you get back is the page's own.
browser_snapshot |
browser_read |
|
|---|---|---|
| gives you | numbered elements + viewport text | document text relevant to a question |
| use it to | act (with browser_act) |
read, quote, answer |
| model calls | none | 1 per ~50k chars, ~1 s each |
| lossless | yes | no — drops blocks, and says which |
When it pays. Long articles, documentation and reference pages. On the WW2 article, "When and why did Japan surrender?" returned 11.8k of 178.5k chars — 15× less — in 3.4 s over 5 Jev calls, keeping the atomic-bomb and surrender paragraphs.
When it doesn't. Short pages, apps and forms. Below a 8,000-char floor (and below your own
max_chars) it returns the page whole and never calls Jev at all, because paying ~1 s to prune
a 2k-token page is a loss. Hacker News, a BBC front page and saucedemo all land under the floor.
It is biased to keep: a block Jev cannot rate — or a whole batch whose request failed — is
kept, and whatever is left out is counted and marked in place with [… N blocks dropped …].
Nothing is ever silently cut.