Let coding agents drive any running Expo or React Native app directly. Seed data, flip flags, fake realtime messages, navigate and check the screen in milliseconds, without tapping through the app. It works alongside device tools such as agent-device and Argent: they open the app and handle native UI, and agent-bridge handles everything inside the app. Use it from a shell, from flows or as an MCP server.
The same 19 checks on the example app, at real speed: 22.4 s with agent-device alone, 1.4 s with agent-bridge. With Argent instead of agent-device it is 17.1 s against 1.4 s while screen recording (Argent alone takes 11.6 s without the recorder). Most calls take a few milliseconds (benchmarks).
Works on Expo (dev-tools socket) and on bare React Native (CDP over Metro). Nothing ships to users: every entry point compiles to an empty stub in release builds, and agent-bridge assert-absent checks a bundle in CI (security).
npx @avasapp/agent-bridge initRun it in the app. It installs the package, writes a dev-only src/dev/agent-bridge.tsx wired for the libraries it finds, adds a smoke flow and the agent skill, and prints the two lines to paste into your root component. Agents run it as init --json; see the CLI.
bun add -d @avasapp/agent-bridge # or npm / yarn / pnpmThe package ships an agent skill that tells your agent when and how to drive the app. Add it to Claude Code, Cursor, Codex and other agents:
npx skills add avas-app/agent-bridgeIn Claude Code you can install it as a plugin instead:
/plugin marketplace add avas-app/agent-bridge
/plugin install agent-bridge@agent-bridge
Mount the hook in a file that only runs in development. You choose every tool; adapters for common libraries are one import away.
import { cdpTransport, useAgentBridge } from '@avasapp/agent-bridge'
import { expoTransport } from '@avasapp/agent-bridge/expo'
import { queryTools } from '@avasapp/agent-bridge/tanstack-query'
import { storeTools } from '@avasapp/agent-bridge/zustand'
import { mmkvTools } from '@avasapp/agent-bridge/react-native-mmkv'
// import { asyncStorageTools } from '@avasapp/agent-bridge/async-storage'
// import { secureStoreTools } from '@avasapp/agent-bridge/secure-store'
// import { reduxTools } from '@avasapp/agent-bridge/redux'
// import { jotaiTools } from '@avasapp/agent-bridge/jotai'
import { routerTools } from '@avasapp/agent-bridge/expo-router'
import { networkTools } from '@avasapp/agent-bridge/network'
import { router, useNavigationContainerRef } from 'expo-router'
export function AgentBridge() {
const queryClient = useQueryClient()
useAgentBridge({
name: 'my-app',
transports: [expoTransport(), cdpTransport()],
tools: {
...queryTools(queryClient),
...storeTools(
{ settings: useSettingsStore, auth: useAuthStore },
// Masks these paths in every store.* output; `store.set` and
// `store.call` never echo the whole store. An action's return value
// is matched by store path only if it is a piece of the state itself,
// not a copy; use a hook function for those.
{ redact: { auth: ['accessToken', 'refreshToken', 'user.phone'] } },
),
...mmkvTools({ storage }),
// Redux and Jotai state often holds tokens: add `redact` before wiring them.
// ...reduxTools(store, { redact: { auth: ['token'] } }),
// ...jotaiTools(getDefaultStore(), { user: userAtom }),
// AsyncStorage and SecureStore often hold tokens: wire them only if you accept that.
// ...asyncStorageTools(AsyncStorage),
// ...secureStoreTools(SecureStore, ['authToken'], { keychainService: 'auth' }),
...routerTools(router, { navigation: useNavigationContainerRef() }),
...networkTools(),
// Your own tools: any function, JSON in and out.
'auth.signIn': (session) => signInWith(session),
},
})
return null
}Every app also gets screen.snapshot (one short line per element), screen.press (taps, long presses, Switch toggles), screen.fill, screen.setValue (Switch, Slider, Picker, SegmentedControl), screen.scroll, screen.waitFor, screen.findText, app.reload, and bridge.restore to undo everything the agent changed, even across a reload in a session. See built-in tools and restore, including how to give agents your own hooks and register their undo.
Most flows need the app in a known state first, such as a user signed in locally with every request mocked. Define that once as a scenario.
npx @avasapp/agent-bridge session start # hold one connection; call/tools/run reuse it
npx @avasapp/agent-bridge tools
npx @avasapp/agent-bridge call scenario.apply '["signedIn", {"user": {"name": "Ada"}}]'
npx @avasapp/agent-bridge call query.pin '[["features"], {"beta": false}]'
npx @avasapp/agent-bridge call screen.press '"add-plant"'
npx @avasapp/agent-bridge call screen.waitFor '"Name is required"'
npx @avasapp/agent-bridge run flows/add-plant.flow.mjs --strict # fail if the app logged an error
npx @avasapp/agent-bridge run flows/ --bail # every *.flow.mjs in order, restore after each
npx @avasapp/agent-bridge session stop # runs bridge.restore, then disconnectsAgents should run ./node_modules/.bin/agent-bridge rather than npx, which is slower and can print npm warnings into their output. See the CLI for flows, batch mode, the REPL, sessions and the connect() client. Agents that prefer MCP tools to a shell can run agent-bridge mcp; see the MCP server.
No device tool is needed. Pair one (such as agent-device or Argent) with the bridge for what it can't reach: system alerts, permission prompts, the keyboard, screenshots, and one real tap per flow. Argent's tap, swipe, screenshot and UI tree tools work next to the bridge as they are. Its debugger tools do too on Expo apps (the bridge uses the dev-tools socket) and on React Native 0.85 and later, which let several debuggers attach. Before 0.85, Metro's debugger socket takes one client at a time, so a bare React Native app on --transport cdp and a tool that holds the debugger open (Argent's debugger tools, React Native DevTools) can take it from each other.
The screen tools measure with Fabric, so they need React Native's New Architecture; on the old one the tools that need positions fail with an error that says so (snapshot {all: true} and findText still return text, without rects).
- Built-in tools and restore
- The CLI, flows and the client
- Scenarios: signed-in state, strict network, fixtures, gates
- Adapters: TanStack Query, Zustand, Redux, Jotai, MMKV, AsyncStorage, SecureStore, Expo Router, React Navigation, network
- WebViews
- Realtime: Ably, socket.io, any other library
- Device events: deep links, app state, offline, push notifications, the clock
- Security and release builds
- Benchmarks
See CONTRIBUTING.md.
MIT © Avas Enterprises
