OmniBrowse
Web Browser for your AI Agent.
Documentation
OmniBrowse
OmniBrowse is a Model Context Protocol (MCP) server providing a private-mode text-based headless browser for AI agents. No Selenium, Playwright, or Chromium — just Node.js with Chrome TLS impersonation (curl-impersonate), jsdom JavaScript execution, full DOM/input handling, and 31 tools designed for AI browsing workflows.
Pages are parsed into an accessibility-style text snapshot with interactive element refs (r1, r2, …). The model clicks, types, scrolls, and runs JavaScript — each action triggers the real protocol (HTTP/curl-impersonate with true Chrome JA3, or local schemes like data:/file:/ftp:/ipfs:).
Install
Add to your MCP client config (Claude Desktop, Kilo, Cursor, etc.):
{ "mcpServers": { "web-browser": { "command": "npx", "args": ["-y", "omnibrowse"] } } }
Until the npm package is published, run locally:
{ "mcpServers": { "web-browser": { "command": "node", "args": ["/absolute/path/to/omnibrowse/dist/index.js"] } } }
Tools
| # | Tool | Description |
|---|---|---|
| 1 | navigate | Open a URL (http/https/data/file/ftp/gopher/ipfs/about/javascript). Set js=true for jsdom SPA rendering. Set newSession=true to wipe history/cookies before navigating. Returns an accessibility snapshot. |
| 2 | read_snapshot | Re-read the current viewport of the page. |
| 3 | scroll_read_more | Advance to the next chunk of a long page (viewport-sized scroll). |
| 4 | click | Click an element by ref. Links navigate; buttons/submit inputs submit their form with all filled fields. |
| 5 | type_text | Type into a textbox/textarea by ref. submit=true submits the enclosing form afterward. |
| 6 | select_option | Choose option(s) in a by ref (values array). |
| 7 | check | Toggle a checkbox/radio by ref (checked true/false). |
| 8 | go_back | Go back one page in session history. |
| 9 | find | Search the current snapshot by plain text (case-insensitive) or regex (/pattern/flags). Returns matching lines with context. |
| 10 | url_bar | Return the current page URL. |
| 11 | view_source | Return raw HTML of the current page (post-JS if js=true was used). Byte-offset pagination supported. |
| 12 | evaluate_js | Run arbitrary JavaScript in the current page's window context via jsdom. Expressions are auto-wrapped and the return value is extracted with JSON.stringify. Returns console output + eval result. |
| 13 | wait_for | Re-run jsdom on the current page and poll until a CSS selector appears (or timeout). Returns the new snapshot. |
| 14 | network_requests | List XHR/fetch/subresource requests observed during the last jsdom run. |
| 15 | console_logs | Dump captured console.log/error/warnings and uncaught script errors from page scripts. Filter by level (all/log/error). |
| 16 | cookie_jar | Inspect cookies in this private session (in-memory only, per-hostname, never persisted). |
| 17 | route_info | Show redirect chain, DNS resolution (via dig), transport info (curl-impersonate binary, TLS impersonation target), and security headers for the current page. |
| 18 | inspect_element | Return raw HTML + parsed attributes for a specific element ref. |
| 19 | list_links | Enumerate all links, headings, and images on the current page for fast scanning. |
| 20 | fetch_resource | Make a raw HTTP(S) request (GET/POST/PUT/HEAD) or fetch a local-scheme resource. Non-HTML content is rendered through content handlers: JSON pretty-printed with summary, PDF text extracted via pdftotext+pdfinfo, images dimensioned and optionally OCR'd, media probed via ffprobe, archives described. |
| 21 | resource_discovery | Auto-discover well-known resources: robots.txt, sitemap.xml, RSS/Atom feeds (/rss, /feed, /atom.xml, etc.), web app manifest.json, OpenSearch descriptor, favicon.ico/png. Set kind to fetch one specifically. |
| 22 | resize_viewport | Change the snapshot window size (lines per viewport) and re-snapshot the current page from the top. |
| 23 | permissions | List which browser Web APIs are stubbed, granted silently, or blocked in this private browsing session. |
| 24 | go_forward | Go forward to the next page in session history (after using go_back). |
| 25 | history_list | List all pages in the browsing session history with the current page marked. |
| 26 | press_key | Press a keyboard key (ref?, key). Enter submits the enclosing form; Escape clears form state; Tab/Backspace/Delete/arrows/Space/page-keys/single characters are simulated on text inputs. |
| 27 | hover | Hover over an element by ref to dispatch mouseenter/mouseover/mousemove events; re-renders via jsdom if the DOM changes (reveals hover-triggered tooltips and menus). |
| 28 | file_upload | Select local file(s) for a by absolute path. Submit the form afterwards (click on submit button or press Enter) to upload via multipart/form-data. |
| 29 | screenshot | Capture a structured text "screenshot" (full-page layout dump) of the current page. Pair with view_source for raw HTML. |
| 30 | handle_dialog | Accept/dismiss an alert/confirm/prompt dialog raised by page JS during the last jsdom run (with optional response for prompts). |
| 31 | dialog_list | List any dialogs (alert/confirm/prompt) queued from the last jsdom run, with the next-to-handle marked. |
All tools with an empty input schema use z.object({}) for MCP SDK compatibility.
Protocols
HTTP/HTTPS goes through curl-impersonate with --impersonate chrome131, producing a true Chrome JA3 fingerprint. Local protocols are handled directly without network access:
| Scheme | Handler |
|---|---|
| data: | Decodes base64 or percent-encoded payload, returns the MIME body. |
| blob: | Unresolvable outside the originating JS context; an error note explains to use read_snapshot. |
| file: | Reads regular files (sniffs MIME from extension) and generates directory listings (via ls -la with HTML-safe escaping). |
| javascript: | Returns the source code for evaluate_js to run in page context. |
| about: | Built-in pages: privatebrowsing, version, networking, preferences, blank, blobs, srcdoc. Default shows scheme + kind. |
| ftp: / ftps: | Delegates to curl-impersonate. Directory listings are detected and wrapped in HTML . |
| gopher: | Fetches the selector text via curl-impersonate. |
| ipfs: / ipns: | Resolves through public gateways in order: ipfs.io → dweb.link → cloudflare-ipfs.com → w3s.link. |
| ws: / wss: | Returns informational text (WebSocket endpoints are detected but not connected). |
| mailto: / tel: / sms: | External scheme — handled by the OS, not navigable. |
Private browsing posture
- Cookies live only in memory for the current MCP session, scoped per hostname. Third-party cookies (cross-registrable-domain Set-Cookie) are dropped. SameSite=None without Secure is rejected.
- DNT: 1 and Sec-GPC: 1 sent on every HTTP request.
- Referer suppressed to origin for cross-origin navigations.
- No persistent cache, history, or storage written to disk between sessions.
- WebRTC (
RTCPeerConnection) blocked entirely. getUserMedia/getDisplayMedia denied. ServiceWorker.register throws SecurityError. - Geolocation, notifications, clipboard resolve silently without user prompts (denied/granted respectively).
- Canvas/WebGL/AudioContext return plausible null contexts with no real pixels or audio output (fingerprint-safe).
- crypto.subtle backed by Node's real WebCrypto — modern auth flows (PKCE, JWE) work.
- fetch/XHR observed and recorded for the network-log tool.
Content rendering
Non-HTML resources fetched via fetch_resource are handled by content-type-specific renderers:
| Content-Type | Renderer |
|---|---|
| image/* | Dimensions via file / identify (ImageMagick). OCR via tesseract+imagemagick pipeline when installed. |
| application/pdf | Text extraction via pdftotext, metadata via pdfinfo. |
| application/json (and *+json) | Pretty-printed with top-level object/array summary. |
| application/xml (and *+xml) | Root element detection + raw text display. |
| audio/*, video/* | ffprobe stream metadata when installed (codec, duration, resolution). |
| Archive types (application/zip,x-tar,gzip, etc.) | Descriptive note (not expanded inline). |
| text/* | Raw text with 30KB truncation. |
How it works
| Layer | File | Description |
|---|---|---|
| TLS/HTTP | src/curl.ts | Detects curl-impersonate binary (style-1 single binary or style-2 per-browser binaries), probes supported --impersonate targets, falls back to plain curl with crafted Chrome TLS ciphers/curves. Emits Chrome User-Agent, sec-ch-ua headers, HTTP/2/3, redirect chain tracking. |
| Private HTTP | src/http.ts | HttpClient wraps curlFetch with in-memory per-hostname CookieJar, SameSite enforcement, third-party cookie dropping, DNT/GPC headers, cross-origin Referer policy, DNS resolution via dig with caching, multipart POST assembly. |
| Protocols | src/protocols.ts | Handles all non-HTTP(S) URL schemes: data URI base64 decoding, file URI read+directory, ftp/gopher via curl, ipfs via public gateways, about: internal pages, javascript: eval forwarding, ws/wss info, external schemes. |
| DOM | src/dom.ts | node-html-parser builds a tree; buildPage() walks it once producing an accessibility snapshot with [L] text lines and [rN] refs for interactive elements. Supports all HTML5 input types (slider, spinbutton, datetime, searchbox, color, file), /, iframes, media elements (video/audio/canvas/object), heading markers, safe viewport scrolling with line-count guard. |
| JS Execution | src/jsdom.ts | jsdom with runScripts: "dangerously", real Internet ResourceLoader for subresources, Web API stubs (crypto.subtle→Node WebCrypto, Canvas/WebGL→null context, AudioContext→stub, Notification/Permission/Clipboard/Geolocation→silent deny, ServiceWorker/WebRTC→blocked), console+network capture. |
| Content | src/content.ts | Non-HTML renderers: JSON pretty-print+summary, XML root detection, image dimensions+OCR (optional tesseract), PDF text extraction (optional pdftotext), media probe (optional ffprobe), archive descriptions. |
| Session | src/browser.ts | Session tracks page history, form state (filled values, toggled checkboxes, selected options), scroll position, and viewport size. performAction() routes all actions to the correct protocol fetch + render pipeline. |
| MCP Server | src/index.ts | 31 tools registered via @modelcontextprotocol/sdk McpServer over StdioServerTransport. Stdio only — no HTTP server, no ports. |
Build from source
npm install npm run build # outputs dist/ node dist/index.js # run the server (reads JSON-RPC from stdin)
Test
node test/test-comprehensive.mjs # 31 automated checks: navigation, JS, all protocols, tools, resources node test/test-ai-browse.mjs # Realistic 20-step AI browsing session (example.com → HN → DDG → resources → JS eval → protocols)
License
MIT