safari-mcp 2.22.10 → 2.22.12
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/extension/background.js +5 -1
- package/index.js +19 -7
- package/package.json +1 -1
package/extension/background.js
CHANGED
|
@@ -914,7 +914,11 @@ async function handleCommand(type, payload) {
|
|
|
914
914
|
const suppliedReceipt = (type === "new_tab" || type === "list_tabs")
|
|
915
915
|
? ""
|
|
916
916
|
: _receiptTokenFromPayload(payload);
|
|
917
|
-
|
|
917
|
+
// getReceipt and close_tab use a receipt only to find its tab; neither runs anything in the
|
|
918
|
+
// page. close_tab was held to the receipt's origin, so a tab a redirect or login bounce had
|
|
919
|
+
// moved could be closed only after a getReceipt rotation, and a page with no http(s) origin
|
|
920
|
+
// never gets one: the tab stayed open with nothing able to close it (geo-audit, 6.10.26).
|
|
921
|
+
const allowReceiptOriginChange = type === "get_tab_receipt" || type === "close_tab";
|
|
918
922
|
let receiptResolved = false;
|
|
919
923
|
// new_tab CREATES its target, so a failure to resolve an existing one must not stop
|
|
920
924
|
// it. Once a profile holds several windows, resolution legitimately fails with
|
package/index.js
CHANGED
|
@@ -1773,7 +1773,7 @@ const _nullMeansFailure = new Set([
|
|
|
1773
1773
|
"read_page", "get_source", "snapshot", "get_element", "query_all",
|
|
1774
1774
|
"scroll", "scroll_to",
|
|
1775
1775
|
// NOTE: "evaluate" intentionally NOT here — null is a valid return value.
|
|
1776
|
-
// CSP fallback is handled separately
|
|
1776
|
+
// Its CSP fallback is handled separately, by the ladder's terminal marker (hardCspBlock).
|
|
1777
1777
|
]);
|
|
1778
1778
|
|
|
1779
1779
|
// Operations that don't need tab ownership (read-only or tab management)
|
|
@@ -2459,16 +2459,18 @@ async function extensionOrFallback(extensionType, extensionPayload, fallbackFn)
|
|
|
2459
2459
|
// The ladder's own terminal marker. It was not in this list, so the fallback the
|
|
2460
2460
|
// marker's text promises never actually ran and its text reached the caller as
|
|
2461
2461
|
// the tool's value instead of as a failure (#106).
|
|
2462
|
-
|
|
2463
|
-
//
|
|
2464
|
-
//
|
|
2465
|
-
|
|
2466
|
-
|
|
2462
|
+
// It is the whole reply of an evaluate, so it is matched as that and nowhere else. A
|
|
2463
|
+
// result is page text as often as not: matched anywhere in it, CSP wording read a page
|
|
2464
|
+
// that mentions Trusted Types as a blocked command, which a profile session then
|
|
2465
|
+
// refused and any other session ran a second time through AppleScript. Every other
|
|
2466
|
+
// command's failure arrives as a rejection, never as its result.
|
|
2467
|
+
hardCspBlock = extensionType === "evaluate" && typeof result === 'string' && result.startsWith('Error: CSP blocked all strategies');
|
|
2468
|
+
const isPermissionDenied = result === '__SCREENSHOT_PERMISSION_DENIED__';
|
|
2467
2469
|
const isElementMiss = typeof result === 'string' && result.startsWith('Element not found');
|
|
2468
2470
|
const isFailed = result === null || isElementMiss;
|
|
2469
2471
|
if (isPermissionDenied) {
|
|
2470
2472
|
console.error(`[Safari MCP] ${extensionType} permission denied (${Date.now() - t0}ms) — falling back to AppleScript`);
|
|
2471
|
-
} else if (
|
|
2473
|
+
} else if (hardCspBlock) {
|
|
2472
2474
|
console.error(`[Safari MCP] ${extensionType} CSP blocked: ${result?.substring(0, 100)} (${Date.now() - t0}ms) — falling back to AppleScript`);
|
|
2473
2475
|
} else if (isFailed && _nullMeansFailure.has(extensionType)) {
|
|
2474
2476
|
if (_preferAppleScript && isElementMiss) {
|
|
@@ -2511,6 +2513,16 @@ async function extensionOrFallback(extensionType, extensionPayload, fallbackFn)
|
|
|
2511
2513
|
}
|
|
2512
2514
|
if (!usedExtension) {
|
|
2513
2515
|
if (_preferAppleScript) {
|
|
2516
|
+
// The extension answered here, so naming it unavailable sent callers after a bridge
|
|
2517
|
+
// that worked (copilot.microsoft.com, 4-5.10.26: Trusted Types turn away its policy).
|
|
2518
|
+
if (hardCspBlock) {
|
|
2519
|
+
throw new Error(
|
|
2520
|
+
"safari_evaluate: this page's Content-Security-Policy refused every way to run a script string " +
|
|
2521
|
+
"(no eval, no inline script the extension can add), so the script did not run. A profile-scoped " +
|
|
2522
|
+
"session never falls back to AppleScript. Tools that run no script string still work here: " +
|
|
2523
|
+
"safari_read_page, safari_snapshot, safari_get_element, safari_click, safari_fill."
|
|
2524
|
+
);
|
|
2525
|
+
}
|
|
2514
2526
|
throw new Error(`Safari profile extension unavailable for "${extensionType}"; refusing AppleScript fallback`);
|
|
2515
2527
|
}
|
|
2516
2528
|
const t0 = Date.now();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "safari-mcp",
|
|
3
|
-
"version": "2.22.
|
|
3
|
+
"version": "2.22.12",
|
|
4
4
|
"mcpName": "io.github.achiya-automation/safari-mcp",
|
|
5
5
|
"description": "Safari browser automation for AI agents — native macOS, zero Chrome overhead. 98 tools via AppleScript + JavaScript.",
|
|
6
6
|
"type": "module",
|