01Safety
0.0 / 30
What changed in the harness
Selection accuracy 90→90, token cost up 5%, 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
16.2 / 30
11.4 / 20
11.8 / 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
78 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 |
Loads the given URL in the current Safari tab and blocks until the page finishes loading. Returns the final page title and URL once loaded. |
safari_go_back |
no_return_description |
Navigates the current tab to the previous page in browser history, equivalent to clicking the back button. Returns the resulting page's title and URL. |
safari_go_forward |
no_return_description |
Navigates the current tab to the next page in browser history, equivalent to clicking the forward button. Returns the resulting page's title and URL. |
safari_reload |
no_return_description |
Reloads the current page in Safari; set hard:true to bypass the cache and force a full reload from the server. Returns the reloaded page's title and URL. |
safari_get_source |
no_return_description |
Retrieves the raw HTML source of the current page, truncated to maxLength characters (default 200000). Returns the HTML as a string. |
safari_click |
no_return_description |
Clicks an element identified by ref, CSS selector, visible text, or x/y coordinates, dispatching a full PointerEvent+MouseEvent sequence (with a React Fiber fallback) so it works on React/Airtable/virtual DOM apps; never moves the real mouse. Returns confirmation of what was clicked or an error if no matching element was found. |
safari_double_click |
no_return_description |
Double-clicks an element identified by CSS selector or x/y coordinates, e.g. to select a word in text. Returns confirmation of the action or an error if no matching element was found. |
safari_right_click |
no_return_description |
Right-clicks an element identified by CSS selector or x/y coordinates to open its context menu. Returns confirmation of the action or an error if no matching element was found. |
safari_native_hover |
no_return_description |
Moves the real macOS cursor (via CGEvent) onto an element identified by ref, selector, text, or x/y to trigger native :hover/mouseenter handlers — for obfuscated UIs like Discord's server sidebar or portal-rendered tooltips where JS-dispatched hover isn't enough. Dwells for dwellMs before optionally restoring the cursor to its original position. Returns confirmation that the hover was performed; use safari_wait_for or safari_evaluate afterward to read any resulting tooltip. |
safari_native_type |
no_return_description |
Focuses the element given by selector or ref (if provided) and pastes value into it via a real OS-level clipboard paste (CGEvent Cmd+V), so frameworks like ProseMirror/Slate/Draft.js process it as a native paste event and update their internal state — unlike safari_fill's direct DOM manipulation. Saves and restores the user's existing clipboard content. Returns confirmation that the paste was performed. |
safari_fill |
no_return_description |
Sets/replaces the value of an input, textarea, select, or contenteditable element identified by ref or selector, handling React controlled inputs, ProseMirror, Draft.js, and Google Closure editors automatically. Returns confirmation of the new value or an error if no matching element was found. |
safari_clear_field |
no_return_description |
Clears the value of the input element matched by selector. Returns confirmation that the field was cleared or an error if no matching element was found. |
safari_select_option |
no_return_description |
Selects an option in a native <select> element (matched by selector or ref) by setting its value and dispatching a change event. Returns the resulting selected value or an error if no matching option was found. |
safari_fill_form |
no_return_description |
Fills multiple input/textarea fields in one call from an array of {selector, value} pairs. Returns confirmation of which fields were filled or errors for any selectors that didn't match. |
safari_press_key |
no_return_description |
Dispatches a keyboard event for the given key (e.g. enter, tab, escape, arrows) to the currently focused element, optionally with modifier keys (cmd, shift, alt, ctrl). Returns confirmation that the key was pressed. |
safari_type_text |
no_return_description |
Types text character-by-character with realistic key events into the element given by ref or selector (or the currently focused element), useful for search boxes, chat inputs, and fields that react to each keystroke. Returns confirmation that the text was typed. |
safari_replace_editor |
no_return_description |
Replaces all content in a code editor (Monaco, CodeMirror, Ace, ProseMirror) with the given text, detecting the editor type and using its native API. Returns confirmation that the content was replaced. |
safari_screenshot |
no_return_description |
Captures a visual screenshot of the current viewport, or the full page if fullPage is true. Returns the image as base64-encoded JPEG data. |
safari_scroll |
no_return_description |
Scrolls the page up or down by amount pixels (default 500) in the given direction (default down). Returns the resulting scroll position. |
safari_scroll_to |
no_return_description |
Scrolls the page to the absolute x,y position (defaults 0,0). Returns the resulting scroll position. |
safari_list_tabs |
name_restates_behavior no_return_description |
Enumerates every open Safari tab, useful for choosing a target for safari_switch_tab. Returns an array of tabs with their index, title, and URL. |
safari_reload_extension |
no_return_description |
Forces the Safari MCP Bridge extension to reload its own code from disk, for use after editing extension/background.js or extension/content.js — the extension disconnects briefly and auto-reconnects within ~2 seconds. Requires extension v2.9.1+ to support this command. Returns confirmation once the reload has been triggered. |
safari_new_tab |
no_return_description |
Opens a new Safari tab, optionally navigating it to url (blank tab if omitted). Returns the new tab's index. |
safari_close_tab |
no_return_description |
Closes the current tab. Returns confirmation that the tab was closed. |
safari_switch_tab |
no_return_description |
Switches the active tab that subsequent commands (click, fill, evaluate, screenshot, scroll, etc.) target, using the 1-based index from safari_list_tabs. Returns confirmation of the newly active tab's title and URL. |
safari_wait_for |
no_return_description |
Polls the page until the element matching selector or the given text appears, up to timeout ms (default 10000). Returns confirmation once found or a timeout error. |
safari_wait_for_new_tab |
no_return_description |
Waits (up to timeout ms, default 10000) for a new Safari tab to open, e.g. after clicking an OAuth login link that opens a popup, optionally restricted to tabs whose URL contains urlContains, and automatically switches to it. Returns the new tab's index, title, and URL. |
safari_evaluate |
no_return_description |
Executes arbitrary JavaScript in the current page context, automatically falling back to AppleScript when CSP blocks execution (e.g. Google Search Console, LinkedIn). Returns the script's result value, serialized as JSON. |
safari_eval_file |
no_return_description |
Executes JavaScript read from the file at path using the same engine as safari_evaluate (extension-first, AppleScript fallback), useful for large scripts or uploading binary data as base64 without pasting it inline. Returns the script's result value, serialized as JSON. |
safari_get_element |
no_return_description |
Looks up the element matching selector and inspects it. Returns its tag name, text content, bounding rect, attributes, and visibility state. |
safari_hover |
no_return_description |
Moves the mouse pointer (via JS events, not a real OS cursor) over the element given by ref, selector, or x/y to trigger hover-based UI like tooltips or dropdown menus. Returns confirmation that the hover was performed. |
safari_handle_dialog |
no_return_description |
Registers a handler that will accept or dismiss the next browser alert/confirm/prompt dialog, optionally supplying text for prompt dialogs. Returns confirmation that the handler was registered. |
safari_resize |
name_restates_behavior no_return_description |
Sets the Safari window to the given width and height in pixels, useful for testing responsive layouts. Returns the window's resulting dimensions. |
safari_drag |
no_return_description |
Drags an element from a source (sourceSelector or sourceX/sourceY) to a target (targetSelector or targetX/targetY) by simulating a mouse-down, move, and mouse-up sequence. Returns confirmation that the drag was performed. |
safari_paste_image |
no_return_description |
Pastes the image at filePath into the currently focused element via a JS DataTransfer event (no clipboard, no focus stealing) — works on Medium, dev.to, HackerNoon, TOI, etc. Returns confirmation that the image was pasted. |
safari_emulate |
no_return_description |
Emulates a mobile device by resizing the Safari window and setting the user agent, either from a known device preset (device, e.g. iphone-14, ipad, pixel-7, galaxy-s24) or custom width/height/userAgent/scale values. Returns the resulting viewport dimensions and user agent. |
safari_reset_emulation |
no_return_description |
Resets device emulation back to normal desktop viewport and user agent. Returns confirmation that emulation was reset. |
safari_get_cookies |
no_return_description |
Reads the cookies visible to the current page. Returns an array of cookies with their name, value, domain, path, and flags. |
safari_local_storage |
no_return_description |
Reads localStorage for the current page, either a single key or all entries if key is omitted. Returns the stored value(s) as JSON. |
safari_run_script |
no_return_description |
Executes a sequence of Safari actions (matching other safari_* tool names without the prefix, e.g. 'navigate', 'click', 'fill', 'evaluate', 'readPage') in one call, running each step's args in order. Returns an array of each step's result. |
safari_start_console |
no_return_description |
Begins capturing console messages (log, warn, error, info) emitted by the current page; call once per page before using safari_get_console. Returns confirmation that capture has started. |
safari_get_console |
name_restates_behavior no_return_description |
Retrieves the console messages captured since safari_start_console was called. Returns an array of messages with their level, text, and timestamp. |
safari_clear_console |
no_return_description |
Clears all console messages captured so far by safari_start_console. Returns confirmation that the buffer was cleared. |
safari_save_pdf |
name_restates_behavior no_return_description |
Renders the current page to a PDF using screencapture-based rendering (no Safari print UI involved) and writes it to path. Returns confirmation of the saved file path. |
safari_accessibility_snapshot |
no_return_description |
Walks the accessibility tree of the page (or the subtree under selector) up to maxDepth levels deep, for a11y auditing. Returns each node's role, ARIA label, focusable/form state, and hierarchy. |
safari_set_cookie |
no_return_description |
Sets a cookie (name, value, and optional domain/path/expires/secure/sameSite) on the current page. Returns confirmation that the cookie was set. |
safari_delete_cookies |
no_return_description |
Deletes the cookie matching name, or every cookie if all is true. Returns confirmation of which cookies were deleted. |
safari_session_storage |
no_return_description |
Reads sessionStorage for the current page, either a single key or all entries if key is omitted. Returns the stored value(s) as JSON. |
safari_set_session_storage |
name_restates_behavior no_return_description |
Writes value to sessionStorage under key on the current page, useful for restoring session state without going through the UI. Returns confirmation that the value was set. |
safari_set_local_storage |
name_restates_behavior no_return_description |
Writes value to localStorage under key on the current page, useful for restoring session state without going through the UI. Returns confirmation that the value was set. |
safari_delete_local_storage |
name_restates_behavior no_return_description |
Deletes the localStorage entry for key, or clears all localStorage if key is omitted. Returns confirmation of what was cleared. |
safari_delete_session_storage |
name_restates_behavior no_return_description |
Deletes the sessionStorage entry for key, or clears all sessionStorage if key is omitted. Returns confirmation of what was cleared. |
safari_export_storage |
no_return_description |
Captures the page's full storage state — cookies, localStorage, and sessionStorage — for saving and later restoring a login session. Returns the state serialized as a JSON string. |
safari_import_storage |
name_restates_behavior no_return_description |
Restores cookies, localStorage, and sessionStorage on the current page from a JSON string previously produced by safari_export_storage. Returns confirmation that the state was applied. |
safari_clipboard_read |
no_return_description |
Reads the current text content of the system clipboard. Returns the clipboard text as a string. |
safari_clipboard_write |
no_return_description |
Writes text to the system clipboard, replacing its current content. Returns confirmation that the clipboard was set. |
safari_clear_mocks |
no_return_description |
Removes all network route mocks registered via safari_mock_route, restoring real network behavior. Returns confirmation that mocks were cleared. |
safari_wait |
no_return_description |
Pauses execution for a fixed ms milliseconds; prefer safari_wait_for when waiting on a specific element or text instead of a blind delay. Returns confirmation once the wait completes. |
safari_start_network_capture |
no_return_description |
Begins intercepting fetch and XHR requests made by the page, capturing headers, status, and timing for requests made after this call; call once per page before using safari_network_details. Returns confirmation that capture has started. |
safari_network_details |
no_return_description |
Retrieves detailed network requests captured since safari_start_network_capture was called, optionally limited to limit results or filtered by URL substring. Returns an array of requests with method, URL, status, headers, and timing. |
safari_clear_network |
no_return_description |
Clears all network requests captured so far by safari_start_network_capture. Returns confirmation that the buffer was cleared. |
safari_performance_metrics |
no_return_description |
Collects the page's navigation timing, Web Vitals (FCP, LCP, CLS), resource breakdown, and memory usage. Returns these metrics as a structured object. |
safari_throttle_network |
no_return_description |
Simulates network conditions for the page using a preset profile (slow-3g, fast-3g, 4g, offline) or custom latency/downloadKbps values; call with no args to reset to normal speed. Returns confirmation of the throttling profile applied. |
safari_console_filter |
no_return_description |
Retrieves captured console messages filtered to a single level (log, warn, error, info); requires safari_start_console to have been called first. Returns an array of matching messages. |
safari_extract_tables |
no_return_description |
Parses HTML tables matching selector (default 'table'), up to limit tables. Returns each table as structured JSON with headers and rows. |
safari_extract_meta |
no_return_description |
Scans the page's <head> for SEO and social metadata. Returns title, meta description, canonical URL, Open Graph tags, Twitter card tags, JSON-LD blocks, alternate-language links, and RSS feed links. |
safari_extract_images |
no_return_description |
Scans the page for <img> elements, up to limit images. Returns each image's src, alt text, dimensions, loading strategy, and current viewport visibility. |
safari_extract_links |
no_return_description |
Scans the page for <a> elements, up to limit links, optionally filtered by URL or text substring. Returns each link's href, text, rel, target, and whether it's external/nofollow. |
safari_get_computed_style |
name_restates_behavior no_return_description |
Reads the browser's computed CSS for the element matching selector, optionally limited to specific properties. Returns a map of property names to their computed values. |
safari_list_indexed_dbs |
no_return_description |
Enumerates the IndexedDB databases available on the current page. Returns an array of database names and versions. |
safari_get_indexed_db |
params_unexplained no_return_description |
Reads records from storeName, an object store within the IndexedDB database dbName, up to limit records (default 20). Returns the store's records as an array of JSON objects. |
safari_css_coverage |
no_return_description |
Runs the CSS coverage profiler across all stylesheets on the page to find unused rules. Returns each stylesheet's URL with used/unused byte counts and coverage percentage. |
safari_safe_area_insets |
no_return_description |
Reads the live CSS safe-area-inset values (top/right/bottom/left) as the page renders them, for notch/Dynamic Island layout debugging. Returns the inset values, whether viewport-fit=cover is set, and whether any stylesheet uses env(safe-area-inset-*). |
safari_webkit_compat |
no_return_description |
Tests every CSS property used on the page against this Safari's live engine via CSS.supports(). Returns a list of unsupported properties, properties needing a -webkit- prefix, and known Safari rendering quirks (e.g. position:sticky inside overflow ancestors). |
safari_detect_forms |
no_return_description |
Scans the page for <form> elements and their fields. Returns each form's fields (name, type, selector) and submit button, for use in automated form filling. |
safari_scroll_to_element |
no_return_description |
Scrolls to the element matching selector, or repeatedly scrolls down (up to timeout ms) until text appears — useful for virtual DOM/lazy-loaded content like Airtable. Returns confirmation once the target is in view, aligned per block (default center). |
safari_fill_and_submit |
no_return_description |
Fills the given fields (array of {selector, value}) and submits the form, auto-detecting the submit button unless submitSelector is given. Returns confirmation of submission or the resulting page state. |
safari_analyze_page |
no_return_description |
Runs a full page audit in one call — title, URL, meta tags, Open Graph tags, headings, link stats, image stats, forms, and a text preview — for SEO/audit workflows. Returns this analysis as a structured object. |
Selection evidence
23 pairs where similar names or overlapping descriptions may send an agent toward the wrong tool.
| Tool A | Tool B | Confidence | Why they collide |
|---|---|---|---|
safari_wait_for |
safari_wait |
high | A task like 'wait for the page to finish loading' could map to either the fixed-ms wait or the smarter element/text wait; both take a generic 'wait' framing even though wait_for is explicitly preferred. |
safari_navigate |
safari_navigate_and_read |
high | 'Go to this URL and tell me what's on the page' plausibly triggers either a navigate call followed by a separate read, or the combined navigate_and_read tool, since both satisfy the same phrasing. |
safari_snapshot |
safari_accessibility_snapshot |
high | Both descriptions reference 'accessibility tree' and page state; a task like 'get the accessibility tree of the page' could pick either, even though snapshot is meant for interaction refs and accessibility_snapshot for a11y auditing. |
safari_click |
safari_click_and_read |
medium | 'Click the button and show me the result' is ambiguous between a plain click (then separately reading) and the combined click_and_read tool. |
safari_click |
safari_click_and_wait |
medium | 'Click submit and wait for the page to update' could resolve to click alone (expecting a follow-up wait) or the combined click_and_wait tool. |
safari_click |
safari_native_click |
medium | A task like 'click the button, it's not registering' doesn't specify WAF/bot-detection context, so the agent may not clearly choose between regular click and OS-level native_click. |
safari_click_and_read |
safari_click_and_wait |
medium | Both are combined click+followup tools differing only in whether they return page content or just wait; a vague 'click and see what happens' task is ambiguous between them. |
safari_double_click |
safari_native_click |
medium | safari_native_click has a doubleClick parameter, so a task like 'double-click this element to select text' could plausibly route to either tool. |
safari_fill |
safari_fill_and_submit |
medium | 'Fill in the login form and log in' is ambiguous between filling then a separate submit action versus the combined fill_and_submit tool. |
safari_fill |
safari_fill_form |
medium | 'Fill out the signup form' doesn't specify single vs multiple fields, so the agent could choose repeated safari_fill calls or the batch fill_form tool. |
safari_native_hover |
safari_hover |
medium | A task like 'hover over the menu item to show the tooltip' doesn't indicate whether native OS-level hover is needed, making the choice between hover and native_hover ambiguous. |
safari_network |
safari_network_details |
medium | 'Show me the network requests' is ambiguous between the no-setup quick overview (safari_network) and the detailed capture-based safari_network_details, especially since both surface request info. |
safari_scroll |
safari_scroll_to_element |
medium | 'Scroll down to the footer/button' could be interpreted as a generic pixel scroll or as scrolling precisely to that element by text/selector. |
safari_scroll_to |
safari_scroll_to_element |
medium | 'Scroll to the login button' vs 'scroll to the top' both use 'scroll to' phrasing but map to different tools (element-targeted vs coordinate-targeted). |
safari_scroll |
safari_scroll_to |
medium | 'Scroll to the top of the page' could be attempted with relative scroll(direction:up, large amount) or the absolute scroll_to(x:0,y:0), since both achieve similar effects via different mechanisms. |
safari_wait_for |
safari_click_and_wait |
medium | 'Click the link and wait until the new content appears' could be done via separate click+wait_for calls or the combined click_and_wait tool, causing overlap in tool choice. |
safari_get_console |
safari_console_filter |
medium | 'Show me the console errors' is ambiguous between getting all console messages (then filtering mentally) and directly calling console_filter with level='error'. |
safari_local_storage |
safari_session_storage |
medium | A generic task like 'check the storage data for this page' doesn't specify local vs session storage, and both tools have nearly identical get-semantics. |
safari_clear_mocks |
safari_clear_network |
medium | 'Clear the network mocking/state' is ambiguous between removing route mocks (clear_mocks) and clearing captured network request logs (clear_network). |
safari_eval_file |
safari_upload_file |
medium | The eval_file description explicitly mentions using it 'to upload binary via a generated .js containing base64', overlapping with upload_file's purpose for a task like 'upload this file to the input'. |
safari_set_session_storage |
safari_set_local_storage |
medium | 'Store this value in the browser' doesn't indicate storage type, so the agent could plausibly pick either set_session_storage or set_local_storage. |
safari_delete_local_storage |
safari_delete_session_storage |
low | 'Clear the site's storage' is somewhat ambiguous between clearing local vs session storage, though the distinct 'local'/'session' terms usually disambiguate when the user is specific. |
safari_new_tab |
safari_wait_for_new_tab |
low | A task like 'handle the tab that opens after clicking login' could be misrouted to opening a new tab versus waiting for one that opens automatically, though their action verbs (open vs wait) differ enough to usually disambiguate. |
Compare the field