Skip to main content
Glama
Safari Web Inspector Bridge

Safari Web Inspector Bridge

by andesco

Safari Web Inspector Bridge

Safari Web Inspector Bridge is an MCP server that gives AI agents the same capabilities a developer gets from Safari Web Inspector — inspect, observe, and automate WKWebViews running on connected iOS devices.

Architecture

graph TD
    Agent["AI Agent<br/>Claude, Codex, etc."]
    Bridge["MCP Server:<br /><code>safari-web-inspector-bridge</code>"]
    Proxy["managed child process: <code>ios-webkit-debug-proxy</code>"]
    Device["iOS Device<br/>WKWebView"]

    Agent <-->|"MCP <code>stdio</code>"| Bridge
    Bridge <-->|"WebSocket<br/>WebKit Inspector Protocol"| Proxy
    Proxy <-->|"usbmuxd<br/>(USB / Wi-Fi)"| Device

    style Agent fill:#f0f0f0
    style Bridge fill:#f0f0f0
    style Proxy fill:#f0f0f0
    style Device fill:#f0f0f0

The server spawns ios-webkit-debug-proxy as a child process, connects to the WebKit Inspector Protocol over WebSocket, and exposes everything as MCP tools. The proxy is managed for its full lifecycle -- started on init, health-checked, auto-restarted on crash, and killed on shutdown.

Proxy Lifecycle

stateDiagram-v2
    [*] --> Checking: Server starts
    Checking --> Spawning: Binary found
    Checking --> Error: Binary missing
    Spawning --> HealthCheck: Process started
    HealthCheck --> Ready: localhost:9221 responds
    HealthCheck --> Error: Timeout (10s)
    Ready --> Crashed: Process exits
    Crashed --> Spawning: Restart (once)
    Crashed --> Error: Already retried
    Ready --> Stopped: Graceful shutdown
    Stopped --> [*]

13 MCP Tools

Discovery: list_devices | list_inspectable_pages | connect

Observation: get_url | get_dom | get_network_log | get_console_log | screenshot

Automation: navigate | execute_javascript | click_element | type_text | wait_for

Related MCP server: Mobile Device MCP

Prerequisites

  • macOS required for usbmuxd and iOS device connectivity

  • ios-webkit-debug-proxy: brew install ios-webkit-debug-proxy

  • iOS: Settings &rsaquo; Safari &rsaquo; Advanced &rsaquo; Web Inspector: enabled

  • target app WKWebView must have isInspectable = true

Installation

git clone https://github.com/andesco/safari-web-inspector-bridge.git
cd safari-web-inspector-bridge
npm install
npm run build

Add to Claude Code

claude mcp add safari-web-inspector-bridge node /path/to/safari-web-inspector-bridge/dist/index.js

Add to any MCP client

Add to your MCP configuration file:

{
  "mcpServers": {
    "safari-web-inspector-bridge": {
      "command": "node",
      "args": ["/path/to/safari-web-inspector-bridge/dist/index.js"],
      "env": {
        "SWIB_NETWORK_CAPTURE": "true",
        "SWIB_CONSOLE_CAPTURE": "true"
      }
    }
  }
}

Tools

Device & Connection

Tool

Parameters

Returns

list_devices

(none)

[{ udid, name, os_version }]

list_inspectable_pages

device_udid? string -- filter to one device

[{ page_id, title, url, app_bundle_id, device_udid }]

connect

page_id string -- from list_inspectable_pages

{ connected, page_id, url, title, warnings? }

Observation

Tool

Parameters

Returns

get_url

(none)

{ url }

get_dom

selector? string -- CSS selector (default: document.documentElement); outer_html? boolean (default: true) -- outerHTML vs textContent

{ html } or { text }

get_network_log

clear? boolean (default: false); filter_url? string -- regex; filter_status? string -- e.g. "302", "4xx"

[{ request_id, method, url, status, mime_type, response_headers, request_headers, redirected_from, redirected_to, timing, error }]

get_console_log

clear? boolean (default: false); level? "log" | "warn" | "error" | "info"

[{ level, text, timestamp, source_url, line_number }]

screenshot

(none)

MCP image content (base64 PNG)

Automation

Tool

Parameters

Returns

navigate

url string

{ url, title, status }

execute_javascript

expression string; await_promise? boolean (default: true)

{ result } -- JSON-serialized return value

click_element

selector string -- CSS selector; index? number (default: 0) -- which match to click

{ clicked, selector, tag_name }

type_text

text string; selector? string -- focus this element first

{ typed: true }

wait_for

One of: selector? string, url_contains? string, network_idle? number (ms); plus timeout_ms? number (default: 10000)

{ matched: true, elapsed_ms }

Configuration

Env Var

Default

Description

SWIB_AUTO_CONNECT

false

Auto-connect to the first inspectable page on startup

SWIB_NETWORK_CAPTURE

true

Capture network requests on connect

SWIB_CONSOLE_CAPTURE

true

Capture console messages on connect

SWIB_PROXY_PORT

9222

Starting port for ios-webkit-debug-proxy device ports

[!note] SWIB_NETWORK_CAPTURE and SWIB_CONSOLE_CAPTURE default to true — set to false to disable. SWIB_AUTO_CONNECT defaults to false — set to true to enable.

Example Workflow

sequenceDiagram
    participant Agent as AI Agent
    participant Bridge as MCP Server
    participant Proxy as ios-webkit-debug-proxy
    participant Device as iOS WKWebView

    Agent->>Bridge: list_inspectable_pages()
    Bridge->>Proxy: GET /json (devices + pages)
    Proxy-->>Bridge: page_id: "1", title: "Banks"
    Bridge-->>Agent: [{ page_id, title, url, app_bundle_id }]

    Agent->>Bridge: connect({ page_id: "1" })
    Bridge->>Proxy: WebSocket connect
    Proxy->>Device: WebKit Inspector attach
    Device-->>Proxy: Connected
    Bridge-->>Agent: { connected: true }

    Agent->>Bridge: get_network_log({ filter_url: "scotiabank" })
    Bridge-->>Agent: [{ url, status: 302, redirected_to: "scotiabank://..." }]

    Agent->>Bridge: execute_javascript("document.title")
    Bridge->>Device: Runtime.evaluate
    Device-->>Bridge: "Banks"
    Bridge-->>Agent: { result: "Banks" }

    Agent->>Bridge: click_element({ selector: "button.next" })
    Bridge->>Device: Runtime.evaluate (querySelector + click)
    Device-->>Bridge: clicked
    Bridge-->>Agent: { clicked: true, tag_name: "button" }

    Agent->>Bridge: wait_for({ url_contains: "/dashboard" })
    loop Poll until match
        Bridge->>Device: Runtime.evaluate (location.href)
        Device-->>Bridge: current URL
    end
    Bridge-->>Agent: { matched: true, elapsed_ms: 1230 }

    Agent->>Bridge: screenshot()
    Bridge->>Device: Page.snapshotRect
    Device-->>Bridge: base64 PNG
    Bridge-->>Agent: image content

Development

npm run build        # Compile TypeScript to dist/
npm run dev          # Watch mode (tsc --watch)
npm test             # Run tests (vitest)
npm run test:watch   # Watch mode tests
npm start            # Run the MCP server (node dist/index.js)

Project structure

src/
  index.ts               # Entry point, server setup, lifecycle
  types.ts               # Interfaces and config loader
  proxy-manager.ts       # Spawns and manages ios-webkit-debug-proxy
  device-discovery.ts    # Queries proxy for devices and pages
  webkit-connection.ts   # WebSocket connection to WebKit Inspector Protocol
  network-buffer.ts      # Ring buffer for network request entries (1000 max)
  tools/
    device-tools.ts      # list_devices, list_inspectable_pages, connect
    observation-tools.ts # get_url, get_dom, get_network_log, get_console_log, screenshot
    automation-tools.ts  # navigate, execute_javascript, click_element, type_text, wait_for

Available Tools

14 tools
click_elementA

Click a DOM element identified by CSS selector

Inputs: selector (required), index.

connectB

Attach to a specific inspectable page for observation and automation

Inputs: page_id (required).

debug_protocolA

Dump the raw WebKit Inspector Protocol messages exchanged (for debugging)

Inputs: clear.

execute_javascriptB

Evaluate a JavaScript expression in the page context

Inputs: expression (required), await_promise.

get_console_logA

Retrieve JavaScript console messages since connection or last clear

Inputs: clear, level.

get_domA

Read the page's DOM as HTML

Inputs: selector, outer_html.

get_network_logA

Retrieve captured network requests since connection or last clear

Inputs: clear, filter_url, filter_status.

get_urlA

Get the current URL of the connected page

Inputs: no named parameters.

list_devicesA

List connected iOS devices with Web Inspector enabled

Inputs: no named parameters.

list_inspectable_pagesA

Enumerate all inspectable WKWebViews across connected devices

Inputs: device_udid.

screenshotB

Capture a screenshot of the webview content

Inputs: no named parameters.

type_textA

Type text into the currently focused element or a specified element

Inputs: text (required), selector.

wait_forC

Wait for a condition before proceeding

Inputs: selector, timeout_ms, network_idle, url_contains.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 14 tools

    Disambiguation5/5

    Each tool has a clearly distinct role: enumeration (list_devices, list_inspectable_pages), connection, state retrieval (get_url, get_dom, get_network_log, get_console_log), actions (navigate, execute_javascript, click_element, type_text, wait_for), and debugging (screenshot, debug_protocol). No two tools target the same resource or action in an ambiguous way.

    Naming Consistency4/5

    Most tools follow a consistent verb_noun pattern (list_devices, get_url, click_element). A few single-word verbs (connect, navigate, screenshot) deviate slightly but are still clear and fit the overall imperative style. No mixed naming conventions like camelCase or inconsistent prefixes.

    Tool Count5/5

    14 tools is well within the ideal range for a comprehensive browser automation/inspection server. Each tool covers a necessary aspect of the workflow without redundancy or bloat.

    Completeness4/5

    The toolset covers the core lifecycle of inspecting and automating WKWebViews: discovery, connection, navigation, DOM access, interaction, waiting, and logging. A minor gap is lack of an explicit clear operation for network/console logs despite descriptions mentioning 'last clear', but this can be worked around with reconnection.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server for iOS automation via rpcclient, enabling AI agents to connect to iOS RPC servers and perform actions like launching apps, clicking, typing, and gesture control.
      2
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      An MCP server that lets AI agents control iOS and Android devices (tap, scroll, type, take screenshots, read UI trees, and run code). Works with multiple devices at the same time.
      94 npm
      54
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      A Model Context Protocol (MCP) server for iOS Simulator automation. Enables AI assistants to visually interact with iOS apps running in the simulator.
      40 npm
      1
      MIT