Skip to main content

Public Browser — Python Script API

Python client for Public Browser automation. Scripts use the same tool implementations as the MCP server (Shared Core) — every improvement to click, navigate, fill_form etc. automatically benefits your scripts too. One codebase, one test suite (1600+ tests), two access paths.

Installation

The Python package is not currently published on PyPI. From the repository root, install the local package:

python -m pip install ./python

If you are already in this python/ directory, use python -m pip install . instead. No manual Chrome launch is needed — Chrome.connect() starts everything automatically via a local public-browser binary or the npx fallback.

Dependencies: websockets (for the Escape Hatch / CdpClient low-level access). The main Shared Core API uses urllib (built-in).

Quick Start

from publicbrowser import Chrome

chrome = Chrome.connect()

with chrome.new_page() as page:
    page.navigate("https://example.com")
    title = page.evaluate("document.title")
    print(title)  # "Example Domain"

chrome.close()

Chrome.connect() auto-starts the Public Browser server as a subprocess, which in turn launches Chrome. When you call chrome.close(), the server subprocess is terminated.

How it works

Python Script                        Escape Hatch (Power User)
    │                                    │
    ▼                                    ▼
HTTP POST /tool/{name}              WebSocket (CDP)
Port 9223                           Port 9222
    │                                    │
    ▼                                    │
Public Browser Server                    │
    │                                    │
    ▼                                    │
registry.executeTool()                   │
    │                                    │
    ▼                                    │
Tool Handler                             │
(click.ts, navigate.ts, ...)             │
    │                                    │
    ▼                                    ▼
Chrome ◄─────────── CDP ────────────────►

Your script sends HTTP requests to the Public Browser server on port 9223. The server executes the exact same tool handlers that the MCP server uses — selector resolution, Shadow DOM traversal, scroll-into-view, paint-order filtering, ambient context — all server-side.

Auto-Start

Chrome.connect() finds and starts the server automatically:

  1. Running server — checks if port 9223 already responds, connects immediately
  2. PATH binary — finds public-browser in PATH (e.g. via Homebrew), starts it with --script
  3. npx fallback — runs npx -y public-browser@latest -- --script
  4. Explicit pathChrome.connect(server_path="/path/to/public-browser") for custom setups

Login and Data Extraction

from publicbrowser import Chrome

chrome = Chrome.connect()

with chrome.new_page() as page:
    page.navigate("https://app.example.com/login")

    # Fill login form
    page.fill({
        "#email": "user@example.com",
        "#password": "secret",
    })
    page.click("#submit")

    # Wait for dashboard
    page.wait_for("text=Dashboard")

    # Extract data
    data = page.evaluate("""
        Array.from(document.querySelectorAll('.item'))
            .map(el => ({ name: el.textContent, href: el.href }))
    """)
    print(data)

chrome.close()

API Reference

Chrome

Method Description
Chrome.connect(host="localhost", port=9223, *, server_path=None, auto_start=True) Connect to or auto-start the Public Browser server
chrome.new_page() Context manager: open a new tab, auto-closes on exit
chrome.close() Close the connection and terminate any auto-started server

Page (via chrome.new_page())

Method Description
page.navigate(url) Navigate to URL and wait for load
page.click(selector) Click element by CSS selector, text, or ref
page.type(selector, text) Type text into input element
page.fill({"sel": "val", ...}) Fill multiple form fields at once
page.wait_for(condition) Wait for JS condition or "text=..." shorthand
page.evaluate(expression) Run JavaScript, return result
page.download() Enable downloads, return download dir
page.close() Close the tab (auto-called by context manager)
page.cdp Escape Hatch — returns a CdpEscapeHatch for direct CDP access (see below)

Escape Hatch: page.cdp.send()

For use cases the high-level API doesn't cover — network interception, console log subscriptions, performance tracing, cookie management, PDF generation — you can drop down to raw CDP commands via page.cdp.send():

with chrome.new_page() as page:
    page.navigate("https://example.com")

    # Enable network tracking
    page.cdp.send("Network.enable")

    # Get all cookies
    cookies = page.cdp.send("Network.getAllCookies")

    # Performance tracing
    page.cdp.send("Tracing.start", {"categories": "-*,devtools.timeline"})

    # Register event handler
    page.cdp.on("Network.requestWillBeSent", lambda e: print(e["request"]["url"]))

The Escape Hatch communicates directly with Chrome via WebSocket (port 9222), bypassing the server entirely. It connects lazily on the first send() call and reuses the connection. Each page gets its own WebSocket routed to the correct tab.

Method Description
page.cdp.send(method, params=None, *, timeout=30.0) Send a CDP command and return the result
page.cdp.on(event, handler) Register a callback for a CDP event
page.cdp.close() Close the WebSocket (auto-called when the page context manager exits)

CdpClient (low-level, legacy)

For direct CDP access without the Shared Core server. This is the v1 code path — it works, but does not benefit from server-side improvements. Use page.cdp.send() instead for most Escape Hatch use cases.

from publicbrowser import CdpClient

# Async API
client = await CdpClient.connect(port=9222)
result = await client.send("Runtime.evaluate", {"expression": "1+1"})
await client.close()

# Sync API
client = CdpClient.connect_sync(port=9222)
result = client.send_sync("Runtime.evaluate", {"expression": "1+1"})
client.close_sync()

MCP Coexistence

When the MCP server and Python scripts need to run at the same time, add --script to the MCP config. Chrome.connect() handles the rest — each script works in its own tab, MCP tabs are never touched.

Claude Code:

claude mcp add --scope user public-browser npx -y public-browser@latest -- --script

Cursor / Cline (mcp.json):

{
  "mcpServers": {
    "public-browser": {
      "command": "npx",
      "args": ["-y", "public-browser@latest", "--", "--script"]
    }
  }
}

Legacy: Single-File Alternative

For quick prototyping, you can copy publicbrowser_standalone.py into your project. This uses the v1 code path (direct CDP via WebSocket) and does not benefit from server-side improvements. Use the local publicbrowser package for the full Shared Core experience.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

publicbrowser-1.0.0.tar.gz (16.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

publicbrowser-1.0.0-py3-none-any.whl (20.6 kB view details)

Uploaded Python 3

File details

Details for the file publicbrowser-1.0.0.tar.gz.

File metadata

  • Download URL: publicbrowser-1.0.0.tar.gz
  • Upload date:
  • Size: 16.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.4

File hashes

Hashes for publicbrowser-1.0.0.tar.gz
Algorithm Hash digest
SHA256 fbbded8ff26296337818f7f113343d5d076a3fde9c2b65d0f55cf9d43cf63ad0
MD5 d50105122bf449b016f7d495a5357d80
BLAKE2b-256 68ca03d789a693a781370e538674996f4f13948d6919bf52e702b58a2d1ca92e

See more details on using hashes here.

File details

Details for the file publicbrowser-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: publicbrowser-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 20.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.4

File hashes

Hashes for publicbrowser-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e8a1d08a68d85df9f4149f8a741baff9eee1ca6b6e6db0ee4b9f2ec9ba1f871e
MD5 c658380408053c731711a1bd406b5573
BLAKE2b-256 12a91006801ca5c5afa0e0b66a35248ad23f05b500970cf4eb5f18028d5c21f1

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page