Skip to main content

Browser automation via CLI — for humans and agents

Project description

webctl

Browser automation for AI agents and humans, built on the command line.

# 1. Install
pip install webctl

# 2. Auto-configure your agent (creates skills/prompts for all supported agents)
webctl init

# 3. Start browsing
webctl start
webctl navigate "https://google.com"
webctl snapshot --interactive-only

webctl init automatically generates the skills and prompts your agents need to drive the browser.

Why CLI Instead of MCP?

MCP browser tools have a fundamental problem: the server controls what enters your context. With Playwright MCP, every response includes the full accessibility tree plus console messages. After a few page queries, your context window is full.

CLI flips this around: you control what enters context.

# Filter before context
webctl snapshot --interactive-only --limit 30      # Only buttons, links, inputs
webctl snapshot --within "role=main"               # Skip nav, footer, ads

# Pipe through Unix tools
webctl snapshot | grep -i "submit"                 # Find specific elements
webctl --format jsonl snapshot | jq '.data.role'   # Extract with jq

Beyond filtering, CLI gives you:

Capability CLI MCP
Filter output Built-in flags + grep/jq/head Server decides
Debug Run same command as agent Opaque
Cache & Cost webctl snapshot > cache.txt Every call hits server
Script Save to .sh, version control Ephemeral
Human takeover Same commands Different interface

Agent Integration

Step 1: Install

pip install webctl
webctl setup  # Downloads Chromium

Step 2: Generate Skills/Prompts

webctl init              # Project-level (recommended)
webctl init --global     # Global (works across all projects)

This creates:

  • Skills for Claude Code and Goose (loaded on-demand when doing web tasks)
  • Lean prompts for Gemini, Copilot, and Codex (always in context)

Supported agents:

Agent Format Location (project) Location (global)
claude Skill .claude/skills/webctl/SKILL.md ~/.claude/skills/webctl/SKILL.md
goose Skill .agents/skills/webctl/SKILL.md ~/.config/agents/skills/webctl/SKILL.md
gemini Prompt GEMINI.md ~/.gemini/GEMINI.md
copilot Prompt .github/copilot-instructions.md -
codex Prompt AGENTS.md ~/.codex/AGENTS.md
claude-noskill Prompt CLAUDE.md (legacy) ~/.claude/CLAUDE.md

Why skills? Skills are loaded on-demand - your agent only reads webctl instructions when actually doing web automation. This keeps your context clean for other tasks.

Select specific agents:

webctl init --agents claude,gemini    # Only Claude and Gemini
webctl init --agents claude-noskill   # Legacy CLAUDE.md format

Step 3: Add to Config (optional)

If your agent doesn't auto-detect the generated files, add this to your system prompt:

For web browsing, use webctl CLI. Run webctl agent-prompt for instructions.

Note: If a browser MCP is already configured, disable it to avoid conflicts.


Quick Start (Human Usage)

Verify the installation works by driving it yourself:

webctl start                    # Opens visible browser window
webctl navigate "https://example.com"
webctl snapshot --interactive-only
webctl stop --daemon            # Closes browser and daemon
Global installation with `uv`
uv tool install webctl
uv tool run webctl
Linux system dependencies
playwright install-deps chromium
# Or manually install libraries listed in Playwright documentation

Core Concepts

Sessions

Browser stays open across commands. Cookies persist to disk.

webctl start                    # Visible browser
webctl start --mode unattended  # Headless (invisible)
webctl -s work start            # Named profile (separate cookies)

Element Queries

Semantic targeting based on ARIA roles - stable across CSS refactors:

role=button                     # Any button
role=button name="Submit"       # Exact match
role=button name~="Submit"      # Contains text (preferred)

Output Control

webctl snapshot                                    # Human-readable
webctl --quiet navigate "..."                      # Suppress events
webctl --result-only --format jsonl navigate "..." # Pure JSON

Commands

Navigation & Observation

webctl navigate "https://..."
webctl back / forward / reload
webctl snapshot --interactive-only        # Buttons, links, inputs only
webctl snapshot --within "role=main"      # Scope to container
webctl query "role=button name~=Submit"   # Debug query
webctl screenshot --path shot.png

Interaction

webctl click 'role=button name~="Submit"'
webctl type 'role=textbox name~="Email"' "user@example.com"
webctl type 'role=textbox name~="Search"' "query" --submit
webctl select 'role=combobox name~="Country"' --label "Germany"
webctl check 'role=checkbox name~="Remember"'
webctl press Enter
webctl scroll down

Wait Conditions

webctl wait network-idle
webctl wait 'exists:role=button name~="Continue"'
webctl wait 'url-contains:"/dashboard"'

Session & Console

webctl status                   # Current state & error counts
webctl save                     # Persist cookies now
webctl console --count          # Just counts by level (LLM-friendly)
webctl console --level error    # Filter to errors only

Architecture

┌─────────────┐  Unix Socket   ┌─────────────┐
│   CLI       │ ◄────────────► │   Daemon    │
│  (webctl)   │   JSON-RPC     │  (browser)  │
└─────────────┘                └─────────────┘
      │                               │
      ▼                               ▼
  Agent/User                   Chromium + Playwright
  • CLI: Stateless, sends commands to daemon
  • Daemon: Manages browser, auto-starts on first command
  • Socket: $WEBCTL_SOCKET_DIR or OS default (see below)
  • Profiles: ~/.local/share/webctl/profiles/

Socket Paths

Platform Default
Linux /run/user/<uid>/webctl-<session>.sock
macOS /tmp/webctl-<session>.sock
Windows %TEMP%\webctl-<session>.sock

Override directory with WEBCTL_SOCKET_DIR environment variable.


Container Deployment

Set WEBCTL_SOCKET_DIR to share the Unix socket between host and container (or between containers).

Daemon in Container, Client on Host

mkdir -p /tmp/webctl-ipc

docker run -d --name webctl-daemon \
  -u $(id -u):$(id -g) \
  -v /tmp/webctl-ipc:/ipc \
  -e WEBCTL_SOCKET_DIR=/ipc \
  my-webctl-image python -m webctl.daemon.server

export WEBCTL_SOCKET_DIR=/tmp/webctl-ipc
webctl start && webctl navigate "https://example.com"

-u $(id -u):$(id -g) ensures the socket file is owned by your host user.

Daemon and Client in Separate Containers

docker volume create webctl-ipc

docker run -d --name webctl-daemon \
  -v webctl-ipc:/ipc \
  -e WEBCTL_SOCKET_DIR=/ipc \
  my-webctl-image python -m webctl.daemon.server

docker run --rm \
  -v webctl-ipc:/ipc \
  -e WEBCTL_SOCKET_DIR=/ipc \
  my-webctl-image webctl navigate "https://example.com"

No UID matching needed - both containers run as the same user.


Security

IPC Authentication

webctl verifies that CLI commands come from the same user as the daemon:

Platform Mechanism Strength
Linux SO_PEERCRED Kernel-enforced UID check
macOS LOCAL_PEERCRED Kernel-enforced UID check
Windows SIO_AF_UNIX_GETPEERPID + process token Kernel-enforced SID check

All platforms use kernel-level credential verification. This prevents other users from controlling your browser session.

Note: Root/Administrator can still access any user's session (OS limitation).


License

MIT

Project details


Download files

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

Source Distribution

webctl-0.3.0.tar.gz (116.9 kB view details)

Uploaded Source

Built Distribution

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

webctl-0.3.0-py3-none-any.whl (89.2 kB view details)

Uploaded Python 3

File details

Details for the file webctl-0.3.0.tar.gz.

File metadata

  • Download URL: webctl-0.3.0.tar.gz
  • Upload date:
  • Size: 116.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for webctl-0.3.0.tar.gz
Algorithm Hash digest
SHA256 60c0a575e901525a1562b96017c12497b1954a240aca3ef21e9ce886b2b70272
MD5 d6ad582c7e1ea67b8d5195674adc12db
BLAKE2b-256 44a2ae6095ff0a7eedd2fa47cc70ab4c8fb0ad182ce0ed9c266b261c5d687cfe

See more details on using hashes here.

Provenance

The following attestation bundles were made for webctl-0.3.0.tar.gz:

Publisher: publish.yml on cosinusalpha/webctl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file webctl-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: webctl-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 89.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for webctl-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 02151347bd32b598296967af007467d35b89736b286f2a77503a6e73401384ba
MD5 933fe5ca18d5ef94f40455534d26a775
BLAKE2b-256 bf170d842a57e2edecab98b6d00429e07042e62147b0c362be64a2551d7f31fc

See more details on using hashes here.

Provenance

The following attestation bundles were made for webctl-0.3.0-py3-none-any.whl:

Publisher: publish.yml on cosinusalpha/webctl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page