Noracu

io.github.sgInnoraPublished by GitHub account sgInnorav0.1.4Updated Nov 6, 2026

Local MCP server: macOS apps and a browser by element index, plus iPhone Mirroring (experimental).

Overview

AI-generated overview

Local MCP server that lets an assistant observe and control macOS apps and a Playwright browser by element index, plus an experimental iPhone Mirroring layer.

What you can do
  • Snapshot and act on macOS apps: read the foreground app's accessibility tree, click, drag, scroll, press keys, type or paste, run menu commands, and capture screenshots
  • Drive a browser: launch a persistent-profile browser, navigate, snapshot the ARIA tree, click, fill, type, press keys, scroll, read visible text, evaluate JavaScript, screenshot and close
  • Operate an iPhone through iPhone Mirroring (experimental): check mirror status, launch or resume the session, screenshot, OCR and find text, tap, long-press, swipe, scroll, type, press keys, open apps, go Home or to the App Switcher
When to use it
Useful when one agent must go beyond the browser and also drive native Mac apps, or when you want local, session-preserving automation on your own machine rather than a hosted VM. The iPhone layer is for experimental, OCR-based control of a mirrored phone.
Requirements
Runs locally over stdio on macOS; Node 20 or newer, installed from npm. Mac input needs cliclick installed via Homebrew. The host app (not node) must be granted macOS Accessibility and Screen Recording permissions, then fully quit and reopened. The browser layer needs Playwright and a one-time Chromium download. The iPhone layer needs iPhone Mirroring, a locked iPhone near the Mac, and a python3 with pyobjc, optionally set via NORACU_PYTHON.
Before you install
Runs with the privileges of your MCP client, with no sandbox or remote approval: it clicks, types, pastes and drives a real browser using your logged-in profile. Screenshots, accessibility names, page content and phone screens (notifications, messages, one-time codes) become model input sent by your client. Start with NORACU_READ_ONLY=1 and NORACU_DISABLE_EVAL=1; file:// URLs are blocked unless NORACU_ALLOW_FILE_URL=1. The focus guard applies only when app is passed. Unattended mirror sessions…

Installation

In SourceWeft

  1. Open Noracu in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

noracu

A local MCP server that lets Claude, Cursor, or any MCP client operate macOS apps and a Playwright browser by element_index — and, experimentally, a real iPhone through Apple's iPhone Mirroring (OCR on the Mac, normalized taps). No API key; no telemetry from the server.

[CI] [npm] [node] [license]

中文说明见 README.zh-CN.md。

One stdio MCP server. The agent snapshots a tree, gets an indexed element list, then clicks or types by element_index instead of raw pixels — the same observe/act split on the Mac and in the browser. The experimental iPhone layer runs the same observe → act loop over OCR text and normalized coordinates.

[browser element_index demo]

browser_snapshot returns an indexed ARIA tree; browser_click acts by element_index. The Mac layer runs the same loop (mac_ax_state → mac_click). Highlight boxes are overlays from demo/record-browser.mjs, not a product feature. The iPhone layer is not in this demo.

What it does

One MCP server, three control surfaces:

LayerToolsDrivesStatus
Mac9 (mac_*)cliclick, screencapture, JXA System Eventsstable
Browser12 (browser_*)Playwright (persistent profile)stable
iPhone14 (phone_*)iPhone Mirroring via the bundled imctl driver (Python + pyobjc)experimental

The iPhone layer is experimental and ships with its driver (imctl, in imctl/). It needs Apple's iPhone Mirroring with the iPhone locked and near the Mac, and a python3 with pyobjc; it observes with OCR and acts on normalized coordinates rather than element_index. Without pyobjc, phone_* tools return a one-line imctl unavailable error with the install command, and the Mac and browser layers are unaffected. The observe/act vocabulary follows the action/observation split popularized by ChatGPT's computer-use, re-implemented on open local tooling.

Why noracu

noracu is the local hands, not the model. Your MCP client (Claude Desktop, Claude Code, Cursor) decides what to do; this server only observes and acts on this machine.

  • The server runs entirely on your machine. No cloud service, no API key, no telemetry, no postinstall scripts. Sole runtime dependency: Playwright. noracu makes no network calls of its own; the only automatic one is the one-time Chromium download on the first browser_launch (NORACU_DISABLE_AUTO_INSTALL=1 turns that off). The browser still fetches the pages you point it at, and your MCP client still sends tool results — screenshots, OCR text — to whatever model it uses.
  • Acts on semantics, not pixels. Observation returns the accessibility tree (Mac) or ARIA tree (browser) as an indexed list — text, not screenshots. Actions reference element_index; indices expire after 30 s, and clicks re-verify the target (tag/name/href/position in the browser, frontmost app on the Mac) before firing. A stale reference returns element_no_longer_valid instead of clicking the wrong thing. Screenshots are there when the tree is not enough.
  • One loop, three surfaces. Native Mac apps and a persistent-profile browser share the element_index protocol (observe a tree, act by index). The experimental iPhone layer runs the same observe → act loop on Apple's iPhone Mirroring, with OCR text and normalized coordinates, because the phone exposes no accessibility tree to the Mac.
  • Uses your real sessions. Your logged-in browser profile, your Mac apps — not a hosted VM. Compared with cloud computer-use: neither is universally more reliable; they fail differently. noracu keeps the screen on your machine.
  • Off switches that are actually off. NORACU_READ_ONLY=1 (observation-only, default-deny), NORACU_DISABLE_EVAL=1, and file:// blocked by default. See Security.
  • Verifiable. 39 end-to-end checks (npm test), CI on macos-latest, MIT, and the whole server is one file: src/server.mjs.

Not a good fit if you need multiple browser tabs or sessions (single session for now), you're not on macOS (the Mac and iPhone layers are macOS-only; the browser layer is cross-platform), or you need a finished iPhone layer today (it ships, but experimental — and Apple does not offer iPhone Mirroring in the EU). If you only need browser automation, playwright-mcp has tab management and selector APIs that noracu v0.1 lacks; noracu is for when one agent also has to leave the browser and drive Mac apps.

Quickstart

First success is mac_ax_state (or a browser snapshot) — not a click, and not a phone_* tool. The biggest day-one failure is granting macOS permissions to node instead of to the host app; grants apply only after a full quit and reopen.

1. Install (Node ≥ 20; no postinstall script):

bash
npm install noracu          # server at node_modules/noracu/src/server.mjsbrew install cliclick       # required for Mac input (mac_click/type/key/paste)

Or from source: git clone https://github.com/sgInnora/noracu.git && cd noracu && npm install && npm test (add NORACU_CI=1 to skip hardware-gated checks).

2. Add to your MCP client — use an absolute path to src/server.mjs. GUI apps launched from Finder/Dock do not inherit your shell PATH, so give an absolute path to the node binary as well (which node) — a bare node, npx, or noracu needs Node on the GUI PATH and otherwise fails with ENOENT.

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json) / Cursor (~/.cursor/mcp.json):

json
{  "mcpServers": {    "noracu": {      "command": "/opt/homebrew/bin/node",      "args": ["/ABSOLUTE/PATH/TO/noracu/src/server.mjs"]    }  }}

Claude Code: claude mcp add noracu -- /ABSOLUTE/PATH/TO/node /ABSOLUTE/PATH/TO/noracu/src/server.mjs

Find your node path with which node (Apple Silicon: /opt/homebrew/bin/node; Intel: /usr/local/bin/node; nvm: the versioned path). Convenience, only if Node is already on the GUI PATH: "command": "npx", "args": ["-y", "noracu"].

Then quit the host app completely (including any menu-bar icon) and reopen it. A loaded tool list means the transport works — it does not need any permission yet.

3. Grant macOS permissions to the host app, not to node. System Settings → Privacy & Security → Accessibility and Screen Recording (macOS 15: "Screen & System Audio Recording") → enable the host (Claude Desktop / Cursor / your terminal). noracu never appears here. Quit and reopen the host after granting.

4. Verify. With Finder in front, call mac_ax_state (no args) — it returns an indexed accessibility tree; indices last 30 s. Browser smoke test (no Accessibility needed): browser_launch (first run downloads Chromium unless NORACU_DISABLE_AUTO_INSTALL=1) → browser_navigate → browser_snapshot → browser_click by index.

Troubleshooting

SymptomCause / fix
mac_* fails with error -1719Accessibility not granted to the host app — grant it, then quit + reopen the host
Screenshots come back all blackScreen Recording not granted to the host — grant it, then quit + reopen
ENOENT mentioning cliclickbrew install cliclick (or set NORACU_CLICLICK)
spawn node / npx ENOENT from a GUI hostuse an absolute node path in the config — nvm/volta/homebrew shims are not on the GUI PATH
browser_launch hangs on first useone-time Chromium download; or auto-install disabled (NORACU_DISABLE_AUTO_INSTALL=1) → run npx playwright install chromium
phone_* says imctl unavailable: no python3 with pyobjc foundinstall pyobjc for a python3 (python3 -m pip install pyobjc-framework-Quartz pyobjc-framework-Vision pyobjc-framework-AppKit; Homebrew python3 is the usual choice) or set NORACU_PYTHON — see iPhone layer
phone_* refused with mirror_not_live: … ("Connection Paused", "iPhone in Use")the mirror is paused or the iPhone is unlocked — lock the iPhone, keep it near the Mac, then call phone_launch (it clicks Resume)
phone_launch says mirror_setup_requirediPhone Mirroring shows a setup or welcome screen (e.g. the first launch after a macOS upgrade) — finish it yourself in the iPhone Mirroring window; noracu never clicks through it
imctl_timeout: phone … did not finish within …sthe Mac is under heavy load or the system is stuck — the action may have partly run, so check phone_status / phone_screenshot before retrying (after phone_open, noracu goes back to the Home Screen); the server keeps answering other calls meanwhile
input_busy: …an earlier phone or Mac action held the input queue for over 60 s; the call did nothing — retry
phone_open returns app not found for an installed appit only taps the label in Spotlight's Top Hit card — use the name exactly as the phone shows it under the icon (e.g. 拼多多, not Pinduoduo). "Spotlight showed no Top Hit" on an installed app usually means the iPhone's language is not English: phone_open reads Spotlight's "Top Hit" label — set NORACU_TOPHIT_LABELS to the label your iPhone shows. open_not_confirmed: … never showed up on the phone means the pasted name did not arrive (a paste can take several seconds to reach the iPhone) — retry. phone_open leaves the app name on the clipboard
Typed text comes out re-spaced (e.g. "no ra cu")a CJK input method is active — use mac_paste instead of mac_type
element_no_longer_validthe snapshot is older than 30 s or the UI changed — re-run mac_ax_state / browser_snapshot and use a fresh index

Node 18 is rejected at startup — use Node ≥ 20. Claude Desktop may start one noracu process per surface (e.g. chat and a second surface); they share the browser profile, so launch the browser from only one at a time.

Full worked examples: docs/usage.md.

iPhone layer (experimental)

noracu can operate a real iPhone through Apple's iPhone Mirroring. The agent observes with OCR on the Mac (phone_screenshot, phone_ocr, phone_find), acts at normalized 0–1 coordinates (phone_tap, phone_swipe, phone_scroll), types by pasting (phone_type) and opens apps by name (phone_open). This is not element_index: the phone exposes no accessibility tree to the Mac, so a tap lands where OCR placed the text. The driver (imctl, Python, MIT, about 1,000 lines) ships in imctl/.

Apple's requirements (support.apple.com/120421): a Mac with Apple silicon or a T2 chip on macOS 15 or later; an iPhone on iOS 18 or later with a passcode; both signed in to the same Apple Account with two-factor authentication; Bluetooth and Wi-Fi on; the Mac not sharing its internet connection or using AirPlay or Sidecar. The iPhone must be locked and near the Mac — unlocking it, or quitting iPhone Mirroring, ends the session. iPhone Mirroring is currently unavailable in the EU. One Mac and one iPhone at a time; camera and microphone are not mirrored.

Unattended sessions: in iPhone Mirroring → Settings, "Authenticate automatically" lets a session start without a password prompt. Trade-off: anyone who can use your unlocked Mac can then open your phone.

Python: the driver needs a python3 that imports pyobjc (Quartz, Vision, AppKit). Set NORACU_PYTHON, or noracu probes /opt/homebrew/bin/python3, /usr/local/bin/python3, python3, /usr/bin/python3 once, on the first phone_* call. Apple's /usr/bin/python3 usually lacks pyobjc; Homebrew's python3 plus python3 -m pip install pyobjc-framework-Quartz pyobjc-framework-Vision pyobjc-framework-AppKit is the common working setup.

Mirror state: phone_status reports live (the phone's screen is being streamed), overlay (the text iPhone Mirroring shows instead, e.g. "Connection Paused" or "iPhone in Use"), paused (that overlay offers a Resume button) and setup (a setup or welcome screen the user must finish — noracu never clicks it). Detection reads the window's structure and shape (the mirror is a tall or wide phone-shaped window; a setup screen is nearly square), not its text. While the mirror is not live, phone_tap, phone_longpress, phone_swipe, phone_scroll, phone_type, phone_key and phone_open refuse with mirror_not_live instead of tapping the overlay, and phone_ocr / phone_find start their output with a warning. The mirror pauses after inactivity; phone_launch brings it forward and clicks Resume.

Privacy: whatever the phone shows — notifications, messages, mail, one-time codes — becomes tool output that your MCP client sends to its model. Turn on a Focus, open a neutral app before you start, and begin with NORACU_READ_ONLY=1 (phone_status, phone_screenshot, phone_ocr and phone_find stay allowed; all phone input is refused). iPhone notifications also reach the Mac once Mirroring is set up.

First success: phone_status → phone_launch if live is not true → phone_screenshot → phone_ocr → phone_find + phone_tap.

Known limits: a tap goes where OCR saw the text, so repeated or misread labels can be hit wrong (phone_open cannot tell an app from a contact or website that Spotlight's Top Hit shows under the same name); phone_scroll moves lists with trackpad-style scroll events (a vertical phone_swipe drag does not scroll a list — use it for gestures such as Home Screen paging); on one tested setup (iPhone 17 Pro Max on iOS 27.0.1, Mac on macOS 26.6.2) iPhone Mirroring draws the phone about 17% larger than its window at every View size, so the right column and bottom row are cut off and cannot be reached; moving the window between displays, changing the main display and relaunching made no difference, and the iOS 27 / macOS 26 version mismatch is the main suspect — check phone_screenshot if taps near the edges miss; phone_open types the app name into Spotlight and taps it only when Spotlight's Top Hit card shows exactly that name (web suggestions, websites and Settings rows of the same name are never tapped), otherwise returns to the Home Screen — it reads Spotlight's English "Top Hit" label, so on an iPhone set to another language it finds nothing until NORACU_TOPHIT_LABELS names that label; phone_home / phone_app_switcher use the iPhone Mirroring menu (English macOS) and work without python while connected.

Environment variables

VariableDefaultPurpose
NORACU_READ_ONLYunset=1 observation-only: a default-deny allowlist of 12 observe + browser-lifecycle tools; every click/type/paste/key/eval tool is refused
NORACU_DISABLE_EVALunset=1 makes browser_evaluate (arbitrary JS) return an error when called (it stays listed)
NORACU_ALLOW_FILE_URLunset=1 lets browser_navigate open file:// URLs (blocked by default)
NORACU_DISABLE_AUTO_INSTALLunset=1 skips the Chromium auto-install on first browser_launch
NORACU_CLICLICK/opt/homebrew/bin/cliclickcliclick binary path
NORACU_PYTHONauto-probepython3 used for the bundled iPhone driver; must import pyobjc (Quartz, Vision, AppKit). Unset → first working of /opt/homebrew/bin/python3, /usr/local/bin/python3, python3, /usr/bin/python3
NORACU_IMCTL_DIRbundled imctl/override the driver location (development only)
NORACU_PHONE_TIMEOUT_MS30000 (45000 for phone_open / phone_type)time limit for each iPhone-layer step, 1000–600000 ms; on expiry the step is stopped and returns imctl_timeout
NORACU_OCR_LANGUAGESzh-Hans,en-USOCR languages for the iPhone screen, in priority order (comma-separated Vision language codes, e.g. zh-Hant,en-US or en-US; codes Vision does not list are skipped with a warning)
NORACU_TOPHIT_LABELSTop Hitthe label of Spotlight's Top Hit section that phone_open looks for, comma-separated — set it to the label your iPhone shows when it is not set to English

Security

noracu runs with the privileges of your MCP client. It clicks, types, pastes, and drives a real browser on this machine — there is no sandbox and no remote approval prompt. Set these on the server entry, then quit the host completely and reopen it:

json
"env": { "NORACU_READ_ONLY": "1", "NORACU_DISABLE_EVAL": "1" }
  • NORACU_READ_ONLY=1 is the safe first-run mode. It refuses every tool not on a 12-name allowlist (the observe tools plus browser_launch/browser_navigate/browser_close); every click, type, paste, and eval tool is denied. browser_navigate is on the allowlist, so it can still load a URL.
  • NORACU_DISABLE_EVAL=1 makes browser_evaluate return an error when called (it stays listed in tools/list). Snapshot, click, and type still work.
  • file:// is refused unless NORACU_ALLOW_FILE_URL=1, so a browser agent can't read ~/Documents by navigating to it. (The initial navigation URL is scheme-checked; redirects are not — see SECURITY.md.)
  • Focus guard: pass app to mac_type / mac_paste / mac_key and input is refused unless that app is frontmost. Omit app and the guard is off.
  • Stale-element protection: an element_index is not a coordinate. It expires after 30 s (element_no_longer_valid); a browser click re-checks tag/name/href/position, and a Mac click is refused if the frontmost app changed since the snapshot.
  • iPhone screen is model input: phone_screenshot / phone_ocr / phone_find return whatever the phone shows — notifications, messages, one-time codes. Use a Focus and a neutral app, and start with NORACU_READ_ONLY=1 (phone observation allowed, phone input refused). Phone input is also refused while the mirror is paused (mirror_not_live).
  • Injection-safe JXA: string parameters are passed to osascript as argv data, never built into the script; the one numeric value (AX read depth) is clamped to 1–10 before use. Regression-tested.

These switches do not make observed content trustworthy. Screenshot text, accessibility names, and page content are untrusted input to your model — do not let them override the task. Full threat model and per-threat status: SECURITY.md.

Tools & docs

Roadmap

  • Browser: tab management (list / select / close)
  • Act vocabulary (browser): hover, set_value, wait_for
  • Snapshot diffing to cut token use — pending measurement
  • iPhone: element_index over OCR regions, re-checking the text before a tap

Maintenance

Solo-maintained, no SLA. macOS 15+ for the Mac layer; the iPhone layer also needs Apple silicon or a T2 Mac, iOS 18+ and iPhone Mirroring (not available in the EU); the browser layer runs wherever Playwright does.

License

MIT — see LICENSE.

Source: README.md at commit 5744c3d

Tools

35

mac_screenshot

Capture the Mac screen as PNG base64, optionally cropped to region {x,y,w,h}. Returns {scale, logical_size, screen:'main'} for the main display; click coordinates are in points (pixels / scale).

Parametersregion

mac_ax_state

Read the foreground app's accessibility tree (element index/role/title/value/frame; indexes feed mac_click/mac_drag for 30s); menu bar pruned by default (use mac_menu_command); optional app name and depth (default 3, clamped 1-10). Returns ax_empty if the app has no AX-visible window (not frontmost / another Space).

Parametersappdepth

mac_click

Click at {x,y} or on a UI element, with optional button (left|right|middle) and count. Use element_index from the latest mac_ax_state (valid 30s); returns element_no_longer_valid when stale.

Parametersxycountbuttonelement_index

mac_drag

Drag from one UI element to another (move windows, sliders, selections). Use from_index/to_index from the latest mac_ax_state (valid 30s); returns element_no_longer_valid when stale.

Parametersto_indexrequiredfrom_indexrequired

mac_scroll

Scroll the frontmost window up/down/left/right by a number of pages (default 1); moves the pointer over that window first.

Parameterspagesdirection

mac_key

Press a key or modifier chord, e.g. 'return', 'esc', 'cmd-c', 'cmd-shift-v', 'arrow-up'. Pass app to refuse input unless that app is frontmost (focus guard).

Parametersappkey

mac_type

Type text at the current focus without modifier keys. Pass app to refuse input unless that app is frontmost (focus guard). Not IME-safe: with a CJK input method active, keystrokes may be re-segmented — use mac_paste for reliable text entry.

Parametersapptext

mac_paste

Write text to the clipboard and paste it with Cmd+V. Pass app to refuse input unless that app is frontmost (focus guard).

Parametersapptext

mac_menu_command

Run an app's menu bar command via accessibility traversal (locale-safe).

Parametersappcommand

browser_launch

Launch the browser with a persistent profile (headed by default; pass headless:true for headless).

Parametersheadless

browser_navigate

Navigate the current tab to an http(s) URL. file:// is disabled unless NORACU_ALLOW_FILE_URL=1.

Parametersurl

browser_snapshot

Return the page's ARIA accessibility snapshot plus a clickable element index list (indexes feed browser_click).

Parametersbrowser_id

browser_screenshot

Screenshot the page with optional fullPage, format png|jpeg, quality (1-100, jpeg) and clip {x,y,width,height}; returns the image plus DPR/viewport metadata.

ParametersclipformatqualityfullPage

browser_click

Click a page element, preferring element_index; selector is a fallback, xy pixel coordinates not recommended. Use element_index from the latest browser_snapshot (valid 30s); returns element_no_longer_valid when stale.

Parametersxybuttonselectorelement_index

browser_fill

Fill an input field matched by selector; fill replaces the whole value (browser_type appends keystrokes).

Parametersvalueselector

browser_press_key

Press a key or chord, e.g. 'Enter' or 'Control+A'.

Parameterskey

browser_type

Type text via the keyboard into the focused element; type appends keystrokes (browser_fill replaces the whole value).

Parameterstext

browser_scroll

Scroll the page up or down by a number of pages.

Parameterspagesdirection

browser_read

Read the page's visible text, optionally scoped to a selector.

Parametersselector

browser_evaluate

Evaluate JavaScript in the page and return the result.

Parametersjs

browser_close

Close the browser.

Takes no parameters.

phone_status

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Return the iPhone Mirroring session status as JSON (running, window, onscreen, frontmost, live, overlay, paused, setup — true when a setup or welcome screen is showing that the user must finish).

Takes no parameters.

phone_launch

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Ensure iPhone Mirroring is running, brought to the front, and clicks Resume when the connection is paused. Never clicks through a setup or welcome screen: reports mirror_setup_required instead.

Takes no parameters.

phone_screenshot

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Screenshot the iPhone screen (the Mirroring window, captured by window id so it works even when covered). Fails with capture_failed instead of returning another window's pixels.

Takes no parameters.

phone_ocr

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. OCR the iPhone screen into text with coordinates. Fails with capture_failed instead of returning another window's pixels.

Takes no parameters.

phone_find

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Locate text on the iPhone screen. Fails with capture_failed instead of returning another window's pixels.

Parameterstext

phone_tap

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Tap at {x, y} in normalized 0-1 coordinates. Refused with mirror_not_live when the mirror is paused. Optional expect: text that must be on screen (OCR) first, else nothing is sent.

Parametersxyexpect

phone_longpress

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Long-press at {x, y} for an optional duration in seconds. Optional expect: text that must be on screen (OCR) first, else nothing is sent.

Parametersxyexpectduration

phone_swipe

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Swipe (mouse drag) from [x,y] to [x,y] over an optional duration in ms, for gestures such as Home Screen paging; to move a list use phone_scroll. Optional expect: text that must be on screen (OCR) first, else nothing is sent.

Parametersmstofromexpect

phone_home

EXPERIMENTAL. Go to the iPhone Home Screen via the iPhone Mirroring menu (works without python/pyobjc when the mirror is connected); falls back to an imctl swipe.

Takes no parameters.

phone_app_switcher

EXPERIMENTAL. Open the iPhone App Switcher via the iPhone Mirroring menu (works without python/pyobjc when the mirror is connected); falls back to an imctl gesture.

Takes no parameters.

phone_scroll

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Scroll up or down by `distance` window heights (0 < distance <= 3, default 0.5) using trackpad-style scroll events. Reports scroll_no_effect when the screen did not change (list end, or the view does not scroll). Optional expect: text that must be on screen (OCR) first, else nothing is sent.

Parametersexpectdistancedirection

phone_type

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Type text via the clipboard (IME-safe). Restores the clipboard only after OCR sees the text appear (more often than before the paste) within its 2.5 s check; a paste that shows up a few seconds later is still reported as typed (do not type it again) but the clipboard keeps the typed text; otherwise type_not_confirmed, and the clipboard keeps the text. replace=true selects all and overwrites the field (over the same text nothing visibly changes, so that may report type_not_confirmed). Optional expect: on-screen text (OCR), else nothing is sent.

Parameterstextexpectreplace

phone_key

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Press a key: return, delete, up, down, left, or right. Optional expect: text that must be on screen (OCR) first, else nothing is sent.

Parameterskeyexpect

phone_open

EXPERIMENTAL. Needs iPhone Mirroring and a python3 with pyobjc. Open an app: Home Screen, Spotlight via the menu, clear, type the name (the clipboard keeps it), then tap only an exact label inside the Spotlight Top Hit card (suggestion, website and Settings rows of the same name are ignored, so a name the Top Hit card does not show is app not found; reads the English "Top Hit" label unless NORACU_TOPHIT_LABELS names the phone's own; a pasted name that never shows up in Spotlight, even after up to 15 s, is open_not_confirmed — retry); confirms the app opened (frame diff + OCR), taps the Top Hit icon when the label tap does nothing, else open_not_confirmed, back on the Home Screen.

Parametersapp

Version history

3
  1. v0.1.4LatestNov 5, 2026
  2. v0.1.3Nov 3, 2026
  3. v0.1.2Nov 3, 2026

Score

90% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts