01Safety
0.0 / 30
What changed in the harness
Selection accuracy 93→89, token cost up 9%, unconfirmed writes 0%→0%.
Category breakdown
Earned points across the four signals Gradable measures. Safety and Legibility are scored out of 30; Economics and Discoverability are scored out of 20.
0.0 / 30
18.2 / 30
12.0 / 20
12.4 / 20
Highest-impact fix
Estimated gain +30 pointsExpose machine-readable principal/tenant confirmation and a non-mutating permission check so agents can verify both before destructive actions.
Description evidence
66 defects found across the exposed tool descriptions. Suggested rewrites make purpose, inputs, boundaries, and returns easier for an agent to understand.
| Tool | Defect types | Suggested rewrite |
|---|---|---|
safari_navigate |
name_restates_behavior no_return_description |
Load the specified url in the active Safari tab and wait until the page has fully loaded before returning, so subsequent snapshot/read calls see the rendered page. Returns the outcome (success/failure) of the navigation. |
safari_go_back |
no_return_description |
Move the active Safari tab one step backward in browser history, equivalent to pressing the Back button. Returns whether the navigation succeeded. |
safari_go_forward |
no_return_description |
Move the active Safari tab one step forward in browser history, equivalent to pressing the Forward button (only meaningful after going back). Returns whether the navigation succeeded. |
safari_reload |
no_return_description |
Re-fetch the current page in the active Safari tab; pass hard: true to bypass the cache and force a full reload. Returns whether the reload succeeded. |
safari_get_source |
no_return_description |
Return the raw HTML source of the current page as a string, optionally truncated to maxLength characters (default 200000). Use to inspect markup or extract data that text-based reading misses. |
safari_click |
no_return_description |
Perform a click on an element in the current page, locating it by ref (take a fresh safari_snapshot first, since refs expire after each snapshot), CSS selector, visible text, or x/y coordinates. Dispatches a full PointerEvent+MouseEvent sequence with a React Fiber fallback so it works on React/Airtable/virtual-DOM apps, and never moves the real mouse. Returns the outcome of the click. |
safari_double_click |
no_return_description |
Double-click an element in the current page, located by CSS selector or x/y coordinates (e.g. to select a word). Returns the outcome of the double-click. |
safari_right_click |
no_return_description |
Right-click an element in the current page, located by CSS selector or x/y coordinates, to open its context menu. Returns the outcome of the right-click. |
safari_native_hover |
no_return_description |
Move the real macOS cursor over an element (by ref, CSS selector, text, or x/y) to trigger native :hover and mouseenter handlers that JS-dispatched events can't reach, such as Discord sidebar or portal-rendered tooltips. Dwells for dwellMs (clamped 0-5000) to let tooltips render, then restores the original cursor position unless restoreMouse is false; requires the Safari window to be visible. After hovering, read the tooltip with safari_wait_for or safari_evaluate. Returns the outcome of the hover. |
safari_native_type |
no_return_description |
Insert text into any editor in the active Safari page by pasting it through the real OS clipboard (CGEvent Cmd+V targeted at the Safari window), so ProseMirror/Slate/Draft.js update their internal model natively. Unlike safari_fill it never manipulates the DOM directly, saves and restores the clipboard, and doesn't steal focus. Use for Discord, Slack, and editors where safari_fill looks right but content isn't 'really there' at submit time. Returns the outcome of the insertion. |
safari_fill |
no_return_description |
Set the value of an input, textarea, select, or contenteditable, replacing any existing content, matching by selector or a fresh-snapshot ref (refs expire after each new snapshot). Handles React controlled inputs, ProseMirror, Draft.js, and Google Closure editors automatically; use safari_replace_editor for code editors and safari_type_text for character-by-character fields. Returns the outcome of the fill. |
safari_clear_field |
no_return_description |
Clear the text of the input identified by selector in the current page. Returns the outcome of the clear action. |
safari_select_option |
no_return_description |
Select the given value (option value or visible label) in a native <select> dropdown by setting .value and dispatching a change event. Pass ref (from safari_snapshot) when the select is inside an iframe or shadow DOM, since a plain selector only reaches the top document; for custom/React dropdowns use safari_click on the trigger then the option instead. Returns the selection outcome. |
safari_fill_form |
no_return_description |
Fill multiple form fields in one call by passing fields as an array of {selector, value} pairs, applying each value to the matching element. Returns the outcome of the batch fill. |
safari_press_key |
no_return_description |
Send a keyboard keypress (enter, tab, escape, space, arrow keys, or a single character) to the current page, optionally with cmd/shift/alt/ctrl modifiers. Returns the outcome of the keypress. |
safari_type_text |
no_return_description |
Type text character-by-character into the current page's focus or an element matched by ref/selector, firing realistic key events so autocomplete and per-keystroke handlers trigger — best for search boxes, chat inputs, and incrementally-reacting fields. Use safari_fill for rich text editors and safari_replace_editor for code editors; refs expire after each new snapshot. Returns the outcome of the typing. |
safari_replace_editor |
no_return_description |
Replaces ALL content in a code editor (Monaco, CodeMirror, Ace, ProseMirror) using its native API. Use ONLY for code editors, e.g. Airtable automations, GitHub gists, CodePen, n8n code nodes. NOT for rich text editors like Medium/LinkedIn (use safari_fill for those). Detects ProseMirror/Draft.js/CodeMirror/Monaco/Ace and returns a confirmation once the new text is written. |
safari_screenshot |
no_return_description |
Takes a visual screenshot of the current viewport (or the full page if fullPage is true) and returns it as a base64 JPEG image. EXPENSIVE: prefer safari_snapshot for most tasks and use this only to verify visual layout, styling, images, or colors that snapshot cannot show. |
safari_scroll |
no_return_description |
Scrolls the page up or down by the given number of pixels (direction defaults to down, amount defaults to 500 px) and returns the resulting scroll position. |
safari_scroll_to |
no_return_description |
Scrolls the page to the given x/y pixel position (both default to 0) and returns the resulting scroll position. |
safari_list_tabs |
name_restates_behavior no_return_description |
Returns the list of all open Safari tabs with their titles and URLs, including the indices used by safari_switch_tab. Call this first to identify which tab to switch to or act on. |
safari_reload_extension |
no_return_description |
Hot-reloads the Safari MCP Bridge extension so it reloads its own code from disk without a manual Safari Preferences > Extensions toggle. Use after editing extension/background.js or extension/content.js in the safari-mcp repo. The extension briefly disconnects and auto-reconnects within about 2 seconds, after which the tool returns a confirmation. NOTE: requires an installed extension supporting the reload_extension command (added in v2.9.1+); older extensions need a one-time manual reload to pick up this feature. |
safari_new_tab |
no_return_description |
Opens a new Safari tab, navigating to the given URL or opening a blank tab when left empty, and returns a confirmation once the tab is open. |
safari_close_tab |
no_return_description |
Closes the currently active Safari tab and returns a confirmation once it is closed. |
safari_switch_tab |
no_return_description |
Switches Safari to the tab at the given index (starting from 1; use safari_list_tabs to see indices) and returns a confirmation. All subsequent commands (click, fill, evaluate, screenshot, scroll) target this tab; call switch_tab again to re-anchor if commands run on the wrong tab. |
safari_wait_for |
no_return_description |
Waits up to the timeout (default 10000 ms) for the given CSS selector or text to appear on the page and returns once it is found. Use before interacting with dynamically loaded content. |
safari_wait_for_new_tab |
no_return_description |
Waits up to the timeout (default 10000 ms) for a new tab to appear, e.g. after an OAuth login click opens a popup, and automatically switches to it. If urlContains is set, only tabs whose URL contains that string match. Returns once the new tab is found. |
safari_evaluate |
no_return_description |
Executes the given JavaScript in the current page and returns the result of the last expression. 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_eval_file |
no_return_description |
Executes JavaScript read from the .js file at the given absolute path (avoids passing huge scripts inline or manual copy) and returns the result. Same engine as safari_evaluate: extension-first (no focus steal) with AppleScript fallback. Use to upload binary via a generated .js containing base64. |
safari_get_element |
no_return_description |
Returns detailed information about the element matching the CSS selector: tag, text, bounding rect, attributes, and visibility. |
safari_hover |
no_return_description |
Moves the pointer over an element targeted by ref (from safari_snapshot), CSS selector, or x/y coordinates to trigger hover effects (e.g. tooltips, dropdowns), and returns a confirmation once the hover is applied. |
safari_handle_dialog |
no_return_description |
Registers a handler for the next alert/confirm/prompt dialog: accepts or dismisses it (default accept) and optionally enters text for prompt dialogs. Returns a confirmation that the handler is set; call it before triggering the dialog. |
safari_resize |
name_restates_behavior no_return_description |
Resizes the Safari window to the given pixel width and height, for example to establish a known viewport before screenshots or layout checks, and returns a confirmation once the resize is applied. |
safari_drag |
no_return_description |
Drags an element from a source (CSS selector or source x/y) to a target (CSS selector or target x/y) to simulate a drag-and-drop gesture, and returns a confirmation once the drag completes. |
safari_paste_image |
no_return_description |
Pastes the image at the given file path (PNG, JPG, WebP) into the currently focused element via JS DataTransfer with no clipboard use and no focus steal. Works on Medium, dev.to, HackerNoon, TOI, etc., and returns a confirmation once pasted. |
safari_emulate |
no_return_description |
Emulates a mobile device by resizing the window and setting the user agent. Use a preset device (iphone-14, iphone-14-pro-max, ipad, ipad-pro, pixel-7, galaxy-s24) or custom width/height/userAgent/scale, and returns a confirmation once emulation is applied. |
safari_reset_emulation |
no_return_description |
Resets device emulation back to desktop mode by clearing the mobile window size and user agent set by safari_emulate, and returns a confirmation once reset. |
safari_get_cookies |
no_return_description |
Returns the cookies currently set for the current page, including their names and values, so you can inspect session or auth state. |
safari_local_storage |
no_return_description |
Get localStorage data for the current page. Returns the stored value for the given key, or all localStorage key-value pairs when no key is specified. |
safari_run_script |
no_return_description |
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'). Returns the result of each step in order. |
safari_start_console |
no_return_description |
Start capturing console messages (log, warn, error, info). Call once per page. Returns nothing; use safari_get_console to retrieve the captured messages. |
safari_get_console |
name_restates_behavior no_return_description |
Retrieve console messages captured since safari_start_console was called. Returns the collected log, warn, error, and info messages with their type and text. |
safari_clear_console |
no_return_description |
Clear all captured console messages. Returns nothing; new messages continue to be captured if safari_start_console is still active. |
safari_save_pdf |
name_restates_behavior no_return_description |
Save the current page as a PDF file at the given absolute path. Uses screencapture + PDF rendering (no Safari UI interaction needed). Returns the path where the PDF was saved. |
safari_accessibility_snapshot |
no_return_description |
Get the accessibility tree of the page (roles, ARIA labels, focusable elements, form states). Essential for a11y auditing. Returns the tree, scoped to the optional CSS selector and limited to maxDepth levels. |
safari_set_cookie |
no_return_description |
Set a cookie on the current page with the given name, value, and optional attributes (domain, path, expires, secure, sameSite). Returns nothing on success. |
safari_delete_cookies |
no_return_description |
Delete a specific cookie by name on the current page, or delete all cookies when 'all' is true. Returns nothing. |
safari_session_storage |
no_return_description |
Get sessionStorage data for the current page. Returns the value for the given key, or all sessionStorage key-value pairs when no key is specified. |
safari_set_session_storage |
name_restates_behavior no_return_description |
Write the given value to the specified key in the current page's sessionStorage. Returns nothing on success. |
safari_set_local_storage |
name_restates_behavior no_return_description |
Write the given value to the specified key in the current page's localStorage. Returns nothing on success. |
safari_delete_local_storage |
name_restates_behavior no_return_description |
Delete a single key from the current page's localStorage, or clear all localStorage when key is omitted. Returns nothing. |
safari_delete_session_storage |
name_restates_behavior no_return_description |
Delete a single key from the current page's sessionStorage, or clear all sessionStorage when key is omitted. Returns nothing. |
safari_export_storage |
no_return_description |
Export all storage state (cookies + localStorage + sessionStorage) as JSON. Returns a JSON string representing the full storage state, useful for saving and restoring login sessions. |
safari_import_storage |
name_restates_behavior no_return_description |
Restore cookies, localStorage, and sessionStorage on the current page from a JSON string exported by safari_export_storage. Returns nothing on success. |
safari_clipboard_read |
no_return_description |
Read the current system clipboard content. Returns the text currently on the clipboard. |
safari_clipboard_write |
no_return_description |
Write the given text to the system clipboard, replacing any current content. Returns nothing on success. |
safari_clear_mocks |
no_return_description |
Remove all network route mocks created by safari_mock_route and restore real network behavior. Returns nothing. |
safari_wait |
no_return_description |
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. Returns nothing after the wait completes. |
safari_start_network_capture |
no_return_description |
Start capturing detailed network requests (fetch + XHR) with headers, status, timing. Call once per page. Intercepts fetch/XHR — captures requests AFTER this call only. Returns nothing; use safari_network_details to retrieve results. For quick overview of already-loaded resources, use safari_network instead. |
safari_network_details |
no_return_description |
Get captured network requests with full details (must call safari_start_network_capture first). Returns a list of captured requests, optionally filtered by URL substring and limited to 'limit' entries (default: 50). |
safari_clear_network |
no_return_description |
Clear all captured network requests. Returns nothing; recording continues unless capture is stopped. |
safari_throttle_network |
no_return_description |
Simulate slow network conditions to test performance under poor connectivity: apply a preset profile (slow-3g, fast-3g, 4g, offline) or custom latency and download speed, or call with no args to reset to normal. Returns a confirmation of the applied settings (or that the network was reset). |
safari_override_geolocation |
no_return_description |
Override the browser's geolocation API so navigator.geolocation calls from the page return custom coordinates using the required latitude and longitude, with optional accuracy in meters (default 100). Returns a confirmation that the override is active. |
safari_scroll_to_element |
no_return_description |
Scroll the page to bring a target into view, by CSS selector or by visible text; for virtual-DOM pages (e.g., Airtable) use text and it scrolls down until the text appears in the DOM. Returns whether the target was found and scrolled into view within the timeout. |
safari_click_and_wait |
no_return_description |
Click an element (by CSS selector or visible text) and wait for the result — either a page load or the appearance of a waitFor selector — in one call instead of separate click + wait steps. Returns the outcome of the click and wait within the timeout. |
safari_fill_and_submit |
no_return_description |
Fill the given fields and submit the form in one operation, auto-detecting the submit button when submitSelector is omitted. Returns the result of the submission (e.g., resulting page state or confirmation). |
Selection evidence
11 pairs where similar names or overlapping descriptions may send an agent toward the wrong tool.
| Tool A | Tool B | Confidence | Why they collide |
|---|---|---|---|
safari_snapshot |
safari_accessibility_snapshot |
high | Both return the page's accessibility tree with near-identical phrasing ('snapshot' vs 'accessibility snapshot'). Tasks like 'get the page structure/state' or 'give me the accessibility snapshot' can select either; only the descriptions (interactive refs vs a11y auditing) differentiate them. |
safari_scroll |
safari_scroll_to |
medium | Both are 'scroll' tools; 'scroll down the page' maps to safari_scroll (direction/amount) while 'scroll to a specific spot' maps to safari_scroll_to (x/y). Tasks like 'scroll to the bottom of the page' fit both, so the agent can pick the one whose schema does not match. |
safari_scroll_to |
safari_scroll_to_element |
medium | Near-identical names. A task like 'scroll to the element / comments section' should use safari_scroll_to_element (selector/text), but an agent may pick safari_scroll_to, which only accepts x/y coordinates and can't target the element. |
safari_native_hover |
safari_hover |
medium | Both perform 'hover over an element' and both mention tooltips; they differ only in the event mechanism (JS-dispatched vs OS-level CGEvent). A task like 'hover over X to reveal the tooltip' matches both, and picking the wrong mechanism may fail to trigger native :hover handlers. |
safari_wait_for |
safari_wait |
medium | Both start with 'wait'. safari_wait is a fixed ms pause while safari_wait_for waits for an element/text; a task like 'wait until the page loads' or 'wait for the content to appear' can be mapped to either, especially since wait_for's description steers users away from wait. |
safari_fill |
safari_fill_form |
medium | 'Fill out this form' maps to both: safari_fill sets a single value, safari_fill_form takes an array of fields. A multi-field task can cause the agent to pick safari_fill with mismatched parameters, or vice versa. |
safari_fill |
safari_fill_and_submit |
medium | Both are 'fill' tools; a task like 'fill in the login form and submit/log in' could make the agent pick safari_fill (dropping the submission) or safari_fill_and_submit, since the descriptions overlap on filling form fields. |
safari_network |
safari_network_details |
medium | Both retrieve network request information. A task like 'give me the details of the network requests' could select safari_network_details, which requires a prior safari_start_network_capture (fails otherwise), or safari_network, the quick no-setup overview; the name/description do not make the prerequisite obvious. |
safari_local_storage |
safari_session_storage |
medium | Identical schema and near-identical descriptions ('Get ... storage data for the current page'), differing only in localStorage vs sessionStorage. A task like 'get the stored storage values for this page' is genuinely ambiguous between them. |
safari_set_session_storage |
safari_set_local_storage |
medium | Both are 'Set a value in ... storage' with identical key/value parameters; a task like 'save this value under this key in storage' without explicitly naming local vs session has no distinguishing signal, so either tool looks correct. |
safari_click_and_read |
safari_click_and_wait |
medium | Both combine 'click' with a follow-up and share the same tokens. A task like 'click the element and wait for the result page to load/update' is ambiguous: click_and_read returns the updated page content, while click_and_wait waits for a selector; the agent may pick the one that omits the other half of the task. |
Compare the field