@ceraph/react-native-mcp
MCP server for React Native and Expo development. Your coding agent drives and tests your app with build error capture, console monitoring, reliable on-device interactions, and prebuild detection.
Works with any MCP client: Claude Code, Cursor, Codex, VS Code, Antigravity, and others.
⚠️ Ignore the npm i line above. Run this once instead:
npx @ceraph/react-native-mcp@latest init
Platform support
Ceraph supports React Native and Expo apps. iOS testing runs on macOS through
Xcode and WebDriverAgent. Android testing runs on macOS, Windows, or Linux
through ADB and UiAutomator2.
| iOS device (USB) | Supported | macOS only. WebDriverAgent installs through Xcode once, then Ceraph manages subsequent starts. |
| iOS Simulator | Supported | macOS only. Windowed and headless targets are supported; Ceraph builds and launches simulator WDA automatically. |
| Android device (USB) | Supported | macOS, Windows, or Linux. Enable USB debugging and authorize the host when prompted. |
| Android Emulator | Supported | macOS, Windows, or Linux. Windowed and no-window modes are supported; Ceraph manages Appium/UiAutomator2. |
| Expo Go | Not supported | Use an Expo dev client or prebuilt app so Ceraph's development shim and native integration are present. |
Choosing a platform and target
On macOS, an otherwise ambiguous auto request defaults to iOS. On Windows and
Linux it defaults to Android. An active automation session stays selected.
With --device, Ceraph selects the platform automatically when exactly one USB
device is connected. When multiple devices are connected, pass the exact Apple
UDID/CoreDevice identifier or Android adb serial; the identifier also identifies
the platform. Exact identifiers remain USB-only unless you also pass --wifi.
ceraph start [--ios|--android] [--device [id] [--wifi] | --simulator | --headless | --emulator | --no-window]
| iOS | --device | --simulator | --headless |
| Android | --device | --emulator | --no-window |
The platform flag is usually unnecessary because physical devices have unique
identifiers and virtual-runtime targets imply their platform. For the ordinary
single-device setup, run ceraph start --device.
Add an exact Apple UDID or Android adb serial when you need to choose among
multiple USB-connected devices. To target a specific paired device over Wi-Fi,
run ceraph start --device <UDID-or-adb-serial> --wifi. Run
ceraph start --no-wifi to revoke this project's prior Wi-Fi device approval.
The rn_target_status tool reports the active platform, exact runtime, target presentation, and automation readiness.
Requirements
- Node.js 20.19+, 22.12+, or 24+
- Expo dev client or prebuilt app (Expo Go is not supported)
- For iOS: macOS with Xcode 14+ and either an iOS 16+ USB device or a booted Simulator. See iOS setup.
- For Android: the Android SDK/JDK used by your project, with
adb available, and either an authorized USB device or a booted Android Emulator. See Android setup.
Multiple agents: One running app instance has one active Ceraph driver. Simultaneous contenders are arbitrated so they receive a busy result instead of fighting over the same device or simulator.
Environment variables
Most users should leave these unset. Use them for a persistent runtime
preference or when Ceraph's remediation asks for an explicit override.
CERAPH_SIGNAL_HOST | auto-detected | Troubleshooting only: override the host address when the selected device cannot reach the address Ceraph detected. |
CERAPH_SIGNAL_PORT | 8101 | Troubleshooting only: change the host listener port when 8101 is already in use. |
CERAPH_TARGET | auto | Persistent target: device, simulator, headless, emulator, no-window, or auto. |
CERAPH_FAST_RELOAD | true | Set to false or 0 to disable Fast Reload for the host session. |
CERAPH_KEEP_AWAKE_HEARTBEAT | enabled | Set to off to disable keep-awake protection for the host session. The ordinary wake/unlock check still runs. |
iOS setup
iOS testing requires macOS, Xcode 14+, and iOS 16 or later.
Physical device
Connect and unlock the device over USB, trust the Mac when prompted, and enable
Developer Mode. Start the sole connected device with ceraph start --device.
When multiple devices are connected, select it with
ceraph start --device <UDID-or-CoreDevice-ID>.
WebDriverAgent installs through Xcode once. Ceraph then manages subsequent
starts, including the USB connection to WDA, and routes supported interaction
and lifecycle tools to the selected device. If the Mac needs an additional
dependency, ceraph doctor provides the exact installation step.
Simulator
The Simulator has two presentation modes:
ceraph start --simulator opens a windowed Simulator for visible testing.
ceraph start --headless runs without Simulator.app for CI, cloud Macs, and
background agent verification. Screenshots, recordings, snapshots, and WDA
remain available; host audio is not disabled.
Ceraph automatically installs the optional appium-webdriveragent package the
first time it targets a Simulator, then builds and launches WDA. The first build
usually takes about a minute; later runs reuse it. Device-only setups do not
install this dependency.
You can let Ceraph boot a Simulator or boot one yourself:
open -a Simulator
Use xcrun simctl list devices booted to see booted Simulators. Call
rn_wda_stop to stop WDA, or exit the MCP server to stop its managed process.
To make headless mode the default for the current environment:
export CERAPH_TARGET=headless
An explicit target still overrides that preference for one run. The standalone
rn_build_ios primitive supports headless builds only when you deliberately
opt into its unmanaged mode.
Physical device and Simulator connected: An active Simulator automation
session remains selected even when a phone is connected. Run
ceraph start --device to select the phone instead. Before a Simulator session
exists, automatic selection uses a USB-connected iPhone; a Wi-Fi-only iPhone is
never selected automatically. Target one explicitly with
ceraph start --device <UDID-or-CoreDevice-ID> --wifi.
Android setup
Install Android Studio and use its SDK Manager to install Platform-Tools and the
SDK components required by your React Native project. Ensure adb is available
on your PATH, then confirm that it can see the intended runtime:
adb devices -l
Physical device
Enable Developer options and USB debugging, connect and unlock the phone over
USB, and accept the host authorization prompt. Start the sole connected device
with ceraph start --device. When multiple devices are connected, select the
phone with ceraph start --device <adb-serial>.
Emulator
The Android Emulator has two presentation modes:
ceraph start --emulator selects or launches a windowed AVD.
ceraph start --no-window runs an AVD without a window or host audio for CI
and background testing.
Use --avd <name> when more than one AVD is installed. Use
--variant <name> when the project has multiple debuggable build variants.
Ceraph manages build and installation, automation, app launch, wake and unlock
verification, and Expo dev-client routing for both Android targets.
Physical device and Emulator connected: An active Emulator automation
session remains selected even when a phone is connected. Run
ceraph start --device to select the phone instead. Before an Emulator session
exists, automatic selection uses a USB-connected Android phone; a Wi-Fi-only
phone is never selected automatically. Target one explicitly with
ceraph start --device <adb-serial> --wifi.
Quick start
Run one command from your project root:
npx @ceraph/react-native-mcp@latest init
init automatically:
- Configures supported MCP clients and runtime-error delivery
- Integrates with Expo Router and Metro while preserving existing project customizations
- Keeps generated local state out of version control
- Signs you in through browser OAuth when you choose Pro; Starter requires no account
- Makes the
ceraph command available when supported; use --no-global to opt out
- In interactive Git projects, offers to commit portable team setup; it never commits for agents, CI, or other non-interactive runs
init configures the project but does not start a runtime. Your coding agent can
call ceraph_start itself. To prime a physical device or start Ceraph manually:
ceraph start
(No ceraph on your PATH — a permissions-restricted machine, --no-global, or a non-interactive init? Use npx @ceraph/react-native-mcp@latest start instead.)
start picks a platform and target automatically using the host defaults above.
See Choosing a platform and target to select
a different runtime or an exact device.
Diagnostics
ceraph start runs the same health checks automatically after bring-up. To
inspect project and runtime readiness without building the app or starting
Metro, run:
ceraph doctor
Doctor accepts the same target flags as start. Use ceraph doctor --json for
automation or a GitHub issue, and ceraph doctor --help for the full command
reference.
Account
Sign in or switch the Ceraph account used by every project on this machine:
ceraph login
Sign out without changing any project setup, hooks, fixtures, or preferences:
ceraph logout
Both commands use a machine-wide credential. Logout affects only this machine;
it does not sign you out of the Ceraph website or other computers.
If the global ceraph command is not installed, use
npx @ceraph/react-native-mcp@latest login or
npx @ceraph/react-native-mcp@latest logout.
Uninstalling Ceraph
npx @ceraph/react-native-mcp uninstall
It reverses the current project's Ceraph setup and reports a per-step status, while preserving camera fixtures and the machine-wide sign-in. Use ceraph logout separately to sign out. Only @ceraph/react-native-mcp is removed from package.json.
Flags
-y, --yes | Skip confirmation prompts. |
--purge-data | Also delete Ceraph run data, generated local state, and this project's external build cache at ~/.ceraph/derived/<project>. Agent hooks and camera fixtures are preserved. |
--dry-run | Preview the step list without writing anything. |
--project-dir <path> | Operate against a specific subpackage. Required when a monorepo has more than one React Native app. |
Not auto-reverted
A few mutations are hand-edits the uninstaller can't safely make for you. Each surfaces as a manual step with the exact lines to remove:
- Bare React Native
Info.plist local-network and scheme entries, plus the AndroidManifest.xml scheme entry.
- Expo dynamic config (
app.config.js / app.config.ts) — the file path and scheme value to remove.
useEffect that contains installCeraph() alongside your own statements — the file and line to edit.
Keep-awake protection
During physical-device testing, ceraph start keeps the device awake through
build, installation, launch, bundle loading, and runtime errors. Ceraph restores
temporary device settings after the final session exits and never relies on
periodic gestures. Expo and bare React Native are supported.
After installing or updating Ceraph, run ceraph init and rebuild the app once
so the bundled development module is linked. ceraph start performs the native
build during normal bring-up. On iPhone, allow the app's Local Network request
so its development shim can reach Ceraph on your computer.
Set CERAPH_KEEP_AWAKE_HEARTBEAT=off to opt out of Ceraph-managed persistent
keep-awake protection for one host session. The ordinary wake/unlock readiness
check still runs, and the device may auto-lock after this explicit opt-out.
To opt a project out persistently, configure the installed development shim:
installCeraph({ keepAwake: false });
Keep-awake is on by default. Either the project option or the session environment
variable can turn it off; a session cannot override a project's explicit
keepAwake: false setting.
Fast Reload
Fast Reload reconnects development builds after Metro restarts, network changes,
or the host resumes from sleep. It is enabled by default.
Turning it off
Two equivalent ways:
CERAPH_FAST_RELOAD=false
installCeraph({ fastReload: false });
What it does and doesn't do
Fast Reload reconnects the JavaScript bundle; it does not apply native or app
configuration changes. Those still require a rebuild. Reloading also remounts
the app, so in-memory component state is lost.
Other MCP clients
ceraph init configures the supported client integrations automatically. Use
the instructions below only for clients Ceraph does not yet configure.
Cline / Roo Code (VS Code extensions)
Open VS Code Settings → Extensions → Cline (or Roo Code) → MCP Servers, then add:
{
"react-native-mcp": {
"command": "npx",
"args": ["-y", "@ceraph/react-native-mcp@latest"]
}
}
JetBrains IDEs
Settings → Tools → MCP Servers → Add, then enter:
- Name:
react-native-mcp
- Command:
npx
- Args:
-y @ceraph/react-native-mcp@latest
Tools
Build & Runtime
ceraph_start | Call this first. Bring the selected runtime to a ready-to-test state. Runs only what is missing and returns remediation for the first failed check. |
ceraph_doctor | Diagnose project and runtime readiness without starting a test session. Returns structured failures with remediation. |
rn_build_ios | Advanced unmanaged iOS build diagnostic. Prefer ceraph_start, or app_launch({ rebuild: true }) when automation is not needed. |
rn_build_android | Advanced unmanaged Android build diagnostic. Prefer ceraph_start, or app_launch({ rebuild: true }) when automation is not needed. |
rn_metro_start | Start or reuse Metro and capture console output. |
rn_get_errors | Return captured build and runtime errors. |
rn_get_console | Return recent Metro console output, filtered by level. |
rn_check_prebuild | Report whether an Expo native project needs a clean prebuild and why. |
rn_stop | Stop processes managed by Ceraph for the current project. |
Target & platform drivers
rn_target_status | Report the active platform, exact runtime, presentation mode, and automation readiness. |
rn_wda_start | Advanced: start or reuse automation for a booted iOS Simulator. Normal testing should begin with ceraph_start. |
rn_wda_stop | Stop the iOS Simulator automation session started by rn_wda_start. |
Screen Interaction
ceraph_snapshot | Structured accessibility-tree snapshot of the current screen. Pass includeStyles: true after visual changes to include bounded render-time styles for uniquely matched visible elements, plus honest style-coverage metadata. |
ceraph_run_hook | Run a project-owned command inside the live app when authentication, gated state, a development-only blocker, or cleanup would otherwise prevent meaningful testing. |
ceraph_record_run | Replay an already-verified path without model pauses and produce a clean MP4 with structured UI snapshots and screenshots tracing the verified flow. |
screen_tap | Tap by snapshot ref, bounds, or coordinates. Prefer a stable ref; screenshot-derived coordinates are best-effort. |
screen_tap_and_verify | Find an element by text/label/type, tap its center, and assert a required follow-up selector is on screen. Use screen_tap when no postcondition is needed. |
screen_tap_chain | Tap a sequence of elements as one compound action; sends nothing if any target cannot be resolved. |
screen_type_into_field | Focus an input and type into it. Set clearFirst: true to clear the field first. |
screen_swipe | Swipe up/down/left/right. Defaults to a 60%-of-axis swipe from screen center. |
screen_scroll_to | Repeatedly swipe until a matching element is on screen (no tap). |
screen_long_press | Long-press a matched element for a configurable duration. |
screen_press_key | Press a hardware/system button: back, home, volumeUp, volumeDown, lock. |
screen_open_url | Open a URL / deep link (e.g. myapp://product/42) to jump straight to a screen — useful for testing your app's deep-link routes. |
screen_screenshot | Capture a pixel screenshot of the current screen (a viewable image block) — for confirming actual rendered appearance. For structured screen data (elements, roles, bounds, refs to act on) use ceraph_snapshot. |
screen_wait_for | Poll until an element is on screen (or no longer on screen). Pass timeoutMs: 0 for a one-shot check. |
App Lifecycle & Device
app_launch | Open or relaunch an installed app. For the project app, Ceraph also makes its Metro bundle ready without starting automation; pass rebuild: true to rebuild and install it first. |
app_terminate | Terminate an app by its iOS bundle ID or Android package ID. |
app_activate | Bring an app to the foreground without a cold restart. |
app_list_installed | List installed apps on the selected runtime. |
app_info | Report the foreground app's identity and name. Android also reports its activity. |
rn_reload | Reload the app's JS bundle, wait for it to come back, then capture a screenshot. Requires installCeraph active in the app; dev-only. |
device_ensure_awake | Ensure the device is awake and unlocked. Returns structured remediation when locked. |
device_set_orientation | Request portrait or landscape and verify the rotation reached both the device and foreground app. |
ceraph_add_media | Add an image or video to the selected runtime's system photo library so the agent can test the app's real native media picker. Supports iOS simulators plus Android emulators and devices. |
Driving the app
Ceraph provides two building blocks for reliable app driving:
ceraph_snapshot returns structured screen elements, roles, values,
bounds, and stable refs. Call it before acting and again after each action.
- The
screen_* and app_* tools act on those refs, navigate, type,
capture screenshots, and manage the app on the selected runtime.
The loop is ceraph_start → ceraph_snapshot → act → snapshot
again. Check rn_get_errors and rn_get_console between steps. Once a path
works, ceraph_record_run can replay it into an MP4 and ordered state trace for
review. For a settled static change, use screen_screenshot plus
ceraph_snapshot instead.
License notice: The Ceraph Software License Agreement strictly prohibits
using Ceraph software, documentation, machine-readable tool descriptions, or
other technical materials as a blueprint to reproduce, emulate, reimplement,
or otherwise create Ceraph features for any purpose, including personal or
internal use. Read the full agreement.
Media picker testing
Call ceraph_add_media before opening a native image or video picker when the selected runtime needs something to choose. Ceraph adds the supplied media to the runtime's system photo library; the agent then opens and drives the app's real picker UI as part of the end-to-end flow.
The tool accepts JPEG, PNG, WebP, and HEIC images up to 5 MB, plus MP4, MOV,
and M4V videos up to 25 MB. It supports iOS Simulators and Android emulators or
devices. Physical iPhones cannot be seeded through this interface, so Ceraph
returns targeted remediation. Added media remains until it is deleted; erasing
a virtual runtime also removes its media.
Camera testing
Ceraph makes camera-dependent flows deterministic in development builds. Each
<CeraphCamera imageKey> can return a configured image from takePictureAsync
and a configured video from recordAsync, while production builds continue to
use the real camera.
Setup
Run ceraph init; it configures the development integration and offers to
replace detected Expo CameraView components. Assign a descriptive key to each
camera scenario:
import { CeraphCamera } from "@ceraph/react-native-mcp/shim";
export function ScanProfileScreen() {
return <CeraphCamera imageKey="profile" style={{ flex: 1 }} facing="front" ref={cameraRef} />;
}
CeraphCamera accepts the same camera props used by Expo Camera. The same
imageKey can select both an image and a video for a screen that supports both
capture modes.
Ask the agent to add fixtures with ceraph_add_camera_image or
ceraph_add_camera_video. Images support JPEG, PNG, WebP, and HEIC up to 5 MB;
videos support MP4, MOV, and M4V up to 25 MB. Set overwrite: true when replacing
an existing key.
For manual fixture management, use lowercase, hyphenated filenames whose stem
matches imageKey:
.ceraph/camera-images/profile.jpg
.ceraph/camera-videos/treadmill.mp4
Run ceraph doctor after adding files manually so they are ready for the next
development bundle.
Production safety
Camera fixtures and test behavior are development-only. Production builds use
the real camera and exclude configured test media.
Hooks
A hook is a project-owned command that Ceraph can run inside the live
development app. It can use the app's own state, navigation, auth, feature-flag,
and API modules. The agent invokes a hook explicitly with ceraph_run_hook.
Prefer the real UI whenever it can reach the required state reliably—the path itself is part of end-to-end coverage and may reveal regressions. A hook is an escape hatch for cases where that path is blocked or cannot establish the state the test needs. For example, an agent can:
- create or sign in as a test persona;
- establish an entitlement, feature flag, or hard-to-reproduce state;
- prepare or clean up disposable test data.
A hook is setup infrastructure, not a substitute for testing the behavior under review. Use one to unblock a representative end-to-end test, not to reduce that test to only the component or screen that changed.
Safety: Development-only bundling does not make a production backend
safe. Point hooks only at disposable, non-production data; reuse the app's
existing secret and environment handling; prefer idempotent, runId-scoped
effects; and clean up anything they create. Never modify production or
real-user data without explicit human authorization.
Create a hook in .ceraph/hooks/, one file per route, named *.hook.ts or *.hook.js. It exports the route it fires on and a run function, and may import your app's own modules:
import { grantTestEntitlement } from "../../src/services/devTesting";
import { getCurrentPath } from "../../src/navigation";
export const route = "/paywall";
export const description = "Grant disposable premium access for this test run";
export const isAvailable = () => getCurrentPath() === "/paywall";
export async function run(ctx) {
await grantTestEntitlement({ runId: ctx.runId });
return { ok: true };
}
Restart Metro after adding or removing a hook. Available routes and descriptions
appear in ceraph_snapshot.meta.availableHooks; invoke one with
ceraph_run_hook({ route: "/paywall" }).
isAvailable controls discovery, not authorization. Every registered route
can still be invoked directly, so hooks must enforce any required checks
themselves.
The run(ctx) contract
run may be synchronous or asynchronous and returns { ok, error? }:
ctx.route | The registered hook route the agent chose to invoke. |
ctx.runId | Stable identifier of the current run. |
Return { ok: true } once setup is complete, or { ok: false, error: "…" } to
explain why it failed. Thrown errors are also recorded as failures.
export const route (required) — the route name presented to the agent, as a string literal.
export const description (optional) — a short explanation surfaced beside the route so the agent knows what state the hook establishes.
export const isAvailable (optional) — a fast, side-effect-free predicate that controls whether the hook is advertised.
Hooks are development-only and are excluded from production builds.
License
Ceraph Software License Agreement — Starter is free for internal development and testing; Pro requires an active subscription. Using Ceraph materials to recreate its features for any purpose, modification, reverse engineering, redistribution, competitive use, and bypassing subscription controls are prohibited. Documented extension points remain available for project-owned hooks, configuration, fixtures, and test artifacts.
Found a Ceraph bug? File an issue.