safari-mcp 2.21.0 → 2.21.2
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/index.js +10 -3
- package/package.json +1 -1
- package/response.js +21 -0
package/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { startTransport } from "./transport.js";
|
|
|
11
11
|
import { currentSessionId } from "./session-context.js";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import * as safari from "./safari.js";
|
|
14
|
-
import { textResult, jsonResult, imageResult, errorResult } from "./response.js";
|
|
14
|
+
import { textResult, jsonResult, imageResult, errorResult, evalResult } from "./response.js";
|
|
15
15
|
import {
|
|
16
16
|
OWNERSHIP_DIR, BLANK_TAB_SENTINEL,
|
|
17
17
|
_openedTabs, _ownedTabURLs,
|
|
@@ -2850,6 +2850,13 @@ server.tool(
|
|
|
2850
2850
|
if (resolvedIndex) safari.setActiveTabIndex(resolvedIndex);
|
|
2851
2851
|
if (safeResult?.safeUrl) safari.setActiveTabURL(safeResult.safeUrl);
|
|
2852
2852
|
if (safeResult?.receipt || token) _setActiveReceipt(safeResult?.receipt || token);
|
|
2853
|
+
// A switch by INDEX gets no receipt back, and the session was still holding the one it
|
|
2854
|
+
// minted for the PREVIOUS tab. Every later command auto-attaches that receipt, so the
|
|
2855
|
+
// switch reported `owned: true` and then the next evaluate/click died with "receipt is
|
|
2856
|
+
// forged, stale, ambiguous, or not valid for this origin" — a dead end with no way out
|
|
2857
|
+
// but opening another tab. Drop the stale capability instead: without one the command
|
|
2858
|
+
// targets the tab this switch just selected, which is exactly what was asked for.
|
|
2859
|
+
else _setActiveReceipt("");
|
|
2853
2860
|
// Say so in the result, not only in the log: an agent that adopted a user's tab should
|
|
2854
2861
|
// be able to see that from the answer it got (#92, condition 3).
|
|
2855
2862
|
const reported = adopted ? { ...safeResult, note: "(user tab, opted-in)" } : safeResult;
|
|
@@ -2975,7 +2982,7 @@ server.tool(
|
|
|
2975
2982
|
return safari.evaluate(args);
|
|
2976
2983
|
}
|
|
2977
2984
|
);
|
|
2978
|
-
return
|
|
2985
|
+
return evalResult(result);
|
|
2979
2986
|
}
|
|
2980
2987
|
);
|
|
2981
2988
|
|
|
@@ -2986,7 +2993,7 @@ server.tool(
|
|
|
2986
2993
|
async (args) => {
|
|
2987
2994
|
const script = readFileSync(args.path, "utf8");
|
|
2988
2995
|
const result = await extensionOrFallback("evaluate", { script }, () => safari.evaluate({ script }));
|
|
2989
|
-
return
|
|
2996
|
+
return evalResult(result);
|
|
2990
2997
|
}
|
|
2991
2998
|
);
|
|
2992
2999
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "safari-mcp",
|
|
3
|
-
"version": "2.21.
|
|
3
|
+
"version": "2.21.2",
|
|
4
4
|
"mcpName": "io.github.achiya-automation/safari-mcp",
|
|
5
5
|
"description": "Safari browser automation for AI agents — native macOS, zero Chrome overhead. 97 tools via AppleScript + JavaScript.",
|
|
6
6
|
"type": "module",
|
package/response.js
CHANGED
|
@@ -28,3 +28,24 @@ export const errorResult = (msg) => ({
|
|
|
28
28
|
content: [{ type: "text", text: msg }],
|
|
29
29
|
isError: true,
|
|
30
30
|
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `safari_evaluate` / `safari_eval_file` result text.
|
|
34
|
+
*
|
|
35
|
+
* Distinct from `textResult`: only these two tools surface a *script's* return value,
|
|
36
|
+
* so they need to say "the script returned nothing" — and the old form said it with a
|
|
37
|
+
* falsy check, `(… || "(no return value)")`. That collapsed three different outcomes
|
|
38
|
+
* into one string: a script returning `undefined` (nothing), a script returning `""`
|
|
39
|
+
* (a real value), and an injection that never ran at all (a failure). An empty return
|
|
40
|
+
* is a legitimate result and must stay distinguishable from no return.
|
|
41
|
+
*/
|
|
42
|
+
export const evalResult = (r) => {
|
|
43
|
+
let text;
|
|
44
|
+
if (typeof r === "string") {
|
|
45
|
+
text = r === "" ? '""' : r;
|
|
46
|
+
} else {
|
|
47
|
+
const json = JSON.stringify(r); // undefined for undefined/function/symbol
|
|
48
|
+
text = json === undefined ? "(no return value)" : json;
|
|
49
|
+
}
|
|
50
|
+
return { content: [{ type: "text", text }] };
|
|
51
|
+
};
|