react-native-eye
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| METRO_PORT | No | Metro's port when it is not one of the standard ones (8081–8083, 19000–19002). The `--port` argument in `args` and the `connect` tool's `port` do the same. | |
| RNE_CACHE_DIR | No | Where the compiled OCR helper lives (default `~/Library/Caches/react-native-eye`). | |
| RNE_OUTPUT_DIR | No | Default directory for recordings and saved profiles (default `~/Downloads`). | |
| RNE_NATIVE_DRIVER | No | Set to `off` to never install or start the on-device runner (iOS) or server (Android). Taps, swipes, typing and OS layout then go through axe/adb exactly as before the driver existed. On Android it also force-stops a server left running by an earlier session. | |
| RNE_FILTERED_TOOLS | No | Comma-separated tool names to hide. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| pingA | Health check. Returns 'pong'. |
| connectA | Connect to a running React Native app via Metro. Optionally pass a port, a target_id from list_targets, or a device name — otherwise standard ports are scanned and the app's JS runtime is picked. |
| list_targetsA | List every debuggable React Native target Metro exposes (a device can have several: the JS runtime, the UI page, stale pages from earlier reloads). Pass a target_id to connect. |
| get_logsA | Recent console logs from the app. Filter by level and/or search substring. Note: only logs emitted after connect are captured — reload_app to catch cold-start output. |
| get_errorsB | Uncaught exceptions and redboxes captured since connecting. Includes the errors LogBox swallows, which never reach the debugger on their own. |
| get_networkA | Recent HTTP requests captured in-app. The list view shortens long URLs (query strings collapse to a char count) — pass detail: for the full URL and request/response bodies. Dev-tooling traffic (Metro, Expo, react-devtools, dev polls) is hidden unless include_dev:true; exclude:[...] drops URLs by substring. Note: requests before connect are missed (cold start). |
| eval_jsA | Evaluate JavaScript in the app's runtime and return the result. Statements are allowed, and a promise result is awaited (await fetch(...), AsyncStorage.getItem(...)) — pass the promise itself, not |
| get_jobA | Collect the result of a background job started by eval_js (background: true). Without an id, lists every job the server is holding. |
| get_stateA | Read app store state (Redux/Zustand/custom), summarised depth-first inside the app so the result stays valid JSON. Optionally a dot-path (e.g. 'user.name') and a depth (default 3). |
| get_component_treeA | Depth-limited React component tree via the React DevTools hook, with testID and text where present. max_depth counts only components a developer actually authored — host components (RCTView, RCTText, ...) and structural wrappers (forwardRef/memo/context, styling HOCs) are free and can repeat any number of times without spending budget. RN's own dev-only infrastructure (LogBox, the debugging overlay, the root tag context) is omitted from the output entirely. Large trees are capped and marked truncated rather than returned unbounded. |
| screenshotA | Capture the current screen. Without output_path returns an inline JPEG downscaled to 800px (pass full_res:true for the native PNG); an image that would still be too large for the caller fails with an actionable error instead of being returned. With output_path saves to a file (format png/jpeg; mask is iOS-only) and returns the path. |
| read_screen_textA | Read the visible text off the screen with on-host OCR (Apple Vision). Works without a Metro connection and sees text the layout tree cannot: images, canvases, WebViews, native dialogs. Returns one line per recognized text run as |
| get_layoutA | Interactive on-screen elements with center coordinates. Elements scrolled out of view are listed separately as off-screen. Optional: filter with search terms (matched against text/testID), narrow by element type, or pass at:{x,y} to get the element at a point. source picks the capture path (js = app runtime, os = axe/uiautomator, default auto). |
| tapA | Tap an element by text, testID, or x+y coordinates. Pass duration_ms for a long press. via picks the delivery path (js = call the component's own press handler through the app runtime, os = a real simulator/device touch, default auto). Returns the coordinates tapped, the path used, and what changed on screen. |
| swipeA | Swipe in a direction (scaled to the screen), or between explicit coordinates. via picks the delivery path: js scrolls the container under the start point by the same distance through the app runtime (no touch), os is a real simulator/device gesture, auto (default) tries js and names the reason when it falls back. |
| scroll_toA | Bring an off-screen element into view. Returns immediately when it is already visible. via: js asks the element's own scroll container to jump to it (no touch, no swipe); os swipes toward it and re-reads the layout after each swipe, stopping as soon as it appears; auto (default) tries js and names the reason when it falls back. |
| type_textA | Type text into a text input. |
| press_keyA | Press a hardware key. "back" goes up one screen — via "js" (tried first by default) calls the navigation container's own goBack() with no touch, via "os" uses the hardware key (Android) or an edge swipe (iOS). "home" is OS-only. "enter" submits a text field by firing its own onSubmitEditing, the only route to a submit handler that does not need the OS keyboard — pass |
| list_devicesA | List available iOS simulators and Android devices (booted ones are marked). |
| launch_appB | Launch an app by bundle id (iOS) / package name (Android) on the active device. Optional: terminate_running to restart a running app, env to pass launch environment variables (iOS only). |
| install_appC | Install an app bundle on the active device (.app/.ipa on iOS, .apk on Android). |
| open_simulatorB | Open the iOS Simulator app (macOS only). |
| open_urlA | Open a deeplink or universal link on the active device (myapp://screen, https://…). Touch-free: it delivers no touch event, so it is unaffected by slow touch delivery and is usually the cheapest way to reach a screen. |
| set_permissionA | Grant, revoke or reset one privacy permission for an app, so the system alert never appears. iOS services: camera, photos, photos-add, location, location-always, contacts, contacts-limited, microphone, motion, calendar, reminders, media-library, siri, all. Android services: camera, microphone, photos, location, contacts, notifications. app_id defaults to the connected app. |
| reload_appA | Reload the JS bundle and wait for the new runtime (captures cold-start logs/network on the next run). |
| observeA | One-shot snapshot of the screen: screenshot + interactive elements + recent errors + recent logs. Sections are independent — one failing does not lose the rest. Prefer this over separate screenshot/get_layout/get_logs calls. |
| check_stateA | One-shot: current route + store state (optional dot-path) + recent errors. The route is discovered automatically — the react-navigation container ref is found by walking the React tree, so no app-side setup is needed (globalThis.RNE_ROUTE / globalThis.RNE_NAV still take precedence when the app publishes them). If no navigator is found the route is null and the hint says why. |
| run_flowA | Run a sequence of UI steps server-side in one call (tap/type/swipe/wait_for/expect/expect_not/eval). Set via:'js' to dispatch taps and typing through the app's fiber tree instead of paying a simulator touch per step; each step's report line names the path it used. Aborts on the first failure or app error, and returns an observe snapshot at the stop point. Prefer this over many separate tap/observe calls. |
| record_videoA | Start a screen recording (one at a time). Stop with stop_recording. Android uses screenrecord, which caps a clip at 3 minutes. codec applies to iOS only. |
| stop_recordingA | Stop the active screen recording and return the file path. |
| navigateA | Read or drive react-navigation (and Expo Router) from outside the app. |
| sample_cpuA | Profile the running app's threads for a few seconds and report which one is hot — the UI/main thread, the JS thread, or something else. iOS simulator: macOS |
| get_fpsB | Measure frame timing for a short window. Android also reports real UI-thread frames from dumpsys gfxinfo; on the iOS simulator only the JS-thread requestAnimationFrame rate is available (UI-thread frames need a physical device and xctrace). |
| profile_rendersA | Count React re-renders over a window: which components rendered, how many times, how long they spent, and which update started each commit. Installs a switchable hook on the React DevTools global hook (dev builds only) that prunes untouched subtrees, so it costs roughly what the commits cost rather than what the tree costs. Nothing re-renders on its own — drive the app during the window, which usually means background: true plus tap/swipe, then get_job to collect. |
| profile_jsA | Record a Hermes CPU profile for duration_ms, then report where the JS thread spent its time: busy/idle/GC split, the functions with the most self time, and the ones with the most inclusive time (symbolicated through Metro when it can). The raw .cpuprofile is saved to disk and opens in Chrome DevTools → Performance → Load. Drive the app (or leave it alone) while this runs — the profile covers whatever happens during the window. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 35 tools
Most tools target distinct resources or actions, but several composite tools intentionally overlap with atomic tools: observe vs screenshot/get_layout/get_logs, check_state vs get_state/navigate, and run_flow vs tap/type_text/swipe/eval_js. Descriptions explicitly recommend the composite tools, which keeps selection mostly clear, though press_key 'back' and navigate 'back' also overlap.
Names consistently use snake_case and are mostly verb-first (list_devices, get_logs, type_text, profile_js). Minor deviations are single-word operational verbs such as tap, swipe, connect, and navigate, plus the noun 'screenshot', but the overall convention remains readable.
With 35 tools, the surface is well beyond a tightly scoped 3–15 range and past the 16–25 'heavy' bracket. Although the server spans many React Native debugging domains, the large number of atomic plus composite tools makes the selection surface excessive for an agent.
Coverage is broad: device/app management, targets, logs/errors/network, eval and background jobs, state/component tree, OCR/layout/tap/type/swipe, navigation/flows, recording, and performance profiling. Minor gaps remain around explicit app terminate/uninstall and standalone wait/assert helpers, but launch_app terminate_running and run_flow provide workarounds.