Safari MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Capabilities
Features and capabilities supported by this server
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| safari_navigate | Navigate to a URL in Safari. Waits for page to fully load. |
| safari_go_back | Go back in browser history |
| safari_go_forward | Go forward in browser history |
| safari_reload | Reload the current page |
| safari_read_page | Read page text content (title, URL, body text). Use for reading article text or page content. For interacting with elements, prefer safari_snapshot (gives ref IDs). Use selector to read specific element. Use maxLength to limit output. |
| safari_get_source | Get HTML source of current page |
| safari_snapshot | PREFERRED way to see page state. Returns accessibility tree with ref IDs for every interactive element. Use refs with click/fill/type instead of CSS selectors. Workflow: snapshot → see refs → click({ref:'0_5'}). PREFER THIS over safari_screenshot (cheaper, structured text vs heavy image) and over safari_read_page (includes interactive refs). Use safari_screenshot only when you need to see visual layout/styling. |
| safari_navigate_and_read | Navigate to a URL and return the page content in one step — saves 1 full round-trip vs navigate+read_page. Use instead of safari_navigate + safari_read_page. |
| safari_click | Click element. Use ref (from snapshot), selector, text, or x/y. Works on React/Airtable/virtual DOM apps via full PointerEvent+MouseEvent sequence + React Fiber fallback. Pure JS — never touches user's mouse. When using ref, always take a FRESH safari_snapshot first — refs expire after each new snapshot. |
| safari_click_and_read | Click an element then return the updated page — saves 1 full round-trip vs separate click+read_page. Handles both React Router navigation and full page loads. |
| safari_double_click | Double-click an element by CSS selector or x/y coordinates (e.g. to select a word in text) |
| safari_right_click | Right-click (context menu) an element by CSS selector or x/y coordinates |
| safari_native_click | OS-level mouse click via macOS CGEvent — produces isTrusted: true events that pass WAF/bot detection (G2, Cloudflare, etc.). Use when regular safari_click fails with 405/403 errors or form submissions are blocked. Trade-off: physically moves the mouse cursor and requires Safari window to be visible. Use ref (from snapshot), selector, text, or x/y. When using ref, always take a FRESH safari_snapshot first. |
| safari_native_hover | OS-level mouse hover via macOS CGEvent — moves the real cursor to an element to trigger native :hover / mouseenter handlers. Use for obfuscated UIs where JS-dispatched mouseenter isn't enough, like Discord server sidebars (tooltips only appear on real hover) or portal-rendered tooltips. After hover, call safari_wait_for or safari_evaluate to read the tooltip. Dwells for dwellMs to let tooltips render, then restores the original cursor position by default. Requires Safari window to be visible. |
| safari_native_keyboard | OS-level keyboard event via macOS CGEvent — sends a real keypress (with optional modifiers) to the Safari window WITHOUT activating Safari or stealing focus. Use when safari_press_key's JS path doesn't reach React trust-gated handlers (Discord ProseMirror Enter, Slack send, virtualized editors). Keys: enter, return, tab, escape, space, delete, backspace, up/down/left/right, home, end, pageup, pagedown, f1-f6, a-z, 0-9 and common punctuation. Modifiers: cmd, shift, alt, ctrl. Produces isTrusted:true events. Never activates Safari — runs entirely in the background. |
| safari_native_type | Insert text into ANY editor via OS-level clipboard paste (CGEvent Cmd+V targeted to Safari window). Unlike safari_fill which manipulates DOM directly (breaking React/ProseMirror state), this goes through the real paste pipeline — ProseMirror/Slate/Draft.js process the paste event natively and update their internal model. After native_type, pressing Enter (via safari_native_keyboard) will actually submit the form because the framework state matches the DOM. Saves and restores the user's clipboard. No focus stealing. Use for Discord, Slack, and any editor where safari_fill works visually but the content isn't 'really there' when you try to submit. |
| safari_fill | Fill/replace value in an input, textarea, select, OR contenteditable (rich text). Handles React controlled inputs, ProseMirror, Draft.js, and Google Closure editors automatically. Use for SETTING a value (replaces existing). For code editors (Monaco/CodeMirror/Ace), use safari_replace_editor instead. For character-by-character typing in search boxes, use safari_type_text. IMPORTANT: When using ref, always take a FRESH safari_snapshot first — refs expire after each new snapshot (prefix changes: 5_xx → 6_xx). |
| safari_clear_field | Clear an input field |
| safari_verify_state | Verify the framework-level state of an editor/input matches the expected value. Returns JSON {match, mode, actual, expected, hint?}. Modern editors (ProseMirror, Lexical, Closure, React-controlled inputs) maintain state separately from the DOM — |
| safari_select_option | Select an option in a native dropdown. Sets .value and dispatches change event. Pass |
| safari_react_select_set | Set a value in a react-select v5 dropdown by walking React fiber to find the Select component and invoking onChange directly — bypasses the menu UI entirely. Use when safari_click on the chevron or option keeps failing (Cloudflare custom token forms after a few rows, portal-rendered selects that intercept synthetic events). Returns JSON {ok, selected} on success, or {ok:false, error, available:[…]} listing up to 30 option labels on miss. Match is by label, value, or case-insensitive label. Either ref or selector required. NOTE: For Permissions-levels combos that are disabled until a Permission is selected, set the Permissions value first — the level combo becomes enabled and its props.options populate. |
| safari_react_select_list_options | List available options of a react-select v5 dropdown without opening the menu. Returns JSON {ok, total, options:[{label,value}…]}. Useful when safari_react_select_set returns 'option not found' and you need to see exact labels (e.g. 'Email Routing Rules' vs 'Email Routing'). Either ref or selector required. |
| safari_fill_form | Fill multiple form fields at once |
| safari_press_key | Press a keyboard key (enter, tab, escape, arrows, etc). Supports modifiers (cmd, shift, alt, ctrl). |
| safari_type_text | Type text character-by-character with realistic key events. Best for: search boxes (triggers autocomplete), chat inputs, and fields that react to each keystroke. For rich text editors (Medium, HackerNoon, LinkedIn), use safari_fill instead — it uses framework-native APIs. For code editors (Monaco/CodeMirror), use safari_replace_editor. When using ref, always take a FRESH safari_snapshot first — refs expire after each new snapshot. |
| safari_replace_editor | Replace ALL content in a code editor (Monaco, CodeMirror, Ace, ProseMirror). Use ONLY for code editors — Airtable automations, GitHub gists, CodePen, n8n code nodes, etc. NOT for rich text editors like Medium/LinkedIn (use safari_fill for those). Detects ProseMirror/Draft.js/CodeMirror/Monaco/Ace and uses their native API. |
| safari_screenshot | Take a visual screenshot (base64 JPEG). EXPENSIVE — use safari_snapshot instead for most tasks. Only use screenshot when you need to verify visual layout, styling, images, or colors that snapshot can't show. |
| safari_screenshot_element | Take a screenshot of a specific element (by CSS selector). Returns base64 PNG image. |
| safari_scroll | Scroll the page up or down by a specified amount |
| safari_scroll_to | Scroll to a specific position on the page |
| safari_list_tabs | List all open tabs in Safari with their titles and URLs |
| safari_reload_extension | Hot-reload the Safari MCP Bridge extension — forces it to reload its own code from disk without requiring manual Safari Preferences → Extensions → toggle. Use after editing extension/background.js or extension/content.js in the safari-mcp repo. The extension briefly disconnects during reload and auto-reconnects within ~2 seconds. NOTE: this tool itself requires the extension version already installed to support the |
| safari_new_tab | Open a new tab, optionally with a URL |
| safari_close_tab | Close the current tab |
| safari_switch_tab | Switch to a specific tab by index (use safari_list_tabs to see indices). All subsequent commands (click, fill, evaluate, screenshot, scroll) will target this tab. If commands seem to run on the wrong tab, call switch_tab again to re-anchor. |
| safari_wait_for | Wait for an element or text to appear on the page |
| safari_wait_for_new_tab | Wait for a new tab to appear (e.g. after OAuth login click opens popup). Automatically switches to the new tab. |
| safari_evaluate | Execute JavaScript in the current page. Automatically falls back to AppleScript when CSP blocks execution (e.g. Google Search Console, LinkedIn). For reading data, prefer safari_read_page or safari_snapshot. For interactions, prefer safari_click/fill with refs. |
| safari_get_element | Get detailed info about an element (tag, text, rect, attributes, visibility) |
| safari_query_all | Find all elements matching a CSS selector (returns tag, text, href, value) |
| safari_hover | Hover over element. Use ref, selector, or x/y |
| safari_handle_dialog | Set up handler for the next alert/confirm/prompt dialog |
| safari_resize | Resize the Safari window |
| safari_drag | Drag an element to another element or position. Use CSS selectors or x/y coordinates. |
| safari_upload_file | Upload a file to a element via JavaScript DataTransfer — NO file dialog, NO UI interaction. IMPORTANT: Do NOT click the file input before calling this tool — just provide the selector and file path. If a file dialog is already open, this tool will close it first. NOTE: 'verified 0 files' may appear even on success if the site uses a custom upload handler — check visually with safari_snapshot. |
| safari_paste_image | Paste an image from a local file into the focused element via JS DataTransfer (no clipboard, no focus steal). Works on Medium, dev.to, HackerNoon, TOI, etc. |
| safari_emulate | Emulate a mobile device by resizing window and setting user agent. Devices: iphone-14, iphone-14-pro-max, ipad, ipad-pro, pixel-7, galaxy-s24. Or use custom width/height. |
| safari_reset_emulation | Reset device emulation back to desktop mode |
| safari_get_cookies | Get cookies for the current page |
| safari_local_storage | Get localStorage data for the current page |
| safari_network | Quick network overview via Performance API (no setup needed). Shows URLs and timing for resources loaded by the page. For detailed request/response info (headers, status codes, POST bodies), use safari_start_network_capture + safari_network_details instead. |
| safari_run_script | Batch multiple Safari actions in ONE call. Steps: [{action, args}]. Actions match other safari_* tool names without prefix (e.g. 'navigate', 'click', 'fill', 'evaluate', 'readPage'). |
| safari_start_console | Start capturing console messages (log, warn, error, info). Call once per page. |
| safari_get_console | Get captured console messages (must call safari_start_console first) |
| safari_clear_console | Clear all captured console messages |
| safari_save_pdf | Save the current page as a PDF file. Uses screencapture + PDF rendering (no Safari UI interaction needed). |
| safari_accessibility_snapshot | Get the accessibility tree of the page (roles, ARIA labels, focusable elements, form states). Essential for a11y auditing. |
| safari_set_cookie | Set a cookie on the current page |
| safari_delete_cookies | Delete a specific cookie or all cookies for the current page |
| safari_session_storage | Get sessionStorage data for the current page |
| safari_set_session_storage | Set a value in sessionStorage |
| safari_set_local_storage | Set a value in localStorage |
| safari_delete_local_storage | Delete a localStorage key, or clear all localStorage (omit key to clear all) |
| safari_delete_session_storage | Delete a sessionStorage key, or clear all sessionStorage (omit key to clear all) |
| safari_export_storage | Export all storage state (cookies + localStorage + sessionStorage) as JSON — useful for saving and restoring login sessions |
| safari_import_storage | Import storage state from JSON (as exported by safari_export_storage) — restores cookies, localStorage, sessionStorage |
| safari_clipboard_read | Read the current clipboard content (text) |
| safari_clipboard_write | Write text to the system clipboard |
| safari_mock_route | Intercept network requests matching a URL pattern and return a mock response. Works with both fetch and XHR. Useful for testing API error states, offline behavior, or replacing API responses. |
| safari_clear_mocks | Remove all network route mocks (restore real network behavior) |
| safari_wait | Wait for a fixed time in milliseconds. Use only when you need a brief pause between actions. PREFER safari_wait_for (waits for element/text to appear) — it's smarter and doesn't waste time. |
| safari_start_network_capture | Start capturing detailed network requests (fetch + XHR) with headers, status, timing. Call once per page. Intercepts fetch/XHR — captures requests AFTER this call only. For quick overview of already-loaded resources, use safari_network instead. |
| safari_network_details | Get captured network requests with full details (must call safari_start_network_capture first) |
| safari_clear_network | Clear all captured network requests |
| safari_performance_metrics | Get detailed performance metrics: navigation timing, Web Vitals (FCP, LCP, CLS), resource breakdown, memory usage |
| safari_throttle_network | Simulate slow network conditions. Profiles: slow-3g, fast-3g, 4g, offline. Or custom latency/speed. Call with no args to reset. |
| safari_console_filter | Get console messages filtered by level (must call safari_start_console first) |
| safari_extract_tables | Extract HTML tables as structured JSON (headers + rows). Perfect for scraping data tables. |
| safari_extract_meta | Extract all meta tags: title, description, canonical, OG tags, Twitter cards, JSON-LD, alternate languages, RSS feeds |
| safari_extract_images | Extract all images with src, alt, dimensions, loading strategy, viewport visibility |
| safari_extract_links | Extract all links with href, text, rel, target, external/nofollow detection |
| safari_override_geolocation | Override the browser's geolocation API to return custom coordinates |
| safari_get_computed_style | Get computed CSS styles for an element. Optionally filter specific properties. |
| safari_list_indexed_dbs | List all IndexedDB databases on the current page |
| safari_get_indexed_db | Read records from an IndexedDB database store |
| safari_css_coverage | Analyze CSS coverage: find unused CSS rules across all stylesheets. Shows coverage percentage per stylesheet. |
| safari_detect_forms | Auto-detect all forms on the page with their fields, types, selectors, and submit buttons. Great for automated form filling. |
| safari_scroll_to_element | Scroll to element by CSS selector OR text. For virtual DOM (Airtable) use text — scrolls down until text appears in DOM. |
| safari_click_and_wait | Click an element AND wait for the result (page load or element). Use instead of click + wait_for separately. |
| safari_fill_and_submit | Fill a form AND submit it in one operation. Finds submit button automatically if not specified. |
| safari_analyze_page | Full page analysis in ONE call: title, URL, meta tags, OG, headings, link stats, image stats, forms, and text preview. Perfect for SEO/audit. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/achiya-automation/safari-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server