loading…
Search for a command to run...
loading…
Native Safari browser automation for AI agents with 80+ tools. No Chrome dependency, optimized for Apple Silicon with 60% less CPU overhead.
Native Safari browser automation for AI agents with 80+ tools. No Chrome dependency, optimized for Apple Silicon with 60% less CPU overhead.

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

Your AI agent needs to browse. So it either:
Your AI drives the Safari you're already logged into — Gmail, GitHub, Ahrefs, Slack, banking.
Native WebKit. ~60% less CPU. Background operation. 80 tools. One npx command. macOS only.
📰 Featured on HackerNoon: I Had to Reverse-Engineer React, Shadow DOM, and CSP to Automate Safari Without Chrome
npx safari-mcp
That's it — no global install needed. Or install permanently:
npm install -g safari-mcp
All clients run Safari MCP the same way — npx safari-mcp. Pick your editor:
claude mcp add safari -- npx safari-mcp
Or edit ~/.mcp.json:
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Restart Claude Desktop after saving.
One-click: Install in Cursor
Or edit .cursor/mcp.json in your project:
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
One-click: Install in VS Code
Or edit .vscode/mcp.json:
{
"servers": {
"safari": {
"type": "stdio",
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Edit .windsurf/mcp.json in your project (or ~/.codeium/windsurf/mcp_config.json globally):
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Open Cline in VS Code → click the MCP icon → Edit MCP Settings → add:
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
Edit ~/.continue/config.yaml (or .continue/config.yaml in workspace):
mcpServers:
- name: safari
command: npx
args:
- safari-mcp
Edit ~/.config/goose/config.yaml:
extensions:
safari:
name: safari
type: stdio
cmd: npx
args:
- safari-mcp
enabled: true
Open LM Studio → Settings → MCP Servers → Add Server:
safarinpxsafari-mcpOpen Zed → Settings → search for "Context Servers" and add:
{
"context_servers": {
"safari": {
"command": {
"path": "npx",
"args": ["safari-mcp"]
}
}
}
}
brew install achiya-automation/tap/safari-mcp
git clone https://github.com/achiya-automation/safari-mcp.git
cd safari-mcp && npm install
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.
| 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) |
| 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 |
| 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 |
| 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 |
| 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 |
| Tool | Description |
|---|---|
safari_scroll |
Scroll up/down by pixels |
safari_scroll_to |
Scroll to exact position |
safari_scroll_to_element |
Smooth scroll to element |
| 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 |
| Tool | Description |
|---|---|
safari_wait_for |
Wait for element, text, or URL change |
safari_wait |
Wait for specified milliseconds |
| Tool | Description |
|---|---|
safari_evaluate |
Execute arbitrary JavaScript, return result |
| 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 |
| Tool | Description |
|---|---|
safari_accessibility_snapshot |
Full a11y tree: roles, ARIA, focusable elements |
| Tool | Description |
|---|---|
safari_drag |
Drag between elements or coordinates |
| Tool | Description |
|---|---|
safari_upload_file |
Upload file via JS DataTransfer (no file dialog!) |
safari_paste_image |
Paste image into editor (no clipboard touch!) |
| Tool | Description |
|---|---|
safari_handle_dialog |
Handle alert/confirm/prompt |
safari_resize |
Resize browser window |
| Tool | Description |
|---|---|
safari_emulate |
Emulate device (iPhone, iPad, Pixel, Galaxy) |
safari_reset_emulation |
Reset to desktop |
| 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 |
| Tool | Description |
|---|---|
safari_clipboard_read |
Read clipboard text |
safari_clipboard_write |
Write text to clipboard |
| 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 |
| 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) |
| Tool | Description |
|---|---|
safari_performance_metrics |
Navigation timing, Web Vitals, memory |
safari_throttle_network |
Simulate slow-3g/fast-3g/4g/offline |
| 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 |
| 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 |
| Tool | Description |
|---|---|
safari_run_script |
Run multiple actions in a single call (batch) |
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 localhost:9224, validated by Safari |
| Code | Fully open source (MIT) — audit every line |
| 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 | 80 | ~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.
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 | 80 | ~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.
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:
activate — Safari is never brought to foregroundThe Safari MCP Extension is optional but recommended. Without it, ~80% of functionality works via AppleScript alone. The extension adds capabilities that AppleScript cannot provide:
| 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).
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 the extension
xcodebuild -project "xcode/Safari MCP/Safari MCP.xcodeproj" \
-scheme "Safari MCP (macOS)" -configuration Release build
# 3. Ad-hoc sign the built app so Safari will load it
# (xcodebuild without a signing identity produces a bundle Safari silently rejects)
APP_PATH=$(find ~/Library/Developer/Xcode/DerivedData/Safari_MCP-*/Build/Products/Release -name "Safari MCP.app" -maxdepth 2 | head -1)
codesign --sign - --force --deep "$APP_PATH"
# 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
# 4. Open the app (needed once so Safari registers the extension)
open "$APP_PATH"
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:
The extension connects automatically to the MCP server on port 9224.
Note: "Allow Unsigned Extensions" resets every time Safari restarts. You'll need to re-enable it in the Develop menu after each restart. The extension itself stays installed.
Toolbar icon status:
Safari MCP needs these one-time permissions:
| Permission | Where | Why |
|---|---|---|
| JavaScript from Apple Events | Safari → Develop menu | Required for do JavaScript |
| Automation → Safari | System Settings → Privacy & Security → Automation | Required for all AppleScript-backed tools |
| Screen Recording | System Settings → Privacy & Security → Screen Recording | Required for safari_screenshot |
| Accessibility | System Settings → Privacy & Security → Accessibility | Required for safari_save_pdf only |
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.
| Issue | Fix |
|---|---|
| "AppleScript error" | Enable "Allow JavaScript from Apple Events" in Safari → Develop |
| "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 |
| Screenshots empty | Grant Screen Recording permission to Terminal/VS Code |
| 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 |
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 |
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 definitionsSafari MCP is free and open source. If it saves you time or CPU cycles, consider supporting its development:
Your support funds:
Need Safari MCP integrated into your product or agent stack? Achiya Automation offers:
Built by the author of Safari MCP. Start a conversation →
When an AI agent drives Safari MCP, it gets things a headless browser can't:
safari_fill_and_submit calls React/Vue/Angular setters natively, no guessing whether input events firedsafari_run_script batches navigation + clicks + extraction into a single roundtripThe 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.
6,000+ monthly npm downloads — developers are building AI agents on macOS with Safari MCP.
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.
If Safari MCP saves you from Chrome overhead, a star helps others discover it:
Share on Twitter/X · Share on LinkedIn · Write about it
MIT — use it however you want.
Add this to claude_desktop_config.json and restart Claude Desktop.
{
"mcpServers": {
"achiya-automation-safari-mcp": {
"command": "npx",
"args": []
}
}
}