An introduction for developers

ExecBro

Your agent reads the code all day. It has never once seen the app run.

MCP server · execbro React Native · iOS + Android Built by Ihor Zheludkov

The problem

Today, you are the agent's hands.

AgentReads the code, guesses at a fix.
YouReload the app. Tap through four screens to reach the bug.
YouCopy the stack trace out of the Metro terminal. Paste it in.
AgentGuesses again, one step better informed.
↺ repeat 6–10 times per bug

Half of every debugging session is a human being used as an I/O device. The agent is not slow because it's dumb — it's slow because it's working from a description of the app instead of the app.

The blind spot

Static truth is not runtime truth.

What the agent has

at rest
  • Every source file, fully
  • Types, imports, the dependency graph
  • Git history and past decisions
  • Test output — for code paths tests reach

What it can't reach

while running
  • What's actually on screen right now
  • Console output and native crashes
  • The real request, the real response body
  • Redux / Query state at the moment it breaks
  • Which component renders that pixel
  • Whether its own fix worked

A large share of real bugs live entirely in that right-hand column. It's exactly the column the agent has to ask you about.

What it is

One MCP server that plugs the agent into the running app.

Claude Code ── MCP ──▸ ExecBro ── CDP / adb / simctl ──▸ Metro ──▸ Your app on the simulator

It speaks the protocols the tooling already uses — Chrome DevTools Protocol through Metro's inspector, plus adb and simctl for the device itself. Nothing to change in the app to start: point it at a running Metro and it connects to every attached device. About sixty tools land in the agent's hands, and it picks the right ones on its own.

Capabilities

Four things it gains: eyes, hands, x-ray, a lever.

Eyes

Read the running app

Console and native logs, full network traffic, screenshots, OCR, and a text snapshot of the whole screen with tap-ready coordinates.

  • get_logs · get_network_requests
  • get_screen_state · ios_screenshot
Hands

Drive the device

Tap by testID, visible text or component name — with fallback down to accessibility, OCR, then raw coordinates. Swipe, long-press, type, pinch, deep-link.

  • tap · swipe · input_text
  • reload_app · navigate
X-ray

Inspect what rendered

The React fiber tree: props, hooks, state, per-ancestor frames, and the source file and line behind any coordinate on screen.

  • inspect_at_point · find_components
  • redux_get_state · execute_in_app
Lever

Bend the conditions

Mock or tamper with a response so the error branch runs through your real code. Go offline, fail only the first retry, replay a captured request with one field changed.

  • network_mock · network_condition
  • network_replay

Nine prebuilt skills chain these into workflows. /session-setup, /debug-logs, /network-inspect, /component-inspect and five more — each with trigger rules, so the agent reaches for the right one unprompted rather than improvising a tool order.

A real session

One prompt. No human in the loop.

The cart badge doesn't update after adding a sneaker. Find out why.
scan_metro
connected · iPhone 16 Pro (Bridgeless) · port 8081
get_screen_state
route /product/8821 · 🔘 "Add to cart" (196, 742)
tap { text: "Add to cart" }
tapped · visual change detected · badge still reads 0
get_network_requests
POST /api/cart → 201 · { "ok": true, "lineId": 4417 }
get_logs { level: "error" }
no errors this run
redux_get_state { path: "cart" }
{ items: [ … 1 item ], count: 0 }
find_components { pattern: "CartBadge" }
CartBadge → useSelector(s => s.cart.count)
The request succeeds and items is written, but cartSlice.addItem never increments count — and the badge selector reads count, not items.length. src/store/cartSlice.ts:52

Nothing here was pasted in by hand. It navigated, tapped, watched the network, read the store, and located the line — then it can fix it and tap the button again to confirm.

Where it pays off

Three jobs most teams do the hard way.

Job one

Debug something reproducible

Describe the symptom and let it reproduce the bug itself. It reaches the screen, triggers the failure, and reads logs, network and state at the moment it happens — instead of asking you for all three, one message at a time.

Job two

Verify what you just shipped

"You changed the checkout flow — go run it." It walks the whole flow on the simulator and reports what it saw. Catches the class of miss where the code is right and the wiring isn't.

Job three

Test error paths without breaking a backend

Mock the 500, the timeout, the malformed body, the flaky first attempt. Your real request builder, error branch and retry all run — no staging changes, no asking a backend team to break something for ten minutes.

Honest limits

What it does not do.

Dev builds onlyIt rides Metro's inspector. A release build has no Metro, no CDP and no LogBox, so there is nothing to attach to.
Cold-start logs need the SDKThe server attaches after the app boots, so the first few log lines and startup requests are missed. The optional execbro-sdk buffers from the first line and closes that gap.
iOS tapping needs one brew installScreenshots work out of the box; tap, swipe and text input on the simulator go through AXe. Android needs nothing beyond adb.
Pinch is Android emulator onlyReal two-finger multi-touch needs the emulator's gRPC bridge. iOS is in progress — it refuses rather than faking a zoom.
It really drives your simulatorNot a sandbox or a replay. If it taps Delete Account on a logged-in build, that is a real request. Point it at a dev environment.

Setup · about four minutes

Getting it running.

01

Register the server with Claude Code

# this project only — writes .mcp.json, commit it for the team
claude mcp add execbro --scope project -- npx -y execbro@latest

Prefer project scope: --scope user starts ExecBro in every session, including repos with no simulator. Swap in user only if you live in React Native. No install either way — npx fetches the latest. Relaunch Claude Code afterwards.

02

Add the iOS automation driver

brew install cameroncooke/axe/axe

Only needed for tap, swipe and typing on the iOS Simulator. Skip it if you're on Android.

03

Optional — but the biggest single upgrade

npm i execbro-sdk

One init() in the app entry hands the agent direct references to the things it otherwise has to guess at from the outside — whatever state containers the app uses, the navigation container, and the HTTP client itself, so it can issue a request through the app's real interceptors. Plus full request and response bodies, and every log from the first line of startup.

// index.js — dev only, tree-shaken out of release
import { init } from "execbro-sdk";
import { store } from "./store";            // Redux, Zustand, MobX…
import { queryClient } from "./queryClient";  // TanStack, Apollo…
import { http } from "./api/client";         // axios, ky, fetch wrapper
import { navigationRef } from "./navigation";

if (__DEV__) {
  init({
    // any state containers, keys are yours to name
    stores: { redux: store, queryClient },
    // routes and params it can inspect
    navigation: navigationRef,
    // anything else worth reaching — HTTP client,
    // MMKV, feature flags, your own singletons
    custom: { http },
  });
}

The ask

Take one bug this week and don't touch the simulator.

› Connect to the simulator and tell me why the cart badge stays at zero

Pick something you'd normally spend an afternoon on, hand it over, and let the agent do the tapping. Then tell me where it fell over — I wrote it, so a broken tool is a bug I can fix, not a limitation you have to work around.