npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opensafari-mcp

v0.1.8

Published

iOS Safari automation MCP server via Xcode Simulator + WebKit Remote Debugging Protocol

Readme


How OpenSafari compares

| | OpenSafari | Playwright WebKit | BrowserStack | Manual Testing | |---|:---:|:---:|:---:|:---:| | Engine | Real Safari (Xcode Sim) | Bundled WebKit (approximation) | Real devices (cloud) | Real devices | | Protocol | WebKit Remote Debugging (direct) | Playwright API (wrapper) | Proprietary | N/A | | iOS Fidelity | exact | close but diverges | exact | exact | | Parallel sessions | N simulators | N browsers | limited by plan | 1 device | | Login persistence | built-in (real Safari cookies) | manual | manual | manual | | LLM integration | MCP native | none | none | none | | Cost | free (Xcode) | free | $29+/mo | device cost | | iOS-specific QA | auto-detect (zoom, safe area, keyboard) | none | manual | manual |

tl;dr — OpenSafari controls the real Safari inside Xcode Simulator via WebKit Remote Debugging Protocol — the same way OpenChrome controls real Chrome via CDP. No middleware, no bundled browsers. Just direct protocol access to the actual Safari.app.


What is OpenSafari?

Imagine testing your e-commerce site on iPhone 17e, iPhone 17, iPhone 17 Pro Max, and iPad — all at the same time, already logged in, with an AI agent that automatically finds iOS-specific bugs. That's OpenSafari.

You: Check our checkout flow for mobile issues across all iPhone sizes

AI:  [4 parallel simulators, all devices simultaneously]
     iPhone 17e:      ⚠ Credit card input triggers iOS auto-zoom (font-size: 14px)
     iPhone 17:       ✓ Layout OK
     iPhone 17 PM:    ⚠ "Place Order" button only 38×32px (below 44px touch target)
     iPad:            ⚠ Shipping form hidden behind keyboard when focused

     Time: 8s | All screenshots captured and analyzed.

| | Manual QA | OpenSafari | |---|:---:|:---:| | 4-device test | ~30 min | ~10s (parallel) | | Login | Each device, each time | Never (persisted) | | iOS bug detection | Human eye | Automatic (LLM vision) | | Consistency | Varies by tester | Deterministic |


Core Architecture

OpenSafari follows the same direct-protocol philosophy as OpenChrome:

OpenChrome:  CDPClient → Chrome DevTools Protocol → Real Chrome
OpenSafari:  SafariClient → WebKit Remote Debugging Protocol → Real Safari in Simulator

No middleware. No bundled browsers. Direct connection.

Claude Code / AI Agent (MCP Client)
    │
    │  JSON-RPC (stdio / HTTP)
    ▼
┌─────────────────────────────────────┐
│         OpenSafari MCP Server       │
│                                     │
│  ┌─────────────┐  ┌──────────────┐  │
│  │ Simulator   │  │ Safari       │  │
│  │ Manager     │  │ Client       │  │
│  │ (simctl)    │  │ (WebKit      │  │
│  │             │  │  Protocol)   │  │
│  └──────┬──────┘  └──────┬───────┘  │
│         │                │          │
│    boot/shutdown    navigate/click   │
│    rotate/appear    screenshot       │
│    multi-device     DOM/JS/cookies   │
│         │                │          │
│  ┌──────▼────────────────▼───────┐  │
│  │     Xcode Simulator(s)       │  │
│  │  ┌────────┐  ┌────────┐      │  │
│  │  │ iPhone │  │ iPhone │ ...  │  │
│  │  │ SE     │  │ 16 PM  │      │  │
│  │  │ Safari │  │ Safari │      │  │
│  │  └────────┘  └────────┘      │  │
│  └───────────────────────────────┘  │
│                                     │
│  ┌─────────────────────────────┐    │
│  │  Shared Infrastructure      │    │
│  │  (from OpenChrome)          │    │
│  │  • Security (sanitizer,     │    │
│  │    domain guard, audit)     │    │
│  │  • Watchdog (event loop,    │    │
│  │    disk, health endpoint)   │    │
│  │  • Orchestration (workflow  │    │
│  │    engine, parallel workers)│    │
│  │  • Session persistence      │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

Key Features

1. Real Safari, Real Bugs

OpenSafari controls the actual Safari.app inside Xcode Simulator via WebKit Remote Debugging Protocol — not a bundled approximation. Every iOS-specific quirk is faithfully reproduced:

  • iOS auto-zoom on inputs with font-size < 16px
  • position: fixed behavior with real software keyboard
  • 100vh viewport height inconsistencies with address bar
  • Safe area insets (notch, home indicator) on real device frames
  • color-scheme dark mode forced rendering
  • Touch target minimum size requirements (44×44px)

2. Parallel Multi-Device Testing

Test across multiple devices simultaneously with a single command:

opensafari serve --devices "iphone-17e,iphone-17,iphone-17-pro-max,ipad-pro"

# 4 simulators boot in parallel
# Each gets its own Safari instance + WebKit Protocol connection
# All share login state via cookie injection

3. Persistent Login Sessions

Log in once, test forever. Cookies and localStorage are extracted directly from the real Safari session:

# First time: login captured from real Safari
opensafari auth save --site myapp.com
  → Exports cookies via WebKit Protocol Network.getAllCookies
  → Saves to ~/.opensafari/auth/myapp.json

# Every subsequent run: auto-restored
opensafari serve
  → Injects cookies via Network.setCookie into each simulator's Safari
  → All simulators start already logged in

4. iOS-Specific Auto-Detection

Built-in QA checks that run on real Safari — no approximation:

| Check | What It Detects | |-------|----------------| | Auto-Zoom Guard | <input> elements with font-size < 16px | | Safe Area Validator | Content hidden behind notch or home indicator | | Keyboard Overlap | Fixed elements covered by real software keyboard | | Touch Target Audit | Clickable elements smaller than 44×44px | | Dark Mode Diff | Visual differences via simctl ui appearance toggle | | Viewport Compare | Layout breaks across different screen sizes | | Scroll Lock Check | Body scroll not restored after modal close |

5. MCP Native

Works with any MCP client — Claude Code, Cursor, VS Code, or custom agents:

// .mcp.json
{
  "mcpServers": {
    "opensafari": {
      "command": "npx",
      "args": ["-y", "opensafari-mcp", "serve"]
    }
  }
}

6. Shared DNA with OpenChrome

OpenSafari shares battle-tested infrastructure with OpenChrome:

| Module | Source | Status | |--------|--------|--------| | MCP Server core | OpenChrome | Shared | | Transport (stdio/HTTP) | OpenChrome | Shared | | Security (sanitizer, guard, audit) | OpenChrome | Shared | | Watchdog (event loop, disk, health) | OpenChrome | Shared | | Orchestration (workflow engine) | OpenChrome | Adapted | | Simulator Manager | NEW | OpenSafari | | Safari Client (WebKit Protocol) | NEW | OpenSafari | | iOS QA Engine | NEW | OpenSafari |


Tools

Core Tools (Tier 1)

| Tool | Description | |------|-------------| | navigate | Open URL in real Safari | | click | Tap element by CSS selector or coordinates | | type | Type text into form elements | | scroll | Scroll page in any direction | | screenshot | Capture real Safari screen via WebKit Protocol | | read_page | Extract visible text content | | query_dom | CSS selector queries with element details | | javascript | Execute JavaScript in page context via Runtime.evaluate | | inspect | Element CSS, accessibility, and layout inspection | | cookies | Get/set/clear real Safari cookies via Network domain |

Device Management (Tier 1)

| Tool | Description | |------|-------------| | device_list | List available simulator device types | | device_boot | Boot a specific device (iPhone SE, 16, iPad, etc.) | | device_shutdown | Shutdown simulator | | device_rotate | Toggle portrait/landscape | | appearance_toggle | Switch light/dark mode via simctl ui |

Auth Tools (Tier 3)

| Tool | Description | |------|-------------| | auth_save | Capture cookies + localStorage from current session | | auth_restore | Restore saved auth state into a simulator | | auth_list | List saved auth profiles |

Parallel & Orchestration (Tier 2)

| Tool | Description | |------|-------------| | batch_screenshot | Capture same URL across all active devices | | batch_execute | Run JS across all simulators in parallel | | batch_navigate | Open same URL on all devices simultaneously | | cross_viewport_compare | Side-by-side visual comparison across devices |

iOS QA Engine (Tier 3)

| Tool | Description | |------|-------------| | qa_auto_zoom | Detect inputs triggering iOS auto-zoom | | qa_touch_targets | Find elements below 44×44px minimum | | qa_safe_area | Check content behind notch/home indicator | | qa_keyboard_overlap | Detect fixed elements hidden by real keyboard | | qa_dark_mode | Compare light vs dark mode rendering | | qa_full_audit | Run all QA checks and generate report |


Quick Start

# Install
npm install -g opensafari-mcp

# Run (stdio mode — for MCP clients like Claude Code)
opensafari serve

# HTTP mode
opensafari serve --http 3100

# With all tool tiers exposed
opensafari serve --all-tools

# With specific devices auto-booted
opensafari serve --devices "iphone-17e,iphone-17-pro-max"

# With auth state
opensafari serve --auth ~/.opensafari/auth/mysite.json

MCP Client Configuration

// Claude Code: .mcp.json
{
  "mcpServers": {
    "opensafari": {
      "command": "npx",
      "args": ["-y", "opensafari-mcp", "serve"]
    }
  }
}
// Claude Desktop: claude_desktop_config.json
{
  "mcpServers": {
    "opensafari": {
      "command": "npx",
      "args": ["-y", "opensafari-mcp", "serve", "--all-tools"]
    }
  }
}

Tool Tiers

Tools are organized into 3 tiers for progressive disclosure:

| Tier | Tools | Access | |------|-------|--------| | Tier 1 | navigate, screenshot, click, type, scroll, read_page, query_dom, javascript, cookies, device_boot, device_shutdown, device_list | Default | | Tier 2 | inspect, wait_for, press, swipe, long_press, batch_navigate, batch_screenshot, cross_viewport_compare | setTier(2) | | Tier 3 | auth_save, auth_restore, auth_list, qa_audit, qa_* detectors, workflow_init, appearance_toggle | --all-tools |


Programmatic API

import { createServer } from 'opensafari-mcp';

// Create and start the MCP server
const server = createServer({
  tier: 3,          // expose all tool tiers
  auditLog: true,   // enable tool call logging
});

// Start with stdio transport (default)
await server.start();

// Or start with HTTP transport
await server.start({ transport: 'http', port: 3100 });

WebKitClient

Direct WebKit protocol access for custom automation:

import { WebKitClient } from 'opensafari-mcp';

const client = new WebKitClient({ host: 'localhost', port: 9322 });
await client.connect({ retries: 5, retryDelay: 2000 });

// Navigate and evaluate
await client.navigate({ url: 'https://example.com', waitUntil: 'load' });
const title = await client.evaluate<string>('document.title');

// Screenshot (returns PNG buffer)
const png = await client.screenshot();

// Cookies
const cookies = await client.getCookies();
await client.setCookies([{ name: 'key', value: 'val', domain: '.example.com',
  path: '/', expires: -1, httpOnly: false, secure: false }]);

// DOM interaction
await client.click('#submit-btn');
await client.type('#email-input', '[email protected]');

await client.disconnect();

SimulatorManager

Programmatic simulator lifecycle control:

import { SimulatorManager } from 'opensafari-mcp';

const manager = new SimulatorManager();

// Boot a device
const device = await manager.boot('iPhone 17 Pro');
console.log(device.udid, device.state); // "XXXX-..." "Booted"

// Open Safari
await manager.openUrl(device.udid, 'https://example.com');

// List booted devices
const booted = await manager.listBooted();

// Shutdown
await manager.shutdown(device.udid);

Requirements

  • macOS (Xcode Simulator is macOS only)
  • Xcode with iOS Simulator runtime installed
  • Node.js >= 18
  • ios-webkit-debug-proxybrew install ios-webkit-debug-proxy

WebInspector Proxy Configuration

OpenSafari uses ios_webkit_debug_proxy to bridge WebKit Remote Debugging from Xcode Simulator. The proxy is auto-started by the device_boot tool — no manual setup is needed in most cases.

Default Ports

| Port | Purpose | |------|---------| | 9321 | Device list (HTML) — serves the proxy's device listing page | | 9322 | Device connection (JSON) — WebKit debugging targets for connected simulators |

Port 9322 is deliberately offset from Chrome DevTools (9222) so OpenSafari and OpenChrome can run simultaneously.

Custom Port

Set the OPENSAFARI_PROXY_PORT environment variable to use a different device port:

# Use port 9500 instead of the default 9322
OPENSAFARI_PROXY_PORT=9500 opensafari serve

Port resolution order:

  1. Explicit port option (programmatic use)
  2. OPENSAFARI_PROXY_PORT environment variable
  3. Default: 9322

Multi-Session Usage

Multiple Claude Code sessions can share the same proxy. When a session detects a healthy proxy already running on its target port, it reuses it instead of starting a new one. When the owning session exits, only its own proxy is terminated — other sessions' proxies remain unaffected.


Relationship to OpenChrome

OpenSafari is the Safari/iOS counterpart to OpenChrome. Same philosophy, same architecture — different browser.

| | OpenChrome | OpenSafari | |---|---|---| | Browser | Real Chrome | Real Safari (in Simulator) | | Protocol | Chrome DevTools Protocol (CDP) | WebKit Remote Debugging Protocol | | Client | CDPClient (puppeteer-core) | SafariClient (WebKit Protocol) | | Execution | chrome --remote-debugging-port | xcrun simctl + WebKit debug socket | | Use Case | Desktop web automation | Mobile web QA & debugging | | Parallel | N tabs in 1 Chrome | N simulators, each with Safari | | Login | Real Chrome sessions | Real Safari sessions |

Together, they provide complete browser coverage — Chrome for desktop, Safari for iOS — both controlled by AI agents through MCP with direct protocol connections. No middleware, no bundled browsers.


License

MIT