safari-mcp 2.8.2 → 2.8.3

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/safari.js +41 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safari-mcp",
3
- "version": "2.8.2",
3
+ "version": "2.8.3",
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.js CHANGED
@@ -156,7 +156,8 @@ let _activeTabIndex = null; // null = use front document (default)
156
156
  let _activeTabURL = null; // URL-based tracking (stable even when tabs shift)
157
157
  let _lastResolveTime = 0; // Cache: skip resolve if verified recently
158
158
  let _lastTabCount = null; // Track tab count for smart cache invalidation
159
- const RESOLVE_CACHE_MS = 500; // Brief cache — invalidated on tab count change
159
+ let _activeTabMarker = null; // window.__mcpTabMarker — survives same-tab navigation, bulletproof tab identity
160
+ const RESOLVE_CACHE_MS = 100; // Brief cache — was 500, reduced to catch tabs added by user/popups (v2.8.3 fix)
160
161
 
161
162
  // ========== DIAGNOSTIC LOG ==========
162
163
  // File-based log for profile/focus issues — survives MCP restart, visible to user
@@ -331,7 +332,41 @@ export function setActiveTabURL(url) { _activeTabURL = url; _lastResolveTime = D
331
332
 
332
333
  // Resolve our tracked URL to current tab index — single combined osascript call
333
334
  async function resolveActiveTab() {
335
+ if (!_activeTabURL && !_activeTabMarker) return _activeTabIndex;
336
+
337
+ // Strategy 1: window.__mcpTabMarker (bulletproof — survives navigation, redirects, query changes)
338
+ // Only attempted if we have a marker AND a cached index hint
339
+ if (_activeTabMarker && _activeTabIndex) {
340
+ try {
341
+ const safeMarker = _activeTabMarker.replace(/'/g, "\\'");
342
+ // Single JS call: check cached index first, then walk all tabs to find marker
343
+ const checkScript = `(function(){return window.__mcpTabMarker==='${safeMarker}'?'1':'0'})()`;
344
+ const matchAtCached = await osascriptFast(
345
+ `tell application "Safari" to do JavaScript "${checkScript}" in tab ${_activeTabIndex} of ${getTargetWindowRef()}`
346
+ ).catch(() => '0');
347
+ if (String(matchAtCached).trim() === '1') return _activeTabIndex;
348
+ // Cached index doesn't match — scan all tabs for marker
349
+ const tabCountStr = await osascriptFast(
350
+ `tell application "Safari" to return count of tabs of ${getTargetWindowRef()}`
351
+ ).catch(() => '0');
352
+ const tabCount = Number(tabCountStr) || 0;
353
+ for (let i = tabCount; i >= 1; i--) {
354
+ const m = await osascriptFast(
355
+ `tell application "Safari" to do JavaScript "${checkScript}" in tab ${i} of ${getTargetWindowRef()}`
356
+ ).catch(() => '0');
357
+ if (String(m).trim() === '1') {
358
+ _activeTabIndex = i;
359
+ _lastTabCount = tabCount;
360
+ return i;
361
+ }
362
+ }
363
+ // Marker not found anywhere — page reloaded or tab closed
364
+ _activeTabMarker = null;
365
+ } catch { /* fall through to URL strategy */ }
366
+ }
367
+
334
368
  if (!_activeTabURL) return _activeTabIndex;
369
+
335
370
  try {
336
371
  const safeUrl = _activeTabURL.replace(/"/g, '\\"');
337
372
  const domain = _activeTabURL.replace(/^https?:\/\//, '').split('/')[0].replace(/"/g, '\\"');
@@ -2112,6 +2147,11 @@ export async function newTab(url = "") {
2112
2147
  _activeTabURL = url || null;
2113
2148
  _lastResolveTime = Date.now();
2114
2149
  _lastTabCount = Number(tabCount); // Update tab count cache
2150
+ // Set bulletproof tab marker — survives same-tab navigation, redirects, query changes (v2.8.3)
2151
+ _activeTabMarker = `MCP_${SESSION_ID}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
2152
+ await osascriptFast(
2153
+ `tell application "Safari" to do JavaScript "window.__mcpTabMarker='${_activeTabMarker}'" in tab ${_activeTabIndex} of ${getTargetWindowRef()}`
2154
+ ).catch(() => {});
2115
2155
  // Wait for page load if URL given
2116
2156
  if (url) {
2117
2157
  try {