Skip to main content
Glama
650,497 tools. Updated 2026-10-13 19:29

"Safari" matching MCP tools:

  • Present a REAL game controller to an iOS device and set its state. The device registers a virtual DualShock 4 and receives genuine controller HID, so NATIVE apps see a GCExtendedGamepad and a web page sees a W3C standard-mapping pad in navigator.getGamepads(). It shows up in Settings > General > Game Controller. No Safari page is required. HOW TO FIRE INPUT PROPERLY: • One call = ONE frame = one instant in time. A button stays pressed until you send a frame WITHOUT it, so every press needs a matching release frame — exactly like keydown then keyup. Sending press after press just holds them all down. • For anything that should look played rather than stepped — mashing, combos, a stick sweep — pass `frames` instead of calling repeatedly. The device plays the whole sequence out at `intervalMs` (default 33 ms ≈ 30 fps). A round trip per frame cannot reach that cadence, so a rapid sequence built out of single calls will always read as held buttons. • Alternate press and release inside `frames`: [{buttons:[1]}, {}, {buttons:[0,1]}, {}] is tap A, release, tap A+B, release. • Axes are [leftX, leftY, rightX, rightY], -1..1. Sweep them across frames to roll a stick; omitted axes read as 0. • READ THIS BEFORE REPORTING A STICK BUG: a stick axis can only express RIGHT and UP on `axes[0..1]`, because the report's joystick fields are unsigned. The left stick's full 360-degree analog push IS delivered, but it arrives on the D-PAD — `buttons[12..15]`, and `GCExtendedGamepad.dpad.xAxis/yAxis` for native apps, which are bipolar. Reconstruct it as x = right - left, y = down - up. Pushing left and reading `axes[0] === 0` is the EXPECTED, structural behaviour, not a fault to debug. • While a sequence is playing, this call owns the pad — a controller streamed from a live viewer is paused and resumes on its own shortly after. • Confirm the pad EXISTS with ios_gamepad_status; confirm VALUES in the app under test. Input routes to whatever holds focus, so foreground the app under test first — measuring while Settings or another app is in front reads as "nothing works".
    ConnectorOAuth
  • Set the files on a file-upload input (<input type="file">) in Safari on an iOS device, WITHOUT opening the device's file picker. This is the only way to test an upload flow end to end on iOS: tapping an upload control opens a native sheet that automation cannot drive. Provide file content inline, or name a file already on the device (see ios_file_push / ios_upload_targets) to have it read from there. Fires the input and change events afterwards so framework bindings and validation run, then reads the input back and reports the file names and sizes the PAGE actually sees — so a silent no-op can't pass as success. Total content limit 2 MB.
    ConnectorOAuth
  • Fill SEVERAL fields of a form in one call on the device browser — the batch form of webpage_type, and the counterpart to Playwright MCP's browser_fill_form. Works on BOTH iOS Safari and Android Chrome. Fields are filled IN ORDER inside a single browser session, which is what makes this different from calling webpage_type N times: order-dependent forms (a country select that repopulates the state list, an autocomplete that rewrites the next field) behave the way they do for a real person, and you pay one round trip instead of N. Each field takes the same target shape as webpage_type — a CSS `selector` or a `ref` from webpage_snapshot — plus `text`, and an optional per-field `clear`. On iOS each field is typed on the DEVICE keyboard so the page receives real trusted keystrokes; where that is unavailable it falls back to setting .value via the native setter and dispatching bubbling input/change (isTrusted:false), reporting `via` and `fallbackReason` PER FIELD so a degraded field is never hidden by its neighbours succeeding. `exact:false` on a field means the keystrokes landed but the component rewrote the value as you typed (masks, autocompletes) — that is a success, not a failure. Stops at the first failure by default, because a form is usually order-dependent and filling past a field that did not take produces a state no one asked for; pass continueOnError:true to attempt every field anyway, which is what you want when diagnosing WHICH selectors are stale. This does NOT submit the form — follow it with webpage_press_key("Enter") or webpage_click on the submit control. ONE CROSS-PLATFORM TRAP, measured on device: `change` fires differently. Android dispatches `change` on every field. iOS fires it only on a real BLUR, and the LAST field still holds focus when this returns — so its `change` has NOT fired, and a form that validates on `change` will not have seen the final field. Follow with webpage_press_key("Tab") or a click elsewhere if that matters. Also on iOS, a field filled with `clear:true` emits one isTrusted:false `input` carrying the empty value (the wipe) before the trusted keystrokes.
    ConnectorOAuth
  • Send a raw DevTools-protocol command to the browser page on a device and return its reply — the escape hatch for capabilities no dedicated tool wraps yet (Emulation, Performance, CSS, Animation, DOM mutation, …). Works on BOTH iOS Safari and Android Chrome, auto-detected from the udid. `method` is "Domain.command" (e.g. "Emulation.setGeolocationOverride"); `params` is that command's parameter object. Scope is the BROWSER — this reaches web content only, never the device or native apps. Prefer a dedicated tool when one exists: they handle setup ordering, lifecycle and teardown that a bare command does not. IMPORTANT, iOS: the two engines do NOT implement the same protocol — Android is full CDP, iOS is WebKit's dialect. Chrome-only domains fail with -32601 "domain was not found" (Accessibility, Emulation, and Page.captureScreenshot are all absent on iOS), and a `*.enable` on iOS reports success without proving the domain exists, so never treat it as a capability probe — probe with a real method.
    Connector
    Destructive
    OAuth
  • Navigate Safari to a URL on an iOS device. If a page is already inspectable, navigates the active tab in place; if Safari has no inspectable page (closed), cold-opens it to the URL (needs Settings → Safari → Advanced → Remote Automation). VERIFIES the resulting URL and returns { navigated, requestedUrl, finalUrl, pageId, method } — never a silent no-op. On success the page it landed on becomes this device's current page, so you can call ios_safari_get_dom / _evaluate / _elements / _console_logs / _capture_network / _cookies straight afterwards WITHOUT pageId and stay on this tab — no ios_safari_list_pages round trip needed. The returned pageId is only needed to come back to this tab after driving a different one. Prefer this over ios_navigate_url for Safari.
    ConnectorOAuth
  • Take a JavaScript heap snapshot of a Safari page on an iOS device and write it to a file on the RDS host, returning { nodeCount, edgeCount, bytes, path }. The file is a V8-format .heapsnapshot that loads directly in Chrome DevTools → Memory → "Load profile", so it can be handed to a human to open. Snapshots are multi-MB, which is why the content is never returned inline. Use it to find what a page is retaining: take one, exercise the leak, take another, and compare nodeCount/edgeCount — a count that climbs and never comes back down across repetitions of the same interaction is the signal. Garbage is collected first by default so unreachable objects do not read as a leak; pass collectGarbage=false only when you deliberately want to measure uncollected garbage. Omit pageId to use the active tab. Requires Web Inspector enabled: Settings → Safari → Advanced → Web Inspector.
    ConnectorOAuth

Matching MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    MCP server that drives Safari on a physically attached iPhone via Apple's safaridriver, enabling navigation, screenshots, DOM snapshots, console logs, network timings, and tap/type/scroll actions on the real device. No Xcode, Appium, or WebDriverAgent required.
    16
    MIT

Matching MCP Connectors

  • Close Safari tabs on an iOS device by title, or list what is open. Safari's tab overview is the only way to close a tab — the Web Inspector protocol behind ios_safari_list_pages cannot do it — so this drives the native UI. Call with no title to list the open tabs and close nothing. Titles match case-insensitively on any part of the title. Note this sees MORE tabs than ios_safari_list_pages, which only reports pages that are loaded and inspectable. Requires Safari in the foreground and an active iOS automation session.
    ConnectorOAuth
  • Get visitors grouped by browser name (Chrome, Safari, Firefox, Edge, and others), ranked by visitors descending, for a date range. Names only: use get_breakdown with dimension browser_version for versions, get_operating_systems for the OS split, and get_devices for desktop versus mobile. Pass filter_browser to other tools to scope them to one browser. Rows carry value, visitors, revenue, and percentage with pagination.total; limit defaults to 100 (max 1000). Dates default to the last 30 days; all filter_* arguments apply. Requires websiteId or domain with a workspace token.
    ConnectorOAuth
  • Collect console logs, exceptions, and log entries from a Safari page on an iOS device over a time window. Enables the Runtime and Log domains, then listens for Runtime.consoleAPICalled, Runtime.exceptionThrown, and Log.entryAdded events, and returns an array of { level, text, url?, line?, source? }. This is a LIVE-WINDOW collector: it only captures events fired AFTER it attaches (plus the buffered history WebKit replays on enable), so triggering the logging from a SEPARATE tool call races the attach and is missed. To capture logs from an action, pass triggerJs (run inside the window). Default window: 5 000 ms. Maximum: 15 000 ms. Omit pageId to auto-pick the active page.
    ConnectorOAuth
  • Add (or update in place, when `id` matches an existing route) a mock/abort rule for Safari/WKWebView requests on this device. mode "mock" (default) serves the given status/headers/body without the request leaving the device; mode "abort" fails it so the page sees a network error. Routes apply immediately and survive navigation. WEB CONTENT ONLY: this intercepts requests made by browser/WebView pages. Requests made by native app code are NOT intercepted and never will be by this tool. Nothing device-wide is changed and no certificate is installed — the effect is scoped to the page. For requests made by native app code use ios_traffic_mock_add instead.
    ConnectorOAuth
  • Start a device-wide NATIVE network packet capture on an iOS device. Captures REAL packets at the network layer, mostly TLS-encrypted, so there are NO HTTP bodies — the result is a Wireshark .pcapng for packet-level analysis. One capture per device. Optionally filter by `process` (process name, e.g. mobilesafari / backboardd) or `interfaceName` (e.g. en0), and auto-stop after `packets` frames. End with ios_pcap_stop. (For Safari HTTP traffic with bodies, use ios_safari_capture_network instead.)
    ConnectorOAuth
  • Scan CSS source and report every feature in it that does not meet a browser support target. Targets are either a Baseline level ("baseline-widely", "baseline-newly") or an explicit browser list ("chrome 120, safari 17.4, firefox 128"). Reports the offending line, what is wrong (unsupported, too old, prefix-only, partial), and the minimum version that would work. Browserslist queries like "last 2 versions" are not supported. IMPORTANT when reading Baseline-level results: Baseline describes a whole feature, and a feature can be "Limited" because one part of it is not interoperable while the exact declaration you wrote works everywhere. "Cursor styles" is Limited, so `cursor: pointer` is reported — yet it only lacks iOS Safari, where a cursor is meaningless rather than broken. Each finding therefore also names the browsers missing that specific key, or says that the key ships everywhere and the status comes from elsewhere in the feature. Read that line before removing anything: a key that ships everywhere is still not proof the code works, since it may be inert without the parts that do not.
    ConnectorNo auth
  • Capture network requests made by a Safari page on an iOS device over a time window. Collects Network.requestWillBeSent, Network.responseReceived, Network.loadingFinished, and Network.loadingFailed events and returns merged records. Returns { records, bodiesOmitted? } in summary format, or a HAR 1.2 document when format="har". Each record: { requestId, method, url, requestHeaders?, status?, statusText?, mimeType?, resourceType?, responseHeaders?, encodedDataLength?, state, errorText?, startTimestamp?, endTimestamp?, body?, bodyTruncated?, bodyError? }. Set includeBodies=true to fetch response bodies for completed text-like responses (json|text|xml|javascript|html|css|svg|x-www-form-urlencoded); per-body cap: 10 000 chars (bodyTruncated=true when hit); total cap: 200 000 chars (excess records counted in bodiesOmitted). Body fetch failures set bodyError on that record. Set throttle to emulate bandwidth for the capture window only (best-effort; cleared afterwards): slow-3g (51 200 B/s) or fast-3g (209 715 B/s). NOTE: throttle="offline" is NOT supported on iOS — WebKit only has bandwidth throttling; use android_devtools_capture_network for offline. When throttle was active, a top-level throttle field appears in the output. Returns at most `limit` records (default 100, most-recent first) so heavy pages stay within the token budget — filter with urlSubstring / onlyErrors; total/returned appear when records were dropped. Default window: 5 000 ms. Maximum: 30 000 ms. Omit pageId to auto-pick the active page. Pass url to navigate inside the capture session and record the full page-load waterfall (pass the current URL to reload). NOTE: some iOS versions report no request timings to a remote inspector. The result then says timestamped=false, startTimestamp/endTimestamp are omitted and no duration can be derived — what each request was and how it answered is still accurate, so do not read speed from a capture that reports it.
    ConnectorOAuth
  • Evaluate a JavaScript expression in the context of a Safari page on an iOS device via the Web Inspector. Uses Runtime.evaluate internally. Returns the result as a string (JSON-stringified for objects), followed by a second block naming the tab it actually ran on, read live from that page — check it if a result looks like it came from the wrong page. Omit pageId to auto-pick the frontmost Safari tab. An expression that evaluates to a promise IS awaited, so `fetch(...).then(r => r.json())` returns the parsed body rather than an empty object; a promise still pending when the call times out is reported as such instead of being silently returned empty. WAITING: UI frameworks paint a frame or two AFTER the click that triggers them, so an evaluate fired immediately can read the DOM before the menu/overlay/row exists. Use waitForSelector to block until an element appears (and waitMs to let it settle) INSTEAD of splitting the work into a separate poll call — both wait inside this same call on the same tab. DO NOT CLICK WITH THIS TOOL. A click dispatched from JavaScript — .click(), or dispatchEvent of a MouseEvent — is an untrusted event (isTrusted false). Component frameworks and anything gated on a real user gesture (file pickers, clipboard, autoplay, anti-bot checks) may ignore it, so the evaluate SUCCEEDS and the page does nothing — a failure that looks like a passing call. Use webpage_click instead: it has the device tap its own screen at the element, delivering a real trusted touch (pointerdown, touchstart, mousedown, click). Same for typing — webpage_type.
    Connector
    Destructive
    OAuth
  • Record a Performance trace of a Safari page on an iOS device over a time window and write it to a file on the RDS host, returning { eventCount, durationMs, bytes, path }. The file loads directly in Chrome DevTools → Performance → "Load profile", so it can be handed to a human to read the flame chart. Traces are multi-MB, so the content is never returned inline. Captures main-thread work (tasks, script execution, style, layout, paint) together with a JavaScript CPU sample run, so the flame chart shows which functions the time went to. Use it for "why is this page slow/janky" — long tasks and repeated layout show up as wide bars. Pass url to navigate INSIDE the recording and capture the full page load, or triggerJs to fire an interaction inside the window (both are the usual way to trace something specific rather than idle time). Set screenshots=true to include a filmstrip (roughly doubles the file size). Default window: 5 000 ms. Maximum: 30 000 ms. Omit pageId to use the active tab. NOTE: on some iOS versions WebKit reports no elapsed time to a remote inspector, and every event is then stamped 0. The recorded work and its nesting are still real, but durations and the flame chart timeline are not; the result says timestamped=false when this happens, so do not read timings from a trace that reports it. Requires Web Inspector enabled: Settings → Safari → Advanced → Web Inspector.
    ConnectorOAuth
  • Find where to push a file so a FILE PICKER on the device can select it — for driving an upload in Safari or in an app under test. Call this before ios_file_push when the file is meant to be chosen by a picker, because writing a file and a picker seeing it are not the same thing: the shared media directory is writable but NO picker reads it (a JPEG pushed to /DCIM does not enter the Photos library, verified on iOS 26.6 even across a reboot, and the media Downloads folder is not the Files app's Downloads). Returns app containers with `pickable_from`: `["files_app"]` means that app declares UIFileSharingEnabled, so its Documents/ is listed under "On My iPad" in the Files app — which is what Safari's "Choose File" browses. An empty `pickable_from` means the push still works and the app itself can read the file, but no picker will show it. Pickable targets sort first; push to `targets[0].push_path`. If nothing is pickable, `advice` says what to change.
    ConnectorOAuth
  • Type text into an input/textarea in the device browser, by CSS selector or by a `ref` from a page-elements listing — works on BOTH iOS Safari and Android Chrome. On iOS it taps the field and types on the DEVICE keyboard, so the page gets real trusted keystrokes (keydown, keypress, beforeinput, input) exactly as from a person — use this for anything that reacts to typing rather than just reading .value. Otherwise it sets the value via the native HTMLInputElement/HTMLTextAreaElement value setter (so React/Vue-controlled inputs register the change) and dispatches bubbling `input` and `change` events, which are isTrusted:false. The response reports which happened via `via` ("nativeKeyboard" or "jsValue"), and when it fell back to jsValue it names the cause in `fallbackReason` — you never have to guess whether a degraded path was taken. Pass require:"native" to make an unavailable device keyboard a hard error instead of a silent downgrade, for fields that only behave correctly under real keystrokes. `exact:false` means the keystrokes landed but the component rewrote the value as you typed (masks, autocompletes) — that is a success, not a failure. By default appends to the existing value; pass clear:true to replace it.
    ConnectorOAuth
  • Poll for an element to appear (and optionally become visible) in the device browser by CSS selector — works on BOTH iOS Safari and Android Chrome. Polls every 250ms inside a single CDP session (no reconnect per tick) until found or timeoutMs elapses. Returns { found, waitedMs } rather than throwing on timeout, so callers can branch on the result. When the selector matches several elements, ANY of them satisfying the test counts as found — so a control duplicated across responsive breakpoints is reported visible when the on-screen copy is.
    ConnectorOAuth
  • Reload the page open in the device browser (CDP Page.reload) — works on BOTH iOS Safari and Android Chrome. Confirms the document was actually replaced rather than trusting the protocol ack: a sentinel is written to `window` before the reload and the call only reports reloaded:true once it is gone. Returns the post-reload `url`, and reloaded:false with a `reason` when the document survived, so a reload that quietly did nothing cannot read as success. Also waits for the NEW document to finish loading and reports `ready`, so the page is drivable when the call returns. This is the state-reset primitive between test cases on iOS, where tabs cannot be opened or closed — and unlike a same-hash navigate it forces a real load, which an SPA router would otherwise skip, leaving stale dialogs and form state behind. Pass hard:true to bypass the HTTP cache.
    ConnectorOAuth
  • Expire all NON-HttpOnly cookies visible to the current page origin in the device browser (iOS Safari or Android Chrome, auto-detected), via document.cookie. PREFER webpage_clear_browsing_data: it reaches HttpOnly cookies (which this cannot — and a session cookie is usually exactly the HttpOnly one you wanted gone), verifies the result by re-reading the cookie jar, and can clear localStorage/sessionStorage/caches in the same call. This tool remains for callers that specifically want the document.cookie behaviour. Returns { cleared } — the count of cookies expired, NOT verified afterwards.
    Connector
    Destructive
    OAuth
  • Clear cookies, localStorage, sessionStorage and/or the Cache Storage API for the page in the device browser (iOS Safari or Android Chrome, auto-detected). USE THIS to get a genuinely clean session between test cases: webpage_reload deliberately PRESERVES localStorage, so a reload keeps you logged in and carries app state into the next case. Every scope is VERIFIED by re-reading it afterwards and the result reports before/after counts, so a scope that could not be cleared says so instead of being assumed. On iOS cookies are removed one at a time through the Web Inspector (Page.deleteCookie), which reaches HttpOnly cookies that document.cookie cannot; on Android Network.clearBrowserCookies clears them in one call. HTTP cache: Android clears it via Network.clearBrowserCache (the browser reports no count, so it is returned as issued rather than verified); iOS has no Web Inspector equivalent, so only the Cache Storage API is cleared there and the HTTP cache is reported unsupported rather than silently skipped. DOES NOT LOG YOU OUT OF HTTP BASIC AUTH. Those credentials live in the browser credential store, which is none of these scopes — verified on qa.tms-hmpu.com, where an authenticated session shows 0 cookies, 0 localStorage and 0 sessionStorage. To force a Basic-auth challenge again, terminate the browser (ios_terminate_app com.apple.mobilesafari) and navigate afresh.
    Connector
    Destructive
    OAuth