com um clique
safari
Navigate, search, click links, read content, and manage tabs in Safari
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Navigate, search, click links, read content, and manage tabs in Safari
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
AX-first automation for Logic Pro — transport, tracks, tempo, mixer, and view controls
Browse conversations, read messages, and send texts in Apple Messages
Minimal Calendar automation using EventKit with optional Shortcuts integration
Search and create contacts via CNContact, with AppleScript/Shortcuts helpers
Automate Apple Mail using AX for UI interactions and AppleScript for draft/message data flows
AX-first automation for Apple Maps search and result interactions
| name | safari |
| description | Navigate, search, click links, read content, and manage tabs in Safari |
Always connect with: connect_app({ name: "Safari" })
Safari's AX tree has two main areas:
web_area element containing the page's DOM-like accessibility tree (headings, links, groups, text, lists, tables, etc.)Navigate to a URL — fill on the address bar sets the AX value but does NOT trigger navigation. Use open via shell instead:
open -a Safari "https://example.com"
Or with the macbeth SDK:
import { execSync } from "node:child_process";
execSync('open -a Safari "https://example.com"');
Read current URL:
{ "query": [
{ "role": "window" },
{ "role": "toolbar" },
{ "role": "text_field", "identifier": "WEB_BROWSER_ADDRESS_AND_SEARCH_FIELD" }
]}
The value field contains the current URL.
Back / Forward: Click the toolbar buttons:
{ "query": [{ "role": "window" }, { "role": "toolbar" }, { "role": "button", "identifier": "BackButton" }] }
{ "query": [{ "role": "window" }, { "role": "toolbar" }, { "role": "button", "identifier": "ForwardButton" }] }
Reload: select_menu_item({ app: "Safari", menuPath: ["View", "Reload Page"] }) or click ReloadButton
| Element | Query |
|---|---|
| Address bar | [{ "role": "window" }, { "role": "toolbar" }, { "role": "text_field", "identifier": "WEB_BROWSER_ADDRESS_AND_SEARCH_FIELD" }] |
| Back | [{ "role": "window" }, { "role": "toolbar" }, { "role": "button", "identifier": "BackButton" }] |
| Forward | [{ "role": "window" }, { "role": "toolbar" }, { "role": "button", "identifier": "ForwardButton" }] |
| Reload | [{ "role": "window" }, { "role": "toolbar" }, { "role": "button", "identifier": "ReloadButton" }] |
| New Tab | [{ "role": "window" }, { "role": "toolbar" }, { "role": "button", "identifier": "NewTabButton" }] |
| Share | [{ "role": "window" }, { "role": "toolbar" }, { "role": "button", "identifier": "ShareButton" }] |
| Sidebar | [{ "role": "window" }, { "role": "toolbar" }, { "role": "button", "identifier": "SidebarButton" }] |
| Tab Overview | [{ "role": "window" }, { "role": "toolbar" }, { "role": "button", "identifier": "TabOverviewButton" }] |
Queries use recursive descent — you don't need to specify every intermediate container. Just scope to web_area and target the element directly:
[{ "role": "window" }, { "role": "web_area" }, { "role": "link", "title": "Sign in" }][{ "role": "window" }, { "role": "web_area" }, { "role": "link", "titlePattern": "Learn.*more" }][{ "role": "window" }, { "role": "web_area" }, { "role": "heading", "title": "Example Domain" }][{ "role": "window" }, { "role": "web_area" }, { "role": "text_field" }][{ "role": "window" }, { "role": "web_area" }, { "role": "button", "title": "Submit" }]Use index to disambiguate when multiple elements match:
[{ "role": "window" }, { "role": "web_area" }, { "role": "link", "titlePattern": "comments", "index": 2 }]
Tabs are radio elements inside an opaqueprovidergroup (TabBar):
[{ "role": "window" }, { "role": "opaqueprovidergroup" }, { "role": "radio", "title": "Example Domain" }]
value: "1", inactive tabs have value: "0"Use select_menu_item instead of press_key — it doesn't steal focus.
| Action | Menu path |
|---|---|
| New tab | ["File", "New Tab"] |
| Close tab | ["File", "Close Tab"] |
| New window | ["File", "New Window"] |
| New private window | ["File", "New Private Window"] |
| Find on page | ["Edit", "Find", "Find…"] |
| Back | ["History", "Back"] |
| Forward | ["History", "Forward"] |
| Reopen closed tab | ["History", "Reopen Last Closed Tab"] |
| Show all history | ["History", "Show All History"] |
| Reload | ["View", "Reload Page"] |
| Zoom in | ["View", "Zoom In"] |
| Zoom out | ["View", "Zoom Out"] |
| Actual size | ["View", "Actual Size"] |
| Next tab | ["Window", "Show Next Tab"] |
| Previous tab | ["Window", "Show Previous Tab"] |
AXPress. Use fill to set its value, or open -a Safari <url> to navigate.open -a Safari <url> instead.maxDepth: 4-5 to keep output manageable. For the web content area specifically, the DOM adds several levels of nesting.wait_for on a known page element to confirm the page has loaded before interacting.open -a Safari "https://example.com" (shell)wait_for a known element on the target pagequery_tree with maxDepth: 6-8 to see the page structureget_element to read specific text valuesquery_tree to find form fieldsfill each text field (web form fields DO support fill, unlike the address bar)click the submit button[{ "role": "window" }, { "role": "opaqueprovidergroup" }, { "role": "radio", "title": "Tab Title" }]select_menu_item({ app: "Safari", menuPath: ["Window", "Show Next Tab"] })