safari-mcp 2.10.7 → 2.10.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -1
- package/package.json +1 -1
- package/safari-helper +0 -0
- package/safari.js +10 -3
- package/scripts/postinstall.cjs +35 -2
package/README.md
CHANGED
|
@@ -645,7 +645,23 @@ Safari MCP needs these one-time permissions:
|
|
|
645
645
|
| JavaScript from Apple Events | Safari → Develop menu | Required for `do JavaScript` |
|
|
646
646
|
| Automation → Safari | System Settings → Privacy & Security → Automation | Required for all AppleScript-backed tools |
|
|
647
647
|
| Screen Recording | System Settings → Privacy & Security → Screen Recording | Required for `safari_screenshot` |
|
|
648
|
-
| Accessibility | System Settings → Privacy & Security → Accessibility | Required for `safari_save_pdf`
|
|
648
|
+
| Accessibility (safari-helper) | System Settings → Privacy & Security → Accessibility | Required for `safari_native_click`, `safari_native_keyboard`, `safari_native_hover` and `safari_save_pdf` |
|
|
649
|
+
|
|
650
|
+
### Granting Accessibility to safari-helper (required for `safari_native_*`)
|
|
651
|
+
|
|
652
|
+
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.
|
|
653
|
+
|
|
654
|
+
1. Open **System Settings → Privacy & Security → Accessibility**.
|
|
655
|
+
2. Click `+` (unlock with your password if needed).
|
|
656
|
+
3. Navigate to the helper binary and add it:
|
|
657
|
+
- npm global install: `$(npm root -g)/safari-mcp/safari-helper`
|
|
658
|
+
- npx / project install: `./node_modules/safari-mcp/safari-helper`
|
|
659
|
+
- From source clone: `/path/to/safari-mcp/safari-helper`
|
|
660
|
+
4. Make sure the toggle next to it is **ON**.
|
|
661
|
+
|
|
662
|
+
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.
|
|
663
|
+
|
|
664
|
+
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.
|
|
649
665
|
|
|
650
666
|
### Granting Automation → Safari (important for IDE users)
|
|
651
667
|
|
|
@@ -668,6 +684,7 @@ That call registers the Terminal app in the Automation database and then trigger
|
|
|
668
684
|
| "AppleScript error" | Enable "Allow JavaScript from Apple Events" in Safari → Develop |
|
|
669
685
|
| "Not authorized to send Apple events to Safari" | Grant Automation → Safari to your IDE (see above) |
|
|
670
686
|
| "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 |
|
|
687
|
+
| `safari_native_click` reports success but page doesn't react | Add `safari-helper` to **System Settings → Privacy & Security → Accessibility** (see [Granting Accessibility](#granting-accessibility-to-safari-helper-required-for-safari_native_) above). Confirm by attaching a `click` listener with `{capture:true}` in the page console — without the grant, no `isTrusted: true` event fires |
|
|
671
688
|
| Screenshots empty | Grant Screen Recording permission to Terminal/VS Code |
|
|
672
689
|
| Tab not found | Call `safari_list_tabs` to refresh tab indices |
|
|
673
690
|
| Hebrew keyboard issues | All typing uses JS events — immune to keyboard layout |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "safari-mcp",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.9",
|
|
4
4
|
"mcpName": "io.github.achiya-automation/safari-mcp",
|
|
5
5
|
"description": "Safari browser automation for AI agents — native macOS, zero Chrome overhead. 80 tools via AppleScript + JavaScript.",
|
|
6
6
|
"type": "module",
|
package/safari-helper
CHANGED
|
Binary file
|
package/safari.js
CHANGED
|
@@ -727,14 +727,21 @@ async function _getSafariWindowGeometry() {
|
|
|
727
727
|
if (parts.length !== 5 || parts.some(isNaN)) {
|
|
728
728
|
throw new Error("Failed to parse Safari window geometry: " + boundsResult);
|
|
729
729
|
}
|
|
730
|
+
// Dynamic toolbar height: outerHeight - innerHeight gives total chrome above content
|
|
731
|
+
// (title bar + URL bar + tab strip + optional bookmarks bar). Hardcoded 74 was wrong
|
|
732
|
+
// for modern Safari (Sequoia+) where chrome is ~90px. Fall back to 74 if JS unreachable.
|
|
733
|
+
let toolbarHeight = 74;
|
|
734
|
+
try {
|
|
735
|
+
const chromeStr = await runJS(`(window.outerHeight - window.innerHeight) + ''`);
|
|
736
|
+
const chrome = Number(chromeStr);
|
|
737
|
+
if (Number.isFinite(chrome) && chrome >= 50 && chrome <= 200) toolbarHeight = chrome;
|
|
738
|
+
} catch (_e) { /* keep fallback */ }
|
|
730
739
|
return {
|
|
731
740
|
windowX: parts[0],
|
|
732
741
|
windowY: parts[1],
|
|
733
742
|
windowRight: parts[2],
|
|
734
743
|
windowBottom: parts[3],
|
|
735
|
-
|
|
736
|
-
// This is approximate — varies with compact tabs, bookmarks bar, etc.
|
|
737
|
-
toolbarHeight: 74,
|
|
744
|
+
toolbarHeight,
|
|
738
745
|
// CGWindow ID for background click targeting (no mouse move, no focus steal)
|
|
739
746
|
windowId: parts[4]
|
|
740
747
|
};
|
package/scripts/postinstall.cjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Safari MCP — postinstall welcome message
|
|
2
|
+
// Safari MCP — postinstall: codesign helper + welcome message
|
|
3
3
|
// Skipped silently in CI and when stdout is not a TTY (npm install in scripts).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { execSync } = require("child_process");
|
|
7
|
+
const fs = require("fs");
|
|
6
8
|
|
|
7
9
|
const c = {
|
|
8
10
|
reset: "\x1b[0m",
|
|
@@ -12,8 +14,36 @@ const c = {
|
|
|
12
14
|
yellow: "\x1b[33m",
|
|
13
15
|
magenta: "\x1b[35m",
|
|
14
16
|
green: "\x1b[32m",
|
|
17
|
+
red: "\x1b[31m",
|
|
15
18
|
};
|
|
16
19
|
|
|
20
|
+
// Re-sign safari-helper with a stable identifier so macOS Accessibility approval persists.
|
|
21
|
+
// The package ships with an adhoc-signed binary whose codesign Identifier is a one-off hash
|
|
22
|
+
// (e.g. `safari-helper-555549441c166aa237e130ddbe3d95629266ecaf`). macOS TCC keys grants by
|
|
23
|
+
// that identifier, so a fresh npm install or rebuild silently invalidates any previously-granted
|
|
24
|
+
// Accessibility approval — the helper keeps running but CGEvent injections to non-frontmost Safari
|
|
25
|
+
// windows stop reaching WebKit content (no isTrusted click events fire on the page).
|
|
26
|
+
// Re-signing here with the fixed identifier `com.achiya-automation.safari-mcp` keeps the grant
|
|
27
|
+
// stable across installs.
|
|
28
|
+
function ensureCodesign() {
|
|
29
|
+
if (process.platform !== "darwin") return;
|
|
30
|
+
const helper = path.join(__dirname, "..", "safari-helper");
|
|
31
|
+
if (!fs.existsSync(helper)) return;
|
|
32
|
+
try {
|
|
33
|
+
// Check current identifier; only re-sign if it doesn't already match.
|
|
34
|
+
const current = execSync(`codesign -d -- "${helper}" 2>&1 | grep ^Identifier=`, { encoding: "utf8" }).trim();
|
|
35
|
+
if (current.includes("com.achiya-automation.safari-mcp")) return;
|
|
36
|
+
const entitlements = path.join(__dirname, "..", "safari-helper.entitlements");
|
|
37
|
+
const entFlag = fs.existsSync(entitlements) ? `--entitlements "${entitlements}"` : "";
|
|
38
|
+
execSync(`codesign -s - -f --identifier com.achiya-automation.safari-mcp ${entFlag} "${helper}" 2>/dev/null`);
|
|
39
|
+
} catch (_e) {
|
|
40
|
+
// codesign may fail in environments without the toolchain; binary still works adhoc-signed.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
ensureCodesign();
|
|
44
|
+
|
|
45
|
+
if (process.env.CI || process.env.SAFARI_MCP_SILENT_INSTALL === "1") process.exit(0);
|
|
46
|
+
|
|
17
47
|
const msg = `
|
|
18
48
|
${c.bold}${c.cyan}🦁 Safari MCP installed${c.reset} ${c.dim}— 80 native browser tools for AI agents${c.reset}
|
|
19
49
|
|
|
@@ -21,6 +51,9 @@ ${c.bold}Next steps:${c.reset}
|
|
|
21
51
|
1. Enable Safari → Develop → ${c.yellow}Allow JavaScript from Apple Events${c.reset}
|
|
22
52
|
2. Add to your MCP client config:
|
|
23
53
|
${c.dim}{ "mcpServers": { "safari": { "command": "npx", "args": ["safari-mcp"] } } }${c.reset}
|
|
54
|
+
3. ${c.bold}For native_click / native_keyboard${c.reset} (no focus stealing):
|
|
55
|
+
System Settings → Privacy & Security → ${c.yellow}Accessibility${c.reset} → add
|
|
56
|
+
${c.dim}node_modules/safari-mcp/safari-helper${c.reset} ${c.dim}(or the global install path)${c.reset}
|
|
24
57
|
|
|
25
58
|
${c.bold}${c.magenta}⭐ Found this useful?${c.reset} A star helps others discover it:
|
|
26
59
|
${c.cyan}https://github.com/achiya-automation/safari-mcp${c.reset}
|