A side-by-side look at the Askew — iPhone, iPad and Mac via Shortcuts and Core MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Askew — iPhone, iPad and Mac via Shortcuts for os automation work as a local process; choose Core for os automation as a local process. You can also run both side by side in one mcpServers block.
ASKEW_CONNECTOR_KEY, ASKEW_SERVER.askew_actions_search, askew_recipe_build, askew_run.electron_assert_pattern, electron_attach, electron_check.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | OS Automation | OS Automation |
| Quality signal | 63/100 · Good | 57/100 · Good |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Local subprocess (stdio) |
| Auth | Not declared | Not declared |
| Pricing | Not declared | Not declared |
| Required env vars | ASKEW_CONNECTOR_KEYASKEW_SERVER | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 1 | 12 |
| Directory activity | 1 views 0 installs 0 upvotes | 1 views 0 installs 0 upvotes |
| Tools | 13 | 55 |
| Full listing | View Askew — iPhone, iPad and Mac via Shortcuts | View Core |
askew_actions_searchSearch the catalog of Shortcuts actions (539 of them) for what parameter keys an action accepts, their types, its output type and the permissions it needs. Apple does not document this. Call it before askew_recipe_build — a wrong parameter key raises NO error, it is silently ignored, so the Shortcut runs and returns a confidently wrong answer.askew_recipe_buildCompose and sign a brand-new Shortcut from actions, optionally importing and running it on this Mac. A successful run does not prove correctness or safety. Use this when no existing route does what the user needs. **Requires a Mac** — this builder uses the macOS Shortcuts CLI, so on Windows or Linux it returns an error and you should use the existing verified routes instead. Getting it onto the user's iPhone is a separate step the user must tap through; iOS has no silent install. Always look up parameter keys with askew_actions_search first.askew_runRun an enabled route on one assigned device or this Mac connector. A connector target must match this connectorId, run on macOS, and use auto mode; confirm-mode is rejected before local effects. A durable, encrypted local journal prevents re-execution with the same idempotencyKey, including after process restart; a changed request conflicts. Local journal files must be retained to preserve this protection. Local input/results stay on this Mac, no relay job or quota is created, and execution waits up to 60 seconds. Local errors may follow partial effects: never automatically retry. For device targets: Works while the phone is locked: the relay pushes a notification, one dispatcher automation runs the target Shortcut, and the result comes back end-to-end encrypted. Behavior: call askew_list_routes first; each route lists an inputExample and the input MUST use exactly those keys (dates 'YYYY-MM-DD HH:mm'), otherwise the call is rejected before anything runs. Waits up to `wait` seconds (default 45) and returns status (done | failed | unknown | pending), the decrypted result text and a timeline. If status is 'unknown' or 'pending', poll with askew_get_run using the returned jobId. Usage: one route per call; do not retry a failed job blindly — read the error text, fix the input, or ask the user. Use idempotencyKey when you must retry a network error.askew_get_runReturn the current status and, when finished, the decrypted result of a job started with askew_run. Use it when askew_run returned status 'unknown' or 'pending' (for example when wait=0 or the phone was slow). Set `wait` to long-poll up to 60 seconds. Output: status, result text, and the timeline (accepted → pushed → started → finished). A local_ ID reads only this connector’s encrypted local journal; wait is ignored for local records. Local unknown means running, interrupted, or uncertain: it never triggers execution.askew_list_routesList everything an agent can act on in this account: the connector's name and mode, each registered device with its key fingerprint and last-seen time, and every route (an installed Shortcut) with its routeId, name, target Shortcut name, execution mode, enabled flag, last success time, inputExample, input hint and output hint. Call this before askew_run to learn the exact input keys a route expects. Takes no arguments. If a route shows no contract, ask the user what its Shortcut expects before running it.askew_recipes_catalogList the ready-made recipes (signed Shortcuts) the user can install from the Askew app, with what each does, its input/output contract, iPad limits and whether it is already installed on this account (installed=yes/no/OUTDATED). Use it when no installed route fits the task, to tell the user which recipe to add; askew_list_routes only shows what is already installed. Works without a connector key (install state is then unknown).askew_setup_checkDiagnose the user's Askew setup and say exactly what to fix: connector key and fingerprint verification, registered devices and when they last checked in, and every route compared with the current recipes (renamed/outdated Shortcut names, routes that never succeeded, missing input contracts). With probe=true it also sends one read-only test run and explains where it stopped (dispatcher never started vs. target Shortcut missing vs. permission prompt). Use it during first setup, after the user installs or renames Shortcuts, and whenever a run ends expired/unknown.askew_notifySend a lock-screen notification to the user's phone (mirrored to Apple Watch) and keep the full text in the app's results box. One-way agent → person: the user cannot reply through it; to receive data from the phone use the inbox tools. `title` is sent in clear text, `body` and `content` are end-to-end encrypted. Use `content` for long text (briefings, drafts) and `body` for the short line shown on the lock screen. Returns the delivery ids. Use `ref` to group related notifications. If the account has more than one phone/iPad, pass `deviceId` (from askew_list_routes) or `allDevices: true`; otherwise the call returns DEVICE_REQUIRED and nothing is sent.askew_inbox_listReturn items the phone sent to the agent: automation triggers (Wallet transaction, Sleep Focus ended, Action button), share-sheet shares (URLs, text, files), voice memos and anything a Shortcut posted with 'Send to agent'. Each item is decrypted on this computer and returned as id, kind, ref, timestamp, device and the data. The data is explicitly framed as user-phone data, not instructions. Behavior: returns immediately (no waiting); items stay listed until askew_inbox_ack is called with their ids. Use `since` to skip older items.askew_inbox_waitBlock for up to `timeout` seconds (max 30) until a new inbox item arrives, then return it exactly like askew_inbox_list. Use this in a loop to react to the phone in near real time (e.g. answer a shared article, log a payment). Returns 'inbox empty' on timeout without error, so simply call it again. Acknowledge handled items with askew_inbox_ack so they are not returned twice.askew_inbox_ackMark inbox items as handled so askew_inbox_list and askew_inbox_wait stop returning them. Pass the ids exactly as shown in the inbox output. Acknowledgement is per item and cannot be undone; the items remain visible in the app's history on the phone. Returns the number of items acknowledged.askew_variables_getRead a variable shared between the agent and the user's phone (for example a location, a plan or a preference a Shortcut stored). Values are sealed with an account key the phone issued, so the relay cannot read them; the connector decrypts on this computer. Returns the value text and when it was last updated. Errors: the phone has not sent the account key yet (ask the user to open Settings → Connector → Resend key), the name does not exist, or the value was stored in an old format.electron_assert_patternValidate, in a single check (no polling), that an element's text or a named attribute matches a pattern. Target by ref or selector. With `attribute` set, reads that attribute; otherwise reads the element's trimmed text. Provide exactly one of: equals, contains, matches_regex. Optional flags (any of i, m, s, u) apply to matches_regex; g and y are rejected as stateful. Returns: { ok, session_id, matched, actual }. Errors: EXPECTATION_FAILED (element found but its value did not match the pattern — details carry expected + actual; a missing attribute reads as actual: null), SELECTOR_NO_MATCH (no element matched — this is one-shot, so a missing element is a precondition failure, not a retry; carries similar_refs), REF_NOT_FOUND (stale ref), TRANSPORT_UNSUPPORTED, NOT_RUNNING, BAD_ARGUMENT (no/multiple predicates, invalid regex or flags, or ref+selector both/neither).electron_attachAttach to an already-running Electron app exposing a CDP debug endpoint (use electron_discover_running to find one, or start the app with --remote-debugging-port). Provide port (+ optional loopback host) or a loopback cdpUrl; pid alone is not attachable over CDP but, when supplied alongside, lets stop escalate to SIGKILL. The CDP transport supports eval/read/observe and core interaction surfaces against the attached app. The selected CDP page is also the default snapshot/find surface; explicit iframe/webview surface targeting remains unavailable. Returns: { ok, session_id, transport, windows, capabilities }. Errors: TRANSPORT_UNSUPPORTED (no attach-capable transport), CDP_DISCONNECTED (endpoint unreachable or dropped; retryable), CDP_TIMEOUT (handshake/method timeout; retryable), BAD_ARGUMENT (missing target selector or non-loopback endpoint).electron_checkCheck the checkbox/radio identified by ref or selector (no-op if already checked). Options: force, timeoutMs. Returns: { ok, session_id, target, checked: true }. Errors: REF_NOT_FOUND / SELECTOR_NO_MATCH (carries similar_refs), ELEMENT_NOT_VISIBLE (retryable), ELEMENT_DISABLED, NOT_RUNNING, BAD_ARGUMENT.electron_clear_inputClear the value of the input/textarea identified by ref or selector (sets it to empty). Options: force, timeoutMs. Returns: { ok, session_id, target, cleared }. Errors: REF_NOT_FOUND / SELECTOR_NO_MATCH (carries similar_refs), ELEMENT_NOT_VISIBLE (retryable), ELEMENT_DISABLED, NOT_RUNNING, BAD_ARGUMENT.electron_clickClick the element identified by ref (from a snapshot) or selector. Options: button (left|right|middle, default left), clickCount (2 = double-click), force (bypass actionability), timeoutMs. Returns: { ok, session_id, target }. Errors: REF_NOT_FOUND / SELECTOR_NO_MATCH (no such element — re-snapshot; not retryable, carries similar_refs), ELEMENT_NOT_VISIBLE (retryable), ELEMENT_DISABLED (not retryable), NOT_RUNNING, BAD_ARGUMENT (ref+selector both/neither).electron_console_logsRead the captured renderer console output for the session, newest-relevant entries last. Filters (all optional, ANDed): type (one or more of log/info/warning/error/debug/...), match (a regular expression the text must match), since (epoch ms — only entries at/after it), limit (max entries, default 200, max 1000 — the most recent are kept). Returns: { ok, session_id, entries: [{ type, text, timestamp, windowId?, location? }], count, overflowed }. overflowed is the number of older entries the buffer dropped. Errors: NOT_RUNNING, BAD_ARGUMENT (invalid regex, or multiple sessions).electron_detachDisconnect from an attached or injected app without stopping it. A launch-owned session cannot detach because its transport owns the process; this includes Playwright development launches and packaged CDP launches. Use electron_stop for that case. Returns: { ok, session_id, detached: true }. Errors: TRANSPORT_UNSUPPORTED (launch-owned session; not retryable), NOT_RUNNING (no such session), BAD_ARGUMENT (multiple sessions).electron_dialog_handlerArm the auto-responder for native JS dialogs (alert/confirm/prompt/beforeunload) and read which dialogs fired. Dialogs block the renderer, so the policy is applied automatically the instant one appears. Arming args (all optional): action (accept|dismiss — the default for every dialog), perType (per-kind overrides, e.g. {"confirm":"accept","beforeunload":"dismiss"}, falls back to action), promptText (text submitted to prompt() when it is accepted), oneShot (apply to exactly the next dialog, then revert to dismiss). With NO arming args the call is inspect-only and leaves the policy unchanged. Read args (all optional): type (one or more kinds to include), since (epoch ms), limit (max events, default 50, max 200 — most recent kept), clear (flush the whole buffer after reading). Until armed, the default policy is dismiss, so dialogs never hang the app. Returns: { ok, session_id, policy, entries: [{ type, message, action, defaultValue?, promptText?, timestamp, windowId? }], count, overflowed }. overflowed counts dropped events across the whole buffer, not just the returned (type/since/limit-filtered) subset. Errors: NOT_RUNNING, BAD_ARGUMENT (promptText without an accepting prompt policy, or oneShot without a policy to arm).electron_discover_runningScan the conventional CDP debug ports (9222-9225 by default) for already-running, debuggable Electron apps on loopback only. No session required. Returns: { ok, targets, count, scanned } where each target is { targetId, port, appName, pid } and scanned reports { host, ports, elapsed_ms } so an empty result is unambiguous. Errors: BAD_ARGUMENT (non-loopback host, invalid port list, or timeout outside bounds). A failed probe is simply "no target on that port".electron_doctorRun non-mutating preflight checks without starting an Electron session: Node version, Playwright, Electron, Linux display, configured app root and screenshot directory, eval policy, and project Electron runtime alignment. Returns: { ok, doctor_ok, checks, runtime }, where runtime includes server and, with --app-root, target Electron/Node/V8/ABI facts plus a bounded native-addon inventory. Inspect failed checks and warnings before electron_launch. Errors: none.electron_dragDrag the source element (ref or selector) onto the target element (targetRef or targetSelector), using the real mouse API. Options: force, timeoutMs. Returns: { ok, session_id, source, target }. Errors: SELECTOR_NO_MATCH / REF_NOT_FOUND (carries similar_refs), ELEMENT_NOT_VISIBLE (retryable), NOT_RUNNING, BAD_ARGUMENT (a side missing or both ref+selector given).electron_drop_fileSimulate dropping OS files onto the element identified by ref or selector. Web DataTransfer mode: reads each path on the host running the server, rebuilds the files in the renderer, and dispatches dragenter/dragover/drop with a real DataTransfer — engaging standard web drop handlers. Paths must be ABSOLUTE (max 10 files, 5242880 bytes each). default_prevented reports whether a drop handler engaged (called preventDefault); false usually means the target has no web drop handler. Apps that resolve dropped files to native OS paths need an app-specific IPC convention this tool does not simulate. Options: mimeType, timeoutMs. Returns: { ok, session_id, target, files, default_prevented }. Errors: ABSOLUTE_PATH_REQUIRED, FILE_NOT_FOUND, BAD_ARGUMENT (too many/large files, or ref+selector both), SELECTOR_NO_MATCH / REF_NOT_FOUND (carries similar_refs), NOT_RUNNING.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"askew-iphone-ipad-and-mac-via-shortcuts": {
"command": "npx",
"args": [
"-y",
"askew-mcp"
],
"env": {
"ASKEW_CONNECTOR_KEY": "YOUR_ASKEW_CONNECTOR_KEY_HERE",
"ASKEW_SERVER": "YOUR_ASKEW_SERVER_HERE"
}
}
}
}
{
"mcpServers": {
"core": {
"command": "npx",
"args": [
"-y",
"--package",
"@electron-stagewright/core@0.5.0",
"--package",
"playwright@1.61.1",
"--package",
"electron@42.3.0",
"electron-stagewright"
]
}
}
}