Safari Server
БесплатноПоддерживаетсяNative Safari browser automation for AI agents. 80 tools via AppleScript — zero overhead, keeps logins, runs silently in background. Drop-in alternative to Chro
Описание
Native Safari browser automation for AI agents. 80 tools via AppleScript — zero overhead, keeps logins, runs silently in background. Drop-in alternative to Chrome DevTools MCP with 40-60% less CPU/heat on Apple Silicon.
README

🦁 Safari MCP
The browser for your coding agent.
Your real Safari, logged in — no Chrome, no heat, no headless.
npm version npm downloads License: MIT macOS
Install in VS Code · VS Code Insiders · Install in Cursor
98 tools · No Chrome/Puppeteer/Playwright needed · ~5ms per command · 60% less CPU than Chrome
Quick Start · All 98 Tools · Examples · Why Safari MCP? · Architecture · Changelog

❌ Without Safari MCP
Your AI agent needs to browse. So it either:
- Spins up Chromium via Playwright — with no logins, no cookies, no sessions
- Uses Chrome DevTools MCP — and melts your fan running a second browser
- Relies on headless scrapers — blocked by Cloudflare, reCAPTCHA, and bot detection
✅ With Safari MCP
Your AI drives the Safari you're already logged into — Gmail, GitHub, Ahrefs, Slack, banking.
Native WebKit. ~60% less CPU. Background operation. 98 tools. One npx command. macOS only.
📰 Featured on freeCodeCamp: How to Connect Your AI Coding Agent to a Browser on macOS · HackerNoon: Reverse-Engineering React, Shadow DOM, and CSP
🍎 Apple ships an official Safari MCP —
safaridriver --mcp, first in Safari Technology Preview 247 (July 2026) and in stable Safari since 27.0. It drives an isolated automation session for debugging. safari-mcp drives the real Safari you're already logged into — your cookies and sessions, in the background, with 98 tools. See the full comparison below.
Highlights
- 98 tools — navigation, clicks, forms, screenshots, network, storage, accessibility, and more
- Zero heat — native WebKit on Apple Silicon, ~60% less CPU than Chrome
- Your real browser — keeps all logins, cookies, sessions (Gmail, GitHub, Ahrefs, etc.)
- Background operation — Safari stays in the background, no window stealing
- No browser dependencies — no Puppeteer, no Playwright, no WebDriver, no Chrome
- Persistent process — reuses a single osascript process (~5ms per command vs ~80ms)
- Framework-compatible — React, Vue, Angular, Svelte form filling via native setters
In users' own words
Not solicited testimonials — quotes lifted from the public issue tracker, each linked to the thread it came from.
"I run multiple Pi sessions/subagents against my normal Safari profile in parallel. Sharing its cookies and logins is intentional." — @maxim, on running concurrent agents against a real browser
"The server has a deliberate tab-ownership model … the code is careful about this, and for the default case that's the right safety posture." — @turner-moore, on why the guards refuse to touch your tabs
"Direct local validation from the package: Safari MCP doctor 6/6." — @jrepp, who found and fixed a queue-alignment bug in the focus helper
Quick Start
Prerequisites
- macOS (any version with Safari)
- Node.js 20+
- Safari → Settings → Advanced → Show features for web developers ✓
- Safari → Settings → Developer → Allow JavaScript from Apple Events ✓
Install (one command)
npx safari-mcp
That's it — no global install needed. Or install permanently:
npm install -g safari-mcp
Configure your MCP client
All clients run Safari MCP the same way — npx safari-mcp. Pick your editor:
Claude Code
claude mcp add safari -- npx safari-mcp
Or edit ~/.mcp.json:
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Restart Claude Desktop after saving.
Cursor
One-click: Install in Cursor
Or edit .cursor/mcp.json in your project:
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
VS Code / VS Code Insiders
One-click: Install in VS Code
Or edit .vscode/mcp.json:
{
"servers": {
"safari": {
"type": "stdio",
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Windsurf
Edit .windsurf/mcp.json in your project (or ~/.codeium/windsurf/mcp_config.json globally):
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Cline
Open Cline in VS Code → click the MCP icon → Edit MCP Settings → add:
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Continue
Edit ~/.continue/config.yaml (or .continue/config.yaml in workspace):
mcpServers:
- name: safari
command: npx
args:
- safari-mcp
Goose
Edit ~/.config/goose/config.yaml:
extensions:
safari:
name: safari
type: stdio
cmd: npx
args:
- safari-mcp
enabled: true
LM Studio
Open LM Studio → Settings → MCP Servers → Add Server:
- Name:
safari - Command:
npx - Args:
safari-mcp
Zed
Open Zed → Settings → search for "Context Servers" and add:
{
"context_servers": {
"safari": {
"command": {
"path": "npx",
"args": ["safari-mcp"]
}
}
}
}
Alternative: Homebrew
brew install achiya-automation/tap/safari-mcp
Alternative: from source
git clone https://github.com/achiya-automation/safari-mcp.git
cd safari-mcp && npm install
Usage Workflow
The recommended pattern for AI agents using Safari MCP:
1. safari_snapshot → Get page state (accessibility tree)
2. safari_click/fill/... → Interact with elements by ref
3. safari_snapshot → Verify the result
Element targeting — tools accept multiple targeting strategies:
| Strategy | Example | Best for |
|---|---|---|
| CSS selector | #login-btn, .submit |
Unique elements |
| Visible text | "Sign In", "Submit" |
Buttons, links |
| Coordinates | x: 100, y: 200 |
Canvas, custom widgets |
| Ref from snapshot | ref: "e42" |
Any element from accessibility tree |
Tip: Start with
safari_snapshotto get element refs, then use refs for precise targeting. This is faster and more reliable than CSS selectors.
Running several agents at once
Multiple agents or subagents driving one Safari at the same time will fight over the active tab — unless you run them against a shared HTTP daemon instead of one process per client:
SAFARI_MCP_HTTP=1 SAFARI_MCP_HTTP_PORT=9225 npx safari-mcp
Then point every client at it:
{ "mcpServers": { "safari-mcp": { "type": "http", "url": "http://127.0.0.1:9225/mcp" } } }
One daemon, many sessions — and each session gets its own tab state. The server keys activeTabIndex, the ownership flag and a unique tab marker off the MCP session id, so session A physically cannot read or steer session B's tab.
Two properties make this safe rather than merely tidy:
Tab identity is a marker, not an index. Each session stamps a unique id into the page it opens, so ownership survives navigation and survives the user reordering or closing other tabs. An index alone would silently drift onto the wrong tab.
It fails closed. If a session's marked tab can't be re-found, every tool refuses instead of falling back to whatever tab is in front — because that tab is usually yours:
Tab tracking lost — refusing to fall back to "current tab of window" (would target the user's active tab). Call safari_new_tab to reopen.
This also drops process count sharply: ~17 node processes for 17 concurrent sessions becomes 1.
SAFARI_PROFILE stays optional — leave it unset and sessions bind to your ordinary Safari windows, cookies and logins intact. Details in docs/http-transport-design.md.
Prefer stdio (one process per agent) over a persistent daemon? That works too — isolation then comes from the process boundary itself. One caveat: if your client multiplexes agents through mcporter, mcporter caches a single MCP client for all of them — whichever transport you pick — so the server never sees distinct sessions and per-session isolation can't engage. mcporter-lanes (a pi extension by @maxim, born out of #76) fixes this upstream: each agent session gets its own daemon dir — and therefore its own safari-mcp — with an idle timeout so processes don't pile up.
Acting on a tab you already have open
By default the server touches only tabs it opened itself. Point it at one of yours and it refuses:
Tab safety: refusing "click" — current tab (https://mail.example.com/inbox) was not
opened by this MCP session. Use safari_new_tab or safari_switch_tab to target your own tab.
That default exists because early versions did click into and close people's tabs. But "read the article I'm looking at" and "fill in the form on my screen" are real, and reopening the page loses the session state that made your tab worth using. Set SAFARI_MCP_ALLOW_USER_TABS=1 and an explicit safari_switch_tab adopts the tab instead of refusing it; from then on the session works in it like one of its own, and says so:
{ "tabIndex": 3, "safeUrl": "https://mail.example.com/inbox", "note": "(user tab, opted-in)" }
What the flag deliberately does not do:
- It unlocks adoption, not the guards. Only
safari_switch_tabadopts, and only the tab you named. An ordinary click or navigate still never lands on whatever tab happens to be in front — the server acts on the tab you pointed it at, not the one you wandered to. safari_close_tabstill refuses. Closing is the one action whose cost you cannot undo, so an adopted tab is writable, never disposable. Close it yourself.- Adoption is session-local. Nothing is written to the shared ownership file, so it ends with the session rather than leaking to the next process on the machine.
safari_doctor prints the flag's state, and every operation on an adopted tab logs (user tab, opted-in) — so "why did it touch my tab" has an answer instead of being a mystery. Default off; set it only for agents you want working inside your own browsing session. Designed in #92.
Environment variables
| Variable | Default | What it does |
|---|---|---|
SAFARI_MCP_HTTP |
off | Run one shared HTTP daemon instead of a process per client (see above). |
SAFARI_MCP_HTTP_PORT |
9225 |
Port for that daemon. |
SAFARI_PROFILE |
unset | Bind sessions to a named Safari profile. Unset = your ordinary windows. |
SAFARI_MCP_ALLOW_USER_TABS |
off | Let safari_switch_tab adopt a tab you already had open, instead of refusing it (see below). |
SAFARI_MCP_RAISE_ON_NAVIGATE |
off | Let navigation bring Safari to the front, and stop the focus guard from putting your previous app back. |
SAFARI_MCP_SCREENSHOT_MAX_WIDTH |
unset | Downscale every safari_screenshot to this pixel width (Retina captures are 2× the viewport). Per-call maxWidth overrides it. |
SAFARI_MCP_KEEPALIVE_TAB |
off | Keep one daemon-served page open in the profile window so Safari never parks the extension worker between commands. |
SAFARI_MCP_OPEN_WINDOW_CMD |
unset | Command run with the profile name when the profile window is absent (e.g. after a reboot). Must open the window without focusing Safari. |
SAFARI_MCP_RAISE_ON_NAVIGATE=1 is for agents whose whole point is showing you a page — a voice assistant answering "open YouTube", a demo driver. Everything else should leave it off: by default Safari MCP works in the background and hands focus back to whatever app you were using, so an agent can drive a page while you keep typing somewhere else.
Tools (98)
Click to expand the full tool list — organized by category
Navigation (4)
| Tool | Description |
|---|---|
safari_navigate |
Navigate to URL (auto HTTPS, wait for load) |
safari_go_back |
Go back in history |
safari_go_forward |
Go forward in history |
safari_reload |
Reload page (optional hard reload) |
Page Reading (3)
| Tool | Description |
|---|---|
safari_read_page |
Get title, URL, and text content |
safari_get_source |
Get full HTML source |
safari_navigate_and_read |
Navigate + read in one call |
Click & Interaction (6)
| Tool | Description |
|---|---|
safari_click |
Click by CSS selector, visible text, or coordinates |
safari_double_click |
Double-click (select word, etc.) |
safari_right_click |
Right-click (context menu) |
safari_hover |
Hover over element |
safari_click_and_wait |
Click + wait for navigation |
safari_click_and_read |
Click then return the updated page — saves a round-trip (React Router + full loads) |
Form Input (11)
| Tool | Description |
|---|---|
safari_fill |
Fill input (React/Vue/Angular compatible) |
safari_clear_field |
Clear input field |
safari_select_option |
Select dropdown option |
safari_fill_form |
Batch fill multiple fields |
safari_fill_and_submit |
Fill form + submit in one call |
safari_type_text |
Type real keystrokes (JS-based, no System Events) |
safari_press_key |
Press key with modifiers |
safari_react_select_set |
Set a react-select v5 value via React fiber — bypasses the menu UI |
safari_react_select_list_options |
List a react-select v5 dropdown's options without opening it |
safari_replace_editor |
Replace all content in a code editor (Monaco, CodeMirror, Ace, ProseMirror) |
safari_verify_state |
Verify an editor's framework-level state matches expected — catch stale DOM before Submit |
Screenshots & PDF (3)
| Tool | Description |
|---|---|
safari_screenshot |
Screenshot as PNG (viewport or full page) |
safari_screenshot_element |
Screenshot a specific element |
safari_save_pdf |
Export page as PDF |
Scroll (3)
| Tool | Description |
|---|---|
safari_scroll |
Scroll up/down by pixels |
safari_scroll_to |
Scroll to exact position |
safari_scroll_to_element |
Smooth scroll to element |
Tab Management (5)
| Tool | Description |
|---|---|
safari_list_tabs |
List all tabs (index, title, URL) |
safari_new_tab |
Open new tab (background, no focus steal) |
safari_close_tab |
Close tab |
safari_switch_tab |
Switch to tab by index |
safari_wait_for_new_tab |
Wait for a new tab (e.g. OAuth popup) and auto-switch to it |
Wait (2)
| Tool | Description |
|---|---|
safari_wait_for |
Wait for element, text, or URL change |
safari_wait |
Wait for specified milliseconds |
JavaScript (3)
| Tool | Description |
|---|---|
safari_evaluate |
Execute arbitrary JavaScript, return result |
safari_eval_file |
Execute JavaScript read from a file path (avoids huge inline scripts) |
safari_list_frames |
List every document in the tab (main page + each iframe) with frameId, URL and text length — pass a frameId to safari_evaluate's frame to run inside a cross-origin iframe |
Element Inspection (4)
| Tool | Description |
|---|---|
safari_get_element |
Element details (tag, rect, attrs, visibility) |
safari_query_all |
Find all matching elements |
safari_get_computed_style |
Computed CSS styles |
safari_detect_forms |
Auto-detect all forms with field selectors |
Accessibility (2)
| Tool | Description |
|---|---|
safari_accessibility_snapshot |
Full a11y tree: roles, ARIA, focusable elements |
safari_snapshot |
Accessibility tree with ref IDs for every interactive element — preferred way to see page state |
Drag & Drop (1)
| Tool | Description |
|---|---|
safari_drag |
Drag between elements or coordinates |
File Operations (2)
| Tool | Description |
|---|---|
safari_upload_file |
Upload file via JS DataTransfer (no file dialog!) |
safari_paste_image |
Paste image into editor (no clipboard touch!) |
Dialog & Window (2)
| Tool | Description |
|---|---|
safari_handle_dialog |
Handle alert/confirm/prompt |
safari_resize |
Resize browser window |
Device Emulation (2)
| Tool | Description |
|---|---|
safari_emulate |
Emulate device (iPhone, iPad, Pixel, Galaxy) |
safari_reset_emulation |
Reset to desktop |
Cookies & Storage (11)
| Tool | Description |
|---|---|
safari_get_cookies |
Get all cookies |
safari_set_cookie |
Set cookie with all options |
safari_delete_cookies |
Delete one or all cookies |
safari_local_storage |
Read localStorage |
safari_set_local_storage |
Write localStorage |
safari_delete_local_storage |
Delete/clear localStorage |
safari_session_storage |
Read sessionStorage |
safari_set_session_storage |
Write sessionStorage |
safari_delete_session_storage |
Delete/clear sessionStorage |
safari_export_storage |
Export all storage as JSON (backup/restore sessions) |
safari_import_storage |
Import storage state from JSON |
Clipboard (2)
| Tool | Description |
|---|---|
safari_clipboard_read |
Read clipboard text |
safari_clipboard_write |
Write text to clipboard |
Network (6)
| Tool | Description |
|---|---|
safari_network |
Quick network requests via Performance API |
safari_start_network_capture |
Start detailed capture (fetch + XHR) |
safari_network_details |
Get captured requests with headers/timing |
safari_clear_network |
Clear captured requests |
safari_mock_route |
Mock network responses (intercept fetch/XHR) |
safari_clear_mocks |
Remove all network mocks |
Console (4)
| Tool | Description |
|---|---|
safari_start_console |
Start capturing console messages |
safari_get_console |
Get all captured messages |
safari_clear_console |
Clear captured messages |
safari_console_filter |
Filter by level (log/warn/error) |
Performance (2)
| Tool | Description |
|---|---|
safari_performance_metrics |
Navigation timing, Web Vitals, memory |
safari_throttle_network |
Simulate slow-3g/fast-3g/4g/offline |
Data Extraction (4)
| Tool | Description |
|---|---|
safari_extract_tables |
Tables as structured JSON |
safari_extract_meta |
All meta: OG, Twitter, JSON-LD, canonical |
safari_extract_images |
Images with dimensions and loading info |
safari_extract_links |
Links with rel, external/nofollow detection |
Advanced (7)
| Tool | Description |
|---|---|
safari_override_geolocation |
Override browser geolocation |
safari_list_indexed_dbs |
List IndexedDB databases |
safari_get_indexed_db |
Read IndexedDB records |
safari_css_coverage |
Find unused CSS rules |
safari_analyze_page |
Full page analysis in one call |
safari_doctor |
Diagnose the macOS permission + daemon chain (Apple Events, Accessibility, Screen Recording, codesign) with per-failure fixes |
safari_reload_extension |
Hot-reload the Safari MCP Bridge extension without a manual toggle |
Automation (1)
| Tool | Description |
|---|---|
safari_run_script |
Run multiple actions in a single call (batch) |
Native Input — CGEvent (4)
| Tool | Description |
|---|---|
safari_native_click |
OS-level mouse click (CGEvent, isTrusted: true) — bypasses WAF/bot detection when safari_click is blocked (405/403) |
safari_native_hover |
OS-level cursor hover — triggers real :hover/mouseenter for tooltips and obfuscated UIs |
safari_native_type |
Insert text via the real paste pipeline — ProseMirror/Slate/Draft.js process it natively so Submit sends real data |
safari_native_keyboard |
OS-level keypress + modifiers to Safari, no focus steal — reaches React trust-gated handlers (Discord/Slack send) |
iOS & WebKit Validation (4)
| Tool | Description |
|---|---|
safari_inspect_viewport |
Validate the <meta name=viewport> tag for iOS Safari (device-width, zoom/WCAG, viewport-fit) |
safari_safe_area_insets |
Read live safe-area-inset values + viewport-fit / env() usage (notch / Dynamic Island) |
safari_check_pwa |
Audit iOS "Add to Home Screen" / PWA readiness (apple-touch-icon, manifest, theme-color, splash) |
safari_webkit_compat |
Check page CSS against this Safari via CSS.supports() — unsupported props, missing -webkit- prefixes, known quirks |
Security
Safari MCP runs locally on your Mac with minimal attack surface:
| Aspect | Detail |
|---|---|
| Network | No remote connections — all communication is local (stdio + localhost) |
| Permissions | macOS system permissions required (Screen Recording for screenshots) |
| Data | No telemetry, no analytics, no data sent anywhere |
| Extension | Communicates only with the local profile bridges (localhost:9224/9228/9232/9236), validated by Safari |
| Code | Fully open source (MIT) — audit every line |
Safari MCP vs Alternatives
| Feature | Safari MCP | Chrome DevTools MCP | Playwright MCP |
|---|---|---|---|
| CPU/Heat | 🟢 Minimal | 🔴 High | 🟡 Medium |
| Your logins | ✅ Yes | ✅ Yes | ❌ No |
| macOS native | ✅ WebKit | ❌ Chromium | ❌ Chromium/WebKit |
| Browser dependencies | None | Chrome + debug port | Playwright runtime |
| Tools | 98 | ~30 | ~25 |
| File upload | JS (no dialog) | CDP | Playwright API |
| Image paste | JS (no clipboard) | CDP | Playwright API |
| Focus steal | ❌ Background | ❌ Background | ❌ Headless |
| Network mocking | ✅ | ❌ | ✅ |
| Lighthouse | ❌ | ✅ | ❌ |
| Performance trace | ❌ | ✅ | ❌ |
Tip: Use Safari MCP for daily browsing tasks (95% of work) and Chrome DevTools MCP only for Lighthouse/Performance audits.
vs Apple's Official Safari MCP (safaridriver)
In July 2026 Apple shipped an official Safari MCP server built on safaridriver — first in Safari Technology Preview 247, and in stable Safari since 27.0. That's great validation for the category — and it's built for a different job. Apple's server drives an isolated WebDriver automation session for debugging; safari-mcp drives the real Safari you're already logged into.
On macOS 27.0 with Safari 27.0, safaridriver --help lists --mcp and its tools/list answers with 17 entries (verified 2026-09-15); Safari 26.5.2 had no --mcp (verified 2026-07-23). Check your own machine with safaridriver --help | grep mcp.
Running both? Apple's setup guide registers its server under the name
safari-mcp(claude mcp add safari-mcp -- /usr/bin/safaridriver --mcp) — the same name as this package. Give one of them a different name, e.g.claude mcp add safari-webdriver -- /usr/bin/safaridriver --mcp, so your client doesn't overwrite one with the other or leave you guessing which server answered.
| 🦁 safari-mcp (this repo) | Apple safaridriver --mcp |
|
|---|---|---|
| Your real logins / cookies | ✅ Your actual Safari | ⚠️ Isolated automation session — no access to AutoFill or browsing activity |
| Runs on | ✅ Stable Safari, every Mac | ⚠️ Safari 27 or later, or Safari Technology Preview 247+ |
| Background (no focus steal) | ✅ Yes | ❌ Dedicated window with a "controlled by automation" banner |
| Tools | 98 | 17 |
| Storage (cookies, localStorage, IndexedDB) | ✅ 10 tools | ❌ |
| Network mocking + throttling | ✅ Yes | ❌ Read-only network inspection |
| Device emulation (iPhone, iPad) | ✅ Yes | ⚠️ Viewport + media type only |
| Setup | npx safari-mcp |
Enable "Allow remote automation and external agents" in Safari's Developer settings, then point your client at safaridriver --mcp |
| Official Apple support | ❌ Community (MIT) | ✅ Apple, WebDriver-standard |
When Apple's server is the right pick: you specifically want a clean-room, WebDriver-standard session for compatibility debugging on Safari 27 or later. For everything else — daily automation on the browser you're already signed into, in the background — safari-mcp is built for exactly that.
Why Safari MCP and Not the Other Safari MCP Projects?
There are several "safari-mcp" projects floating around. Here's how they compare:
| Feature | 🦁 safari-mcp (this repo) | lxman/safari-mcp-server | Epistates/MCPSafari | HayoDev/safari-devtools-mcp |
|---|---|---|---|---|
| Tools | 98 | ~10 | 23 | ~15 |
| Install | npx safari-mcp |
Manual | Binary | npx |
| Engine | Dual (Extension + AppleScript) | WebDriver | Extension only | DevTools Protocol |
| Keeps your real Safari logins | ✅ Yes | ⚠️ Limited | ✅ Yes | ❌ Debug session |
| Background (no focus steal) | ✅ Yes | ❌ No | ⚠️ Sometimes | ✅ Yes |
| Storage tools (cookies, localStorage, IndexedDB) | 10 | 0 | 0 | 2 |
| Data extraction (tables, meta, images, links) | 4 | 0 | 0 | 0 |
| Network mocking | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Device emulation (iPhone, iPad, Pixel) | ✅ Yes | ❌ No | ❌ No | ❌ No |
| File upload (no dialog) | ✅ JS DataTransfer | ❌ No | ❌ No | ❌ No |
| Image paste (no clipboard touch) | ✅ Yes | ❌ No | ❌ No | ❌ No |
| PDF export | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Console capture | 4 tools | 0 | 1 | 1 |
| Performance metrics + Web Vitals | ✅ Yes | ❌ No | ❌ No | ⚠️ Partial |
| Active maintenance | ✅ Multiple releases/week | 🟡 Sporadic | 🟡 Slow | 🟡 Slow |
| License | MIT | MIT | None specified | MIT |
| In MCP Registry | ✅ | ❌ | ❌ | ✅ |
| In Awesome MCP | ✅ | ❌ | ❌ | ❌ |
TL;DR — if you want the most complete Safari MCP with the smoothest install, the most tools, and active maintenance, this is the one.
Architecture
Safari MCP uses a dual-engine architecture — the Extension is preferred for speed and advanced capabilities, with AppleScript as an always-available fallback:
Claude/Cursor/AI Agent
↓ MCP Protocol (stdio)
Safari MCP Server (Node.js)
↓ ↓
Extension (HTTP) AppleScript + Swift daemon
(~5-20ms/cmd) (~5ms/cmd, always available)
↓ ↓
Content Script do JavaScript in tab N
↓ ↓
Page DOM ←←←←←←←←←← Page DOM
Key design decisions:
- Dual engine with automatic fallback — Extension is preferred; if not connected, AppleScript handles everything seamlessly
- Persistent Swift helper — one long-running process instead of spawning per command (16x faster)
- Tab-indexed operations — all JS runs on a specific tab by index, never steals visual focus
- JS-first approach — typing, clicking, file upload all use JavaScript events (no System Events keyboard conflicts)
- No
activate— Safari is never brought to foreground
Safari Extension (Optional)
The Safari MCP Extension is optional but recommended. Without it, ~80% of functionality works via AppleScript alone. The extension adds capabilities that AppleScript cannot provide:
What the Extension Adds
| Capability | With Extension | AppleScript Only |
|---|---|---|
| Closed Shadow DOM (Reddit, Web Components) | ✅ Full access | ❌ Invisible |
| Strict CSP sites | ✅ Bypasses via MAIN world | ❌ Often blocked |
| React/Vue/Angular state manipulation | ✅ Deep (Fiber, ProseMirror) | ⚠️ Basic |
| Loading state detection (spinners, skeletons) | ✅ Smart detection | ❌ No |
| Dialog handling (alert/confirm) | ❌ | ✅ Only AppleScript |
| Native OS-level click (CGEvent) | ❌ | ✅ Only AppleScript |
| PDF export | ❌ | ✅ Only AppleScript |
When do you need the extension? If you're automating modern SPAs with closed shadow DOM (e.g., Reddit), sites with strict Content Security Policy, or framework-heavy editors (Draft.js, ProseMirror, Slate).
Installing the Extension
The extension requires a one-time build with Xcode (free, included with macOS).
Note for npm users: The
xcode/directory is not included in the npm package. Clone the GitHub repository to build from source.
Prerequisites: Xcode (install from App Store — free)
# 1. Clone the repo (the npm package does not include the Xcode project)
git clone https://github.com/achiya-automation/safari-mcp.git
cd safari-mcp
# 2. Build a clean, Apple Development-signed app
# If needed, first choose your Team in Xcode → Signing & Capabilities.
xcodebuild -project "xcode/Safari MCP/Safari MCP.xcodeproj" \
-scheme "Safari MCP (macOS)" -configuration Release \
-allowProvisioningUpdates clean build
# 3. Locate the signed app
APP_PATH=$(find ~/Library/Developer/Xcode/DerivedData/Safari_MCP-*/Build/Products/Release -name "Safari MCP.app" -maxdepth 2 | head -1)
# 4. Re-sign safari-helper with the Apple Events entitlement
# (helps macOS surface the TCC Automation prompt reliably)
codesign --sign - --force --entitlements safari-helper.entitlements safari-helper
# 5. Open the app (needed once so Safari registers the extension)
open "$APP_PATH"
Do not ad-hoc re-sign
Safari MCP.appwithcodesign --deep --sign -. That replaces the Apple Development identity on the wrapper and embedded extension; current Safari can then silently disable or mark the extension as removed. If a previous build was ad-hoc signed, run theclean buildcommand above again.
Alternatively, open xcode/Safari MCP/Safari MCP.xcodeproj directly in Xcode, select your Apple ID under Signing & Capabilities, and click Run. A free personal Apple Developer account is sufficient for local use.
Then in Safari:
- Safari → Settings → Advanced → enable Show features for web developers
- Safari → Settings → Developer → Allow unsigned extensions (required each Safari restart)
- Safari → Settings → Extensions → enable Safari MCP Bridge
The extension connects automatically to the first local bridge whose declared Safari profile matches its own. The default bridge ports are 9224, 9228, 9232, and 9236; a single-profile setup normally uses 9224.
Note: "Allow unsigned extensions" resets every time Safari restarts. You'll need to re-enable it in Safari → Settings → Developer after each restart. The extension itself stays installed.
Toolbar icon status:
- ON — connected to MCP server
- OFF — manually disabled via popup
- (no badge) — server not running, will auto-reconnect
macOS Permissions
Safari MCP needs these one-time permissions:
| Permission | Where | Why |
|---|---|---|
| JavaScript from Apple Events | Safari → Settings → Developer | Required for do JavaScript |
| Automation → Safari | System Settings → Privacy & Security → Automation | Required for all AppleScript-backed tools |
| Screen Recording | System Settings → Privacy & Security → Screen & System Audio Recording | Required for safari_screenshot without the extension, and for safari_save_pdf |
| Accessibility (safari-helper) | System Settings → Privacy & Security → Accessibility (named Device Control and Data Access on macOS 27) | Required for safari_native_click, safari_native_keyboard, safari_native_hover and safari_save_pdf |
Granting Accessibility to safari-helper (required for safari_native_*)
The safari_native_click, safari_native_keyboard and safari_native_hover tools inject OS-level CGEvent events into Safari without stealing focus. macOS requires the underlying helper binary to be approved in Accessibility before those events can reach a non-frontmost window.
- Open System Settings → Privacy & Security → Accessibility (on macOS 27 the pane is named Device Control and Data Access).
- Click
+(unlock with your password if needed). - Navigate to the helper binary and add it:
- npm global install:
$(npm root -g)/safari-mcp/safari-helper - npx / project install:
./node_modules/safari-mcp/safari-helper - From source clone:
/path/to/safari-mcp/safari-helper
- npm global install:
- Make sure the toggle next to it is ON.
The postinstall script re-signs the helper with a stable identifier (com.achiya-automation.safari-mcp) so this permission survives future upgrades — without that step, every npm update would silently revoke approval because the binary's adhoc-signed identifier changes per build.
If safari_native_click reports success but the page doesn't react (no isTrusted: true click events fire), the helper is most likely missing this approval. The safari_* (non-native_) tools don't need it.
Granting Automation → Safari (important for IDE users)
macOS TCC grants Automation permission to the parent process that spawns the MCP server, not to safari-mcp itself. So you need to grant Automation → Safari to the app that runs Claude Code / Cursor / Windsurf — typically Visual Studio Code or Terminal.
If the permission dialog never appears automatically, run this command once from a Terminal that already has Automation permission:
osascript -e 'tell application "Safari" to get URL of current tab of window 1'
That call registers the Terminal app in the Automation database and then triggers the prompt for Safari. After you approve it, subsequent MCP calls from any child process chain will work.
Troubleshooting
| Issue | Fix |
|---|---|
| "AppleScript error" | Enable "Allow JavaScript from Apple Events" in Safari → Settings → Developer |
| "Not authorized to send Apple events to Safari" | Grant Automation → Safari to your IDE (see above) |
"Not authorized" after npm update |
Updating changes the binary's cdhash — macOS silently revokes Automation permission. Re-run the osascript one-liner above to re-grant it |
safari_native_click reports success but page doesn't react |
Add safari-helper to System Settings → Privacy & Security → Accessibility (see Granting Accessibility above). Confirm by attaching a click listener with {capture:true} in the page console — without the grant, no isTrusted: true event fires |
| Screenshots empty | Grant Screen & System Audio Recording to Terminal/VS Code — or, when safari-mcp runs as a LaunchAgent, to the node binary itself (a Homebrew node upgrade changes its path, so re-grant after upgrading). safari_doctor names the exact binary |
| Extension missing after enabling "Allow unsigned extensions" (macOS 27) | Known macOS 27.0 issue (183044008): with the setting on, rebuild the extension app (see Installing the Extension) and it appears |
| Tab not found | Call safari_list_tabs to refresh tab indices |
| Hebrew keyboard issues | All typing uses JS events — immune to keyboard layout |
| HTTPS blocked | safari_navigate auto-tries HTTPS first, falls back to HTTP |
| Safari steals focus | Ensure you're on latest version — newTab restores your active tab |
Works With
Safari MCP works with any MCP-compatible client:
| Client | Status |
|---|---|
| Claude Code | ✅ Tested daily |
| Claude Desktop | ✅ Tested |
| Cursor | ✅ Tested |
| Windsurf | ✅ Compatible |
| VS Code + Continue | ✅ Compatible |
Contributing
PRs welcome! See CONTRIBUTING.md for setup instructions.
The codebase is two files:
safari.js— Safari automation layer (AppleScript + JavaScript)index.js— MCP server with tool definitions
Sponsors
Safari MCP is free and open source. If it saves you time or CPU cycles, consider supporting its development:
Your support funds:
- 🧪 Testing across macOS versions and Safari releases
- 🛠️ New tools and features
- 📖 Documentation and examples
Commercial Support
Need Safari MCP integrated into your product or agent stack? Achiya Automation offers:
- Priority bug fixes and custom tool development for your use case
- Integration consulting — wiring Safari MCP into production agent systems (Claude, Cursor, n8n, custom)
- Private deployment support — multi-user Safari MCP, non-standard macOS environments, CI/CD
- Training workshops for engineering teams adopting MCP-based automation
Built by the author of Safari MCP. Start a conversation →
What agents unlock with Safari MCP
When an AI agent drives Safari MCP, it gets things a headless browser can't:
- Real authenticated sessions — Gmail, GitHub, Ahrefs, Slack, banking dashboards are all already logged in
- Framework-aware form filling —
safari_fill_and_submitcalls React/Vue/Angular setters natively, no guessing whetherinputevents fired - Background operation — the agent works in parallel while you keep using your Mac
- One MCP call per workflow —
safari_run_scriptbatches navigation + clicks + extraction into a single roundtrip
The pattern holds across models: drive the browser the human already trusts — you inherit logins, cookies, extensions, and the user's exact environment in one step.
Community
6,000+ monthly npm downloads — developers are building AI agents on macOS with Safari MCP.
- GitHub Discussions — ask questions, share use cases
- Issues — bug reports and feature requests
- Good First Issues — start contributing
Ecosystem
Other macOS MCP servers that complement Safari MCP:
| Project | What it does | When to use |
|---|---|---|
| mcp-server-macos-use | OS-level macOS automation (accessibility, screen control) | System-wide interactions beyond Safari |
| chrome-devtools-mcp | Chrome DevTools Protocol | Lighthouse audits, Chrome-specific performance traces |
Using Safari MCP alongside Chrome DevTools MCP? Safari handles 95% of daily browsing (zero overhead), Chrome handles the 5% that needs Lighthouse or Chrome-specific traces.
Like it? Give it a ⭐
If Safari MCP saves you from Chrome overhead, a star helps others discover it:
Share on Twitter/X · Share on LinkedIn · Write about it
Listed On
Glama MCP Registry MCP Scoreboard CI
License
MIT — use it however you want.
Built by Achiya Automation — automation & AI agents for business.
Установить Safari Server в Claude Desktop, Claude Code, Cursor
unyly install safari-mcp-serverСтавит в Claude Desktop, Claude Code, Cursor и VS Code — сам разбирается с npx, uvx и сборкой из исходников.
Впервые? Поставь CLI: curl -fsSL https://unyly.org/install | sh
Или настроить вручную
Выполни в терминале:
claude mcp add safari-mcp-server --env APP_PATH="" --env SAFARI_MCP_HTTP="" -- npx -y safari-mcpПошаговые гайды: как установить Safari Server
FAQ
Safari Server MCP бесплатный?
Да, Safari Server MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Safari Server?
Да, требуются переменные окружения: APP_PATH, SAFARI_MCP_HTTP. Unyly подставит их в конфиг при установке.
Safari Server — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Safari Server в Claude Desktop, Claude Code или Cursor?
Открой Safari Server на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Изменения
Версии и запрашиваемые доступы со временем.
- Новая версия опубликована
- Новая версия опубликована
- Новая версия опубликована
- Новая версия опубликована
- Новая версия опубликована
Похожие MCP
Playwright
Browser automation, scraping, screenshots
автор: MicrosoftPuppeteer
Browser automation and web scraping.
автор: modelcontextprotocolGarmin Connect
An MCP server for Garmin Connect that provides access to fitness activities, health statistics, and sleep data by routing requests through a headless browser to
автор: etweisbergHiggsfield Unlimited
MCP server for Higgsfield AI that enables unlimited-mode image, video, audio generation, uploads, and job management via multiple parallel accounts, using brows
автор: nukIeeropentabs-dev/opentabs
Plugin-based MCP server + Chrome extension that gives AI agents access to web applications through the user's authenticated browser session. 100+ plugins with a
автор: opentabs-devrobhunter/agentdeals
1,500+ developer infrastructure deals, free tiers, and startup programs across 54 categories. Search deals, compare vendors, plan stacks, and track pricing chan
автор: robhunterhlydecker/ucsc-genome-mcp
MCP server to interact with the UCSC Genome Browser API, letting you find genomes, chromosomes, and more.
автор: hlydecker34892002/bilibili-mcp-js
A MCP server that supports searching for Bilibili content. Provides LangChain integration examples and test scripts.
автор: 34892002achiya-automation/safari-mcp
Native Safari browser automation for AI agents with 80+ tools. No Chrome dependency, optimized for Apple Silicon with 60% less CPU overhead.
автор: achiya-automationagent-infra/mcp-server-browser
Browser automation capabilities using Puppeteer, both support local and remote browser connection.
автор: bytedanceCompare Safari Server with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории browse
