### CLI Hook Pattern - Setup Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-cli.md Instructions for setting up Browser39 to be used with CLI hooks, including starting the watch process. ```APIDOC ## CLI Hook Setup ### Start browser39 in the background ```bash mkdir -p .cache/browser39 touch .cache/browser39/commands.jsonl browser39 watch .cache/browser39/commands.jsonl \ --output .cache/browser39/results.jsonl & BROWSER_PID=$! ``` ``` -------------------------------- ### Installation Source: https://context7.com/alejandroqh/browser39/llms.txt Instructions on how to install browser39 using npm, Cargo, or by downloading a pre-built binary. ```APIDOC ## Installation Install browser39 via npm or cargo for immediate use with any MCP-compatible AI agent. ```bash # Install via npm npm install @aquintanar/browser39 # Or install via Cargo cargo install browser39 # Or download pre-built binary and auto-configure for AI clients curl -fsSL https://raw.githubusercontent.com/alejandroqh/marketplace/main/h39.sh | sh ``` ``` -------------------------------- ### Install browser39 Plugin via Quick Install Prompt Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-openclaw.md Use this prompt in OpenClaw to automatically install the browser39 plugin. It checks for the binary, downloads it if necessary, installs the plugin, restarts the gateway, and verifies the installation. ```bash Install the browser39 plugin for web browsing. It's a headless browser that converts pages to token-optimized markdown. Steps: 1. Check if browser39 binary exists: which browser39 2. If not found, download it from https://github.com/alejandroqh/browser39/releases/latest/download/ — assets are named browser39-{os}-{arch} (macos-arm64, macos-x64, linux-arm64, linux-x64, windows-x64.exe). Save to ~/.local/bin/browser39 and make it executable. 3. Install the plugin: openclaw plugins install https://github.com/alejandroqh/browser39.git 4. Restart the gateway: openclaw gateway restart 5. Verify: openclaw plugins list ``` -------------------------------- ### Complete Examples Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-cli.md Links to ready-to-use single-file integrations with Browser39. ```APIDOC ## Complete Examples Ready-to-use single-file integrations with `web_search`, `visit_website`, tool dispatch, and LLM tool definitions: - **[Python](../examples/browser39_tools.py)** — zero dependencies, `python3 examples/browser39_tools.py` - **[TypeScript](../examples/browser39_tools.ts)** — zero dependencies, `npx tsx examples/browser39_tools.ts` - **[Rust](../examples/browser39_tools.rs)** — only `serde_json`, copy into your project Each file includes `TOOL_DEFINITIONS` / `tool_definitions()` with the JSON schemas for LLM tool-calling APIs (Anthropic, OpenAI, etc.). ``` -------------------------------- ### Browser39 Config: Full Agent Setup Source: https://github.com/alejandroqh/browser39/blob/main/docs/config.md A comprehensive configuration including start URL, API authentication, session cookies, local storage, and security settings. Sensitive cookies and MCP redaction are configured. ```toml [session] start_url = "https://app.example.com" [auth.app] header = "Authorization" value_env = "APP_JWT" value_prefix = "Bearer " domains = ["app.example.com", "api.example.com"] [[cookies]] name = "session" value_env = "SESSION_ID" domain = "app.example.com" sensitive = true [[storage]] origin = "https://app.example.com" key = "user_prefs" value = '{"lang": "en", "timezone": "UTC"}' [security] sensitive_cookies = ["session", "token", "jwt"] [security.mcp] redact = true ``` -------------------------------- ### Install browser39 Native Plugin Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-openclaw.md Install the browser39 native plugin from a GitHub clone or link it for development. This involves cloning the repository, navigating to the plugin directory, installing npm dependencies, and then installing the plugin. ```bash # From the GitHub repo (native plugin subdirectory) git clone https://github.com/alejandroqh/browser39.git cd browser39/openclaw-plugin npm install openclaw plugins install . # Or link for development openclaw plugins install -l . ``` -------------------------------- ### Install browser39 via Claude Prompt Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-claude.md Copy and paste this prompt into Claude to automatically install browser39 as an MCP server. Ensure the binary is downloaded, saved to ~/.local/bin/browser39, made executable, and added to MCP settings. ```text Install browser39 as an MCP server. Download the binary for this system from https://github.com/alejandroqh/browser39/releases/latest/download/ — assets are named browser39-{os}-{arch} (macos-arm64, macos-x64, linux-arm64, linux-x64, windows-x64.exe). Save to ~/.local/bin/browser39, make it executable, and add it to MCP settings with command "browser39" and args ["mcp"]. ``` -------------------------------- ### Minimal Browser39 Config: Start Page Source: https://github.com/alejandroqh/browser39/blob/main/docs/config.md Use this minimal configuration to set a single start URL for the browser session. ```toml [session] start_url = "https://news.ycombinator.com" ``` -------------------------------- ### Start browser39 watch Source: https://github.com/alejandroqh/browser39/blob/main/docs/watch.skill.md Initiates the browser39 watch process. Ensure the commands file exists before starting. ```bash # Create the commands file (must exist before starting) touch commands.jsonl # Start watching browser39 watch commands.jsonl --output results.jsonl ``` -------------------------------- ### Install browser39 Plugin (Claude Bundle) Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-openclaw.md Install the browser39 plugin as a Claude bundle from the GitHub repository or a local clone. This method is recommended for its simplicity. ```bash # From the GitHub repo openclaw plugins install https://github.com/alejandroqh/browser39.git # Or from a local clone git clone https://github.com/alejandroqh/browser39.git openclaw plugins install ./browser39 ``` -------------------------------- ### Config Action Example Source: https://github.com/alejandroqh/browser39/blob/main/docs/watch.skill.md Demonstrates setting run-level options, such as `step_delay`, using the 'config' action. The delay can be a fixed number of seconds or a range. ```json {"id":"cfg","action":"config","v":1,"seq":0,"step_delay":0.5} ``` ```json {"step_delay": [0.5, 2.0]} ``` -------------------------------- ### Install browser39 with npm Source: https://github.com/alejandroqh/browser39/blob/main/README.md Install the browser39 package using npm. This is the primary method for Node.js environments. ```bash npm install @aquintanar/browser39 ``` -------------------------------- ### Install browser39 for AI CLI/IDE Source: https://github.com/alejandroqh/browser39/blob/main/README.md Automates the installation of the browser39 binary and its configuration for various AI CLI and IDE clients like Claude Code, Codex, and OpenClaw. ```bash curl -fsSL https://raw.githubusercontent.com/alejandroqh/marketplace/main/h39.sh | sh ``` -------------------------------- ### JSONL Protocol Command Example Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-cli.md An example of a command object conforming to the JSONL protocol. This specific example is for fetching a URL. ```json {"id": "unique", "action": "fetch", "v": 1, "seq": 1, "url": "https://..."} ``` -------------------------------- ### MCP Configuration Source: https://context7.com/alejandroqh/browser39/llms.txt Configuration example for setting up browser39 as an MCP server for AI clients like Claude Desktop. ```APIDOC ## MCP Configuration Configure browser39 as an MCP server for Claude Desktop, Claude Code, or any MCP client. ```json { "mcpServers": { "browser39": { "command": "browser39", "args": ["mcp"] } } } ``` ``` -------------------------------- ### Install browser39 via npm or Cargo Source: https://context7.com/alejandroqh/browser39/llms.txt Install browser39 using npm or Cargo. Alternatively, download a pre-built binary. ```bash # Install via npm npm install @aquintanar/browser39 ``` ```bash # Or install via Cargo cargo install browser39 ``` ```bash # Or download pre-built binary and auto-configure for AI clients curl -fsSL https://raw.githubusercontent.com/alejandroqh/marketplace/main/h39.sh | sh ``` -------------------------------- ### Run MCP Server Source: https://github.com/alejandroqh/browser39/blob/main/docs/mcp.skill.md Starts the MCP server, either over stdio or Streamable HTTP. ```bash # MCP server over stdio browser39 mcp ``` ```bash # MCP server over Streamable HTTP browser39 mcp --transport sse --port 8039 ``` -------------------------------- ### Example commands.jsonl Content Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md A sample `commands.jsonl` file demonstrating various actions like fetching URLs, retrieving links, querying the DOM, and navigating history. ```jsonl # commands.jsonl {"id":"a","action":"fetch","v":1,"seq":1,"url":"https://news.ycombinator.com","options":{"max_tokens":2000}} {"id":"b","action":"links","v":1,"seq":2} {"id":"c","action":"fetch","v":1,"seq":3,"index":5} {"id":"d","action":"dom_query","v":1,"seq":4,"selector":"h1","attr":"textContent"} {"id":"e","action":"back","v":1,"seq":5} {"id":"f","action":"info","v":1,"seq":6} {"id":"g","action":"quit","v":1,"seq":7} ``` -------------------------------- ### Verify browser39 Plugin Installation Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-openclaw.md Verify the installation of the browser39 plugin by listing and inspecting the installed plugins. The output should indicate 'Format: bundle' with subtype 'claude'. ```bash openclaw plugins list openclaw plugins inspect browser39 ``` -------------------------------- ### Manual POST Fetch Example Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Example of performing a manual POST request for login or API calls using the 'fetch' action. Requires specifying the URL, method, headers, and a JSON string for the body. ```json { "action": "fetch", "url": "https://api.example.com/login", "method": "POST", "headers": {"Content-Type": "application/json"}, "body": "{\"username\":\"agent\",\"password\":\"secret\"}" } ``` -------------------------------- ### Install browser39 with Cargo Source: https://github.com/alejandroqh/browser39/blob/main/README.md Install the browser39 binary directly using Cargo, the Rust package manager. This is suitable for Rust projects or system-wide installation. ```bash cargo install browser39 ``` -------------------------------- ### Preloading Credentials Configuration Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Configuration example for preloading credentials such as session tokens and API keys via environment variables for secure startup. ```toml [session] start_url = "https://app.example.com/dashboard" [auth.app] header = "Authorization" value_env = "APP_JWT" value_prefix = "Bearer " domains = ["app.example.com"] [[cookies]] name = "session" value_env = "SESSION_TOKEN" domain = "app.example.com" sensitive = true [[storage]] origin = "https://app.example.com" key = "api_token" value_env = "API_TOKEN" sensitive = true ``` -------------------------------- ### TypeScript Usage Example Source: https://context7.com/alejandroqh/browser39/llms.txt Demonstrates how to use the webSearch and visitWebsite functions. Logs the results to the console. ```typescript // Usage const results = await webSearch("typescript tutorial"); console.log(results); const page = await visitWebsite("https://example.com", "article"); console.log(page); ``` -------------------------------- ### Full browser39 Configuration Example Source: https://github.com/alejandroqh/browser39/blob/main/docs/config.md This TOML file demonstrates a comprehensive configuration for browser39, covering session defaults, multiple authentication profiles, preloaded cookies and local storage, default headers, and detailed security redaction patterns. ```toml # ~/.config/browser39/config.toml # ─── Session Defaults ─────────────────────────────────────────────── [session] # Page to load automatically on startup (before any agent commands) start_url = "https://dashboard.example.com" # Session persistence: "disk" (default) or "memory" persistence = "disk" # session_path = "/custom/path/session.enc" # override default location # HTTP client defaults user_agent = "browser39/0.1" timeout_secs = 30 max_redirects = 10 # Default fetch options (can be overridden per-request) [session.defaults] max_tokens = 8000 strip_nav = true include_links = true include_images = false # ─── Auth Profiles ────────────────────────────────────────────────── # Credentials stored outside the LLM conversation. # Agent references by name: {"auth_profile": "github"} # browser39 resolves and attaches the header. LLM never sees the value. [auth.github] header = "Authorization" value = "Bearer ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" domains = ["api.github.com", "github.com"] [auth.openai] header = "Authorization" value_env = "OPENAI_API_KEY" # read from environment variable value_prefix = "Bearer " # prepended to env value domains = ["api.openai.com"] [auth.internal] header = "X-API-Key" value_env = "INTERNAL_API_KEY" domains = ["internal.company.com", "*.internal.company.com"] [auth.ci] header = "Authorization" value_env = "CI_TOKEN" value_prefix = "Bearer " domains = ["ci.internal.com"] # ─── Preloaded Cookies ────────────────────────────────────────────── # Injected into the cookie jar before the first request. # Use for pre-authenticated sessions — agent starts already logged in. [[cookies]] name = "session" value_env = "SESSION_TOKEN" # load from env (recommended) domain = "app.example.com" path = "/" secure = true http_only = true sensitive = true # redacted in MCP responses [[cookies]] name = "csrf_token" value_env = "CSRF_TOKEN" domain = "app.example.com" path = "/" sensitive = true [[cookies]] name = "lang" value = "en" # inline value (non-sensitive) domain = "app.example.com" # ─── Preloaded LocalStorage ───────────────────────────────────────── # Injected into the in-memory storage before the first request. # Useful for tokens, preferences, feature flags. [[storage]] origin = "https://app.example.com" key = "api_token" value_env = "APP_API_TOKEN" sensitive = true # redacted in MCP responses [[storage]] origin = "https://app.example.com" key = "theme" value = "dark" [[storage]] origin = "https://app.example.com" key = "feature_flags" value = '{"beta_ui": true, "new_api": false}' # ─── Default Headers ──────────────────────────────────────────────── # Sent with every request to matching domains. # Merged with per-request headers (per-request wins on conflict). [[headers]] domains = ["api.example.com", "*.api.example.com"] values = { "Accept" = "application/json", "X-Client" = "browser39" } [[headers]] domains = ["internal.company.com"] values = { "X-Request-Source" = "agent" } # ─── Security & Redaction ─────────────────────────────────────────── [security] # Cookie values to always redact (matched by name, case-insensitive) sensitive_cookies = ["session", "sid", "token", "jwt", "auth", "csrf", "csrf_token"] # Headers to never include in results sensitive_headers = ["authorization", "x-api-key", "cookie", "set-cookie"] # Regex patterns — matched values are auto-redacted and replaced with handles [security.patterns] jwt = 'eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}' github_pat = 'ghp_[A-Za-z0-9]{36}' github_fine = 'github_pat_[A-Za-z0-9_]{82}' openai_key = 'sk-[A-Za-z0-9]{32,}' anthropic_key = 'sk-ant-[A-Za-z0-9-]{32,}' slack_bot = 'xoxb-[A-Za-z0-9-]+' slack_user = 'xoxp-[A-Za-z0-9-]+' stripe_key = 'sk_live_[A-Za-z0-9]{24,}' aws_key = 'AKIA[A-Z0-9]{16}' # Transport-specific redaction behavior [security.mcp] redact = true # always on for MCP (cannot be disabled) [security.jsonl] redact = false # off by default for JSONL agents ``` -------------------------------- ### Browser39 Tool Naming Conventions Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-openclaw.md Illustrates the naming difference between bundle-installed and natively installed Browser39 tools. ```bash browser39__browser39_fetch ``` ```bash browser39_fetch ``` -------------------------------- ### Example Usage of Web Search and Visit Website Source: https://context7.com/alejandroqh/browser39/llms.txt Demonstrates how to use the web_search function to find information and visit_website to retrieve content from a URL. Prints the search results and the content of the visited page. ```python # Usage results = web_search("python asyncio") for r in results: print(f"{r['title']} | {r['url']}") page = visit_website("https://example.com") print(page) ``` -------------------------------- ### Start browser39 Watch Process Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-cli.md Initializes the browser39 watch process in the background, creating necessary cache directories and command/result log files. ```bash mkdir -p .cache/browser39 touch .cache/browser39/commands.jsonl browser39 watch .cache/browser39/commands.jsonl \ --output .cache/browser39/results.jsonl & BROWSER_PID=$! ``` -------------------------------- ### Browser39 MCP Server Startup Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Commands to start the Browser39 MCP server for AI agent communication. Supports stdio transport locally and HTTP+SSE transport for remote connections. ```bash browser39 mcp # stdio transport (local) ``` ```bash browser39 mcp --transport sse --port 8039 # HTTP+SSE transport (remote) ``` -------------------------------- ### JSONL Protocol Result Example Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-cli.md An example of a result object returned by the browser39 process. It mirrors the command structure and includes additional details about the fetched content. ```json {"id": "unique", "ok": true, "seq": 1, "url": "...", "title": "...", "markdown": "...", "links": [...]} ``` -------------------------------- ### Append Commands to commands.jsonl Source: https://github.com/alejandroqh/browser39/blob/main/docs/watch.skill.md Examples of appending various commands to the commands.jsonl file for real-time processing by browser39 watch. ```bash # Fetch a page echo '{"id":"1","action":"fetch","v":1,"seq":1,"url":"https://example.com"}' >> commands.jsonl # List links echo '{"id":"2","action":"links","v":1,"seq":2}' >> commands.jsonl # Follow a link by index echo '{"id":"3","action":"fetch","v":1,"seq":3,"index":0}' >> commands.jsonl # Navigate back echo '{"id":"4","action":"back","v":1,"seq":4}' >> commands.jsonl # Session info / heartbeat echo '{"id":"5","action":"info","v":1,"seq":5}' >> commands.jsonl # Shut down echo '{"id":"6","action":"quit","v":1,"seq":6}' >> commands.jsonl ``` -------------------------------- ### Example results.jsonl Content Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md A sample `results.jsonl` file showing the corresponding results for the commands in `commands.jsonl`, including success status, extracted data, and performance metrics. ```jsonl # results.jsonl {"id":"a","ok":true,"seq":1,"url":"https://news.ycombinator.com","title":"Hacker News","status":200,"markdown":"# Hacker News\n...","links":[{"i":0,"text":"Show HN: ...","href":"..."}],"meta":{...},"stats":{"fetch_ms":450,"tokens_est":1800,"content_bytes":45000},"truncated":false,"next_offset":null} {"id":"b","ok":true,"seq":2,"links":[{"i":0,"text":"Show HN: ...","href":"..."},...],"count":30} {"id":"c","ok":true,"seq":3,"url":"https://example.com/show-hn","title":"Show HN: ...","status":200,"markdown":"...","links":[...],"meta":{...},"stats":{...},"truncated":false,"next_offset":null} {"id":"d","ok":true,"seq":4,"results":["Show HN: browser39"],"count":1,"exec_ms":2} {"id":"e","ok":true,"seq":5,"url":"https://news.ycombinator.com","title":"Hacker News","status":200,"markdown":"...","links":[...],"meta":{...},"stats":{...},"truncated":false,"next_offset":null} {"id":"f","ok":true,"seq":6,"alive":true,"current_url":"https://news.ycombinator.com","title":"Hacker News","history_length":2,"history_index":0,"cookies_count":3,"uptime_secs":12} {"id":"g","ok":true,"seq":7} ``` -------------------------------- ### Restart OpenClaw Gateway Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-openclaw.md Restart the OpenClaw gateway after installing the browser39 plugin to make the tools available. ```bash openclaw gateway restart ``` -------------------------------- ### Secure Login Flow Example Source: https://context7.com/alejandroqh/browser39/llms.txt Demonstrates a secure login process where sensitive data like passwords and session tokens are never exposed to the LLM. Sensitive fields are marked and automatically redacted. ```bash # 1. Fetch login page browser39_fetch url="https://app.com/login" # Returns: Login form HTML as markdown ``` ```bash # 2. Fill credentials (password marked sensitive - never echoed back) browser39_fill fields='[{"selector": "#user", "value": "agent"}, {"selector": "#pass", "value": "secret123", "sensitive": true}]' # Returns: {"ok": true, "filled": 2} ``` ```bash # 3. Submit form browser39_submit selector="form#login" # Server responds with Set-Cookie: session=abc123 # browser39 stores cookie, redacts in response # Returns: Dashboard page markdown (LLM never sees session token) ``` ```bash # 4. Make authenticated requests - cookies sent automatically browser39_fetch url="https://app.com/api/data" # Cookie jar sends session cookie automatically # LLM never saw the session token ``` ```bash # 5. Check cookies (values redacted) browser39_cookies # Returns: {"cookies": [{"name": "session", "value": "••••••", "handle": "${browser39_secret_1}"}]} ``` -------------------------------- ### Python Example for browser39 watch Source: https://github.com/alejandroqh/browser39/blob/main/docs/watch.skill.md A Python script demonstrating how to send commands (fetch, links, quit) to browser39 watch and read the results. It includes helper functions for sending commands and reading results. ```python import json import time COMMANDS = "commands.jsonl" RESULTS = "results.jsonl" def send(action, seq, **fields): cmd = {"id": f"cmd-{seq}", "action": action, "v": 1, "seq": seq, **fields} with open(COMMANDS, "a") as f: f.write(json.dumps(cmd) + "\n") f.flush() def read_results(): with open(RESULTS) as f: return [json.loads(line) for line in f if line.strip()] # Fetch a page send("fetch", 1, url="https://example.com") time.sleep(1) # Get links send("links", 2) time.sleep(0.5) # Done send("quit", 3) time.sleep(0.5) for r in read_results(): print(f"seq={r['seq']} ok={r['ok']}") ``` -------------------------------- ### Browser39 Development Commands Source: https://github.com/alejandroqh/browser39/blob/main/README.md Standard Cargo commands for building, running, testing, linting, and formatting the Browser39 project. Ensure you have Rust and Cargo installed. ```bash cargo build # Build ``` ```bash cargo run # Run ``` ```bash cargo test # Run all tests ``` ```bash cargo clippy # Lint ``` ```bash cargo fmt # Format ``` -------------------------------- ### Start Browser39 Watch Mode Source: https://context7.com/alejandroqh/browser39/llms.txt Initiates browser39 in watch mode for JSONL-based IPC. Requires creating a commands file and specifying an output file for results. ```bash # Start watch mode touch commands.jsonl browser39 watch commands.jsonl --output results.jsonl ``` -------------------------------- ### Browser39 Config: Pre-authenticated Browser Session Source: https://github.com/alejandroqh/browser39/blob/main/docs/config.md Set up a browser session with a start URL and pre-authenticated cookies. The SESSION_COOKIE environment variable must be set. ```toml [session] start_url = "https://app.example.com/dashboard" [[cookies]] name = "session" value_env = "SESSION_COOKIE" domain = "app.example.com" secure = true http_only = true sensitive = true ``` -------------------------------- ### Secure Login Flow Example (MCP) Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Illustrates a secure login process for an LLM agent (MCP) where sensitive information like passwords and session tokens are handled via secret handles and never directly exposed to the LLM. ```text 1. LLM calls: browser39_fetch(url: "https://app.com/login") → browser39 returns login page markdown (form visible) 2. LLM calls: browser39_fill(fields: [ {selector: "#user", value: "agent"}, {selector: "#pass", value: "secret123", sensitive: true} ]) → browser39 stores password as browser39_secret_1, returns {"filled": 2} → LLM provided the password, but it's never echoed back 3. LLM calls: browser39_submit(selector: "form#login") → Server responds with Set-Cookie: session=abc123 → browser39 stores cookie, redacts in response → Returns page markdown (dashboard) 4. LLM calls: browser39_fetch(url: "https://app.com/api/data") → Cookie jar sends session cookie automatically → LLM never saw the session token 5. LLM calls: browser39_cookies() → {"cookies": [{"name": "session", "value": "••••••", "handle": "${browser39_secret_2}"}]} ``` -------------------------------- ### Start browser39 with HTTP Transport Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-claude.md Run browser39 with the HTTP transport enabled, specifying the Server-Sent Events (SSE) transport and a port. This is for remote agent connections. ```bash browser39 mcp --transport sse --port 8039 ``` -------------------------------- ### Example Agent Login and Token Usage Flow Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Demonstrates an agent logging in, receiving a JWT redacted as a secret handle, and then using that handle in a subsequent request. browser39 resolves the handle before executing the action. ```json {"action": "fetch", "url": "https://api.example.com/login", "method": "POST", "headers": {"Content-Type": "application/json"}, "body": "{\"user\":\"agent\",\"pass\":\"secret\"}"} ``` ```json {"ok": true, "markdown": "Login successful.\n\nToken: ${browser39_secret_1}", ...} ``` ```json {"action": "fetch", "url": "https://api.example.com/data", "headers": {"Authorization": "Bearer ${browser39_secret_1}"}} ``` -------------------------------- ### Browser39 TOML Configuration Source: https://context7.com/alejandroqh/browser39/llms.txt Example TOML configuration file for Browser39, specifying session settings, default options, authentication profiles, preloaded cookies, storage, and headers. ```toml # ~/.config/browser39/config.toml [session] start_url = "https://app.example.com/dashboard" user_agent = "browser39/1.6" timeout_secs = 30 max_redirects = 10 persistence = "disk" # or "memory" [session.defaults] max_tokens = 8000 strip_nav = true include_links = true include_images = false # Auth profiles - credentials stored securely, never returned via MCP [auth.github] header = "Authorization" value_env = "GITHUB_TOKEN" # Read from environment variable value_prefix = "Bearer " domains = ["api.github.com", "github.com"] [auth.internal] header = "X-API-Key" value = "secret-key-here" # Inline value domains = ["internal.company.com", "*.internal.company.com"] # Preloaded cookies - injected before first request [[cookies]] name = "session" value_env = "SESSION_TOKEN" domain = "app.example.com" path = "/" secure = true http_only = true sensitive = true # Redacted in MCP responses # Preloaded localStorage [[storage]] origin = "https://app.example.com" key = "api_token" value_env = "API_TOKEN" sensitive = true [[storage]] origin = "https://app.example.com" key = "theme" value = "dark" # Default headers for matching domains [[headers]] domains = ["api.example.com", "*.api.example.com"] values = { "Accept" = "application/json", "X-Client" = "browser39" } ``` -------------------------------- ### Batch Mode Processing Source: https://context7.com/alejandroqh/browser39/llms.txt Example of using Browser39 in batch mode to process a file of JSONL commands. This is useful for automating a sequence of actions and collecting results. ```bash # Create commands file cat > commands.jsonl << 'EOF' {"id":"1","action":"fetch","v":1,"seq":1,"url":"https://example.com"} {"id":"2","action":"links","v":1,"seq":2} {"id":"3","action":"quit","v":1,"seq":3} EOF ``` ```bash # Process all commands browser39 batch commands.jsonl --output results.jsonl # Results written to results.jsonl ``` -------------------------------- ### DOM Query Examples Source: https://github.com/alejandroqh/browser39/blob/main/README.md Execute JavaScript queries against the Document Object Model (DOM) to retrieve information or modify elements. Ensure the DOM is loaded before executing these. ```json {"action": "dom_query", "script": "document.querySelectorAll('a').length"} ``` ```json {"action": "dom_query", "script": "document.getElementById('content').closest('section').textContent"} ``` ```json {"action": "dom_query", "script": "document.querySelector('h1').setAttribute('class', 'modified')"} ``` -------------------------------- ### Build browser39 Release Binary Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-cli.md Clone the repository, navigate to the directory, build the release binary, and copy it to your agent's tools directory. Ensure you replace `/path/to/your-agent/tools/browser39` with the actual path. ```bash git clone https://github.com/alejandroqh/browser39.git cd browser39 cargo build --release cp target/release/browser39 /path/to/your-agent/tools/browser39 ``` -------------------------------- ### Build browser39 from Source Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-claude.md Clone the browser39 repository, build the release binary using Cargo, and copy the executable to `/usr/local/bin/`. ```bash git clone https://github.com/alejandroqh/browser39.git cd browser39 cargo build --release cp target/release/browser39 /usr/local/bin/ ``` -------------------------------- ### Redacted Page Content Example Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Shows an example of page content where a secret handle is used to represent a sensitive value, such as an API key. ```json {"markdown": "Your API key is: ${browser39_secret_4}\n\nUse it in the Authorization header."} ``` -------------------------------- ### Show All Configuration Source: https://context7.com/alejandroqh/browser39/llms.txt Display the current browser39 configuration settings. Sensitive values are masked, and the raw config file is not exposed. ```bash browser39_config_show ``` -------------------------------- ### Run Browser39 with Configuration Source: https://github.com/alejandroqh/browser39/blob/main/README.md Use the --config flag to specify a custom configuration file for Browser39. This flag takes precedence over environment variables and default configuration file locations. ```bash browser39 --config path/to/config.toml fetch https://example.com ``` -------------------------------- ### Get Browser39 Session Info Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Retrieves session state and confirms liveness. This action requires no parameters. ```javascript browser39_info(): Promise ``` -------------------------------- ### BrowserClient Initialization and Paths Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-cli.md Initializes the BrowserClient with paths to cache directory and binary. It sets up the necessary file paths for commands and results. ```rust use std::fs::{self, File, OpenOptions}; use std::io::{BufRead, BufReader, Write}; use std::path::PathBuf; use std::process::{Child, Command, Stdio}; use std::sync::{LazyLock, Mutex}; use std::time::{Duration, Instant}; use serde_json::Value; static CLIENT: LazyLock> = LazyLock::new(|| { Mutex::new(BrowserClient::new()) }); struct BrowserClient { process: Option, seq: u64, dir: PathBuf, bin: PathBuf, } impl BrowserClient { fn new() -> Self { let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); let dir = cwd.join(".cache/browser39"); let bin = cwd.join("tools/browser39"); Self { process: None, seq: 0, dir, bin } } fn commands_path(&self) -> PathBuf { self.dir.join("commands.jsonl") } fn results_path(&self) -> PathBuf { self.dir.join("results.jsonl") } ``` -------------------------------- ### Local Storage API Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md APIs for interacting with the browser's localStorage, including getting, setting, deleting, and listing entries. ```APIDOC ## GET /api/storage_get ### Description Gets a localStorage value for the current page's origin. ### Method GET ### Endpoint /api/storage_get ### Parameters #### Query Parameters - **key** (string) - Required - The key of the localStorage item. - **origin** (string) - Optional - Overrides the current page's origin. Format: `scheme://host:port`. ### Response #### Success Response (200) - **ok** (boolean) - Indicates success. - **key** (string) - The key of the localStorage item. - **value** (string) - The value of the localStorage item, or `null` if the key is not found. #### Response Example ```json {"ok": true, "key": "user_pref", "value": "dark_mode"} ``` ### Errors - `NO_PAGE`: No page loaded and no `origin` specified. ``` ```APIDOC ## POST /api/storage_set ### Description Sets a localStorage value for the current page's origin. ### Method POST ### Endpoint /api/storage_set ### Parameters #### Request Body - **key** (string) - Required - The key of the localStorage item. - **value** (string) - Required - The value to set for the localStorage item. - **origin** (string) - Optional - Overrides the current page's origin. - **sensitive** (boolean) - Optional - If true, the value is stored but redacted in outputs. ### Response #### Success Response (200) - **ok** (boolean) - Indicates success. #### Response Example ```json {"ok": true} ``` ``` ```APIDOC ## DELETE /api/storage_delete ### Description Deletes a localStorage key for the current page's origin. ### Method DELETE ### Endpoint /api/storage_delete ### Parameters #### Request Body - **key** (string) - Required - The key of the localStorage item to delete. - **origin** (string) - Optional - Overrides the current page's origin. ### Response #### Success Response (200) - **ok** (boolean) - Indicates success. - **deleted** (boolean) - True if the key was deleted, false if it did not exist. #### Response Example ```json {"ok": true, "deleted": true} ``` ``` ```APIDOC ## GET /api/storage_list ### Description Lists all localStorage entries for the current page's origin. ### Method GET ### Endpoint /api/storage_list ### Parameters #### Query Parameters - **origin** (string) - Optional - Overrides the current page's origin. ### Response #### Success Response (200) - **ok** (boolean) - Indicates success. - **origin** (string) - The origin for which the entries are listed. - **entries** (object) - An object containing key-value pairs of localStorage entries. - **count** (integer) - The number of entries listed. #### Response Example ```json {"ok": true, "origin": "https://example.com", "entries": {"user_pref": "dark_mode", "token": "abc"}, "count": 2} ``` ``` ```APIDOC ## DELETE /api/storage_clear ### Description Clears all localStorage entries for the current page's origin. ### Method DELETE ### Endpoint /api/storage_clear ### Parameters #### Request Body - **origin** (string) - Optional - Overrides the current page's origin. ### Response #### Success Response (200) - **ok** (boolean) - Indicates success. - **cleared** (integer) - The number of entries cleared. #### Response Example ```json {"ok": true, "cleared": 5} ``` ``` -------------------------------- ### browser39 LocalStorage Tools Source: https://github.com/alejandroqh/browser39/blob/main/docs/mcp.skill.md Tools for managing localStorage data, including getting, setting, deleting, listing, and clearing entries. ```APIDOC ## LocalStorage Tools ### `browser39_storage_get` Get a localStorage value. ### `browser39_storage_set` Set a localStorage value. ### `browser39_storage_delete` Delete a localStorage key. ### `browser39_storage_list` List localStorage entries for an origin. ### `browser39_storage_clear` Clear localStorage for an origin. ``` -------------------------------- ### Get Local Storage Result Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Represents the result of retrieving a localStorage value, including the key and its associated value (or null if not found). ```json {"ok": true, "key": "user_pref", "value": "dark_mode"} ``` -------------------------------- ### Basic CLI page fetch Source: https://context7.com/alejandroqh/browser39/llms.txt Perform a one-shot page fetch from the command line, returning token-optimized Markdown. Supports JSON output and custom configuration files. ```bash # Basic fetch returning markdown browser39 fetch https://example.com ``` ```bash # Fetch with JSON output (compact link references) browser39 fetch --output json https://example.com ``` ```bash # Custom config file browser39 --config path/to/config.toml fetch https://example.com ``` -------------------------------- ### Redacted Storage Output Example Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Demonstrates how sensitive values stored in storage are redacted in the output, displaying a masked value and its associated secret handle. ```json {"ok": true, "key": "token", "value": "••••••", "handle": "${browser39_secret_5}"} ``` -------------------------------- ### Configure browser39 for Claude Desktop (macOS/Windows/Linux) Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-claude.md Add this JSON configuration to your `claude_desktop_config.json` file to register browser39 as an MCP server. If the binary is not in your PATH, specify the full command path. ```json { "mcpServers": { "browser39": { "command": "browser39", "args": ["mcp"] } } } ``` ```json { "mcpServers": { "browser39": { "command": "/usr/local/bin/browser39", "args": ["mcp"] } } } ``` -------------------------------- ### Redacted Cookie Output Example Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Illustrates how cookie values are redacted in the output when using secret handles, showing a masked value and the corresponding handle. ```json {"cookies": [ {"name": "session", "value": "••••••", "domain": "example.com", "handle": "${browser39_secret_3}"} ]} ``` -------------------------------- ### DOM Query Error Result Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md An example of an error response from a DOM query operation, indicating failure with an error message and a specific error code. ```json {"ok": false, "error": "ReferenceError: foo is not defined", "code": "DOM_QUERY_ERROR"} ``` -------------------------------- ### Fetch a webpage using browser39 CLI Source: https://github.com/alejandroqh/browser39/blob/main/README.md Perform a one-shot fetch of a webpage using the browser39 command-line interface. The output is token-efficient Markdown. ```bash browser39 fetch https://example.com ``` -------------------------------- ### Get Session State Information Source: https://context7.com/alejandroqh/browser39/llms.txt Retrieve current session state, including URL, title, history length, and uptime. Useful for heartbeat checks. ```bash browser39_info ``` -------------------------------- ### Custom Config Fetch Source: https://github.com/alejandroqh/browser39/blob/main/docs/mcp.skill.md Fetches content using a custom configuration file. ```bash # Custom config browser39 --config fetch ``` -------------------------------- ### Get localStorage Value Source: https://context7.com/alejandroqh/browser39/llms.txt Retrieve a specific value from the browser's localStorage by its key. Specify an origin if not using the current page's origin. ```bash browser39_storage_get key="user_pref" ``` ```bash browser39_storage_get key="token" origin="https://app.example.com" ``` -------------------------------- ### CLI Fetch Source: https://context7.com/alejandroqh/browser39/llms.txt How to perform a one-shot page fetch using the browser39 CLI, with options for Markdown or JSON output and custom configuration files. ```APIDOC ## CLI Fetch Perform a one-shot page fetch from the command line, returning token-optimized Markdown. ```bash # Basic fetch returning markdown browser39 fetch https://example.com # Output: # # Example Domain # This domain is for use in documentation examples without needing permission. # [Learn more](https://iana.org/domains/example) # Fetch with JSON output (compact link references) browser39 fetch --output json https://example.com # Custom config file browser39 --config path/to/config.toml fetch https://example.com ``` ``` -------------------------------- ### Implement visit_website Tool in Rust Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-cli.md Fetches website content using browser39, optionally focusing on a specific CSS selector. Handles initial section discovery or direct content retrieval. ```rust fn visit_website(url: &str, selector: Option<&str>) -> Result { let mut options = serde_json::json!({ "max_tokens": 4000, "strip_nav": true, "include_links": true, }); if let Some(sel) = selector { // Agent chose a section — fetch it directly options["selector"] = serde_json::Value::String(sel.to_string()); options["show_selectors_first"] = serde_json::Value::Bool(false); } // Without a selector, show_selectors_first defaults to true — // browser39 returns the page's content sections so the agent can // choose which to read in the next round. let result = send_command("fetch", serde_json::json!({ "url": url, "options": options, }))?; if result["ok"] != true { return Err(result["error"].as_str().unwrap_or("fetch failed").into()); } Ok(result["markdown"].as_str().unwrap_or("").to_string()) } ``` -------------------------------- ### Show Specific Configuration Section Source: https://context7.com/alejandroqh/browser39/llms.txt Display configuration settings for a specific section, such as 'auth'. Sensitive values are masked. ```bash browser39_config_show section="auth" ``` -------------------------------- ### MCP Resource URIs Source: https://context7.com/alejandroqh/browser39/llms.txt Examples of URIs for accessing current page state, links, metadata, and cookies through MCP resources. These are read-only views updated after navigation. ```bash # Current page as markdown # URI: browser39://page # MIME: text/markdown ``` ```bash # Links on current page # URI: browser39://page/links # MIME: application/json ``` ```bash # Page metadata (lang, description) # URI: browser39://page/meta # MIME: application/json ``` ```bash # Cookies for current domain # URI: browser39://cookies # MIME: application/json ``` -------------------------------- ### DOM Query Script Mode Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Example of querying the DOM using a JavaScript snippet executed by the boa_engine. This mode is more flexible but has limitations compared to CSS selectors. ```json { "script": "document.querySelectorAll('a').length" } ``` -------------------------------- ### DOM Query CSS Selector Mode Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Example of querying the DOM using a CSS selector and specifying an attribute to retrieve. This is the recommended mode for its simplicity and reliability. ```json { "selector": "h1", "attr": "textContent" } ``` -------------------------------- ### Agent integration with browser39 CLI (watch mode) Source: https://github.com/alejandroqh/browser39/blob/main/README.md Run browser39 in watch mode for long-running agent integration. It communicates via JSONL files, allowing any language to interact with it. ```bash touch commands.jsonl browser39 watch commands.jsonl --output results.jsonl ``` -------------------------------- ### Browser39 CLI Modes Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Illustrates the different command-line interface modes for interacting with Browser39, including one-shot fetching, batch file processing, and long-running file watching. ```bash browser39 fetch [--output text|json] # one-shot, no files ``` ```bash browser39 batch [--output f.jsonl] # process file and exit ``` ```bash browser39 watch [--output f.jsonl] # long-running file watcher ``` -------------------------------- ### List Cookies by Domain Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Use to retrieve cookies from the session cookie jar, optionally filtered by a specific domain. Omit the `domain` field to get all cookies. ```json {"domain": "example.com"} ``` -------------------------------- ### browser39_config_show Source: https://github.com/alejandroqh/browser39/blob/main/docs/mcp.skill.md View the current configuration. ```APIDOC ## POST /browser39_config_show ### Description View the current configuration with sensitive values masked. The raw config file is never exposed. ### Method POST ### Endpoint /browser39_config_show ### Parameters #### Request Body - **section** (string) - no - Filter by section: `session`, `search`, `auth`, `cookies`, `storage`, `headers`, `security`. Omit to show all. ``` -------------------------------- ### Get Local Storage Value Source: https://github.com/alejandroqh/browser39/blob/main/docs/jsonl-protocol.md Use to retrieve a value from the current page's localStorage by its key. An optional `origin` override can be specified. Returns `null` if the key is not found. ```json {"key": "user_pref"} ``` ```json {"key": "x", "origin": "https://example.com"} ``` -------------------------------- ### visit_website CLI Hook Script Source: https://github.com/alejandroqh/browser39/blob/main/docs/install-cli.md Bash script to replace the Rust visit_website tool. It constructs fetch options based on whether a selector is provided, sends the command to browser39, and polls for the result. ```bash #!/bin/bash # hooks/visit_website.sh — replace visit_website tool URL="$1" SEQ="$2" SELECTOR="${3:-}" # optional if [ -n "$SELECTOR" ]; then OPTIONS="{\"max_tokens\":4000,\"strip_nav\":true,\"include_links\":true,\"selector\":\"$SELECTOR\",\"show_selectors_first\":false}" else OPTIONS="{\"max_tokens\":4000,\"strip_nav\":true,\"include_links\":true}" fi echo "{\"id\":\"v-$SEQ\",\"action\":\"fetch\",\"v\":1,\"seq\":$SEQ,\"url\":\"$URL\",\"options\":$OPTIONS}" >> .cache/browser39/commands.jsonl for i in $(seq 1 50); do RESULT=$(tail -2 .cache/browser39/results.jsonl 2>/dev/null | grep "\"seq\":$SEQ") if [ -n "$RESULT" ]; then echo "$RESULT" exit 0 fi sleep 0.1 done echo '{"ok":false,"error":"timeout"}' ```