omni
Attach to an existing Chrome/Chromium CDP session and control explicit tabs through seven MCP tools.
List stable tab IDs, titles, and URLs without selecting or focusing tabs.
Navigate an explicit tab or create a new tab only when new_tab=true; no hidden observation.
Observe cleaned page content, links, controls, frames, and exact refs with cursor pagination and a byte budget; no form values.
Act on exact observed refs with native click, fill, press, or native HTML select; no replay or hidden observation.
Scroll one main-document native wheel input up or down by CSS pixels and report before/after positions.
Capture a viewport or bounded full-page PNG screenshot as MCP ImageContent plus metadata, with optional atomic file output.
Download via cookie-scoped HTTP GET streaming with atomic output, overwrite/mkdir options, and a byte cap; no POST/blob/challenge fallback.
All tools support an optional phase timeout, use exact tab_id where applicable, and do not implicitly select or focus tabs.
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., "@omni open https://example.com in a new tab and 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.
OmniBrowser v2
A small attach-only Chrome/Chromium helper: independent CLI and persistent STDIO MCP, seven commands, no agent loop/broker/recipes or arbitrary eval tool. Python 3.10+; existing runtime dependencies only (Playwright, httpx, markdownify, MCP).
Install and entries
git clone https://github.com/huutrungle2001/OmniBrowser-v2.git OmniBrowser
cd OmniBrowser
python3 -m venv .venv
.venv/bin/python -m pip install -e .
export PATH="$PWD/.venv/bin:$PATH"
export OMNI_CDP_URL=http://127.0.0.1:17082Chrome must already expose loopback CDP; Omni never launches a browser/profile.
Default is port 17082; CLI accepts --cdp-url, MCP reads OMNI_CDP_URL.
Playwright 1.64+ attach uses no_defaults=True, leaving download policy, focus
and media defaults unchanged. Do not expose CDP to a network.
Root python /absolute/path/OmniBrowser/omni.py, installed omni, and omni-mcp
work independently of cwd. --help/--version do not contact Chrome.
For Python package imports from the checkout, use PYTHONPATH=src with safe-path
mode or run outside the root: the development entry omni.py otherwise shadows
the installed package in ordinary python -c/stdin imports.
Related MCP server: Chrome MCP Bridge
Commands
omni --list-tabs
omni --goto https://example.com --new-tab
omni --goto https://example.com --tab TAB_ID
omni --observe --tab TAB_ID
omni --observe --tab TAB_ID --cursor CURSOR_FROM_PREVIOUS_RESULT
omni --act --tab TAB_ID --ref REF --action click
omni --act --tab TAB_ID --ref REF --action fill --value 'text'
omni --act --tab TAB_ID --ref REF --action press --key Enter
omni --act --tab TAB_ID --ref REF --action select --value blue
omni --act --tab TAB_ID --ref REF --action select --label Blue
omni --act --tab TAB_ID --action click_at --x 320.5 --y 240
omni --scroll --tab TAB_ID --direction down --amount 600
omni --screenshot --tab TAB_ID --output /absolute/capture.png
omni --screenshot --tab TAB_ID --output /absolute/full.png --full-page
omni --download --tab TAB_ID --url https://example.com/notes.pdf --output ./notes.pdfUse real returned tab IDs/refs, not indexes, text matches or guessed selectors.
Only explicit goto --new-tab creates a tab; no implicit focus/tab selection.
Irrelevant/ambiguous action arguments are rejected before browser interaction.
Press is native element input; select is exact value OR label on native HTML
select only (custom dropdowns return UNSUPPORTED_ACTION). No force/JS click,
JS value assignment, action replay, hidden observe or post-action screenshot.
CLI stdout is one JSON object, optionally --pretty. Errors retain
{"ok":false,"error":{"code":"STALE_REF","message":"..."}}.
Exit codes: 0 success; 2 invalid/unsupported/limit; 3 connection/missing tab;
4 stale/unavailable target; 5 timeout; 6 file/download/auth; 1 internal.
--timeout is milliseconds, default 10000 (download 120000); it is a phase
bound, not a hard wall-clock interruption of CDP capture/local parsing.
Filled/selected values and native call logs are omitted from errors/results;
shell history/arguments can still expose CLI values.
Complete paged observation
Observe joins batch DOM/AX/DOMSnapshot captures using vendored browser-use
snapshot/visibility/clickable/paint-order logic, then clean HTML -> markdown.
It returns content, absolute links, named controls, frames, coverage,
truncated, has_more, and next_cursor. Follow the cursor to continue all
four streams (including frame metadata) until has_more=false/next_cursor=null.
Capture is best-effort, not an atomic snapshot: unrelated DOM mutations during capture do not reject the whole observation. Document identity, URL/baseURI and frame topology/loader checks remain; unresolved candidates and extraction limits are reported. Signed cursors still reject meaningful changes to cleaned results or document/frame identity, not arbitrary DOM mutation counts.
Main, nested same-origin, cross-origin and out-of-process iframe documents are
read once in stable frame-tree order. Content has [Frame FRAME_ID] boundaries;
every link/control carries frame_id. Frame metadata includes parent identity,
sanitized URL/name, read status and concise reasons for unread frames. Duplicate
sibling URLs/labels are not identities. Hidden/inert/fully clipped owners are
filtered; partial clipping and transforms are not projected into child content.
Ordinary below-viewport content is retained. Closed shadow roots remain unsupported.
Each full Omni JSON page + newline is bounded to 15360 UTF-8 bytes by default
(--max-bytes 2048..65536). Streams are interleaved fairly; positions in
coverage.positions are [start,end) offsets for content characters/items.
Only the returned page binds refs, at most 96 combined links/controls; candidate
discovery is not globally capped at 96. An item that cannot fit by itself raises
OUTPUT_BUDGET; names, URLs and refs are not clipped to fit.
Cursors are signed with a document-local key and a keyed fingerprint of cleaned
extraction, exact frame/node identities, frame topology and document generations.
They expose offsets, not raw content,
credentials or editable values. Each call captures afresh; no disk registry or
observation daemon. Cursors work between CLI processes and MCP. Malformed,
wrong-tab/document, navigation or changed extracted state returns STALE_CURSOR
and requires a fresh observe without cursor. Title-only/irrelevant chrome text
outside the extracted main content is not itself a reason to invalidate.
truncated and *_omitted describe the CURRENT response, so they may remain
true/nonzero on the final page because earlier pages held other items.
has_more=false only ends recoverable pagination, not all site coverage.
Unrecoverable aggregate limits remain explicit: 50000 processed DOM nodes,
3000 paint-filter nodes, 1000000 HTML characters before markdown, 32 read frames,
8 descendant levels, 256 discovery records/attachment attempts, and acquisition
limits of 100000 snapshot nodes/8000000 string characters across target batches.
CDP batch replies arrive before their size can be checked; a single batch and
local rendering are not hard wall-clock interruptible. Overlong names are omitted.
Check processing_limited, paint_filter_limited,
extraction_content_truncated, frames_unread, frames_unrouted,
frame_discovery_limited, frame_geometry_limited, frame statuses/limitations
and omission/unsupported counts.
Frame metadata is paginated too; omitted metadata does not imply a successful read.
Refs resolve from exact backend node objects into a document-local Map; no Nth element rebinding. They work across CLI/MCP, with at most 512 retained refs. Navigation/BFCache, detachment/reattachment, identity changes (e.g. href), another tab or eviction rejects a ref. On stale refs, observe/select again. Refs include an opaque exact frame/document identity and resolve only to the bound node in that document, including iframe/open Shadow DOM nodes. The 512-ref retention limit is per document. Transient writes/removals of watched identity attributes do not invalidate a ref when the exact node's stored identity is restored; a different final identity still rejects it. No raw DOM/AX, editable values, hidden fields, cookies or browser credentials are serialized. Visible page text/URLs are not a general secret-redaction service.
Screenshot, scroll and downloads
Screenshot is viewport PNG by default. Full-page uses bounded CDP capture beyond
the viewport, without tab activation, viewport resize or hidden scrolling.
Output is CSS-resolution PNG, limited to 6000000 pixels, 8192px per side and
4 MiB encoded-image bytes. Oversized capture returns SCREENSHOT_LIMIT.
CLI requires output; MCP returns actual standard ImageContent plus JSON metadata,
with optional explicit output file. Existing files/missing parents require
--overwrite/--mkdir; publication is atomic and failures leave no partial PNG.
Screenshots can reveal visible secrets; use only authorized pages.
For visual-only targets, capture a viewport screenshot and choose the point:
omni --screenshot --tab TAB_ID --output /absolute/viewport.png
omni --act --tab TAB_ID --action click_at --x 320 --y 240click_at sends one native mouse click at finite fractional-capable coordinates
in top-level viewport CSS pixels, including hits inside iframes. It requires
both x/y, rejects ref/value/label/key, and checks current viewport bounds.
Ref actions require a ref and reject coordinates. Success confirms input dispatch,
not application intent. No screenshot, scroll, correction or retry is hidden inside
the operation; canvas typing, dragging, resizing and rotating are not supported.
PNG metadata includes actual image dimensions, viewport CSS size, page-CSS capture
origin/extent, page scroll, DPR, browser zoom and visual viewport scale. When
image_to_input.supported=true, map original image pixel coordinates using
input_x = viewport_origin.x + image_x * css_per_image_pixel.x (likewise y).
This uses actual PNG dimensions rather than assuming device pixels equal CSS
pixels. Account separately for any resizing by your image viewer/client.
Full-page positions are not directly viewport click coordinates: take a
viewport screenshot for clicking. Pinch zoom/nonzero visual viewport offsets
are explicitly unsupported for coordinate input/mapping. Normal desktop browser
zoom is accounted for through current CSS metrics, DPR and actual PNG dimensions.
A screenshot is not a lock: capture again after layout, navigation, viewport or
scroll changes. No automatic visual targeting or arbitrary JavaScript API is exposed.
Scroll sends one native wheel input to a hit-tested main-document location with
no inner scroller in its ancestry. Direction is up/down; amount is 1..10000 CSS
pixels, default 75% viewport height capped at 800 (minimum 100).
It returns before/after positions, movement, document/viewport sizes and
at_current_bottom. This is not a claim that lazy loading is finished or an
infinite list is complete. No inner-pane/iframe scrolling, focus switching,
implicit observe or repeated auto-scroll.
HTTP downloads preserve the existing scoped in-memory cookie jar, redirects, streaming/byte cap (512 MiB default), HTML/PDF checks and atomic no-clobber output. No browser Save dialog or download-policy changes. POST/blob, partitioned-cookie auth, browser-only tokens, client certificates and challenges have no fallback.
MCP, skill and source layout
Exactly seven tools: list_tabs, goto, observe, act, download,
screenshot, scroll. MCP uses snake_case arguments (tab_id, full_page,
max_bytes, cursor, etc.). For example:
act(tab_id, ref, action="press", key="Enter");
act(tab_id, action="click_at", x=320, y=240);
observe(tab_id, cursor=next_cursor); screenshot(tab_id).
Results/errors share CLI objects in text + structuredContent; screenshot adds a
real PNG ImageContent. Observe's byte budget excludes MCP protocol/duplicate
representation overhead.
Add an MCP registration to ~/.codex/config.toml, replacing the example paths
with your checkout's absolute paths:
[mcp_servers.omni]
command = "/absolute/path/OmniBrowser/.venv/bin/python"
args = ["/absolute/path/OmniBrowser/omni_mcp.py"]
tool_timeout_sec = 180
[mcp_servers.omni.env]
OMNI_CDP_URL = "http://127.0.0.1:17082"One dedicated thread owns sync Playwright, attaches lazily and serializes MCP
calls. CLI is independent with per-call connections. Broken transport is dropped
for a later call; no replay. Shutdown disconnects, not Chrome/user tabs.
Other clients need agent coordination. Already-running clients may need MCP
reconnect/new session for changed schemas; no other agent/session was restarted.
Install the bundled skill by linking skills/omnibrowser into ~/.codex/skills/omnibrowser.
See skills/omnibrowser/SKILL.md. The product name is
OmniBrowser; CLI omni and MCP registration omni remain unchanged.
Each top-level operation is implemented once in src/omni/commands/*.py.
Entries stay tiny; CLI/MCP share validation and dispatch. Canonical layout:
omni.py, omni_mcp.py thin development entries
src/omni/
cli.py, mcp_server.py validation/dispatch, persistent STDIO owner
errors.py, models.py
browser/ connection.py, tabs.py, frames.py, targets.py, refs.js
reading/ capture.py, render.py, pagination.py
commands/ list_tabs.py, goto.py, observe.py, act.py,
download.py, screenshot.py, scroll.py
_vendor/browser_use_dom/ small existing MIT DOM subsetThere is no backward-compatibility layer. Former package-root cdp, tabs,
reader, pagination, refs.js, observe, actions, download and scanner.js
are removed. Python callers must import the canonical modules above; tab command
convenience exports are gone. Old refs/cursors require a fresh observation.
Non-editable wheels include omni/browser/refs.js. Vendor MIT/provenance is in
src/omni/_vendor/browser_use_dom/UPSTREAM.md.
Verification
This revision was exercised directly through actual CLI/MCP on a harmless temporary local demo
in a newly-created tab of existing Chrome. No pytest, automated suite, mocks,
benchmark or new fixture project was added/run; existing test callers received
canonical import, registry/resource and changed observation-schema maintenance only.
No Moodle/account/agent-fetch tab was used. Existing fixture-based checks are
included under tests/; local handoff notes and screenshots are not published.
Direct checks on 2026-10-21, Chrome 155, existing authorized session:
Same-origin siblings, nested documents, cross-origin OOPIF and a nested OOPIF read/action checks passed. Separate CDP
type: iframetargets confirmed actual OOPIFs; duplicate URLs/labels changed only their selected sibling.CLI → MCP and MCP → CLI refs worked; native click/fill/press and native select by label/value passed. Navigation, detachment, node reinsertion and restored identity mutations rejected old refs. Changed child state/navigation rejected cursors.
Alternating real CLI/MCP continuation recovered 230 links, 233 controls (including three extension groups), three frame records and both final content rows in 35 pages at a 4096-byte budget; largest actual response was 3944 bytes. Sibling candidates appeared on the first page; the global cap returned 96 refs.
Hidden/inert/fully clipped owners were skipped; transformed owners were marked geometry-limited. A 37-frame page reported 32 reads and five explicit limits.
Viewport screenshots guided native main/OOPIF canvas clicks, including after main-document scrolling. Missing/conflicting/non-finite/bool/out-of-bounds coordinates were rejected; MCP rejection checks left the click count unchanged. Full-page mapping was marked unsupported for direct viewport input.
Seven-tool STDIO listing, actual MCP PNG ImageContent, help/version and an outside-checkout non-editable wheel/resource check passed; legacy paths were absent from the wheel. No other MCP clients were restarted.
Display verification used DPR 2 at 100% desktop browser zoom, including fractional scroll offsets. Other DPR/browser zoom modes were not directly verified; pinch zoom/offset visual viewports are explicitly unsupported, not silently corrected.
Available Tools
7 toolsactB
Native click/fill/press/select on an exact observed ref. No replay or hidden observe. select is native HTML only.
Inputs: ref (required), action (required), tab_id (required), key, +3 more.
downloadB
Cookie-scoped HTTP GET streaming; atomic output, no Save dialog. No POST/blob/challenge fallback.
Inputs: url (required), output (required), tab_id (required), mkdir, +3 more.
gotoA
Navigate an explicit tab or create one only with new_tab=true. No hidden observe.
Inputs: url (required), tab_id, new_tab, timeout.
list_tabsB
List stable page-target IDs, titles and URLs without selecting/focusing a tab.
Inputs: timeout.
observeC
Clean content, links and controls with exact refs; continue all streams using next_cursor. No field values.
Inputs: tab_id (required), cursor, timeout, max_bytes.
screenshotB
Return actual PNG ImageContent and metadata; optional safe output file. No focus/resize/scroll. Max 6M pixels, 8192px/side, 4 MiB.
Inputs: tab_id (required), mkdir, output, timeout, +2 more.
scrollC
One main-document native wheel input. CSS pixels; reports before/after movement, not lazy-load completeness. Inner panes unsupported.
Inputs: tab_id (required), direction (required), amount, timeout.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 7 tools
Each tool targets a distinct browser primitive: list_tabs (enumerate), goto (navigate), observe (read state), act (mutate state), download (fetch file), screenshot (capture image), scroll (viewport movement). The descriptions explicitly delimit boundaries (e.g. observe returns refs but no field values, act works only on observed refs), leaving no real overlap. observe vs screenshot are the closest pair but are clearly separated as DOM-content vs PNG image.
Six of seven names are clean single-verb actions (goto, observe, act, download, screenshot, scroll), giving a predictable action-oriented convention. list_tabs is the lone verb_noun outlier, a minor deviation rather than a different style. Overall readable and consistent.
Seven tools is well-scoped for a browser automation server, covering the essential perceive/act/navigate/capture primitives. Each tool earns its place with no redundant or filler entries. Falls squarely in the ideal 3-15 range.
Core browser lifecycle is covered: tab enumeration, navigation, observation, interaction, scrolling, screenshots, and downloads. Minor gaps exist, such as no explicit tab-close or history back/forward operation and no dedicated wait-for-load primitive, but agents can work around these via goto and repeated observe. The surface is solid for the stated purpose.
Related MCP Connectors
Control a real browser from AI agents: navigate, click, type, screenshot, read the page.
Live browser debugging for AI assistants — DOM, console, network via MCP.
Web scraping, browser automation, screenshots and AI data extraction for AI agents via MCP.
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to control the Google Chrome browser through a Node.js WebSocket bridge and a dedicated browser extension. It provides tools for capturing screenshots, executing JavaScript, managing tabs, and extracting page content via the MCP protocol.2-
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to control and interact with a Chrome browser via MCP, providing tools for navigation, screenshots, clicking, form filling, content extraction, and tab management.1-
- AlicenseNot gradedqualityBmaintenanceEnables local opencode agents to control a live Chrome browser via MCP tools, including tab management, JavaScript execution, clicking, form filling, page reading, screenshots, and console log retrieval.2MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to control, read, and verify a live Chrome browser through 46 schema-validated MCP tools, including DOM pruning, coordinate-aware interaction, and trusted CDP input events.77 npm41AGPL 3.0