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:
- Running server — checks if port 9223 already responds, connects immediately
- PATH binary — finds
public-browserin PATH (e.g. via Homebrew), starts it with--script - npx fallback — runs
npx -y public-browser@latest -- --script - Explicit path —
Chrome.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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbbded8ff26296337818f7f113343d5d076a3fde9c2b65d0f55cf9d43cf63ad0
|
|
| MD5 |
d50105122bf449b016f7d495a5357d80
|
|
| BLAKE2b-256 |
68ca03d789a693a781370e538674996f4f13948d6919bf52e702b58a2d1ca92e
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8a1d08a68d85df9f4149f8a741baff9eee1ca6b6e6db0ee4b9f2ec9ba1f871e
|
|
| MD5 |
c658380408053c731711a1bd406b5573
|
|
| BLAKE2b-256 |
12a91006801ca5c5afa0e0b66a35248ad23f05b500970cf4eb5f18028d5c21f1
|