rn-devtools-hub MCP server
Drive a running React Native or Expo app from inside its JS runtime: see, act, prove.
4 stars604 downloads/wk
Reviews
Write oneNobody has reviewed rn-devtools-hub yet.
If you have run it, two minutes of your experience saves the next person an afternoon.
rn-devtools-hub tools (93, 14 write)
write = sends, deletes, buys or postsRead from the package source without running it. The installed server may list more.
advance_timeMoves the frozen clock forward by ms. It moves the clock, it does NOT fire pending timers: driving the scheduler would mean replacing setTimeout under a running app.
asc_distribute_buildHands a build to a TestFlight group, or takes it back (action: add or remove). An internal group receives it immediately; an external one needs a beta review, which submitForReview requests in the same call. The group is matched by name or id, and an unknown name is refused with the list of real ones rather than guessed.
asc_expire_buildExpires a build, which removes it from TestFlight for every tester. Irreversible: an expired build cannot be un-expired, only replaced by a new upload.
asc_list_appsLists the apps this App Store Connect key can see (id, name, bundleId, sku). Call it when a bundle identifier is refused: the key may be scoped to other apps.
asc_list_beta_groupsLists the TestFlight groups of the app: name, internal or external, and the public link when one is enabled. Internal groups get a build immediately; external ones need a beta review first.
asc_list_buildsLists recent TestFlight builds, newest first: build number, the version they belong to, processingState (PROCESSING, VALID, FAILED, INVALID) and whether they expired. This is the answer to "is the build I just uploaded usable yet", which nothing inside the app can give. Defaults to the bundle identifier the project declares.
asc_list_versionsLists the App Store versions and the state each one is in (PREPARE_FOR_SUBMISSION, WAITING_FOR_REVIEW, IN_REVIEW, PENDING_DEVELOPER_RELEASE, READY_FOR_SALE, REJECTED...), with the build attached to each. The state field Apple returns is passed through under the name it used, because it has changed between API versions.
asc_phased_releaseDrives the phased release of a version: start it, pause it when something looks wrong, resume it, complete it to reach everyone at once, or cancel it. Apple advances the phases on its own daily schedule; this only controls the state.
asc_prepare_versionBrings an App Store version to the point where it can be submitted, in one idempotent call: creates the version if it does not exist, attaches the build, and writes the release notes for a locale. Safe to run twice; the answer lists what it actually changed and what was already right. Nothing is submitted here, asc_submit_for_review does that.
asc_release_versionReleases a version that Apple has approved and that is waiting on you (PENDING_DEVELOPER_RELEASE). It goes to the App Store, for everyone. Refused with the current state when the version is not actually waiting for a release.
asc_requestSigned GET against any App Store Connect endpoint, for what the other tools do not cover (app info localizations, sales reports, custom filters). path is an absolute API path such as /v1/apps/{id}/appStoreVersions; query is passed through, including filter[...] and include. Reads only: asc_write_request is the one that changes things.
asc_set_whats_newSets the "What to Test" note testers read on a build, creating the localization if it does not exist yet. TestFlight refuses an external distribution without one, so this is usually the step before asc_distribute_build.
asc_submit_for_reviewwrite actionSubmits a prepared version to App Review. This is the step a human normally takes deliberately: after it, the version leaves your hands and Apple starts reviewing. It reuses an open review submission when there is one rather than creating a second, and returns the submission with its state.
asc_upload_screenshotswrite actionUploads the screenshots capture_store_screenshots wrote to an App Store version, grouped by locale and by device class. The display type is read from each capture's pixel size and a size the table does not recognise is refused rather than uploaded as the wrong class; pass displayType to override. replace clears the existing set first, which is what a new release usually wants. Nothing is resized.
asc_write_requestwrite actionSigned POST, PATCH or DELETE against any App Store Connect endpoint, for the parts of the release this file does not model (pricing, in-app purchases, app info, territories). body is the JSON:API document Apple expects. The blunt instrument: it will do exactly what it is told, including things no other tool here will do.
assertChecks runtime evidence. network_response RETRIES until timeoutMs for an observed response matching required urlContains, method and status; mocked responses require allowMocked:true. Success carries the response evidence. network_ok means only no observed network error, never that a request completed. Negative event assertions require current capture coverage; unavailable coverage returns ok:fals
audit_accessibilityCompares what React renders with what the OS exposes and reports the DIFFERENCE: pressables with no accessible name, and text or controls missing from the accessibility tree. A tool with only the accessibility tree cannot find what is missing from it. Returns conclusive:false rather than a clean report when either tree came back empty.
boot_deviceBoots an iOS simulator by target id (sim:<udid>) and opens the Simulator app. Android emulators must be started externally.
build_appBuilds the app by delegating to expo run:ios, expo run:android or eas build, and streams the build's failures onto the SAME timestamped bus as the crashes and the requests. Compile error at t0, relaunch at t1, first JS crash at t2, one clock: the agent reads a continuous stream from broken code to running app. xcodebuild and gradle are delegated to, never reimplemented.
capture_store_screenshotsCaptures the App Store and Play Store screenshots from the running app, driven by a manifest (devices from list_targets, locales, screens). Each screen is reached with the app's own dev actions (nav:*, seed:*, auth:*) and waited on with an event rather than a sleep, so the same set can be regenerated identically next release. Files are written to disk at the device's native resolution and NOTHING
compare_snapshotCompares the current screen with a named baseline and EXPLAINS the difference: the changed ratio, the bounding box, the component that owns that region with its source file and line, and what the event bus recorded since the baseline. A percentage alone says something moved; this says what to edit.
export_flowExports runtime actions and subsequent observed events. attribution:temporal means ordering, not proven causality; review background traffic before using the recording as a test. format:mcp returns replay calls, text a readable script, json the structure. Recorded failures make clean:false.
export_sessionExports one session as a single correlated artifact: network, console, crashes, navigation and UI changes on ONE timeline relative to the session start, plus the crash, failed-request and console-error sections an investigation opens first. Omit sessionId for the most recent session. format:"markdown" pastes into a GitHub issue as it is; format:"json" is for further processing.
freeze_timeFreezes the app's JS clock at an ISO instant (or now), so relative dates and time-dependent rendering stop drifting between runs. Deterministic at the JS level only: Date, new Date() and Date.now(). Native animations and Reanimated read native clocks and are unaffected.
get_accessibility_treeReads the accessibility tree the OS exposes, which is what assistive technology and any external automation see. Works when the React runtime cannot be reached (release build, WebView, native screen, splash). Android uses uiautomator; iOS needs AXe installed. Every node carries source:"accessibility" so it is never confused with a React node.
get_app_infoReturns app, device, OS, development mode and network connection information.
get_capabilitiesReports what the selected app attached and the exact SDK call that enables each missing capability.
get_crashesReturns recent crashes and unhandled errors.
get_endpoint_statsComputes calls, errors and p50/p95 latencies per endpoint from the captured requests. With nothing captured it returns {endpoints: [], capture} instead, where capture says whether the network is instrumented at all.
get_events_sinceReturns device events after a cursor (monotonic seq). Poll with the returned cursor to follow network, console, crash, nav, screen.ready and ui.change without missing anything. Omit cursor for the most recent events.
get_flowReads and validates one project-relative .hubflow scenario.
get_logsReturns captured JavaScript console entries filtered by level, text and cursor.
get_native_logsNative device logs the JS console cannot see, with looping duplicates collapsed. Android: bounded logcat dump, fast (filter by app package via process). iOS: unified log dump of the last sinceMinutes (default 1); SLOW, roughly 10-30 s per minute of window, so keep the window small and filter by process. Catches crashes before the bundle loads and dev-client startup issues.
get_orientationReads the current rotation (Android).
get_overlaySays whether the expo-dev-menu floating button is currently shown, and returns the dev-menu preferences behind it.
get_project_contextReturns what the project DECLARES (installed versions, Expo SDK, New Architecture, JS engine, plugins), what the app ACTUALLY runs (engine, Fabric, bridgeless, TurboModules, native React Native version, mounted React renderer), and the contradictions between the two. The divergence block catches stale native builds, Expo Go running a plugin project and release bundles before any other debugging.
get_recent_networkReturns recent network events and explains whether an empty result means quiet traffic or missing instrumentation.
get_stateReads a store the app registered with devtools.registerStore (Zustand, Redux, React Query or a custom adapter). Omit store to list what is available. path drills in with dots; for React Query it is the JSON query key.
get_ui_treeReturns the visible React semantic tree with props, measured rectangles and source locations. Requires attachUiAutomation.
gplay_get_trackSame as gplay_list_tracks for a single track, when only production or only internal matters.
gplay_list_artifactsLists the app bundles and APKs uploaded to Play, with their version codes and sha256. Answers whether the artifact a build just produced actually reached Play, and which version code it was given.
gplay_list_reviewsReturns recent Play Store reviews with their star rating, text, device, Android version and the app version they were written against. Reviews name the crashes a reporter never receives and the regressions that only happen on one OEM. Play only exposes reviews from the last week or so, and none at all for an app that has never been published.
gplay_list_tracksLists every Play track (production, beta, alpha, internal and any custom one) with the releases sitting in it: version codes, status (completed, inProgress, draft, halted), staged rollout fraction and release notes. This is where a rollout still at 10% shows up, which no signal inside the app reports. Reads through an edit that is opened and immediately discarded, never committed.
gplay_promote_releaseTakes what a track is serving and puts it in another one, then commits: internal to beta, beta to production. Release notes are carried over unless new ones are given. Promoting to production with status completed ships to every user, so the usual first step is status inProgress with a userFraction.
gplay_reply_reviewwrite actionPublishes a public reply under a Play Store review, signed as the developer and visible to everyone. One reply per review: sending another replaces the previous one.
gplay_requestAuthenticated GET against any Play Developer API v3 endpoint, for what the other tools do not cover (subscriptions, in-app products, device tier configs). path is relative to androidpublisher/v3, e.g. /applications/{packageName}/reviews/{reviewId}. Paths under /edits are refused: an edit read outside the tools that manage one would be left open on the listing.
gplay_update_trackwrite actionWrites the release of a track and COMMITS it, which is what makes it live: start a staged rollout (status inProgress with userFraction 0.1), widen it, halt it when something looks wrong, or send it to everyone (status completed). Omit versionCodes to keep the ones the track already serves, which is how a rollout is paused or widened without repeating them. The answer carries the track before and a
gplay_upload_screenshotswrite actionUploads the screenshots capture_store_screenshots wrote to the Play listing, grouped by language and by form factor (phone, seven inch, ten inch, read from the shortest side of each capture). replace clears the existing images of that form factor first, which is what a new release usually wants. Everything happens in one Play edit that is committed at the end, so a listing is never left half updat
gplay_write_requestwrite actionAuthenticated POST, PUT, PATCH or DELETE against any Play Developer API v3 endpoint, for the parts of the console this file does not model (in-app products, subscriptions, testers). Paths under /edits stay refused even here: an edit opened outside gplay_update_track is a lock nobody closes.
install_appInstalls an app bundle on the target (simctl install / adb install -r).
launch_appLaunches the app with zero dialogs and nothing floating over the screen. iOS: simctl launch with --initialUrl (dev-client loads the given Metro URL directly, no deep link). Android: explicit-component am start with the URL as VIEW data. coldStart terminates first; the expo dev-menu onboarding is skipped automatically, and its floating action button is hidden (iOS), because it sits in its own windo
list_actionsLists the dev actions the app registered (name, label, description, argsSchema, danger). Conventions: nav:* navigate directly to a screen, auth:* instant sessions, seed:* fixtures, reset:* deterministic state.
list_devicesLists the React Native devices known to the hub and their connection state.
list_flowsLists versioned .hubflow scenarios in tests/hub with their latest local run.
list_pluginsLists the hub plugins, which reach the services around the app rather than the app itself (App Store Connect, Google Play). For each one: whether it is configured, which tools it exposes, which of them CHANGE something (writeTools) and whether writes are enabled, the exact configuration keys still missing and where to set them, and every host it will contact. A plugin with no credentials exposes n
list_previewsLists the components the app registered with devtools.registerPreview, and whether the preview outlet is mounted.
list_sessionsLists the persisted sessions, most recent first. Sessions survive hub restarts and app reloads, so an investigation can start long after the run it is about.
list_targetsLists OS-level targets: iOS simulators (simctl) and Android devices/emulators (adb), with their boot state. Native tools take these targets, distinct from the JS deviceId of list_devices.
mock_networkControls the requests going through the instrumented fetch: rules to stub a status and a body, or a condition among normal, offline, 3g and flaky. Failures are spaced deterministically rather than randomly, so a flaky profile stays reproducible. Mocked responses are flagged on the bus so an agent never mistakes a fixture for the backend. Only covers wrapFetch; an axios instance keeps its own adapt
open_urlOpens a deep link on the target (simctl openurl / am start VIEW). On Android pass appId to pin the handler and avoid the chooser.
propose_flow_repairwrite actionWrites a sibling .candidate.hubflow for a failed target only when recorded identity evidence strongly matches a reported candidate. It never changes assertions or the original file.
query_sqlitewrite actionRuns a SQLite SELECT or PRAGMA query on the connected device.
query_uiFinds visible elements by semantic selector and returns values, rectangles and source locations. Retries until timeoutMs.
render_componentMounts a registered component INSIDE the running app, under its real providers, session and cache: nothing to mock, unlike an isolated preview. Returns the measured rect and the rendered subtree. Verifying a component no longer means navigating to the screen that contains it.
repair_adb_routesRestores adb reverse mappings lost after an emulator or USB reconnection. Defaults to Metro 8081 and the current Hub port. Omit target only when exactly one Android target is ready.
resolve_sourceRetries Metro symbolication for a source that remained via stack after the short inline budget expired. This is separate so source enrichment never delays a successful UI action.
restore_timeGives the app its real clock back.
run_actionwrite actionRuns an action declared by the app with optional typed args (e.g. nav:packageDetail {id}). Discover actions and their schemas with list_actions. Dangerous actions must be confirmed in the MCP client.
run_flowwrite actionRuns a validated .hubflow with explicit expectations and writes screenshots and a human-readable report under .rn-devtools. Recorded event association is temporal, not proven causality.
save_flowSaves the current action and consequence recording as a versioned .hubflow file under tests/hub by default.
screenshot_nativePixel screenshot of the target screen (PNG), complementing get_ui_tree: the tree gives structure, this gives the actual rendering.
send_pushwrite actionSimulates a remote push notification on an iOS simulator (simctl push). payload is the APNs JSON with a top-level aps key. Rendering still depends on the app having notification permission.
session_startOne-call bootstrap and app switch: resolve target, pre-grant permissions, launch the dev build on the given Metro server, then wait for the expected app to connect. Pass appName when several runtimes share the hub so an event from the wrong app cannot satisfy the wait. Android derives the development-client scheme from app.json when scheme is omitted.
set_animationsEnables or disables system animations on Android (window/transition/animator scales) for deterministic captures. Remember to re-enable at session end.
set_appearanceSwitches the target to light or dark mode (simctl ui appearance / adb cmd uimode night). Useful before screenshots.
set_locationSets the simulated GPS position: simctl location (iOS) or the emulator console geo fix (Android emulators only). Deterministic replacement for real GPS in dev.
set_orientationRotates the device. Android only: iOS simulators expose no scriptable rotation, and that limit is reported rather than faked.
set_overlayShows or hides the expo-dev-menu floating button, immediately and without relaunching the app. That bubble lives in its own window above everything the app renders, so no UI tree can show it and it swallows the taps meant for whatever is underneath, the button in the top corner of the iOS photo picker being the usual casualty. Hide it before driving a native modal, put it back afterwards. Runtime
set_permissionPre-grants or revokes an app permission so the popup never appears: location, location-always, camera, microphone, photos, media-library, contacts, calendar, notifications (Android 13+ only; iOS cannot pre-grant notifications), motion/reminders/photos-add (iOS). Grant BEFORE launching.
set_statewrite actionWrites a store (the store name may be given as "store" or "name", the payload as "value" or "patch"), putting the app into an exact state without walking through ten screens. Only possible from inside the runtime, and what makes a recorded flow hermetic: start from an injected session instead of replaying a login. Redux is written by dispatching an action, React Query by query key.
shutdown_deviceShuts down an iOS simulator, or kills an Android emulator (adb emu kill).
snapshot_baselineCaptures the current screen and stores it as the named visual baseline. Take it on the device and orientation the comparison will use.
start_recordingRecords runtime actions and events observed between them. Association is temporal and may include background work. Everything up to stop_recording becomes an exportable flow.
start_screen_recordingStarts recording the screen. Returns startedAt on the SAME clock as the event bus, so a frame maps back to the event that produced it: video offset = event.ts - startedAt. Attaching a run's video to a bug report is what this is for.
stop_recordingStops the current recording. The flow stays available for export_flow.
stop_screen_recordingStops the recording and returns the file, its start instant and its duration.
swipe_nativeDrags a real finger across the screen, from x1,y1 to x2,y2 in device coordinates. This is the gesture ui_act cannot perform: it acts through the JS props, so it reaches onPress and scrollTo but never produces a MOVING touch. Use it to scroll a long form that exposes no scrollable instance, and to exercise anything reading the native touch stream: a Swipeable row, a pan handler, a carousel, any rea
tap_nativeLAST-RESORT tap for native dialogs the JS runtime cannot reach. Android: adb input tap (x/y). iOS: AXe if installed (tap by accessibility label, e.g. label='Allow', or x/y in points), else idb. Prefer ui_act and set_permission/launch_app, which make this unnecessary.
terminate_appForce-stops the app (simctl terminate / am force-stop).
ui_actActs inside the app runtime. tap and longPress invoke JS handlers, bypassing native hit testing; execution reports the mode and nativeGesture:false. Use tap_native or swipe_native to test physical reachability or gestures. Ambiguous or invalid targets are refused.
uninstall_appwrite actionRemoves the app from the target, wiping its data.
unmount_componentClears the preview outlet and gives the screen back to the app.
wait_for_eventWaits for a matching event. With since, first checks retained events after that cursor, so events emitted during the action are not missed. Without since, waits for future events only. type and payloadContains are substrings. Use assert network_response to check HTTP method and status.
Public scan report
scanner v0.1.9 · 2026-09-20 · same rubric, same numbers if you re-run it
- Code scan54 source files scanned; 54 source files scanned25/25
- –Live reliabilityno gateway calls yet and no remote to proben/a
- –Tool poisoningtools not inspected (local package is not executed); not countedn/a
- Auth qualitylocal package, no credentials required12/15
- Maintenancelast push 15 days ago15/15
- Maintainer identityregistry namespace matches repository owner7/10
Install directly
Runs npx -y rn-devtools-hub on your machine. Read the scan report first; the gateway never runs local packages.
claude mcp add rn-devtools-hub -- npx -y rn-devtools-hub
rn-devtools-hub: common questions
- Is rn-devtools-hub MCP server safe?
- Yes, by our scan: it is graded A (91/100). Read the rn-devtools-hub safety report
- How do I install rn-devtools-hub?
- It runs on your machine. Copy the Claude Code, Claude Desktop or Cursor config from the install section.
- Does rn-devtools-hub need an API key?
- Not as far as the registry entry and our scan can tell: no credentials are declared or required.
- Is rn-devtools-hub maintained?
- The last commit was 15 days ago (2026-09-05). The latest release is v0.22.0.
- What can I use instead of rn-devtools-hub?
- Servers from other publishers that do the same job: Agent-Native Dispatch MCP server, Hunch MCP server and gflow-cli MCP server. Compare all rn-devtools-hub alternatives.
Alternatives to rn-devtools-hub
Same job from other publishers: the closest match first, then the best rated.
- Agent-Native DispatchCentral Slack/Telegram router with jobs, memory, approvals, and A2A delegationnot reviewedEstablishedA
- HunchFocus-free macOS control for AI agents: drive your real Mac apps in the background, no stolen focus.not reviewedGrowingA
- gflow-cliDrive Google Flow from an agent: Veo video and Imagen image generationnot reviewedGrowingA
- Google Drive MCPSecure access to Google Drive, Docs, Sheets, Slides, and Calendar through MCP.not reviewedGrowingB
- Obsidian TurbochargedModel-agnostic, agent-ready Obsidian MCP server with RBAC, SLSA provenance, and native search.not reviewedGrowingB