Reference for the MCP tools provided by React Native AI DevTools. For the exact tool list your installed version exposes, ask the agent — the server advertises them on connection.
| Tool | Description |
|---|---|
get_usage_guide |
Get recommended workflows for all tools. Call without params for overview, with a topic (setup, inspect, layout, interact, logs, network, state, bundle) for the full guide |
The server also sends instructions on connection, so MCP clients automatically learn about get_usage_guide.
| Tool | Description |
|---|---|
scan_metro |
Scan for Metro servers and auto-connect. Called automatically by the agent at session start — no need to invoke manually |
connect_metro |
Connect to a specific Metro port. Usually called automatically — use manually only when you need a non-standard port |
disconnect_metro |
Disconnect from all Metro servers. Frees the CDP slot for the built-in RN debugger. Reconnect with scan_metro |
get_apps |
List connected apps. Run scan_metro first if none connected |
get_connection_status |
Get detailed connection health, uptime, and recent disconnects |
ensure_connection |
Verify/establish connection with health checks |
get_logs |
Retrieve console logs (filtering, truncation, summary) |
search_logs |
Search logs for specific text (truncation) |
clear_logs |
Clear the log buffer |
| Tool | Description |
|---|---|
get_network_requests |
Retrieve network requests (filtering, summary) |
search_network |
Search requests by URL pattern |
get_request_details |
One request's headers, body, timing. Body comes back as its shape; query="dotted.path" returns a field in full and renders only that side, include overrides. Credentials render as [secret:<handle>] — no argument lifts that, verbose included |
clear_network |
Clear the network request buffer |
network_mock |
Replace or tamper with responses (add / list / remove / clear) |
network_condition |
Simulate offline / slow / normal network |
network_replay |
Re-issue a captured request, with optional overrides |
http_request |
Issue a request from the host rather than through the app, carrying a vaulted credential by auth: { secret }. Placement defaults to Authorization: Bearer; header sends a key header (X-API-Key) and scheme another scheme (Basic, or "" for a bare value). No app TLS trust, proxy or cookie jar, and mock rules do not intercept it — so a difference against app_request separates a server bug from a client one. Cookie-authenticated sessions cannot be sent from here: use app_request |
list_secrets |
The credentials captured this session, by handle, with origin, age and JWT expiry. Values are never shown. Memory-only |
vault_capture |
Read a credential out of the running app into the vault without returning it — for a cold session, a background-refreshed token, or one held in a keychain |
| Tool | Description |
|---|---|
execute_in_app |
Run a JS expression in the app. state/store, apollo/cache()/deref(), router, summary() and require() are pre-resolved in scope; oversized results are bounded structurally. timeoutMs sizes the promise poll ladder; a promise that outlives it returns a handle to collect with collect/waitMs |
app_request |
Issue an HTTP request from inside the app as the logged-in user. auth="auto" resolves the bearer token in-app, so no credential enters the transcript |
list_debug_globals |
Discover available debug objects (Apollo, Redux, Expo Router, etc.) |
inspect_global |
Inspect a global object to see its properties and callable methods |
reload_app |
Reload the app (auto-connects if needed, and reconnects afterwards - the reply says so when the fresh runtime is still booting). Use sparingly - Fast Refresh handles most changes |
Tip: Install the SDK — optional, but recommended: full network capture from app startup (including request/response bodies), enhanced log collection, and direct agent control over navigation, state stores, and any custom reference you pass in.
| Tool | Description |
|---|---|
get_screen_state |
Screenshot-free orientation snapshot — active route + navigation stack, overlays, and every on-screen pressable, text, and image with a tap-ready (x, y). Switches and checkboxes carry their value as [switch:ON]/[switch:OFF]. Use pressablesOnly=true for just the tappable list |
get_screen_layout |
Screen map of visible components with positions, sizes, and text content. Use extended=true for layout styles |
get_component_tree |
React fiber tree. Compact names-only by default; pass structureOnly=false for the full detailed tree |
find_components |
Find components by name pattern. Use includeLayout=true for styles |
inspect_component |
Inspect a component's props, state (hooks), and children |
inspect_at_point |
Per-ancestor frames + props + style + source: {file, line, column} at (x, y) — pure JS, no overlay flicker |
get_images |
Access shared image buffer (screenshots, tap verification frames) |
All of these tools — plus the screenshot summaries and tap — speak one screen-space coordinate system. A coordinate from any of them can be passed to any other unchanged, with no conversion.
On a screen presented as a modal sheet (presentation:'modal'), UIKit insets it from the top of the window and React Native's own measurements do not include that inset. Every one of these tools corrects for it, derived from the sheet's measured height rather than assumed, so they keep agreeing with each other and with a screenshot; get_screen_state names the correction when it applies.
See Layout & Component Inspection guide for detailed workflows.
| Tool | Description |
|---|---|
get_bundle_status |
Get Metro bundler status and build state |
get_bundle_errors |
Get compilation errors with file locations. clear=true also resets the buffer |
| Tool | Description |
|---|---|
navigate |
Navigate the router directly and verify the route actually changed. Expo Router takes paths, React Navigation takes route names; unknown names are rejected before dispatch with nearest-match suggestions, and the error lists the registered routes. routeTable=true with no destination just lists the route names. An app written as a plain <NavigationContainer> with no ref prop is reached through a mounted screen's own navigation object instead; that path has no getRootState, so the route table is empty and destination validation is skipped, and the note says so |
tap |
Unified tap — auto-detects platform, tries fiber tree → accessibility → OCR → coordinates. Accepts text, testID, component name, or coordinates from any layout tool or screenshot summary. Returns a post-tap screenshot by default and verifies visual change via before/after diff, with verification.regions locating where the screen changed (screenshot pixels — feed a centre straight into inspect_at_point). Use native=true for coordinate taps without React Native connection (system dialogs, non-RN apps). Use duration (ms) to long press — held touch for context menus, drag starts and multi-select, reported back with longPress.handlerFound. Switches and checkboxes resolve by testID/component despite having no onPress, and the response carries switch.before/after/changed read back from the element. Use device (substring match) or udid (iOS, exact) to pin to a specific device when multiple are connected |
input_text |
Unified text entry — target a field with testID/component/textMatch (focuses itself, no prior tap needed) and it writes through React, reading the result back and comparing exactly. replace:true clears the field first instead of appending. Use native=true to type into whatever the OS reports as focused with no RN targeting at all — system dialogs, non-RN screens; auto-applied when no fiber tree is reachable even without the flag. The comparison forgives what the field itself did (autoCapitalize, autocorrect respacing, a display mask) and names a maxLength truncation rather than retrying it; a masked secureTextEntry field is reported as delivered but unverifiable, since the accessibility tree only ever exposes bullets. A testID that sits on a WRAPPER around the input resolves too, provided exactly one mounted input is under it; a wrapper around several is refused rather than guessed between. ASCII in and another script out is named as the simulator's active keyboard layout remapping US keycodes, not as a field problem. |
dismiss_keyboard |
Blur the focused TextInput, closing the on-screen keyboard. Nothing focused with the keyboard already down is a success, not an error. Nothing focused with the keyboard UP is a real failure: a native field owns it (system dialog, WebView, non-RN screen), so only a platform dismiss will close it |
swipe |
Unified swipe — auto-detects platform (iOS/Android), dispatches to the native driver, and returns a verification block. verification.meaningful is false when the swipe produced no visual change, and warning then names which one it was — end-of-list, non-scrollable surface, wrong axis, missed coordinates, or (with no React Native connection) that the screen could not be inspected at all. Set burst:true to surface transient overscroll/bounce feedback. Set verify:false, screenshot:false for the fastest path. |
pinch |
Real two-finger pinch-to-zoom — ANDROID EMULATOR ONLY (iOS in progress). Sends two genuine kernel touch contacts through the emulator's multi-touch bridge, so it drives any surface on screen (React Native, native views, WebViews, maps), not just RN. direction:"out" zooms in, "in" zooms out; x/y set the focal point in the shared screenshot-pixel space; scale is the finger-separation ratio and chains into multiple gestures when too large for one; angle picks the finger axis. span is the fraction of the screen the gesture occupies — 1 by default for "out", 0.5 for "in" (a pinch-in starts with the fingers far apart, so a full span lands them on a top bar or bottom sheet). Lower it further if a gesture still lands on surrounding UI. Returns verification.meaningful like swipe. Physical Android devices and iOS return an explicit error rather than a partial result |
Examples:
tap with text="Submit" # Finds and taps by visible text
tap with testID="login-btn" # Finds by testID prop
tap with component="HamburgerIcon" # Finds by React component name
tap with x=300 y=600 # Taps at coordinates from any layout tool or screenshot
tap with text="Menu" strategy="ocr" # Forces OCR strategy only
tap with x=300 y=600 native=true # Taps directly via ADB/simctl (no RN connection needed)
tap with testID="row-3" duration=800 # Long press — holds the touch (RN's onLongPress fires at 500ms)
swipe with startX=200 startY=600 endX=200 endY=200 # Scroll up; reads verification.meaningful
swipe with startX=200 startY=600 endX=200 endY=200 burst=true # Catches overscroll/bounce feedback
| Tool | Description |
|---|---|
android_screenshot |
Take a screenshot from an Android device/emulator |
android_launch_app |
Launch an app by package name |
android_list_packages |
List installed packages (with optional filter) |
android_long_press |
Long press at raw coordinates, for holds with no RN connection (prefer tap with duration) |
android_key_event |
Send key events (HOME, BACK, ENTER, etc.) |
| Tool | Description |
|---|---|
ios_screenshot |
Take a screenshot from an iOS simulator, or from a USB-attached physical iPhone/iPad (capture only — see below) |
ios_launch_app |
Launch an app by bundle ID |
ios_open_url |
Open a URL (deep links or web URLs) |
ios_terminate_app |
Terminate a running app. An app that had already quit is reported as stopped, not as a failure — simctl gives the same error for an uninstalled bundle id, so the result never claims a termination happened |
ios_boot_simulator |
Boot a simulator by UDID |
ios_button |
Press hardware button: HOME, LOCK, SIRI (requires IDB) |
ios_screenshot is the only iOS tool that reaches a USB-attached iPhone or iPad. Pass the device's UDID or name from list_devices; the physical lookup runs only after the simulator lookup fails, so nothing changes for simulator use.
It is capture only — no pressable list, no tap / swipe / input_text. iOS exposes no touch injection to a host below iOS 17, and the iOS 17+ path is not implemented. Requires pipx install pymobiledevice3 and a mounted DeveloperDiskImage; see setup.md.