Skip to content

Release history

achiya-automation/safari-mcp releases

Native Safari browser automation for AI agents with 80+ tools. No Chrome dependency, optimized for Apple Silicon with 60% less CPU overhead.

All releases

70 shown

Review required
v2.12.0 Breaking risk
Auth RBAC RCE / SSRF +2 more

Async fixes + injection hardening + tab safety

No immediate action
v2.11.9 Bug fix

MCP init no longer blocks

No immediate action
v2.11.8 Maintenance

Routine maintenance and dependency updates.

Upgrade now
v2.11.6 Security relevant
Dependencies

qs bump

Upgrade now
v2.11.5 Security relevant
RCE / SSRF

`ws` version bump

Config change
v2.11.4 Bug fix
Auth

Proxy‑command profile guard

No immediate action
v2.11.3 Bug fix

safari_select_option fix

No immediate action
v2.11.2 Breaking risk

MCP concurrency fix + evaluate improvement

No immediate action
v2.11.1 Maintenance

Routine maintenance and dependency updates.

No immediate action
v2.11.0 Bug fix

Reliability fixes + visibility spoofing

No immediate action
v2.10.10 Bug fix

Profile-window detection + runJS fallback fix

No immediate action
v2.10.9 Maintenance

Routine maintenance and dependency updates.

No immediate action
v2.10.8 Maintenance

Routine maintenance and dependency updates.

No immediate action
v2.10.7 Maintenance

Routine maintenance and dependency updates.

No immediate action
v2.10.6 Maintenance

Routine maintenance and dependency updates.

Upgrade now
v2.10.5 Security relevant
Auth RBAC RCE / SSRF +1 more

Security fixes

v2.10.4 Bug fix

Fixed `safari_fill` dropping middle paragraphs in ProseMirror editors and prevented duplicate text on synthetic paste.

Full changelog

Fixed

safari_fill no longer loses middle paragraphs in ProseMirror/Tiptap editors with no view access (Hashnode-style)

When a ProseMirror-based editor doesn't expose its view via pmViewDesc or React Fiber walk (Hashnode's contenteditable has neither), safari_fill previously fell through to char-by-char beforeinput + execCommand('insertText') per line. On multi-paragraph content with markdown-like first characters (>, **, [), the editor dropped middle paragraphs silently — the result was the first paragraph + a chain of empty paragraphs + the last paragraph.

The fix verifies actual content length after the char-by-char pass; if the rendered text is less than 60% of the expected value length, it clears the editor and re-fills via execCommand('insertHTML') with paragraph-wrapped HTML. The new return marker Filled CE (ProseMirror insertHTML fallback, <actual>/<expected>) makes the path observable.

Originally surfaced cross-posting a dev.to article body to a Hashnode draft — char-by-char filled 446 chars out of an expected 6800+, retaining only the first and last paragraphs.

safari_fill synthetic-paste path now explicitly clears pre-existing content before paste

X's tweetTextarea_0 at /intent/post?text=... pre-fills the textarea with the URL's text parameter before the page settles. Previous fill logic positioned cursor at the end of the existing content (via selectNodeContents + collapse) but didn't delete the selection before dispatching the synthetic paste event — most editors replace selection on paste, but X's React handler appended, resulting in duplicated text (<URL prefill> <safari_fill value>).

The fix adds an explicit document.execCommand('delete', false, null) after the selection is set and before the ClipboardEvent('paste') dispatch. Affects any contenteditable whose initial content needs to be replaced via the synthetic-paste path (Quill in some configurations, X composer with URL-prefilled text, similar React-driven editors).


Install: npx safari-mcp (auto-updates) or pin npx [email protected]
Source: https://github.com/achiya-automation/safari-mcp

v2.10.3 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.10.2...v2.10.3

v2.10.2 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.10.1...v2.10.2

v2.10.1 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.10.0...v2.10.1

v2.10.0 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.9.3...v2.10.0

v2.9.3 Breaking risk

Fixed safari_fill functionality inside dialog-hosted rich editors (LinkedIn share composer) by routing through native Cmd+V pipeline.

Full changelog

Fixed

  • safari_fill now works inside dialog-hosted rich editors (LinkedIn share composer). Three issues previously made LinkedIn posting impossible:

    1. The contenteditable fallback dispatched a blur event at the end — LinkedIn's composer listens for focusout and dismisses the dialog.
    2. When ProseMirror was detected but its EditorView could not be reached via pmViewDesc or the React Fiber walk, fill fell back to beforeinput+execCommand char-by-char (isTrusted:false, rejected by ProseMirror).
    3. The synthetic ClipboardEvent('paste') returned 'Filled CE (synthetic paste)' even when ProseMirror called preventDefault without accepting the payload.

    Fix: when the contenteditable sits inside a [role="dialog"], route through the existing CGEvent Cmd+V pipeline (_nativeTypeViaClipboard). Real paste, isTrusted:true, window-targeted so no focus steal. The blur dispatch is removed — React detects the change from input alone.

v2.9.2 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.9.1...v2.9.2

v2.9.1 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.9.0...v2.9.1

v2.9.0 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.8.9...v2.9.0

v2.8.9 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.8.8...v2.8.9

v2.8.8 Maintenance

Minor fixes and improvements.

Full changelog

What's Changed

  • ci: bump actions/setup-node from 6.3.0 to 6.4.0 by @dependabot[bot] in https://github.com/achiya-automation/safari-mcp/pull/24

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.8.5...v2.8.8

v2.8.5 Breaking risk

Minor fixes and improvements.

Full changelog

Research-backed overhaul of the README hero section — no API/tool/code changes.

What changed

  • Tagline shortened from 11 words to Serena-style category claim: "The browser for your coding agent." A second italic line carries the wedges: "Your real Safari, logged in — no Chrome, no heat, no headless."
  • Badges reduced from ~10 above the fold to 4 core (npm version, downloads, MIT, macOS). Others moved to "Listed On" at the bottom.
  • "❌ Without / ✅ With Safari MCP" block added immediately after the hero — Context7's killer pattern. Frames against Playwright, Chrome DevTools MCP, and headless scrapers.
  • 80-tool list collapsed into <details> — still fully documented, but no longer dominates the scroll. Top-level content now skimmable in under 30 seconds.
  • Star-scolding block removed — "Less than 1% star it" was an anti-pattern per every top README analyzed. Kept the silent star-history chart at the bottom.
  • "What agents unlock with Safari MCP" new section — Serena-style framing of capabilities an LLM benefits from (authenticated sessions, framework-aware form setters, background operation, batched workflow calls).
  • Community count corrected from "2,000+ monthly" → "6,000+ monthly" to match current npm downloads.

Reference

Patterns distilled from: Context7 (49K⭐), Serena (23K⭐), Desktop Commander (5.9K⭐), BrowserMCP (6K⭐).

v2.8.4 Bug fix
⚠ Upgrade required
  • Upgrade immediately from v2.8.3; form‑driven workflows are broken due to the SESSION_ID ReferenceError.
Full changelog

Fixed

  • SESSION_ID is not defined runtime error on safari_new_tab — introduced by v2.8.3's bulletproof tab marker. The marker code at safari.js:2151 references SESSION_ID, but that const was only declared in index.js (not exported), so the separate safari.js ES module threw ReferenceError: SESSION_ID is not defined whenever a new tab went through the marker path.

Symptom

safari_new_tab returned "SESSION_ID is not defined" but the tab still opened in Safari. Subsequent calls then failed the tab-ownership safety check, making the MCP unusable for form-driven workflows (HubSpot AEO, Semrush AI Visibility, GTmetrix, Mangools AI Search Grader, etc.).

Fix

Declare a local const SESSION_ID = randomUUID().slice(0, 8) in safari.js. Each ES module gets its own — the marker only needs per-process uniqueness.

How it was caught

Real /seo-geo run that needed 5 form-driven browser checks across HubSpot, Mangools, Semrush, GTmetrix, and Google Search Console. All 5 failed immediately after safari_new_tab.

Upgrade immediately if you were on v2.8.3 — form-driven workflows are broken on that version.

v2.8.3 Bug fix

Fixed tab tracking bug where `safari_evaluate` could target the wrong tab after redirects or new tabs.

Full changelog

🐛 Bug fix: tab tracking via window.__mcpTabMarker

Discovered during the v2.8.2 launch campaign that `safari_evaluate` could land on the wrong tab when `safari_new_tab` and `safari_evaluate` were called more than ~500 ms apart and a popup/redirect added a new tab in the meantime.

Root cause

  • `resolveActiveTab` relied on URL prefix matching, which fails when:
    • The page redirects (e.g. LinkedIn `/feed/` → `/feed/?shareActive=true`)
    • Query strings change (login redirects, share params)
    • Multiple tabs share the same domain
  • The 500 ms cache masked the race in many cases but not all.

Fix

  1. Every `safari_new_tab` now writes `window.__mcpTabMarker` = unique ID after the page loads. The marker survives same-tab navigation, redirects, and `pushState` changes — it's tied to the JS context, not the URL.
  2. `resolveActiveTab` tries the marker FIRST: it asks the cached tab "are you my marker?", and if not, walks all tabs in the profile window looking for the marker.
  3. Resolve cache reduced from 500 ms → 100 ms. The marker check is cheap (~5 ms), so the tighter cache adds negligible latency.

URL/domain matching stays as a fallback for tabs created before the marker was set, or when the marker is wiped by a hard reload.


Full diff: https://github.com/achiya-automation/safari-mcp/compare/v2.8.2...v2.8.3

v2.8.2 Maintenance

Minor fixes and improvements.

Full changelog

The new HackerNoon technical deep-dive launches today.

📰 Featured Article

I Had to Reverse-Engineer React, Shadow DOM, and CSP to Automate Safari Without Chrome

The article walks through the three hardest problems behind safari-mcp:

  1. React's `_valueTracker` — why `input.value = "x"` doesn't fire onChange in React, and the tracker-reset + native property descriptor + event sequence fix
  2. Shadow DOM piercing — recursive multi-root collector with MutationObserver-backed caching (~15ms per query saved)
  3. CSP bypass — 4-strategy fallback chain: direct eval → Trusted Types script injection → Web Worker → AppleScript nuclear option

🎨 README Updates

  • Prominent "Featured on HackerNoon" badge added to the badge row
  • Quote linking to the article right under the TL;DR
  • Deep-dive link in the star CTA section

Full diff: https://github.com/achiya-automation/safari-mcp/compare/v2.8.1...v2.8.2

v2.8.1 New feature
Notable features
  • Automatic sync of server.json version to match package.json
  • Download and use mcp-publisher binary in CI workflow
  • Publish every GitHub release to both npm and registry.modelcontextprotocol.io
Full changelog

Patch release that wires up automatic publishing to registry.modelcontextprotocol.io on every GitHub release.

Why

Until now, `server.json` had to be bumped manually and `mcp-publisher` had to be run by hand for every release. As a result, versions 2.7.7 → 2.8.0 never made it to the MCP Registry — only 2.7.6 was visible there.

What changed

`.github/workflows/release.yml` now has three new steps after the existing npm publish:

  1. Sync server.json version to match `package.json` automatically (single source of truth).
  2. Download mcp-publisher binary from the official release.
  3. Login via GitHub OIDC and `mcp-publisher publish` to registry.modelcontextprotocol.io.

From v2.8.1 onward, every GitHub release lands on both npm and the MCP Registry automatically.


Full diff: https://github.com/achiya-automation/safari-mcp/compare/v2.8.0...v2.8.1

v2.8.0 Breaking risk
Notable features
  • Postinstall welcome banner (scripts/postinstall.cjs) with star CTA, silenced via SAFARI_MCP_SILENT_INSTALL=1
  • Once‑per‑day startup banner in index.js printed to stderr, suppressed by SAFARI_MCP_QUIET=1
  • Added smithery.yaml deployment configuration leveraging existing smithery-entry.js
Full changelog

This release is a focused push to convert Safari MCP's healthy ~4,300 npm downloads/month into actual GitHub stars and visible community signals.

✨ Added

  • Postinstall welcome banner (`scripts/postinstall.cjs`) — printed once after `npm install` with next-step setup and a star CTA. Skipped silently in CI and when `SAFARI_MCP_SILENT_INSTALL=1`.
  • Once-per-day startup banner in `index.js` — written to stderr (never stdout, MCP protocol untouched) on the first server start of the day. Includes version, capability summary, and a one-line star link. Suppressed by `SAFARI_MCP_QUIET=1`.
  • `smithery.yaml` — Smithery deployment config (uses the existing `smithery-entry.js` to avoid top-level `await` issues).

🎨 Changed

README revamp for discoverability

  • New social-proof badge row: MCP Registry, Glama, Awesome MCP, CLI-Anything.
  • Prominent star CTA block right after Quick Start (was buried at line 568).
  • New "Why Safari MCP and Not the Other Safari MCP Projects?" comparison table — clarifies how this project differs from `lxman/safari-mcp-server`, `Epistates/MCPSafari`, and `HayoDev/safari-devtools-mcp`.
  • Removed duplicate "vs. Chrome DevTools MCP / Playwright MCP" section.

GitHub topics

Updated for better organic discovery — added `claude-code`, `safari-mcp`, `mcp` to the topic set.

🔇 Opt-out flags

  • `SAFARI_MCP_SILENT_INSTALL=1` — skip postinstall banner
  • `SAFARI_MCP_QUIET=1` — skip startup banner

Full diff: https://github.com/achiya-automation/safari-mcp/compare/v2.7.16...v2.8.0

v2.7.16 Breaking risk
Breaking changes
  • Removed the native `Enter` fallback that activated Safari; `safari_press_key enter` now dispatches a JavaScript keydown event and returns an `isTrusted:false` rejection instead of bringing Safari to foreground.
Full changelog

Changed

  • Removed the native Enter fallback that activated Safari. safari_press_key enter on contenteditable now dispatches the JS keydown and returns a clear message if the app didn't handle it (isTrusted:false rejection), instead of briefly activating Safari to send a real keystroke. This eliminates ALL focus-stealing from Safari MCP — no operation will ever bring Safari to the foreground.

Why

Discord/Slack require isTrusted:true on keyboard events, which is only possible when Safari is the frontmost app. The previous fallback (activate → keystroke → restore) worked but caused a visible ~130ms flash of Safari appearing. Users reported this as disruptive. The correct approach: MCP fills the editor in the background, then the user presses Enter in Safari when ready.

Full changelog: v2.7.15…v2.7.16

v2.7.15 Bug fix

Fixed safari_press_key Enter to work on Discord, Slack, and other isTrusted-gated editors by falling back to a native keystroke.

Full changelog

Fixed

safari_press_key Enter now works on Discord, Slack, and other isTrusted-gated editors. When the JS keydown isn't handled (rejected as isTrusted:false), falls back to briefly activating Safari (~80ms), sending a real keystroke, then immediately restoring the previous app. Total flash <130ms.

Full Discord chain now works end-to-end:

  1. safari_new_tab → Discord channel
  2. safari_click → focus editor
  3. pbcopy + safari_press_key cmd+v → paste (Slate state-aware)
  4. safari_press_key enter → submit (native fallback, auto-restore)

v2.7.14…v2.7.15

v2.7.14 New feature
⚠ Upgrade required
  • After `npm update`, re‑grant Automation permission using the provided `osascript` one‑liner if "Not authorized" errors appear.
Notable features
  • `com.apple.security.automation.apple-events` entitlement added to `safari-helper`
  • Entitlement file now ships with npm package for source builds
Full changelog

Fixed

  • safari-helper now includes com.apple.security.automation.apple-events entitlement. This helps macOS correctly identify the binary's intent and surface the TCC Automation prompt more reliably on first launch from an IDE. Previously, the ad-hoc signature had no entitlements, causing some setups to silently deny Apple Events without ever prompting.
  • Added troubleshooting note for "Not authorized" errors after npm update — updating changes the binary's cdhash, which causes macOS to silently revoke Automation permission. Users need to re-grant via the osascript one-liner.
  • Entitlements file (safari-helper.entitlements) now ships with the npm package for users building from source.

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.7.13...v2.7.14

v2.7.13 Bug fix

Fixed Enter key behavior in contenteditable to let apps handle submission without inserting a line break.

Full changelog

Fixed

safari_press_key Enter on contenteditable no longer inserts a line break. Modern editors (Discord Slate, Slack, Notion) handle Enter in their own keydown listener to trigger submit/send. The old fallback execCommand('insertLineBreak') double-acted: the app tries to submit AND MCP adds a newline — corrupting editor state and preventing submission.

Now:

  • INPUT → form submit (unchanged)
  • TEXTAREA → insertLineBreak (unchanged)
  • ContentEditable + Enter → keydown event only, let the app decide (fixed)
  • ContentEditable + Shift+Enter → insertLineBreak (newline)

Full changelog: v2.7.12…v2.7.13

v2.7.12 New feature
Notable features
  • `safari_native_type` inserts text via OS-level clipboard paste, integrating with ProseMirror/Slate/Draft.js models and preserving user clipboard
Full changelog

Added

  • safari_native_type — inserts text via OS-level clipboard paste (CGEvent Cmd+V targeted to Safari window). Goes through the real browser paste pipeline so ProseMirror/Slate/Draft.js update their internal model state. After native_type, pressing Enter via native_keyboard actually submits the form. Saves and restores the user's clipboard. No focus stealing.

Why this matters

Closes the last gap in the Discord/Slack automation chain:

  1. safari_hover → find server by tooltip
  2. safari_click → enter channel
  3. safari_native_type → paste message (state-aware, not just DOM)
  4. safari_native_keyboard {key: "enter"} → submit (no focus steal)

Previously step 3 used safari_fill which wrote to the DOM but not to Discord's ProseMirror internal state — leading to empty submissions on Enter.

Full changelog: v2.7.11…v2.7.12

v2.7.11 New feature
Notable features
  • `safari_native_keyboard` provides OS-level keyboard events via macOS CGEvent to a specific Safari window, producing `isTrusted: true` without stealing focus
Full changelog

Added

  • safari_native_keyboard — OS-level keyboard event via macOS CGEvent, targeted to the Safari window ID without activating Safari. Produces isTrusted: true events that bypass React trust checks in Discord ProseMirror, Slack virtualized editors, and similar trust-gated UIs. Supports all common keys (enter, tab, escape, arrows, letters, digits, punctuation) and modifiers (cmd, shift, alt, ctrl). No focus stealing — runs entirely in the background.

Why this matters

Operations that required a real keypress previously had no zero-focus-steal path. Pressing Enter in Discord, Slack, or any ProseMirror-backed editor forced a fallback to osascript "tell application \"Safari\" to activate" which brings Safari to the foreground and interrupts whatever the user is doing.

safari_native_keyboard closes that gap. It uses the same CGEvent path as safari_native_click and safari_native_hover — event is posted directly to the Safari process with the target window ID, so Safari stays in the background and the user's focus is never disturbed.

Full changelog: v2.7.10…v2.7.11

v2.7.10 Breaking risk
Notable features
  • `safari_native_hover` OS‑level mouse move via macOS CGEvent for real `:hover` handling
  • Tab ownership persisted to `~/.safari-mcp/owned-tabs.json` with 30‑minute TTL across MCP restarts
  • `safari-helper` rebuilt targeting macOS 12.0+ (arm64) fixing crashes on older macOS versions
Full changelog

Added

  • safari_native_hover — OS-level mouse move via macOS CGEvent. Triggers real :hover / mouseenter handlers on obfuscated UIs (Discord server sidebars, portal-rendered tooltips, virtualized React components) where JS-dispatched events alone aren't enough to reveal tooltips. Dwells for a configurable duration so tooltips can render, then optionally restores the cursor position. Complements safari_native_click using the same CGEvent path.

Fixed

  • Tab ownership now persists across MCP process restarts. Previously, every time Claude Code (or any other MCP client) recycled the Safari MCP server, the in-memory _ownedTabURLs set was wiped. The next tool call then failed with ⚠️ Tab safety: no tabs opened yet even though the previously-opened tab was still live in Safari. Ownership is now persisted to ~/.safari-mcp/owned-tabs.json with a 30-minute TTL and hydrated on startup.
  • safari-helper now targets macOS 12.0+ (root cause of #15). v2.7.9 shipped a helper compiled with minos 26.0 because swiftc defaults to the current SDK. That binary silently failed to launch on macOS 12–15, producing the exact crash loop that @alex-konkov reported (safari-helper crashed (restart #1, retrying in 500ms)...). The v2.7.10 helper is built with swiftc -O -target arm64-apple-macos12.0 and runs on Monterey, Ventura, Sonoma, Sequoia, and Tahoe.

Why this matters

Both fixes remove real blockers for driving complex SPAs from an MCP client:

  • Native hover unlocks Discord, Slack, and any UI that puts critical info (tooltips, reveal states, custom dropdowns) behind a CSS :hover or real pointer position.
  • Persistent tab ownership removes the "first tool call after any restart fails" paper cut that forced callers to preemptively re-open tabs and re-establish state.
  • macOS 12+ helper closes the compatibility gap that made safari-mcp unusable on anything older than macOS 26 — which was most of the world, since macOS 26 is the current release.

Full changelog: v2.7.9…v2.7.10

v2.7.9 Breaking risk
Breaking changes
  • -E flag removed from `pgrep` invocation in memory monitor
Full changelog

Fixes

  • #15 pgrep -E flag removed — the memory monitor was emitting pgrep: illegal option -- E on every check because macOS pgrep uses extended regex by default and the -E flag doesn't exist. Thanks @alex-konkov for the report.
  • #18 Extension popup was stuck on "Checking..." because MV3 script-src 'self' CSP blocks inline scripts. The logic is now in popup/popup.js — byte-for-byte identical to the previous inline code. Thanks @mikhailkogan17 for the fix.
  • safari-helper daemon re-signed with an explicit ad-hoc code signature so macOS TCC has a stable cdhash to track Automation permissions.

Documentation

  • #16 Added Automation → Safari to the macOS Permissions table in the README, plus the osascript workaround to register a parent IDE with TCC. Thanks @mikhailkogan17 for the diagnosis.
  • #17 Added codesign --sign - --force --deep to the extension build instructions so Safari actually loads the .app bundle produced by xcodebuild. Thanks @mikhailkogan17 and @Lionad-Morotar for the workaround.

Security & infrastructure

  • Pinned hono / @hono/node-server via package.json overrides to silence 6 transitive dependabot advisories (not exploitable — safari-mcp only uses StdioServerTransport).
  • Added .github/CODEOWNERS and expanded Dependabot to cover GitHub Actions.
  • Publishing to npm now uses OIDC Trusted Publisher — no long-lived NPM_TOKEN. Releases gate through a manual-approval npm-publish environment.
  • Branch protection on main: signed commits required, no force push, no deletion.
  • Outside collaborator workflows require approval before running.
  • All action versions pinned to commit SHAs.

Full changelog: v2.7.8…v2.7.9

v2.7.8 Bug fix

Fixed LinkedIn field filling and added beforeinput event handling for better contenteditable compatibility.

Full changelog

Improved

  • ProseMirror/Tiptap fill: React Fiber walk to discover EditorView + char-by-char beforeinput fallback
  • LinkedIn support: Fixes fill on Shadow DOM + Quill/ProseMirror editors
  • beforeinput events: Added to execCommand fallback for better contenteditable compatibility
v2.7.7 Bug fix

Fixed React Fiber traversal depth limit to support deep portal trees and enriched synthetic click events with additional methods.

Full changelog

Fixed

  • React Fiber traversal depth increased from 10 to 20 — React portals (modals, dialogs, tooltips) can have deeply nested fiber trees. This fix ensures safari_click correctly triggers onClick handlers on portal buttons.
  • Enriched synthetic click event — Added nativeEvent, isDefaultPrevented(), and isPropagationStopped() to the synthetic React event for better compatibility.
v2.7.6 Maintenance
Notable features
  • Added Dockerfile for MCP introspection testing (Glama, Smithery)
Full changelog
  • Added Dockerfile for MCP introspection testing (Glama, Smithery)
  • Bumped ws to 8.20.0
  • Bumped @modelcontextprotocol/sdk to 1.28.0
v2.7.5 Breaking risk

Fixed VS Code focus stealing from Safari by removing redundant daemon-level focus guard.

Full changelog

Bug Fixes

  • Fix focus stealing from Safari — The swift daemon (safari-helper) was saving/restoring focus on every AppleScript call, including background checks every 3 seconds. This caused VS Code to steal focus whenever the user switched to Safari. Removed the daemon-level focus guard — the Node.js layer already handles focus preservation during tool calls only.

  • Fix Google Workspace services breaking — The content script's attachShadow monkey-patch (for closed shadow DOM capture) was injected into all pages including Gmail, Calendar, Chat, and Meet. These services use closed shadow DOM heavily and the patch interfered with their initialization (Gmail error 1010). Added exclude_matches for all affected Google Workspace domains.

What's Changed

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.7.4...v2.7.5

v2.7.4 New feature
Notable features
  • TypeScript type declarations (index.d.ts) added
Full changelog

What's Changed

  • feat: add TypeScript type declarations (index.d.ts) by @mahek395 in https://github.com/achiya-automation/safari-mcp/pull/11

New Contributors

  • @mahek395 made their first contribution in https://github.com/achiya-automation/safari-mcp/pull/11

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.7.3...v2.7.4

v2.7.3 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.7.1...v2.7.3

v2.7.1 Breaking risk

Fixed FIFO mismatch when helper queue callbacks time out.

Full changelog
  • Clipboard double-release fix in _nativeTypeViaClipboard
  • clipboardRead now acquires lock (prevents reading during restore)
  • Helper queue: timed-out callbacks → no-op consumers (fixes FIFO mismatch)
  • Native paste sleep: 300ms → 100ms
  • screenshotElement: cropFile cleaned in error path
  • Profile verification: JSON parse wrapped in try/catch
  • onRemoved: now also cleans _sessionOwnedTabs
  • Result delivery: logging instead of silent swallow
v2.7.0 Breaking risk
Notable features
  • Zero Focus Stealing: No operation steals focus or moves the mouse.
  • Clipboard Protection: Mutex lock for concurrent operations and reduced restore window to 2 seconds with immediate native type restoration.
  • Tab Ghost Prevention: Proactive tab count tracking with cache invalidation and smart 500 ms resolve caching.
Full changelog

Zero Focus Stealing

No operation in Safari MCP will ever steal your focus or move your mouse.

Clipboard Protection

  • Mutex lock prevents concurrent clipboard operations
  • Restore window reduced from 5s to 2s, native type restores immediately

Focus Elimination

  • savePDF rewritten: screencapture + Python Quartz (no activate, no System Events)
  • CGEvent windowId=0 fallback removed (moved mouse)
  • All native operations throw if window ID unavailable

Tab Ghost Prevention

  • Proactive tab count tracking with cache invalidation on open/close
  • Smart 500ms resolve cache (was always re-resolving)

about:blank Dynamic Wait

  • Dynamic polling (300ms × 10) instead of fixed 1s sleep

Memory Monitoring

  • Warning at 70%, checks every 30s, closes up to 2 tabs

Extension Keepalive (3-Layer)

  • fetch polling + storage heartbeat (20s) + alarms (60s)
v2.6.1 Bug fix

Fixed tab ownership guard in AppleScript mode and allowed same-origin redirects to be recognized.

Full changelog

Bug Fixes

  • Tab ownership guard broken in AppleScript modesafari_new_tab returned a JSON string from AppleScript fallback, but the handler expected an object. result?.url was always undefined, so _addOwnedURL was never called. This caused every subsequent safari_click, safari_fill, safari_evaluate etc. to be blocked with "no tabs opened yet" error.
  • Same-origin redirects rejected — When a page redirected after opening (e.g. /login/device/login/device/select_account), the ownership check rejected the new URL. Added subpath matching for same-origin URLs.

MCP Registry

  • Published to the Official MCP Registry as io.github.achiya-automation/safari-mcp v2.6.1
  • PulseMCP ingests from this registry daily

npm

npx [email protected]
v2.6.0 Breaking risk
Breaking changes
  • Write operations (navigate, click, fill) are blocked on tabs not opened by MCP
Full changelog

Tab Ownership Guard

Prevents MCP from operating on tabs it didn't open.

How it works

  • Tracks tabs opened via safari_new_tab
  • Write operations (navigate, click, fill) blocked on non-owned tabs
  • Read-only operations (screenshot, read_page, snapshot) allowed on any tab
  • Dual enforcement: URL-based (index.js) + tab ID per session (extension)
v2.5.3 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.5.2...v2.5.3

v2.5.2 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.5.1...v2.5.2

v2.5.1 Feature
Notable features
  • Click support in cross‑origin iframes via Extension allFrames fallback with zero focus stealing
Changelog

Click in cross-origin iframes via Extension allFrames fallback. Zero focus stealing.

v2.5.0 New feature
Notable features
  • CGEvent native keyboard support in `performNativeKeyboard()` for typing/pasting into cross‑origin iframes without focus change
  • Daemon JSON protocol now accepts a `"keyboard"` section with keyCode, flags, and windowId
  • CLI shortcut `safari-helper --paste --window <WID>` added
Full changelog

What's New

CGEvent Native Keyboard — Type into Cross-Origin Iframes Without Stealing Focus

The Swift helper now supports CGEvent keyboard events, enabling:

  • No focus stealing: typeText and pressKey (Cmd+V) into cross-origin iframes (Intercom, Zendesk, etc.) without activating Safari or interrupting the user's work
  • Background operation: Keystrokes are sent directly to the Safari window via PID targeting, same as how nativeClick already works

Technical Details

  • New performNativeKeyboard() in safari-helper.swift
  • _nativeTypeViaClipboard uses CGEvent paste instead of System Events activate
  • Daemon JSON protocol: {"keyboard": {"keyCode": 9, "flags": ["cmd"], "windowId": 1234}}
  • CLI shortcut: safari-helper --paste --window WID

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.4.0...v2.5.0

v2.4.0 Bug fix

Fixed fill() to throw a clear error when called without selector or reference.

Full changelog

Highlights

  • Deleted ~300 lines of dead code (_LEGACY_INLINE_START) — 11KB savings per startup

Bug Fixes

  • fill() now throws clear error when called without selector/ref (was TypeError crash)
  • _helperNativeClick stdin .writable guard (prevents permanently-pending promises)
  • Extension go_back/go_forward now update session URL cache
  • mcpIsActionable checks pointer-events: none (elements visible but not clickable)
  • Extension click checks visibility: collapse + uses parseFloat for opacity
  • navigateAndRead suppresses onbeforeunload dialogs

Reliability

  • Signal handlers deduplicated (prevents double process.exit on rapid SIGTERM)
  • MAX_BODY_SIZE moved to module scope
  • Screenshot .png.jpg uses regex anchor (/\.png$/)
v2.3.1 Breaking risk
Breaking changes
  • _validateFilePath used require() in ES module context, causing crashes on every upload/paste call
Full changelog

Critical

  • BREAKING FIX: _validateFilePath used require() in ES module — crashed on every upload/paste call since v2.3.0
  • DoS prevention: HTTP POST body size capped at 10MB

High

  • Memory monitor non-functional: pgrep needs -E flag for regex alternation
  • Memory monitor tab close: resolves by URL not stale index
  • Proxy polling: stops self-scheduling when already hosting

Medium

  • Snapshot __mcpRefsTime in AppleScript path, profile test tab leak fix, wait_for_new_tab state sync, signal handler consolidation, dialog dismiss order
v2.3.0 Security relevant
Security fixes
  • Fixed AppleScript injection by whitelisting profile name in verify-profile endpoint
  • Blocked sensitive file paths in upload/paste tools via enhanced validation
  • Resolved helper queue race condition by pushing only after confirming stdin writable
Full changelog

Security Fixes

  • AppleScript injection — Profile name whitelisted in verify-profile endpoint
  • File path validation — Block sensitive paths in upload/paste tools
  • Helper queue race — Push only after confirming stdin writable

Bug Fixes

  • Tab cleanup — Close by URL not stale index
  • Closure editor fill — Escape backslashes in selector
  • Full-page screenshot — Restore bounds via try/finally
  • Import storage — runJSLarge for large sessions
  • Snapshot generation — Fix double increment
  • goBack/goForward/reload — Update _activeTabURL
  • Drag and drop — Add dragstart/dragover/dragend + fix escaping
  • handleDialog — Fix monkey-patch chain
  • resetEmulation — Fix user-agent reset no-op
  • Opacity/waitForTime/scroll_to_element — Various fixes

Reliability

  • Helper daemon — Kill only after 3 consecutive timeouts
  • waitForTabLoad — Check status before registering listener
  • Server version — Read from package.json
v2.2.0 Bug fix

Fixed cookie import for multi-cookie pages, corrected URL tracking in navigation and new tabs, unified toolbar height to 74px.

Full changelog

Bug Fixes

  • Cookie importsafari_import_storage now sets cookies one at a time (was broken for multi-cookie pages)
  • navigateAndRead URL trackingsafari_navigate_and_read now correctly updates internal tab URL tracking
  • Snapshot ref consistency — Extension and AppleScript engines now use the same generation-based ref prefix
  • New tab URL tracking — Extension correctly tracks requested URL when tab initially shows about:blank
  • Wait for new tabsafari_wait_for_new_tab detects about:blank tabs (OAuth popups)
  • Toolbar height — Unified to 74px (was 52px in screenshotElement)
  • Duplicate signal handlers — Consolidated shutdown handlers
  • Command timeouts — Added missing replace_editor timeout

Performance

  • Extension deep query injection — Skips re-injection on already-injected tabs
  • Memory monitor — Direct pgrep + ps instead of bash | grep | awk

Maintainability

  • INJECT_MCP_HELPERS extracted to mcp-helpers.js — proper JS file with syntax highlighting and linting
v2.1.8 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.1.7...v2.1.8

v2.1.7 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.1.6...v2.1.7

v2.1.6 Security relevant
Security fixes
  • Fixed path-to-regexp HIGH severity ReDoS vulnerability
Full changelog

Security

  • npm tokens scoped to safari-mcp only (no more full-access tokens)
  • Branch protection enabled on main (no force push)
  • Dependabot enabled for dependency monitoring
  • Vulnerability alerts enabled
  • Fixed path-to-regexp HIGH severity ReDoS vulnerability

Project Infrastructure

  • GitHub Actions CI workflow (macOS-latest, Node 18)
  • 5 practical usage examples in examples/
  • CHANGELOG.md — full version history from 1.0.0
  • CONTRIBUTING.md — setup & PR guidelines
  • Bug report and feature request issue templates
  • 13 GitHub topics for discoverability

npm Install

npm install -g safari-mcp

Full Changelog: https://github.com/achiya-automation/safari-mcp/blob/main/CHANGELOG.md

v2.1.5 Feature
Notable features
  • Synthetic ClipboardEvent paste fallback for modern contenteditable editors (Hashnode, HackerNoon, Notion) to enable correct handling by ProseMirror-based editors
Full changelog

Adds synthetic ClipboardEvent paste fallback for modern contenteditable editors (Hashnode, HackerNoon, Notion). The fill command now sends a proper paste event that ProseMirror-based editors handle correctly.

v2.1.4 Bugfix

Fixed crash when two MCP instances start by only killing truly stale (>10 seconds) processes.

Full changelog

Problem: Claude Code VSCode starts 2 MCP instances simultaneously. Singleton detection killed the first, crashing both connections.

Fix: Only kill instances running >10s (truly stale). Fresh sibling instances preserved.

v2.1.3 Bug fix

Fix: Kills stale MCP instances on startup to prevent zombie process accumulation.

Full changelog

Fix: Singleton — kill stale MCP instances on startup

מונע הצטברות של תהליכי זומבי כשנפתחים סשנים חדשים של Claude Code.

  • On startup, the MCP server finds and kills any stale instances of itself
  • Prevents memory bloat from zombie processes accumulating across sessions
  • Uses execFileSync("pgrep") for safe process detection (no shell injection)
v2.1.2 Maintenance

Routine maintenance release for achiya-automation/safari-mcp.

Changelog

Full Changelog: https://github.com/achiya-automation/safari-mcp/compare/v2.1.1...v2.1.2

v2.1.1 New feature
Notable features
  • Official MCP Registry at registry.modelcontextprotocol.io
  • Cursor Directory listed as an MCP plugin
  • Dual-engine architecture documented (Extension vs AppleScript)
Full changelog

What's New

  • Official MCP Registry — Safari MCP is now published on registry.modelcontextprotocol.io
  • npm package — install with npm install safari-mcp or npx safari-mcp
  • Cursor Directory — listed as an MCP plugin
  • Dual-engine architecture documented — README now explains Extension vs AppleScript
  • Safari Extension docs — installation instructions with Xcode build
  • mcp.json + server.json — Open Plugins & MCP Registry manifests
  • Social preview — new project image

Extension Improvements

  • Reconnect exponential backoff (prevents timer growth)
  • 90s safety timeout on poll connections
  • Overlay click pattern detection (transparent buttons over text labels)
  • evaluate wrapper fix for multi-statement scripts in Safari

Install

npm install safari-mcp

Or clone:

git clone https://github.com/achiya-automation/safari-mcp.git
cd safari-mcp && npm install
v2.0.1 New feature
Notable features
  • 80+ tools for native Safari browser automation
  • Zero Chrome overhead using AppleScript + JavaScript on macOS
  • Dual engine: Safari Extension (~5-20ms) and AppleScript daemon fallback (~5ms)
Full changelog

Safari MCP Server v2.0.1

What's New

  • Added glama.json for Glama directory listing
  • Minor stability improvements

Features

  • 80+ tools for native Safari browser automation
  • Zero Chrome overhead — uses AppleScript + JavaScript natively on macOS
  • Keeps all your logins, cookies, and sessions
  • Dual engine: Safari Extension (~5-20ms) + AppleScript daemon (~5ms fallback)
  • Drop-in alternative to Chrome DevTools MCP with 40-60% less CPU/heat on Apple Silicon
v1.0.0 New feature
⚠ Upgrade required
  • Requires macOS 13 (Ventura) or later
  • Enable Safari Develop menu → Allow JavaScript from Apple Events
  • Node.js version must be 18 or higher
Notable features
  • 80 tools covering navigation, clicks, forms, screenshots, JavaScript execution, storage management, network mocking, console logging, data extraction, and device emulation
  • Zero overhead by using Apple's native WebKit via AppleScript—no Chromium or Playwright required
  • Runs silently in the background without stealing focus and provides 40-60% lower CPU/heat on Apple Silicon
Full changelog

Safari MCP Server v1.0.0

Native Safari browser automation for AI agents via Model Context Protocol (MCP).

Highlights

  • 80 tools — navigation, clicks, forms, screenshots, JavaScript execution, network mocking, storage management, and more
  • Zero overhead — uses Apple's native WebKit via AppleScript. No Chromium, no Playwright, no headless browser
  • Keeps your logins — works with your real Safari profile. Log in once, automate forever
  • Runs silently — never steals focus or interrupts your work. Tab management happens in the background
  • 40-60% less CPU/heat compared to Chrome DevTools MCP on Apple Silicon

Tools by Category

| Category | Count | Examples |
|----------|-------|---------|
| Navigation | 4 | navigate, go_back, reload |
| Click & Input | 9 | click, fill, type_text, press_key, double_click, right_click |
| Screenshots | 3 | screenshot, screenshot_element, save_pdf |
| Tab Management | 4 | list_tabs, new_tab, switch_tab, close_tab |
| JavaScript | 2 | evaluate, run_script (multi-step) |
| Storage | 9 | cookies, localStorage, sessionStorage, export/import |
| Network | 6 | capture, mock_route, throttle, performance_metrics |
| Console | 3 | start_console, get_console, filter |
| Data Extraction | 4 | extract_tables, extract_meta, extract_images, extract_links |
| Device Emulation | 2 | emulate (iPhone, iPad, Pixel), reset |
| Advanced | 10+ | accessibility_snapshot, css_coverage, detect_forms, geolocation override |

Quick Start

{
  "mcpServers": {
    "safari": {
      "command": "node",
      "args": ["/path/to/safari-mcp/index.js"]
    }
  }
}

Requirements

  • macOS 13+ (Ventura or later)
  • Safari → Develop → Allow JavaScript from Apple Events
  • Node.js 18+
v2.0.0 New feature
⚠ Upgrade required
  • Requires macOS with Safari
  • Node.js 18+ is required
  • Enable Safari → Develop → Allow JavaScript from Apple Events
Notable features
  • 80 tools spanning navigation, click/interaction, form input, screenshots/PDF, scroll, tab management, wait conditions, JavaScript execution, element inspection, accessibility, drag & drop, file operations, dialog/window handling, device emulation, cookies/storage, clipboard, network control, console logging, performance measurement, data extraction, and advanced automation
  • Background operation without window stealing or focus changes
  • Persistent osascript with ~5 ms per command latency
Full changelog

Safari MCP v2.0.0

Native Safari browser automation for AI agents — 80 tools, zero Chrome overhead.

Highlights

  • 80 tools — the most comprehensive Safari automation MCP available
  • Native macOS — AppleScript + JavaScript, no Chrome/Puppeteer/Playwright
  • Your real browser — keeps all logins, cookies, and sessions
  • Background operation — no window stealing, no focus changes
  • Persistent osascript — ~5ms per command (vs ~80ms with process spawning)
  • JS-first input — typing and clicking via JavaScript events (no keyboard conflicts)
  • Framework-compatible — React, Vue, Angular form filling via native setters

Tool Categories

| Category | Count |
|----------|-------|
| Navigation | 4 |
| Page Reading | 3 |
| Click & Interaction | 5 |
| Form Input | 7 |
| Screenshots & PDF | 3 |
| Scroll | 3 |
| Tab Management | 4 |
| Wait | 2 |
| JavaScript | 1 |
| Element Inspection | 4 |
| Accessibility | 1 |
| Drag & Drop | 1 |
| File Operations | 2 |
| Dialog & Window | 2 |
| Device Emulation | 2 |
| Cookies & Storage | 11 |
| Clipboard | 2 |
| Network | 6 |
| Console | 4 |
| Performance | 2 |
| Data Extraction | 4 |
| Advanced | 5 |
| Automation | 1 |

Quick Start

git clone https://github.com/achiya-automation/safari-mcp.git
cd safari-mcp && npm install

Add to your MCP config (~/.mcp.json):

{
  "mcpServers": {
    "safari": {
      "command": "node",
      "args": ["/path/to/safari-mcp/index.js"]
    }
  }
}

Requirements

  • macOS with Safari
  • Node.js 18+
  • Safari → Develop → Allow JavaScript from Apple Events

Beta — feedback welcome: [email protected]