mac-browser

Control the user's running browser on macOS via AppleScript. Supports Microsoft Edge, Google Chrome, and Safari. Reuses existing sessions, cookies, and login state — no restart or special flags needed. Use this skill whenever the user asks you to read, check, or extract content from their browser — especially for authenticated pages (private GitHub repos, Google Docs, internal tools, Notion, etc.), checking what tabs are currently open, navigating to a URL in their browser, or running JavaScript on a page. Triggers include "read this page in my browser", "what tabs do I have open", "get content from the page I have open", "use my browser session", "access this private URL", "open this in my browser", "check my browser".

mac-browser

Control the user's running browser on macOS via AppleScript. Supports Microsoft Edge, Google Chrome, and Safari. Reuses existing login sessions and cookies — no remote debugging port required.

Prerequisites

  • macOS only
  • A supported browser must be running (Edge, Chrome, or Safari)
  • Enable Allow JavaScript from Apple Events in the browser:
    • Edge: View → Developer → Allow JavaScript from Apple Events
    • Chrome: View → Developer → Allow JavaScript from Apple Events
    • Safari: Develop → Allow JavaScript from Apple Events (enable Develop menu first: Settings → Advanced → Show Develop menu)

Browser Selection

Scripts auto-detect the running browser (priority: Edge → Chrome → Safari). find-tab.sh, eval-js.sh, and get-text.sh search across all running browsers to find the right tab automatically. Override with the MAC_BROWSER env var:

MAC_BROWSER=chrome ./scripts/find-tab.sh "github.com"
MAC_BROWSER=safari ./scripts/get-text.sh "example.com"

Common Workflows

Read content from a tab that's already open:

SKILL_DIR="/path/to/mac-browser"
bash "$SKILL_DIR/scripts/get-text.sh" "docs.google.com"

Open a new URL and read it:

# navigate.sh waits ~1.5s internally for redirects to settle, then returns the actual landed URL
# Output format: <browser> <window> <tab> <url>
RESULT=$(bash "$SKILL_DIR/scripts/navigate.sh" "https://example.com")
OPENED_URL=$(echo "$RESULT" | cut -d' ' -f4-)
sleep 1  # allow additional time for page content to fully load
bash "$SKILL_DIR/scripts/get-text.sh" "$OPENED_URL"

If the tab is not found, navigate first:

# Capture find-tab.sh output; if not found, navigate and use its output instead
FOUND=$(bash "$SKILL_DIR/scripts/find-tab.sh" "example.com") || {
  RESULT=$(bash "$SKILL_DIR/scripts/navigate.sh" "https://example.com")
  sleep 1
  FOUND="$RESULT"
}
bash "$SKILL_DIR/scripts/get-text.sh" "$(echo "$FOUND" | cut -d' ' -f4-)"

Script paths: All ./scripts/ references below are relative to this skill's directory. Always use the absolute path when calling scripts from the terminal (e.g. bash /path/to/mac-browser/scripts/list-tabs.sh).

List All Tabs

./scripts/list-tabs.sh

Lists all open tabs across all running browsers: <browser> <window_num> <tab_num> <url>

Find a Tab

./scripts/find-tab.sh "lists.apache.org"

Returns browser window_num tab_num url for the first tab whose URL matches the pattern. Exits with code 1 if not found.

Evaluate JavaScript

./scripts/eval-js.sh "lists.apache.org" 'document.title'
./scripts/eval-js.sh "github.com" '(() => { return document.querySelectorAll("a").length })()'

Execute JavaScript in the tab matching the URL pattern. Uses a temp file to safely pass arbitrary JS without quoting issues.

IMPORTANT: Code must be a single expression or IIFE for multiple statements.

Get Page Text

./scripts/get-text.sh "lists.apache.org"
./scripts/get-text.sh "lists.apache.org" 5000   # limit to 5000 chars

Convenience wrapper: finds the tab and returns document.body.innerText.

Navigate (Open New Tab)

./scripts/navigate.sh "https://example.com"

Opens a new tab and navigates to the URL. Focus is automatically restored to your current app afterwards.

Output format: <browser> <window> <tab> <url> — same as find-tab.sh. Capture this to get the exact tab coordinates for subsequent calls.

Screenshot

./scripts/screenshot.sh
./scripts/screenshot.sh "/tmp/my-screenshot.png"

Takes a screenshot of the browser window. Returns the file path.

⚠️ IMPORTANT — Before calling screenshot.sh, always use ask_user to confirm with the user:

The browser window needs to be briefly activated (brought to front) to take a screenshot. Focus will be restored to your current app automatically afterwards. Please avoid switching apps during this time — continue?

Only proceed if the user confirms. If they decline, skip the screenshot.

Known Limitations & Workarounds

Google Docs (and other canvas-rendered pages)

get-text.sh uses document.body.innerText, which returns only UI chrome for Google Docs — the document content is rendered as canvas/SVG, not DOM text nodes.

Workaround: Access the mobilebasic version, which serves a simple HTML rendering:

# Replace {DOC_ID} with the document ID from the URL
RESULT=$(bash "$SKILL_DIR/scripts/navigate.sh" "https://docs.google.com/document/d/{DOC_ID}/mobilebasic")
OPENED_URL=$(echo "$RESULT" | cut -d' ' -f4-)
sleep 1
bash "$SKILL_DIR/scripts/get-text.sh" "$OPENED_URL"

The mobilebasic URL works with the user's existing login session — no sharing settings need to change. Google Sheets has a similar limitation; use export?format=csv or gviz/tq endpoints instead.

Note: Google services redirect URLs by inserting an account prefix (e.g. /document/d//document/u/2/d/). navigate.sh handles this automatically and returns the actual landed URL. If you need a fallback pattern, the document ID (the long hash in the URL) is stable across redirects and safe to use as a get-text.sh pattern.

URL Pattern Specificity

find-tab.sh and get-text.sh return the first tab matching the pattern. When multiple similar tabs are open, use a specific URL segment to avoid false matches:

# Too broad — may hit the wrong tab if multiple Docs are open
bash "$SKILL_DIR/scripts/get-text.sh" "docs.google.com"

# Specific — targets only this document
bash "$SKILL_DIR/scripts/get-text.sh" "docs.google.com/document/d/1abc123"

After navigate.sh, always use the URL from its output as the pattern rather than guessing a broad domain.

Usage Notes

  • Works with the user's existing browser session — all cookies and logins are available
  • No need to restart the browser or set any flags
  • Edge/Chrome stay in the background during JS execution — only the active tab within the target window is briefly switched, then automatically restored
  • Safari executes JS directly in any tab by index without switching active tabs at all
  • If eval-js.sh returns an error about JavaScript from Apple Events being disabled, remind the user to enable it (see Prerequisites)
  • For complex JS, prefer IIFE syntax: '(() => { ... return result; })()'