CLI Reference

The haptix command-line tool gives you full control over the Haptix platform from your terminal. Everything your AI agent can do via MCP, you can do from the CLI through the same daemon and endpoint (localhost:4278/mcp).

URL for agents: If you're an AI agent reading this, the CLI docs are at https://docs.haptix.dev/cli


Installation

Install the Haptix runtime with the one-shot installer:

curl -fsSL https://get.haptix.dev/install.sh | bash

The installer places the haptix binary at /usr/local/bin/haptix, installs the Haptix background service, and starts the daemon. If /usr/local/bin isn't writable without sudo, the installer falls back to a user-writable directory and prints what it picked.

haptix --version

Quick start

haptix session start
haptix tool tap --label "Settings"
haptix tool screenshot
haptix session end

That's the core loop: start a session → interact with the device → end the session.

start_session auto-discovers USB devices and auto-selects if only one is connected. You don't need list_devices or select_device unless you have multiple devices.


Architecture

The CLI talks to the haptix-server daemon through two channels:

Channel Transport Commands
Control API Unix socket (~/Library/Application Support/Haptix/haptix.sock) status, sessions, devices, logs, start, stop, health, capture, permissions, tools
MCP endpoint Streamable HTTP (POST http://localhost:4278/mcp) All device interaction commands (tap, swipe, screenshot, actions, …)

Device interaction commands construct JSON-RPC tools/call requests, track the Mcp-Session-Id header for session affinity, and format responses for the terminal.


Session management

Device commands require an active MCP session. The CLI manages this automatically.

haptix session start                    # start session, prints session ID
haptix session start --name "testing"   # start with a description
haptix tool tap 200 300                 # uses the active session
haptix tool screenshot                  # uses the active session
haptix session end                      # end session, clean up

How sessions work

  • haptix session start calls the start_session MCP tool, stores the Mcp-Session-Id in ~/.haptix-session
  • All device commands read from ~/.haptix-session automatically
  • haptix session end calls end_session and removes the file
  • If no session exists and a device command is run, a session is auto-created

Global flags

Flag Description
--json Output raw JSON instead of human-friendly formatting
--session <id> Use a specific MCP session ID instead of the stored one

Admin commands

These talk to the daemon's control API over the Unix socket.

haptix status

Server state, port, uptime, and session count.

$ haptix status
Haptix MCP Server
  State:    running
  Port:     4278
  Uptime:   2h 15m
  Sessions: 3
  Logs:     142

haptix sessions

Active MCP sessions.

$ haptix sessions
ID       CODE   AGENT                TRANSPORT    STATE
a3d1f2b8 A3D1   cursor               streamable   active
b7e9c4d1 B7E9   claude-code          streamable   idle

haptix devices

Connected USB devices.

$ haptix devices
UDID              NAME                    MODEL             CONNECTION
----------------------------------------------------------------------
00008020-00197098  Psyphone                iPhone XS         usb

When nothing's plugged in, the command tells you what to try next instead of just printing an empty table.

haptix devices refresh

Force a fresh discovery cycle. Use this when you've plugged a device in and it isn't showing up, or right after tapping Trust on the device.

$ haptix devices refresh
Refreshed. 1 device(s):
UDID              NAME                    MODEL             CONNECTION
----------------------------------------------------------------------
00008020-00197098  Psyphone                iPhone XS         usb

If nothing comes back, the command lists the usual prereqs so you can check them in order (Camera permission on the Mac, Developer Mode + UI Automation on the iOS device, device added to your Apple Developer Team).

haptix logs [--last N] [--follow]

Recent server log entries.

Flag Description
--last N Number of entries to show (default: 50)
--follow Stream new entries as they arrive (Ctrl+C to stop)

haptix start

Start the Haptix background service. If already running, prints a message and exits.

haptix stop

Graceful daemon shutdown.

haptix health

Liveness check. Exits 0 if healthy, 1 if not.

haptix trial

Start a free trial. Creates a trial license key and activates it on this machine. One trial per machine.

$ haptix trial
Starting free trial...
  Key:     HPTX-TRIAL-A7K3-M9P2
  Expires: 2026-03-07
  Days:    3

Trial activated. Full access to all features.

haptix license <KEY>

Activate a purchased license key on this machine.

haptix license HPTX-XXXX-XXXX-XXXX

Recovery and diagnostics

These commands let you inspect and recover the moving parts that have to be working before Haptix can drive a device — screen capture, OS-level permissions, USB discovery, and the daemon's MCP tool list. Most users won't need them day-to-day, but they're the first thing to reach for if something's stuck.

haptix capture status

Show the state of the Mac's screen-capture pipeline — whether it's running, which device it's locked onto, and how many frames it's seen recently.

$ haptix capture status
Screen capture:    ready (idle)
Camera access:     granted
Connected device:  iPhone
Recent activity:   115 frames captured (last 2ms ago)

When stuck, the same command tells you what to try, in order:

$ haptix capture status
Screen capture:    needs attention
Camera access:     granted
Connected device:  (none — Mac doesn't currently see the device)

Recovery: the Mac isn't seeing the iOS device. Try in order:
  1. Unplug and re-plug the device (tap Trust on the device if prompted)
  2. Try a known-good USB cable (power-only cables won't work)
  3. Reboot the Mac — sometimes screen capture needs a fresh start

haptix capture start | stop | restart

Bring screen capture up, take it down, or cycle it. Common pattern: haptix capture restart is the single fastest fix when capture has stuck but everything else is healthy.

haptix capture start     # bring it up (idempotent — safe if already running)
haptix capture stop      # take it down (warns if active sessions are attached)
haptix capture restart   # stop then start; the standard "kick it" recovery

haptix permissions status

Show whether macOS Camera permission is granted, which devices are paired, and what to check on the iPhone itself.

$ haptix permissions status
Status:            ready
Camera access:     granted

Connected device (1):
  iPhone (iPhone11,2) — paired
    Haptix on-device app: not installed yet (will install on first session)

Things to verify on the iPhone itself (the Mac can't check these):
  • Developer Mode is on
      iOS Settings → Privacy & Security → Developer Mode
  • UI Automation is enabled
      iOS Settings → Developer → Enable UI Automation
  • The device is added to your Apple Developer Team
      developer.apple.com → Account → Devices

haptix permissions fix

Walk through fixing whatever's wrong on the Mac side. If Camera permission was never requested, it triggers the system prompt. If it was denied, it opens System Settings → Privacy & Security → Camera for you and tells you what to do after.

haptix permissions fix

The iPhone-side prereqs (Developer Mode, UI Automation, Developer Team) can't be fixed from the Mac — permissions status lists them so you know what to check on the device itself.

haptix tools list

List the MCP tools the daemon currently exposes. Useful when an AI client (Cursor, Claude Code, Claude Desktop) has cached its tools/list from before the latest daemon upgrade and isn't seeing a new tool yet — restart the client to refresh.

$ haptix tools list
Haptix daemon v1.5.3 — 24 MCP tools registered:

  start_session              Begin an agent session and bind to a connected device
  end_session                Gracefully end the current agent session
  screenshot                 Capture the device screen, optionally annotated
  tap                        Tap a point or a labeled / identified element
  …
  actions                    Execute a sequence of taps and text input in ONE call

Device interaction commands

These send MCP tool calls to the daemon via Streamable HTTP.

haptix tool select <device>

Bind the session to a device. Matches against service name, bundle ID, or app name (case-insensitive, partial match).

haptix tool select MyApp
haptix tool select "com.example.myapp"

haptix tool device-info

Device model, screen size, orientation, app details, debugger status.

haptix tool device-status

Connection status and session state.


Screenshots and inspection

haptix tool screenshot [options]

Capture the device screen.

Screenshots come from the Mac-side capture of the device's video feed. Haptix waits for a fresh frame from the selected device and fails clearly if it cannot get one. There is no device-side screenshot fallback, and if the capture source drifts across attached phones, Haptix may return a drift error instead of another device's pixels.

Flag Description
--annotated Overlay accessibility bounding boxes and labels
--format <png|jpeg> Image format (default: jpeg)
--output <path> Save to file (default: screenshot_<timestamp>.jpg)
--filter <type> Filter annotations: interactive, button, text, input, image
--highlight <id> Highlight a single element by accessibility identifier
haptix tool screenshot                               # save to current dir
haptix tool screenshot --annotated --output ui.png  # annotated PNG
haptix tool screenshot --highlight "submitBtn"      # highlight one element

haptix tool tree [--mode compact|full]

Get the accessibility tree.

Flag Description
--mode <compact|full> compact (default): flat list. full: nested hierarchy

haptix tool console [options]

Get recent app console output (print, NSLog, os_log).

Flag Description
--limit N Max entries (default: 100)
--contains <text> Filter by substring
--source <stdout|stderr|os_log> Filter by source
--level <debug|info|notice|error|fault> Filter by log level

Gesture commands

All coordinates are in screen points (not pixels), origin (0, 0) at top-left.

haptix tool tap [options]

Single tap. Three targeting modes:

haptix tool tap 200 300               # by coordinates
haptix tool tap --label "Submit"      # by accessibility label
haptix tool tap --id "submitButton"   # by accessibility identifier

haptix tool double-tap [options]

Double tap. Same targeting as tap.

haptix tool long-press [options]

Long press. Same targeting as tap, plus --duration <seconds> (default: 1.0).

haptix tool swipe <startX> <startY> <endX> <endY> [--duration <seconds>]

Swipe between two points. Use for picker wheels, dismissing sheets, reveal actions.

haptix tool swipe 200 400 200 200              # swipe up
haptix tool swipe 200 300 200 250 --duration 0.1

haptix tool drag <startX> <startY> <endX> <endY> [options]

Drag with dwell phase. Use for reordering lists, moving items.

Flag Description
--hold <seconds> Hold at start before moving (default: 0.15)
--duration <seconds> Movement duration (default: 1.0)

haptix tool draw-path --points <json> [options]

Continuous one-finger path through many waypoints. Use it for drawing, signatures, circles, and organic drags where lifting between segments would be wrong.

Flag Description
--points '<json>' Ordered JSON array of {x, y} waypoints
--hold <seconds> Pause after touch-down before moving (default: 0)
--duration <seconds> Total stroke duration (default: 1.0)
--closed Append the first point at the end if needed
haptix tool draw-path --points '[{"x":100,"y":100},{"x":140,"y":120},{"x":180,"y":180}]'
haptix tool draw-path --points '[{"x":160,"y":300},{"x":220,"y":300},{"x":220,"y":360},{"x":160,"y":360}]' --closed

Separate draw-path calls imply lifting the finger on purpose.

haptix tool scroll <direction> [options]

Page scroll. No coordinates needed.

Flag Description
--amount <size> small, medium (default), large, full_page
--id <identifier> Target a specific scrollable element
haptix tool scroll down
haptix tool scroll up --amount large

haptix tool pinch <centerX> <centerY> <scale> [--duration <seconds>]

Pinch gesture. Scale greater than 1 zooms in, less than 1 zooms out.

haptix tool rotate <centerX> <centerY> <angle> [--duration <seconds>]

Two-finger rotation. Positive angle is clockwise.


Text commands

haptix tool type <text>

Type text into the focused input field. The keyboard must be visible.

haptix tool type "hello world"
haptix tool type "user@example.com"

haptix tool clear-text

Clear all text in the focused input field.


Annotation commands

haptix tool annotate [options]

Draw annotations on the device screen. Annotations appear in screenshots.

Flag Description
--accessibility Auto-render bounding boxes from the accessibility tree
--filter <type> Filter: interactive, adjustable, button, text, input, image
--highlight <id> Highlight one element by identifier
--add Layer on top of existing annotations (default replaces)
--lock Persist through multiple screenshots

haptix tool clear-annotations

Remove all annotations.


Notes commands

Agent learning persistence across sessions.

haptix tool note <content> [--scope app|universal]

Store a learning.

haptix tool note "Login button is at the bottom of the scroll view"
haptix tool note "Always dismiss keyboard before tapping nav bar" --scope universal

haptix tool notes [--scope app|universal|all]

Recall stored learnings.

haptix tool consolidate <summary> [--scope app|universal]

Replace scattered notes with a consolidated summary.


Examples

Quick screenshot workflow

haptix tool screenshot --annotated --output annotated.jpg
haptix tool tree
haptix tool tap --label "Settings"
haptix tool screenshot --output after-tap.jpg

Scripted test flow

haptix session start --name "login test"
haptix tool tap --id "emailField"
haptix tool type "test@example.com"
haptix tool tap --id "passwordField"
haptix tool type "secret123"
haptix tool tap --label "Sign In"
haptix tool screenshot --output login-result.jpg
haptix session end

Machine-readable output

haptix tool tree --json | jq '.elements[] | select(.traits | contains("button"))'
haptix tool device-info --json