WebKitUI MCP

Native WebKit for Mac. Human approval and persistent sign-ins. Requires the companion app.

OtherSwiftv0.6.24

WebKitUI MCP

Native WebKit automation for Apple Silicon, exposed through MCP and designed for an LLM rather than a human.

This repository is a Swift rewrite. The retained TypeScript/Playwright files are prior art only and are not the implementation being extended.

What exists

  • Native WKWebView runtime using the persistent system WebKit data store for real authenticated sessions.
  • MCP 2026-07-28 stdio server with legacy initialization compatibility.
  • Thirteen bounded tools, including authenticated native downloads, explicit page/element scrolling, and bounded text extraction for rendered virtualized logs.
  • Observation-scoped element symbols backed by semantic locator recipes and fresh action-time resolution.
  • Five separate addressing counters: address_resolution_failed, address_now_ambiguous, logical_target_changed, node_replaced_but_semantic_locator_recovered, and coordinate_invalidated_by_layout_change.
  • Provenance attached to every serialized page string.
  • Observation-time privacy minimization: non-rendered controls are removed before bounding, sensitive/token-like fields never serialize values, and a sensitive select publishes neither its option labels, nor how many it has, nor the one currently chosen in it — a selected label is that control's value. The control is still reported, with its role and accessible name, so it can be handed to a human.
  • Checkpoint-plus-delta observation history and Minimal Failure Set coverage metrics.
  • Optional local Ollama ranker with think: false, strict budgets, and deterministic fallback.
  • Transaction ledger with preconditions, exact post-conditions, idempotency keys, indeterminate outcomes, receipts, and reconciliation without replay.
  • Human confirmation before every exposed click and open-world navigation. Navigation defaults to a server-owned native macOS dialog so a client cannot silently decline the round trip; MCP multi-round navigation remains opt-in.
  • browser_session can set a bounded 320×240–3840×2160 CSS viewport and can navigate back, forward, or reload using WebKit's own history list. History movement is confirmed against the destination WebKit reports; reloading a page produced by a form submission is refused before confirmation to prevent replay.
  • Camera, microphone, and geolocation requests are always denied by the native WKUIDelegate. The observation names the requesting origin and permission, with repeated requests counted and distinct records bounded; there is no MCP operation that can grant one.
  • The confirmation names the origin a submit control's data would reach, computed server-side from the freshly re-resolved element rather than from its accessible name, and says so explicitly when that origin differs from the page the operator is on. A submit button's formaction overrides its form's action, so a control whose accessible name reads harmlessly can post elsewhere.
  • Confirmations are counted in a rolling 60-second window. From the fifth, the dialog states how many have been requested; the twentieth is refused instead of presented, because a dialog a human clicks twenty times in a row has stopped being a gate.
  • Legacy MCP clients receive the same exact-action authority boundary through a server-owned native macOS confirmation dialog; the model cannot supply or forge the approval value.
  • Local human handoff: handoff_start immediately returns an opaque session-bound resume token while the actual WebKit session becomes a visible window. handoff_status is non-blocking; handoff_resume consumes the token only after local confirmation and returns a fresh observation. The agent remains locked out throughout human control.
  • An active human-control window can be brought forward again with handoff_focus without changing who controls the page. Session status says whether that surface is currently presented, and restricted authentication status exposes only bounded rendering booleans—never page text, field values, private URL components, or network payloads.
  • The packaged handoff app installs a native Edit menu, so standard first-responder shortcuts such as Command-X/C/V/A work inside WebKit form controls.
  • MCP sessions use a loopback SOCKS5 boundary, one per profile and shared by its sessions, with failover disabled: hostnames are resolved once, public addresses are pinned, and private/reserved destinations plus non-TCP SOCKS commands fail closed.
  • One process at a time controls the browser for the macOS account: the host lease belongs to that process and is released with its last session or on process death. The production CLI holds one session.
  • Private remote clients can use the app-owned broker plus a forced-command SSH relay; WebKit and authenticated profile data remain on the logged-in Mac.
  • Multiple relay clients may use the same long-lived broker concurrently, each in its own session and window, up to three, on the shared default profile. A session belongs to one client: another client gets session_in_use with wait_only=true and cannot observe, navigate, act, invalidate addresses, or request duplicate user control in it. Ownership transfers after the owning client disconnects, or through a human-confirmed client_handoff. Native confirmations are shown one at a time across the app, and each names the requesting agent as a quoted, self-reported name.
  • Each independent task must call browser_session(operation: "open") and keep its returned session_id for all page operations. On a shared transport, a full broker refuses an open that would return an already-owned page. Status without an ID selects the caller's sole session; when it owns several, it asks for an explicit ID. A new client with spare capacity is directed to open its own page. Host lease metadata reports the broker's remaining session slots.
  • While a person holds the human control window, a pop-up the page opens (a sign-in with Google or Apple, say) is a real window sharing the page's profile, protected proxy and window.opener, at most four; all close when the agent resumes. Under agent control new windows stay refused and reported.
  • The app broker owns one live browser across MCP client reconnects. Every reconnect invalidates observations, pending approvals, capabilities, and transaction coordinators before returning the preserved session handle.
  • The native host starts the AppKit event loop before MCP work and keeps the live WKWebView attached to one opaque window. Agent control orders that window out; human handoff presents the same view without reparenting it. A packaged visual smoke mode checks the actual WindowServer pixels.
  • The stdio relay stays alive across broker restarts. It reconnects before the next undispatched request and never silently replays a request whose outcome became unknown during a restart.
  • Durable multi-client status works without a session ID and reports only privacy-safe holder metadata (client name/version, PID, age, inactivity and policy). open(wait_timeout_ms: ...) can wait up to 60 seconds for the local host lease without stealing it; dead transports release their session owner. browser_session(operation: "client_handoff") can transfer an idle session only after local human confirmation and a final no-active-call check.
  • A secretless SiliconPass fill can return credential_not_found; WebkitUIMCP then offers a native human handoff for manual sign-in and addition/update in SiliconPass. No credential value crosses MCP, JSON, logs, or the clipboard.
  • Web-content termination invalidates every observation immediately; recovery reloads the host-owned last URL without replaying an action. A forced-crash fixture verifies that an HttpOnly authenticated cookie plus localStorage and sessionStorage survive in the same view/data-store lifetime.
  • The macOS companion is a regular Dock app with a local activity window. It records only allowlisted MCP tool names, outcome, timestamp, duration and a bounded error type. Parameters, URLs, page content, credentials, cookies, keystrokes and response bodies are structurally absent. Owner-only JSONL files rotate automatically and can be exported or cleared from the app.

Deliberate limits

  • browser_act exposes click, native submit-control click, bounded non-sensitive input/textarea fill, Enter/Tab/Escape, blur, explicit input commit, and answering the one JavaScript alert/confirm/prompt dialog a page is suspended on (dialog_accept, dialog_dismiss, dialog_accept_value). A pending dialog is reported in the observation as untrusted site content, blocks every other operation until it is answered, is never answered automatically, and resolves to an explicit indeterminate outcome if nobody answers it. beforeunload is out of scope: the public macOS SDK exposes no before-unload panel to an embedder. Native approval also routes public text fills through AppKit insertion with a measured trusted input receipt. Fill verifies both the freshly re-resolved semantic target's exact value and that its live validation state is not invalid. Other actions support exact URL or URL prefix, title, heading, exact/contains semantic text, checked, selected, enabled, semantic target absence (target_absent with value="true"), value, validation state, character count, bounded state attributes, dialog, named panel, or selected-option postconditions. A same-URL SPA mutation returns same_url_page_state_changed guidance instead of implying success. UI state never proves backend commit.
  • browser_download converts authenticated attachment responses to WKDownload from either a fresh observed control or a protected same-origin URL fallback. It requires exact native confirmation plus a save-panel destination, never overwrites an existing file, and succeeds only after an on-disk receipt reports the HTTP status, suggested/final filename, MIME type, byte count, SHA-256, and decoded provisioning-profile UUID when available. Cookies, headers, and the absolute local destination path stay outside MCP.
  • macOS file inputs use WebKit's native open-panel delegate. Selected regular files are bounded to 10 files and 50 MiB each; receipts may expose filenames, byte counts and SHA-256 values, but never local paths or file contents. A file-selection receipt is not provider acceptance: the action's independent postcondition must still verify the uploaded preview or saved state.
  • Fill dispatches normal input/change events, so site handlers may autosave or cause server effects. It is destructive and human-confirmed; password controls require local human handoff.
  • approval_mode: "native" sends confirmed click/submit and Enter/Tab/Escape through public AppKit NSEvent handling on the freshly re-resolved WKWebView target. An isolated message handler must observe the matching DOM event with event.isTrusted == true before the receipt reports trust. Missing/mismatched receipts fail indeterminate; no flag is synthesized. approval_mode: "mcp", blur, and commit remain JavaScript-dispatched and report untrusted. Native public-text fill reports trust only when its matching AppKit insertion receipt is observed.
  • Action results separately expose confirmation_mode, dispatch_mode, and trusted_gesture_state. Native confirmation alone never establishes event trust.
  • Navigation readiness and rendered-content availability are separate. A document can reach readyState=complete and mutation quiescence while remaining blank; navigation, observation, and text reads report empty_or_unusable in that case and explicitly forbid treating the absence as page truth. A rendered textless canvas or media document remains usable.
  • browser_read_text reads only currently rendered virtualized lines. Use bounded scroll plus another read for additional ranges.
  • No arbitrary JavaScript, raw CDP escape hatch, coordinate retry, proxy fleet, anti-bot bypass, or headless claim. A gate an agent can step around is not a gate: one JavaScript-evaluation call would do anything the confirmation exists to authorize one action at a time. Browserbase's MCP server also refuses JavaScript, so refusing it is not distinctive on its own; the combination surveyed for and not found elsewhere on 2026-09-09 is no JavaScript tool and a per-action gate and a real authenticated session.
  • Viewport resizing changes desktop CSS layout only. It does not emulate a mobile device—the public macOS SDK exposes no WKWebView ContentMode API—and set_emulated_media is deliberately absent because it does not help operate a person's site.
  • Camera, microphone, and geolocation access cannot be granted through WebKitUI MCP. Requests are denied and reported in the next observation instead of leaving a page failure silent; use human control in a complete browser when the workflow requires one of those permissions.
  • No multiple tabs and no new-window handling. One session holds one exclusive host lease, which is what lets an approval refer to an unambiguous page.
  • No subresource or XHR request inspection. It is possible and it is not offered. WKWebsiteDataStore.proxyConfigurations is public from macOS 14 and is Apple DTS's own recommendation for reading a WKWebView's request contents, and a bundled WKWebExtension with the webRequest permission is public from macOS 15.4. Neither is free: the proxy route needs a trusted root certificate to see inside HTTPS, and the extension route reports no headers and cannot block. What is reported instead is narrow — the download receipt's HTTP status, and the egress proxy's accepted, blocked, pinned and timed-out connection counts. A main-frame navigation result carries no HTTP status today, and the proxy does not name the hosts it allowed or refused.
  • Native AppKit dispatch is not a distinguishing feature and is not claimed as one. safaridriver dispatches NSEvent through [window sendEvent:] exactly as this does, and the WebDriver specification requires every conformant driver to produce trusted events. What differs is the measurement: an action whose trusted DOM receipt is missing or mismatched fails indeterminate here, where Playwright's hit-target interceptor treats an absent event as success.
  • The WKFormInfo submission gate is implemented, unit-tested, and does not fire. WebKit did not call webView(_:willSubmitForm:submissionHandler:) on either macOS 27.0 build measured here, 26A5416b and 26A5419a — not for submit(), not for requestSubmit(), and not for a native trusted-gesture click. It is therefore not a second live gate and must not be read as one; it will decide if WebKit begins delivering the callback. The live defence against a submit control that posts to another site is the destination line in the confirmation.
  • Registered cross-origin frames expose bounded rendered semantics with THIRD_PARTY_EMBED provenance and a sanitized origin. Their geometry is explicitly frame-local; frameActionModes (or compact frame_action_modes) names eligible attempts even when actionable=false means no native pointer geometry. A confirmed hover or select_option runs only as untrusted JavaScript in the exact child frame; non-sensitive press_key and fill can use AppKit only when that frame returns a matching trusted DOM receipt. A native pointer click has no public frame-to-window coordinate transform, refuses before confirmation with cross_origin_native_geometry_unavailable, and points to live human handoff. This is bounded control reach, not proof of hosted checkout completion. Sensitive and authentication controls still require a human. Frames that cannot be evaluated, and frames beyond the bounded native registry, remain explicitly unreadable rather than being treated as empty page content.
  • Some identity providers require a complete browser surface and do not render inside an app-embedded WKWebView (for example a WebAuthn-only step without the browser passkey entitlement). Those return full_browser_required with an internal safari_compatibility requirement. Any already-restricted authentication origin may also use compatibility_start as an explicit manual fallback when its native human handoff is present but unusable. The operation opens the exact private URL in Safari only after native confirmation; it never auto-switches merely because a page timed out, never replaces the native handoff, provides no Safari observation/control, and cannot return a Safari login to this MCP session. Complete the blocked workflow manually in the external browser. WebKitUI never copies cookies, passkeys, AutoFill data, or credentials between backends.
  • takeSnapshot may omit GPU-composited effects.
  • No exactly-once or rollback claim for an uncooperative website.
  • Low concurrency is intentional because WKWebView has no per-view hard memory quota.
  • The protected network path is the MCP session registry or WebKitRuntime(protectedWebsiteDataStore:); the lower-level WebKitRuntime(websiteDataStore:) initializer is intentionally unprotected for fixtures and embedding.
  • This is a bounded website-traffic control, not a complete process egress sandbox. The exact tested transports, exclusions and safe-use rule are in docs/network-boundary.md.

Compared with the Safari MCP server

Apple's Safari MCP server ships inside safaridriver and is started with safaridriver --mcp; Apple's own help text for the flag reads "Run as an MCP (Model Context Protocol) server using stdio transport." It exposes seventeen tools — page content, screenshots, network requests, console logs, JavaScript evaluation, DOM interaction, viewport and media emulation, tab management. It needs two settings in two different panes: "Show features for web developers" in Safari > Settings > Advanced, and "Allow remote automation and external agents" in Safari > Settings > Developer. Apple's floor is Safari 27 beta or Safari Technology Preview 247 or later, and Apple documents Safari 27 beta on macOS 26 and macOS Sequoia as well as macOS 27, so it is not a macOS 27 requirement.

Apple aims it at web developers: it "gives your agent the ability to know how your code actually renders in the browser", and both sets of release notes file it under WebDriver > New Features. Apple also states it "runs entirely on your local machine and makes no network calls of its own" and that it "does not have access to your personal information in Safari (e.g. AutoFill or other browser activity)". For developing a site it is the better tool, it is included with Safari and Apple lists no separate price, and this project does not compete with it.

The difference is what happens when the site is not yours and you are signed in to it:

Safari MCP serverWebKitUI MCP
Purposeinspect and debug a site you are developingact on a site you are signed in to
Approvalno confirmation step is documented, and none of the seventeen tools requests onenative macOS confirmation before every exposed click and open-world navigation
Tool annotationsnone: every tool carries only name, description and inputSchema, so a client gets no signal separating page reading from JavaScript evaluationreadOnlyHint, destructiveHint, idempotentHint and openWorldHint on every tool
JavaScript evaluationexposed as a toolabsent, with no CDP or coordinate fallback
SessionApple's WebDriver documentation states automation windows are "isolated from normal browsing windows, user settings, and preferences" and that a session "always starts from a clean slate"its own WKWebView and its own persistent profile, which is what authenticated work requires
Protocolreports 2024-11-052026-07-28, the current revision
Network detailfull request inspection, including headers, body and timingno per-request inspection: the download receipt's HTTP status, and the egress proxy's connection counts without host names
Verificationinspection toolsan explicit postcondition per action, plus separate confirmation_mode, dispatch_mode and trusted_gesture_state

The isolation wording above is Apple's WebDriver documentation, about automation sessions generally. Apple has published no equivalent statement about the MCP server itself.

Both can be installed at once, and for most developers both should be.

Two things this project will not say about Apple's server, because Apple does not: that it cannot reach your cookies or your logged-in sessions — Apple's MCP post names AutoFill and "other browser activity" only — and that Apple states there is no confirmation step. The absence is documented; a denial is not.

Build and test

The product targets macOS 15+ on Apple silicon. Building the optional WKJSHandle probe requires the macOS 27 SDK; those APIs stay feature-gated.

swift build -c release --arch arm64
xcrun swift-format lint --strict --recursive Sources Tests Package.swift
swift test --arch arm64
swift test -c release --arch arm64

For a reproducible verification of the current checkout plus the signed local installation and its two-client transport:

scripts/verify-native-installed.sh

Run the server:

swift run -c release --arch arm64 webkitui-mcp

Before registration, run the secret-free local readiness check:

webkitui-mcp doctor

It checks the native confirmation helper, local owner-authentication policy, architecture, macOS version, and locally stored license state. It does not open a website, read browser data or credentials, authenticate the user, or contact the license service.

webkitui-mcp --help prints the stdio contract without starting the server. The sibling webkitui-mcp-confirm helper owns legacy native dialogs so closing an AppKit alert cannot terminate or corrupt the long-lived stdio server.

The process reads newline-delimited JSON-RPC from stdin, writes protocol responses only to stdout, and reserves stderr for diagnostics.

For a disposable direct process, install the CLI and register it normally:

release_bin="$(swift build --show-bin-path -c release --arch arm64)/webkitui-mcp"
release_dir="$(dirname "$release_bin")"
install -m 0755 "$release_bin" "$HOME/.local/bin/webkitui-mcp"
install -m 0755 "$release_dir/webkitui-mcp-confirm" "$HOME/.local/bin/webkitui-mcp-confirm"
codex mcp add webkitui-mcp -- "$HOME/.local/bin/webkitui-mcp"
claude mcp add --scope user webkitui-mcp -- "$HOME/.local/bin/webkitui-mcp"

Install the two together and leave them side by side. Before showing a confirmation the server checks the helper next to it, and what it can demand depends on how the server itself is signed:

  • a notarized server pins its Developer ID team and the exact identifier the release script stamps, so nobody can drop a helper of their own beside it;
  • a server built from source is ad-hoc signed and has neither, so the check can only require that the helper sits next to it under its own name. That gives nothing away: whoever can write a helper beside an unsigned server can replace that server too.

Mixing the two is refused in both directions. If you re-sign one binary, re-sign the other with the same identity, or the confirmation will not appear and every navigation will fail closed.

Keyboard behaviour of the confirmation

The confirmation uses a nonactivating panel: it can receive keyboard input while the caller remains the active application, without adding a separate helper icon to the Dock. Two preferences, shared by a source build and the notarized app, decide what the keyboard may do:

# Return cancels and Escape closes after 1 s of initial keyboard focus (default).
defaults write com.lorislab.webkitui-mcp ConfirmationKeyboardDefault cancel
# Return/Escape have no default action; click, or Tab to a button then Space.
defaults write com.lorislab.webkitui-mcp ConfirmationKeyboardDefault none
# Arming delay for Return/Escape, 0 to 5 seconds (default 1).
defaults write com.lorislab.webkitui-mcp ConfirmationArmingDelaySeconds -float 2

The arming delay starts after the initial display and key-window acquisition. Queued Return/Escape input is checked against its original event timestamp. Initially no button has focus: stray Return/Space press nothing, and Escape is ignored until armed. Deliberately selecting a button with Tab and Space or clicking still works. In none mode Escape stays disabled. Approve is never a keyboard default and cannot be made one. No per-project or per-session auto-approval exists; the design constraints for one are recorded in docs/research/2026-09-01-goal-delegation-and-browser-addressing-sota.md.

For authenticated sessions that must survive Codex conversation reconnects, build the self-contained app instead:

scripts/package-preview.sh dist
scripts/verify-package-preview.sh dist

Unzip WebKitUI-MCP-0.6.24-preview.zip, move WebKitUI MCP.app to the Applications folder, and open it. In the status window:

  1. Enable Launch at Login. macOS may require approval in System Settings.
  2. Copy and run the Codex setup command. It points to the relay embedded in the app bundle and the owner-only local Unix socket.
  3. Keep the app in Applications after registration so the saved relay path remains valid.

The Activity journal button opens the local privacy-safe event history. Its files live under ~/Library/Application Support/WebkitUIMCP/Activity with owner-only permissions. Rotation retains at most seven 5 MiB archives plus the active file. Clear removes activity files but preserves transaction receipts; Export writes only the same redacted event schema.

The app uses Apple's Service Management API and does not install a mutable plist in ~/Library/LaunchAgents. Prepare to uninstall disables Launch at Login while preserving receipts and license data; then quit the app and move it to the Trash. The preview archive is unsigned. Signing, notarization, clean-Mac installation and publication are separate release gates.

Each MCP client receives isolated observations, confirmations, transactions, and capability grants over the same durable browser. A logical browser_session close detaches that client authority but deliberately keeps the live browser; quitting the app destroys that in-memory page session.

Restart conversations opened before registration: MCP tool catalogs are fixed for a running conversation and are not retroactively replaced.

MCP flow

  1. List the secretless persistent profile with browser_session { operation: "profiles" }, then browser_session { operation: "open", profile_id: "default" }.
  2. browser_navigate opens a native exact-destination approval dialog by default. Use approval_mode: "mcp" only when the client reliably supports multi-round elicitation.
  3. browser_observe; use element_scroll_into_view, browser_scroll, or browser_session { operation: "set_viewport", ... }, then observe again when needed. back, forward, and reload are native-confirmed session operations.
  4. Use the fresh observationID and elementID once.
  5. For a login form, call browser_fill_siliconpass. If it returns credential_not_found, accept the native handoff and add or update the credential directly in SiliconPass.
  6. browser_act returns input_required; approve the exact bound action.
  7. Read, export as canonical ReceiptV1, or reconcile the receipt with browser_transaction. One redacted canonical object produces both JSON and Markdown; only the SHA-256 of the idempotency key is exported. Exported evidence never authorizes replay.

Large semantic pages can be read without oversized MCP results: browser_observe supports role/name filters, maximum_elements, maximum_field_characters, and element_offset; follow nextElementOffset until it is absent. The enforced limits and the remaining release measurements are documented in docs/performance-budgets.md. The authority boundaries, attack paths, current controls and residual release gates are maintained in docs/security-threat-model.md. Local data lifetimes and explicit-export boundaries are documented in docs/privacy-retention.md. The stable diagnostic allowlist and forbidden support data are documented in docs/support-diagnostics.md. The manual signed-update, rollback, support and incident rules are documented in docs/release-maintenance-policy.md.

While a person holds the human control window, its bar offers Fill with SiliconPass when the SiliconPass broker is installed. The person asks, not the agent: WebKitUI binds the one visible password form in the frame being used, including a restricted sign-in frame such as idmsa.apple.com, and SiliconPass matches the saved sign-in by that frame's exact host. The values are typed into the two fields through AppKit, so the page sees real input events; nothing is submitted and nothing reaches MCP, logs or the agent.

SiliconPass credential release is deliberately stronger than ordinary browser confirmation. After the exact native fill summary is approved, macOS evaluates deviceOwnerAuthentication uses zero Touch ID reuse and delegates the available authentication method to macOS. WebKitUI does not infer that Touch ID, Apple Watch, password fallback, or closed-lid authentication will be available on a particular Mac. If the Mac is locked or authentication UI cannot be presented, WebKitUI receives only user_presence_unavailable: no secret is released and no automatic retry is performed.

The protected browser never connects to private address space. The one exception is a service on the owner's Tailscale network: navigating to a name that resolves only into 100.64.0.0/10 asks for a native Allow Tailscale Access confirmation for that exact origin and port. Once allowed, the proxy connects to that host on that port only, pinned against rebinding; every other private address, and the same name on another port, stays refused. Grants last until the app quits. Any other private destination returns privateNetworkDestination instead of an opaque WebKit error.

Restricted authentication origins such as idmsa.apple.com return only their canonical origin and a local handoff requirement. Observe, text, capture, scroll, credential fill, and actuation remain blocked until the human leaves that origin. A cross-origin redirect is not reported as an ambiguous WebKit failure: it returns redirect_requires_human_approval with source and target origins only.

When App Store Connect embeds that restricted Apple Account origin, the sign-in takes the native human handoff like any other restricted origin. It used to return full_browser_required: the frame measured as stalled was a hidden-page defect of the parked window (fixed 2026-09-23), not an engine limit.

Navigation blocks on an exact native macOS confirmation by default and returns a normal terminal tool result. Human handoff is two calls: the first transfers control to the visible window; after completing the sensitive step, call browser_session { operation: "handoff", ... } again and approve the native resume dialog. MCP 2026-07-28 clients continue to use multi-round input_required results.

Use browser_session { operation: "handoff" } when a human must control the same local WebKit session. Declining resume leaves human control active. If a previous handoff token survives while agent control is active, status and handoff_start point to handoff for recovery. A local confirmation is required before invalidating the old token and starting a new human handoff. A confirmed tokenless resume also invalidates earlier tokens. Another client's handoff remains excluded, and human work in progress is reported as wait-only. When a click's cross-origin navigation is refused, the action result and status report a navigation_blocker containing only the source and destination origins and a human-handoff instruction. The click is not automatically replayed, and the private authentication request is not exposed. Semantic appearance and target absence postconditions require an unfiltered, untruncated baseline: otherwise postcondition_baseline_incomplete refuses the action before dispatch. Observe again with enough elements and field characters to establish absence. Covered controls name the hit-tested covering element in coveredBy (compact: covered_by). Name filters normalize common apostrophes; visible inline onclick controls are included even without a pointer cursor. Identically sized link wrappers without independent state or explicit handlers are omitted in favour of their link. The handoff window becomes a regular foreground Mac app with a Dock icon and uses the same rendered view as semantic observation and capture.

Linux headless lane

linux/ is a separate, ephemeral Playwright worker for disposable workloads on a dedicated Linux VM. It does not inherit Mac cookies or passwords and it does not replace native WebKit. Route authenticated sessions and human handoff to the Mac; route public, unauthenticated headless work to Linux. See linux/README.md and docs/architecture/linux-headless-worker.md.

Evidence

The dated adversarial corpus measurement records 53 deterministic tests across 57 concrete fixture executions for provenance, dialog truth, destinations, and export leakage. It measures those product boundaries, not human or model attack success.

The first measured local lane uses 30 runs of the same deterministic fixture at 2560×1600. It compares WKWebView with Playwright 1.61.1 driving installed Chrome 151. It does not yet measure full process-tree memory, visible-window behavior, authenticated task success, or Playwright's pinned Chromium binary; no broader superiority claim is made.

Licensing

WebKitUI MCP 0.6.22 Developer Preview is available under the Business Source License 1.1. The source is readable, auditable and modifiable; production use is granted for personal noncommercial, qualifying noncommercial organization and evaluation use, and commercial production use requires an agreement with the Licensor. This version converts to Apache 2.0 on 2030-08-28. See LICENSING.md for the exact scope, third-party boundary and treatment of earlier revisions.

Safety

The model cannot mint capability handles. Page content never becomes trusted policy. Password values are omitted from observations. Unknown dispatch is indeterminate and is never automatically retried. No commit, push, deployment, or external account mutation is performed by the project itself.

Navigation actor attribution uses the first main-frame navigation within two seconds of an action. It is diagnostic evidence, not proof of causality or authorization: autonomous or more delayed navigation can be misattributed.

A disappearing or renamed Save/Done control cannot satisfy enabled_equals=false: that predicate requires the original semantic target to remain present and disabled. Use target_absent with value="true" for disappearance, or a heading/text predicate for the resulting page. Absence is proved only by a complete observation; it means the original semantic identity is no longer observed, and does not prove server-side persistence. An indeterminate result after dispatch requires inspection or receipt reconciliation, never automatic replay.

Setup from the maintainer

This listing does not have a supported local package template. Use the maintainer’s documentation for its hosted endpoint, authentication, and client-specific setup. No install command has been inferred.

Package

https://github.com/lowrisk75/webkitui-mcp/releases/download/v0.6.24/webkitui-mcp-0.6.24-macos-arm64.mcpbother

Compatible MCP Clients

WebKitUI MCP works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.

  • Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.
  • Cursor~/.cursor/mcp.jsonRestart Cursor for changes to take effect.
  • VS Code.vscode/mcp.jsonReload VS Code window for changes to take effect.
  • Windsurf~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect.
  • Claude Code.mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.

Learn More