Claude
Skills
Sign in
Back

cli-anything-safari

Included with Lifetime
$97 forever

Safari browser automation CLI on macOS via safari-mcp. Controls real Safari (native, keeps logins) by wrapping the safari-mcp MCP server. Every one of the 84 MCP tools is exposed 1:1 with schema-accurate arguments — guaranteed parity, no manual drift.

AI Agents

What this skill does


# cli-anything-safari

A command-line interface for Safari browser automation on macOS. Wraps the
[`safari-mcp`](https://github.com/achiya-automation/safari-mcp) Node.js MCP
server in a Python Click CLI.

**Feature parity is guaranteed.** Every Click command is generated
automatically from `safari-mcp`'s tool schema (bundled as
`resources/tools.json`). All 84 tools are reachable with the exact
argument names and types the MCP server expects.

## When to use this CLI

Each CLI invocation spawns a fresh subprocess, so there is per-call
overhead. If your agent speaks MCP natively (Claude Code, Cursor, Cline,
etc.), using `safari-mcp` directly over MCP stdio will be faster.

**Use this CLI when:**
- Your agent framework does **not** speak MCP (Codex CLI, GitHub Copilot
  CLI, custom scripts, older agent frameworks).
- You need to **script browser automation from bash** —
  `cli-anything-safari --json tool snapshot | jq '...'`.
- You run in **CI/CD** and want cron-able, subprocess-friendly output.
- You're **debugging interactively** from Terminal.

## Installation

### Prerequisites

1. **macOS** — Safari MCP is macOS-only.
2. **Safari** — already installed on macOS.
3. **Node.js 18+** — `brew install node` or from https://nodejs.org/
4. **Python 3.10+**
5. **Enable Apple Events for Safari**: Safari → Develop → Allow JavaScript from Apple Events

### Install the CLI

```bash
cd safari/agent-harness
pip install -e .
```

The first `tool` call will download the `safari-mcp` npm package (one-time, a few MB).

## Command Structure

The CLI has 5 top-level commands:

| Command   | Purpose                                                           |
|-----------|-------------------------------------------------------------------|
| `tool`    | Call any of safari-mcp's **84 tools** (dynamic, schema-driven)    |
| `tools`   | Inspect the bundled tool registry (`list`, `describe`, `count`)   |
| `raw`     | Escape hatch — call a tool by full name with raw JSON args        |
| `session` | In-memory session state (last URL, current tab)                   |
| `repl`    | Interactive REPL (default when no subcommand given)               |

## Usage Examples

### Discover the tool surface

```bash
# Count of tools (sanity check — must match safari-mcp's registered tools)
cli-anything-safari tools count
# → 84

# List every tool
cli-anything-safari tools list
cli-anything-safari tools list --filter click   # filter by substring

# Full schema for one tool (JSON or human format)
cli-anything-safari tools describe safari_scroll
cli-anything-safari --json tools describe safari_click
```

### Call a tool (schema-driven)

```bash
# Navigate
cli-anything-safari tool navigate --url https://example.com

# Take a snapshot (preferred over screenshot — structured text with ref IDs)
cli-anything-safari --json tool snapshot

# Click by ref (refs come from snapshot; they expire on the next snapshot!)
cli-anything-safari tool click --ref 0_5

# Click by selector or visible text
cli-anything-safari tool click --selector "#submit"
cli-anything-safari tool click --text "Log in"

# Fill a field
cli-anything-safari tool fill --selector "#email" --value "[email protected]"

# Scroll by direction/amount (NOT x/y — note the schema!)
cli-anything-safari tool scroll --direction down --amount 500

# Drag one element onto another
cli-anything-safari tool drag \
    --source-selector ".card" \
    --target-selector ".trash"

# Screenshot — returns base64 JPEG in stdout. Decode with:
cli-anything-safari --json tool screenshot --full-page \
    | python3 -c "import sys,json,base64; \
        d=json.load(sys.stdin); \
        open('/tmp/shot.jpg','wb').write(base64.b64decode(d['data']))"

# Save as PDF (this one writes to disk directly)
cli-anything-safari tool save-pdf --path /tmp/page.pdf

# Evaluate JavaScript (note: parameter is --script, not --code)
cli-anything-safari tool evaluate --script "document.title"
```

### Navigate and read in one round-trip

```bash
cli-anything-safari --json tool navigate-and-read --url https://example.com
```

### Form fill (bulk)

`safari_fill_form` takes an **array** of `{selector, value}` objects.
Pass it as a JSON string:

```bash
cli-anything-safari tool fill-form --fields '[
  {"selector": "#email",    "value": "[email protected]"},
  {"selector": "#password", "value": "hunter2"}
]'
```

Run `cli-anything-safari tools describe safari_fill_form` to see the
exact schema, including any new fields safari-mcp adds upstream.

### Network monitoring

```bash
cli-anything-safari tool start-network-capture
cli-anything-safari tool navigate --url https://example.com
cli-anything-safari --json tool network
cli-anything-safari tool performance-metrics
```

### Storage

```bash
cli-anything-safari tool get-cookies
cli-anything-safari tool set-cookie --name session --value abc123 --domain example.com
cli-anything-safari tool local-storage --key theme
# export-storage returns JSON to stdout — no --path arg. Pipe to a file:
cli-anything-safari --json tool export-storage > /tmp/storage.json
```

### Raw JSON escape hatch

When you need to pass a complex nested object or want to drive the CLI from
a pre-built JSON blob:

```bash
cli-anything-safari raw safari_evaluate \
    --json-args '{"code":"[...document.querySelectorAll(\"a\")].map(a => a.href)"}'
```

### Interactive REPL

```bash
cli-anything-safari
```

The REPL banner prints the absolute path to this SKILL.md so agents can
self-discover capabilities.

## JSON Output

All commands support `--json` as a global flag:

```bash
cli-anything-safari --json tool snapshot
cli-anything-safari --json tool list-tabs
cli-anything-safari --json tools list
```

## State Management

The CLI maintains a small amount of in-memory state for REPL display only:

- **`last_url`** — last URL the CLI navigated to (updated after every
  successful `tool navigate`, `tool navigate-and-read`, or
  `tool new-tab`)
- **`current_tab_index`** — last known active tab index

There is **no persistent session**, no undo/redo, no document model.
Every CLI invocation starts with fresh state. Safari MCP itself is
stateless per-call: each `tool` command spawns a fresh
`npx safari-mcp` subprocess, performs the action, and exits. This is a
deliberate design choice; see `HARNESS.md` and `TEST.md` for the
reasoning behind the deviation from the standard undo/redo pattern.

## Output Formats

All commands support dual output modes:

- **Human-readable** (default): indented key-value text for `dict`
  results, bullet lists for arrays, plain text otherwise
- **Machine-readable** (`--json` flag): structured JSON for agent
  consumption

```bash
# Human output
cli-anything-safari tool snapshot

# JSON output for agents
cli-anything-safari --json tool snapshot
cli-anything-safari --json tools list
cli-anything-safari --json tools describe safari_click
```

## For AI Agents

When using this CLI programmatically:

1. **Always use `--json` flag** for parseable output.
2. **Check return codes** — 0 for success, non-zero for errors (URL
   validation failures, MCP call failures, invalid JSON args).
3. **Parse stderr** for error messages; use stdout for data.
4. **File-handling tools have inconsistent path arg names** — always
   check `tools describe <name>` first:
   - `tool save-pdf --path /tmp/x.pdf`
   - `tool upload-file --selector ... --file-path /tmp/x.txt` (note: `--file-path`, not `--path`)
   - `tool export-storage` — no path arg; pipe JSON output to a file
   - `tool import-storage --path /tmp/x.json`
   - `tool screenshot` / `screenshot-element` — return base64 in
     the JSON response, no path arg (decode it yourself)
5. **Snapshot before click** — refs from `tool snapshot` expire on the
   next snapshot. Always snapshot → find ref → click in close
   succession.
6. **Discover tools via `tools list`** — the bundled registry is the
   source of truth for what's available. Do not hard-code tool names
   that may change upstream.
7. **Use `tools describe <name

Related in AI Agents