Skip to content
verify mcp Beta VerifyMCP is currently in beta. If you notice any issues, get in touch and we’ll put it right.

ExecBro

NPM · EXECBRO

Gives AI agents eyes and hands into running React Native apps: logs, REPL, tap, screenshots

Available components

−3 this week 83 Trust /100

Last scanned (11 hours ago)

Scores higher than 74% of npm servers

Category Testing and QA

Trust breakdown (7 categories)

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →

Supply Chain Security81
  • No malware found by supply-chain analysis.Pass
  • CVE check failed: a known high-severity CVE affects zod 3.25.76, a direct dependency. A fixed version is available. View diagnostics → Fail
  • No install/post-install scripts declared.Pass
  • 32 of 140 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency97
  • Source repository is publicly reachable at the declared URL. View diagnostics → Pass
  • Cryptographically verified build provenance (signed, bound to igorzheludkov/execbro). View diagnostics → Pass
  • Clear OSI-approved license (MIT).Pass
  • Actively maintained (last published 9 days ago).Pass
  • Security-disclosure policy not yet verified: we couldn't inspect the source repository.Unverified
Schema Quality & AI Usability63
  • AI-judged instruction clarity (excellent).Pass
  • Context-footprint check failed: tool/resource definitions use about 24941 tokens (~366/item across 68 items; 68 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
  • Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management80
  • Stability observed for 24 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage100
  • 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
  • 100% of tool parameters carry a description.Pass
Tool Safety75
  • No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
  • 0 of 5 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "delete_account" implies "delete" and declares no destructiveHint at all, which the MCP spec reads as destructive by default. See how to fix → Fail
  • An AI judge read all 69 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
  • Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
What we tested

Checks we ran (15)

  • Malware scan
  • Known CVEs
  • Install scripts
  • Dependency health
  • Source repository
  • Provenance
  • License
  • Maintenance
  • Schema quality
  • Stability
  • Tool coverage
  • Injection markers
  • Destructive annotations
  • Judged manipulation
  • MCP protocol

Checks we could not complete (1)

These are gaps on our side, not findings about this package. We only credit what we can confirm, so a category made up entirely of them scores 0.

  • Security disclosure in Provenance & TransparencySecurity-disclosure policy not yet verified: we couldn't inspect the source repository.

Does not apply to this package (2)

  • Endpoint SecurityScored on hosted endpoints only. A package has no live endpoint of its own for us to probe.
  • Transport & ReachabilityScored on hosted endpoints only. A package has no live endpoint of its own for us to probe.

The score does not measure everything. What the score does not measure →

Changelog

Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.

  • 7 Nov 26 −3
    • Security disclosure: fail → unverified ▼ functional
    • Stability: pass → 0.80 functional
  • 6 Nov 26 0
    • Security disclosure: unverified → fail ▼ functional
  • 5 Nov 26 0
    • Security disclosure: fail → unverified ▼ functional
  • 4 Nov 26 0
    • Security disclosure: unverified → fail ▼ functional
  • 3 Nov 26 0
    • Security disclosure: fail → unverified ▼ functional
  • 2 Nov 26 0
    • Security disclosure: unverified → fail ▼ functional
  • 1 Nov 26 0
    • Security disclosure: fail → unverified ▼ functional
  • 31 Oct 26 0
    • Security disclosure: unverified → fail ▼ functional
Diagnostics

Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.

Captured 7 Nov 2026 · Analysed npm/execbro@2.19.0

Provenance Verified

A signed build attestation was found and verified, binding this exact artifact to the source repository it claims to come from.

Result Verified
Ecosystem npm
Reason Verified
Discovered via Registry attestation endpoint
Source repo igorzheludkov/execbro
Certificate issuer https://token.actions.githubusercontent.com
Certificate SAN https://github.com/igorzheludkov/execbro/.github/workflows/publish.yml@refs/tags/v2.19.0
Rekor log index 3547312034
Predicate type SLSA build provenance https://slsa.dev/provenance/v1
Subject digest sha512:da7c62e05cb405588ee666786be995b2e5e26a38475c7bb806c3c165fc002fd6357f9a044944cd599b74fb03426bf31dd201b32c559a8836f10129fa8

Background: How many MCP packages publish verified provenance →

Vulnerabilities 1 finding
ID CVE Severity Vector Fix available
GHSA-mrhx-c7xc-m3w5 CVE-2023-54404 high CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H yes

Background: What a vulnerability scan can and cannot prove →

Dependencies 140 packages
Packages resolved 140
Stale 32
Tree resolution Complete

Background: SBOMs and build attestations, explained →

MCP tools · 68 exposed · ~24,356 tokens

The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →

Tool Tokens
navigate ~428

Navigate the app's router directly, and verify it actually moved. PURPOSE: Jump to a screen without tapping through the UI, with a settled before/after route check. WHY THIS EXISTS: hand-written router calls report success whenever nothing throws. A path sent to a React Navigation ref changes nothing and warns only in LogBox, so a no-op reads as a success. DESTINATIONS ARE NOT INTERCHANGEABLE: Expo Router takes paths ("/event-details?id=1"); React Navigation takes route names ("TarotNav"). The response reports which router resolved. Unknown React Navigation names are rejected before dispatch, with nearest-match suggestions. WORKFLOW: navigate({ to: "/settings" }) -> check changed -> get_screen_state. LIMITATIONS: push/replace are unavailable on a React Navigation root ref (stack-scoped). changed=false means it settled without moving; indeterminate=true means no settled reading. DISCOVERY: navigate({ routeTable: true }) with no destination just LISTS the registered route names — call it first when you do not know them. NATIVE ANDROID: on an app with execbro-sdk-android, drives its Jetpack Navigation NavController. to is a route pattern ("settings", "detail/{id}") and params fill its placeholders. GOOD: navigate({ to: "TarotNav" }); navigate({ action: "back" }); navigate({ routeTable: true }) BAD: navigate({ to: "/TarotNav" }) on React Navigation — that is a path, not a route name.

NameTypeReqDescription
actionstring–Default "navigate".
devicestring–Target device name (substring match).
paramsobject–Route params, passed as the second navigate argument.
routeTableboolean–Include the app's registered route names in the response. Pass it alone, with no `to`, to just list them.
screenstring–Alias for `to`.
tostring–Destination: a path for Expo Router, a route name for React Navigation. Required unless action is "back" or "reset".

No output schema declared.

No examples provided.

network_condition ~243

Simulate offline or slow network for the running app. PURPOSE: Reach the offline / timeout branches through the app's real code. WHEN TO USE: 'what does this screen do with no network', 'is there a loading state', 'does the retry banner appear'. WORKFLOW: network_condition({mode:"offline"}) -> reproduce -> network_condition({mode:"normal"}). OFFLINE also patches NetInfo when installed, because many apps gate their offline UI on useNetInfo() rather than on a failed request. The result reports netInfo as patched / reads-patched-only / not-installed. Request failure works regardless. LIMITATIONS: JS-originated HTTP only. Does not change the device's real connectivity. Leaves network_mock rules untouched. GOOD: network_condition({mode:"slow", latencyMs:3000}) BAD: forgetting network_condition({mode:"normal"}) afterwards — it survives reload_app.

NameTypeReqDescription
devicestring–RN device name (substring). Omit for all devices; see get_apps.
latencyMsnumber–Delay per request in slow mode.
modestringyesoffline fails every request; slow delays them; normal clears.

No output schema declared.

No examples provided.

network_mock ~642

Intercept the app's HTTP requests and replace or modify the response. PURPOSE: Drive the app down an error path through its REAL code — the request builder, the error branch, the retry, the toast. redux_dispatch writes the post-failure state directly and skips all of it. WHEN TO USE: 'what does this screen do on a 500', 'what if this field is null', 'does the retry work'. WORKFLOW: network_mock({action:"add", url:"/orders", status:500}) -> reproduce -> get_network_requests (rows show [MOCK m1]) -> network_mock({action:"clear"}). MODES: replace returns a canned response; tamper fetches the real one and mutates it (set/remove take dotted paths). MATCHING: url is a substring by default; wrap it in slashes for a regex ("/\\/orders\\/\\d+$/"). First matching rule wins, so add specific rules before broad ones. GRAPHQL: every operation shares one URL, so add operation:"GetUser" to match one operation by name (operationName in the body, the name in the query document, or operationName in a GET URL). url is optional then. Batched array bodies are not matched. LIMITATIONS: JS-originated HTTP only — native-module traffic (native SDKs, <Image> loading) is not intercepted. Rules are per-device and survive reload_app; clear them when done (a bare clear covers every device). GOOD: network_mock({action:"add", url:"/orders", mode:"tamper", remove:["data.email"]}) BAD: leaving a rule active and then debugging why the app 'always fails' — check network_mock({action:"list"}) hit counts first.

NameTypeReqDescription
actionstringyesWhat to do.
bodystring–Response body (replace mode).
bodyReplacestring–tamper: replace the whole body.
delayMsnumber–Delay before delivering.
devicestring–RN device name (substring). Omit for all devices; see get_apps.
headersobject–Response headers (replace mode).
idstring–Rule id, for action="remove".
methodstring–Restrict to one HTTP method.
modestring–replace = canned response; tamper = mutate the real one.
networkErrorstring–Fail the request instead of responding.
operationstring–GraphQL operation name to match exactly, e.g. "GetUser". Narrows url; with no url it matches that operation on any endpoint.
removearray–tamper: dotted paths to delete.
setobject–tamper: dotted path -> value, e.g. {"data.user.email": null}.
statusnumber–Response status. In tamper mode, overrides the real status.
timesnumber–Fire at most N times, then pass through. Use times:1 to test retry logic.
urlstring–URL substring, or /regex/ when slash-wrapped.

No output schema declared.

No examples provided.

network_replay ~425

Re-issue a request the app already made, optionally with changes. PURPOSE: Retry a captured call without driving the UI back to the screen that made it — and vary one field at a time to find what the backend actually rejects. WHEN TO USE: A request 4xx'd and you want to know whether it was the body, a header, or the endpoint. Or a flaky call you want to run again. WORKFLOW: get_network_requests -> copy the id -> network_replay({requestId:"js-x1-7"}) -> network_replay({requestId:"js-x1-7", body:"{...}"}). GOES THROUGH THE APP: same network stack, TLS trust, proxy and credentials as the original — cookies are attached by the native cookie jar, so a cookie-authenticated call replays as the logged-in user with no token handling. An active network_mock rule will intercept the replay too; the response says so when it does. LIMITATIONS: ids come from get_network_requests and expire when the buffer rolls over or clear_network runs. Headers replace wholesale, they do not merge. GOOD: network_replay({requestId:"js-x1-7", body:"{\"qty\":99}"}) BAD: guessing a requestId — read one from get_network_requests first.

NameTypeReqDescription
authstring–Default "none": the captured headers already carry the original Authorization. Use "auto" to resolve a fresh token instead.
bodystring–Replace the captured request body. Sent verbatim, already-encoded.
devicestring–Target device name (substring match). Omit for the default device.
headersobject–Replace the captured headers entirely (not merged).
maxResultLengthnumber–Target size for the returned body in characters (default 25000).
methodstring–Override the captured HTTP method.
requestIdstringyesId of a captured request, from get_network_requests or search_network.
urlstring–Override the captured URL.

No output schema declared.

No examples provided.

pinch ~800

Pinch-to-zoom using REAL two-finger touch events, with pixel-diff verification. ANDROID EMULATOR ONLY (iOS in progress). [PRIMARY INTERACTION TOOL — works on iOS and Android; prefer over ios_*/android_* siblings] PURPOSE: Zoom a map, image gallery, photo viewer, or any zoomable surface. pinch({ direction: "out" }) zooms in at screen centre; "in" zooms out. Pass x/y to zoom around a specific point. HOW IT WORKS: Two independent contacts sent through the emulator's multi-touch bridge as real kernel touch events. It works below the app, so it drives React Native, native views, WebViews — anything on screen. VERIFICATION: verify=true (default) returns `verification.meaningful` — false means nothing zoomed (not zoomable, already at a zoom limit, or the focal point missed). WORKFLOW: pinch({ direction: "out" }) -> read verification.meaningful. Take x/y from get_screen_state or a screenshot; no conversion needed. IF A GESTURE DOES NOTHING: lower `span` — the contacts may be landing on surrounding UI (a top bar, a bottom sheet) rather than the zoomable surface. direction="in" already defaults to a reduced span for this reason. LIMITATIONS: Physical Android devices and iOS have no multi-touch channel and return an explicit error, never a partial result. Success means real fingers moved — this never fakes zoom by calling app code.

NameTypeReqDescription
anglenumber–Axis the two fingers sit on, in degrees. 0 = horizontal (default), 90 = vertical.
burstboolean–Capture rapid sequential frames after the pinch to catch transient feedback (rubber-band snap-back at a zoom limit) even when the final state is unchanged. Frames land in the image buffer; retrieve w…
devicestring–Target device. Accepts (a) an Android adb serial like 'emulator-5554', (b) the emulator name (substring match), or (c) a connected RN app's deviceName (substring match against get_apps output). Omit…
directionstring–"out" spreads the fingers apart and zooms IN (default). "in" brings them together and zooms OUT.
durationMsnumber–Gesture duration in milliseconds (default: 300).
scalenumber–How far the fingers travel, as a ratio between their start and end separation. Default 3. Large values are split automatically into several chained gestures.
screenshotboolean–Return the post-pinch image bytes in the response. Default true. Set to false to drop the PNG bytes — verification still runs (set verify=false to skip that too).
spannumber–How much of the screen the gesture occupies, as a fraction 0-1. Defaults to 1 for direction="out" and 0.5 for direction="in", because a pinch-in starts with the fingers far apart and at span 1 they l…
verifyboolean–Compare before/after screenshots to detect whether the pinch produced a visual change. Set false to skip. When skipped, the response contains `verification: { skipped: true, skippedReason }`.
xnumber–Focal point X in screenshot pixels — the point the zoom centres on. Default: screen centre.
ynumber–Focal point Y in screenshot pixels — the point the zoom centres on. Default: screen centre.

No output schema declared.

No examples provided.

press_key ~530

Press a keyboard key or shortcut in the focused element: Enter, Escape, Tab, arrows, Backspace, or a combo like Shift+Tab / Meta+K. PURPOSE: Keys that are not text: submit with Enter, close a dialog with Escape, move focus with Tab, walk a list with arrows, delete with Backspace. WHEN TO USE: After input_text, or on any focused element. Pass testID or text to focus a target first; otherwise the key goes to whatever has focus. For typing text use input_text. PLATFORMS: chromium (Electron / Chrome) sends real key events through CDP and reports the focused element before and after. Android sends adb keyevents (no modifier combos). iOS simulator sends HID keys through AXe or IDB (combos need AXe); a physical iPhone is not supported. WORKFLOW: input_text({ testID, text }) -> press_key({ key: "Enter" }). GOOD: press_key({ key: "Backspace", repeat: 3 }) GOOD: press_key({ key: "Meta+K", device: "FluentTalk" }) BAD: press_key({ key: "hello" }) - text is input_text's job.

NameTypeReqDescription
devicestring–Target device or chromium window name (substring match). Omit when one is connected; see get_apps.
keystringyesA named key (Enter, Escape, Tab, Backspace, Delete, Space, ArrowUp/Down/Left/Right, Home, End, PageUp, PageDown, F1-F12) or one character, optionally after modifiers joined by + (Shift, Control, Alt,…
repeatinteger–Press it this many times. Default 1.
testIDstring–Focus this element first. On chromium (data-testid / data-test-id / id) it only focuses. On iOS and Android it focuses by TAPPING the element, which fires a button's onPress and puts the caret where…
textstring–Focus the element with this visible text first (same tap caveat on iOS and Android as testID).

No output schema declared.

No examples provided.

redux_dispatch ~505

Dispatch a Redux action to the store bound to the app's <Provider>, triggering useSelector subscribers and React re-renders. Resolves the live store by walking the React fiber tree on each call (no SDK setup needed; works even if no store was registered with init()). PURPOSE: Drive state-controlled UI (loaders, modals, toasts, error overlays) without exercising the real flow (network, OTP, etc.). WHY THIS EXISTS: __RN_AI_DEVTOOLS__.stores.redux often holds a different store reference than the one passed to <Provider>, so dispatching through it updates state but does NOT notify react-redux subscribers. This tool dispatches through the actual Provider store, so views re-render. WHEN TO USE: Verify state-driven UI by seeding redux state directly. Example: dispatch app/setIsLoading: true, then screenshot to confirm the loader rendered. WORKFLOW: redux_dispatch({ action: { type: 'app/setIsLoading', payload: true } }) -> screenshot -> redux_dispatch({ action: { type: 'app/setIsLoading', payload: false } }). BATCH: action accepts an array — dispatched in order, one round trip. LIMITATIONS: Requires React DevTools hook (dev mode). Action must be plain JSON-serializable (no thunks/functions). If the app has multiple <Provider> roots, pass storeIndex (default 0). GOOD: redux_dispatch({ action: { type: 'app/setIsLoading', payload: true } }) BAD: redux_dispatch({ action: () => ... }) — actions must be plain objects; for thunks use execute_in_app to call your action creator.

NameTypeReqDescription
action–yesPlain JSON-serializable Redux action object, e.g. { type: 'app/setIsLoading', payload: true }. Pass an ARRAY to dispatch several in order in one round trip — restoring a 17-field settings slice is on…
devicestring–RN device name (substring). Omit for default; see get_apps.
returnPathstring–Optional dotted path into the post-dispatch state to return for verification (e.g. 'app' or 'auth.user'). Omit to skip returning state — keeps the response small. Use redux_get_state for ad-hoc reads.
storeIndexinteger–Index of the Provider store to dispatch to when the app has multiple <Provider> roots (default: 0).

No output schema declared.

No examples provided.

redux_get_state ~515

Read state from the Redux store bound to the app's <Provider>, resolved live via the fiber tree (same store redux_dispatch targets). PURPOSE: Inspect the current app state without relying on __RN_AI_DEVTOOLS__.stores.redux (which may point at a different store instance than the Provider). WHEN TO USE: Verify state shape before/after redux_dispatch, or check what slice keys exist before crafting an action. WORKFLOW: redux_get_state() -> craft action -> redux_dispatch -> redux_get_state({ path: 'app' }) to confirm. SHAPE FIRST: a large state comes back as its structure — every key path kept, arrays and objects annotated with real sizes, leaves clipped. Read it, then narrow. NARROWING, TWO WAYS: path drills IN THE APP, so the rest of the state never crosses the wire — prefer it when you know the slice. query runs here and adds [0], [-1], [*] and quoted keys. They compose: path picks the slice, query picks the field inside it. LIMITATIONS: Requires React DevTools hook (dev mode). State must be JSON-serializable; non-serializable values are replaced with an error marker. GOOD: redux_get_state({ path: 'app' }) | redux_get_state({ path: 'cart', query: 'items[*].sku' }) BAD: redux_get_state({ path: 'app.isLoading.0' }) when isLoading is a boolean — path traversal returns undefined.

NameTypeReqDescription
devicestring–RN device name (substring). Omit for default; see get_apps.
maxResultLengthnumber–Byte target for the rendered state (default: 25000, 0 for unlimited). Bounded structurally, so size is traded for depth and array width rather than cutting the text off.
pathstring–Optional dotted path into state (e.g. 'app' or 'auth.user'), resolved in-app so only that slice crosses the wire. Omit for the full state.
querystring–Dot-path into the state that came back, returned in full: "items[0].sku", "orders[*].status", "byId[\"a.b\"]". Applies after path. A path that matches nothing returns the shape plus what is actually…
storeIndexinteger–Index of the Provider store to read from when the app has multiple <Provider> roots (default: 0).

No output schema declared.

No examples provided.

reload_app ~343

Reload the React Native app (triggers JavaScript bundle reload like pressing 'r' in Metro). PURPOSE: Force a full JS bundle reload when Fast Refresh isn't enough — clears in-memory state and re-runs the bundle from scratch. WHEN TO USE (only these cases): (1) native code, app.json, Info.plist, Podfile, or a native module changed; (2) Fast Refresh visibly failed (red-screen or stale render confirmed via screenshot after a few seconds); (3) the app is in a broken state; (4) you need to reset app state completely; (5) the user explicitly asks. AVOID: reloading reflexively after JS/TS/TSX/style edits — Fast Refresh applies those in 1-2s. A reload discards navigation stack, context, hooks state, BLE/WebSocket connections, paired devices, and auth sessions, which can force re-pairing or re-login and break your verification loop. WORKFLOW: screenshot → wait 2s for Fast Refresh → if still stale, reload_app. Auto-connects to Metro if no connection exists (with or without a device argument) — no need to call scan_metro first. After reload, wait a few seconds before running other tools. SEE ALSO: get_refresh_status (did Fast Refresh accept?), get_bundle_status (did Metro compile?).

NameTypeReqDescription
devicestring–Target device name, substring match against the name shown by get_apps (a simulator UDID or adb serial also works). OMIT THIS unless several devices are connected — passing a name copied from list_de…

No output schema declared.

No examples provided.

run_app_command ~249

Run a named command that a native Android app registered with execbro-sdk-android (ExecBro.init { command("name") { ... } }). PURPOSE: Drive app behaviour (lock, log out, reset state, seed data) through actions the app's developer exposed, without tapping through the UI. The native counterpart of calling a store action with execute_in_app. WORKFLOW: list_debug_globals (lists commands) -> run_app_command({ name }) -> inspect_global to confirm the state changed. Call with no name to list the commands. LIMITATIONS: Native Android apps with execbro-sdk-android only. Commands run on the app's main thread with a 5 s limit. GOOD: run_app_command({ name: "lock" }); run_app_command({ name: "seed", args: { users: 3 } }) BAD: run_app_command on a React Native app: use execute_in_app there.

NameTypeReqDescription
argsobject–Arguments passed to the command as a JSON object.
devicestring–RN device name (substring). Omit for default; see get_apps.
namestring–Command name. Omit to list the registered commands.

No output schema declared.

No examples provided.

scan_metro ~302

Discover and connect to debuggable apps: React Native via Metro, Chromium/Electron via CDP, and native Android apps that include execbro-sdk-android. This is typically the FIRST tool to call when starting a debugging session - it establishes the connection needed for other tools like get_logs, list_debug_globals, execute_in_app, and reload_app. PURPOSE: Discover Metro on ports 8081-8090 and auto-connect every React Native debugger target it advertises. Also probes Chromium debug ports: 9222 is listed but not auto-connected (attach with connect_metro), ports in EXECBRO_CHROMIUM_PORTS auto-connect. NATIVE ANDROID: also finds native apps that include execbro-sdk-android on every online adb device and connects them (runtime native). WHEN TO USE: At the start of any session, or after the user restarts Metro / boots a new simulator. WORKFLOW: scan_metro -> get_apps -> get_logs / screenshot / tap. GOOD: scan_metro() BAD: scan_metro() called repeatedly in a loop — use ensure_connection to re-verify an existing connection.

NameTypeReqDescription
endPortnumber–End port for scanning (default: 8090). Metro allocates upward from 8081, so ten ports covers several apps side by side; widen only if your bundler is pinned elsewhere.
startPortnumber–Start port for scanning (default: 8081)

No output schema declared.

No examples provided.

screenshot ~359

Take a screenshot of whichever target `device` resolves to: iOS simulator, USB-attached iPhone/iPad, Android device/emulator, or chromium (Electron / Chrome) window. Returns the image plus a screen-state summary: active route, overlay-grouped tappable elements (pressables behind an open sheet/modal excluded), component names as JSX tags, labels, testIDs and frames in ready-to-tap pixel coordinates. Use component names for inspect_component/find_components. PURPOSE: The capture tool for every platform. See what the user sees AND get tap-ready pressables in one call. WHEN TO USE: Visual verification, before/after comparison, or the starting point for tapping by coordinates. WORKFLOW: screenshot -> pick a pressable -> tap(text=...) if exact and unique, else tap(x, y) -> screenshot to verify. COORDINATES: image pixels are the coordinates tap(x, y) takes, on every platform. Never scale them yourself. PHYSICAL iOS: pass the UDID or name from list_devices. Capture only: no pressables, no tap/swipe/input_text. LIMITATIONS: a hidden or minimised chromium window cannot be captured: the call fails at once and says so. GOOD: screenshot(); screenshot({ device: "FluentTalk" }) BAD: a made-up device name or UDID, run list_devices first. SOURCE: inspect_at_point(x, y) maps a pixel to the file and line that renders it.

NameTypeReqDescription
devicestring–Target: a connected app name (get_apps), or a simulator/emulator name, UDID or adb serial. Omit for the default target.
outputPathstring–Optional path to also save the image to.

No output schema declared.

No examples provided.

search_logs ~253

Search console logs for text (case-insensitive). PURPOSE: Find log lines matching a substring across the connected app's console buffer. WHEN TO USE: User reports a known error/warning, or wants to trace a specific event (e.g., "redux", "auth failed"). For unfocused exploration, prefer get_logs. WORKFLOW: scan_metro -> search_logs(text="...") -> if empty, get_logs to verify buffer populated. LIMITATIONS: Only matches text captured AFTER the app connected; won't find pre-connect logs. GOOD: search_logs({ text: "TypeError" }) BAD: search_logs({ text: "" }) (use get_logs for a raw dump)

NameTypeReqDescription
devicestring–RN device name (substring). Omit for all devices; see get_apps.
maxMessageLengthnumber–Max characters per message (default: 500, set to 0 for unlimited)
maxResultsnumber–Maximum number of results to return (default: 50)
querystring–Alias for `text`.
textstring–Text to search for in log messages
verboseboolean–Disable all truncation and return full messages

No output schema declared.

No examples provided.

search_network ~261

Search network requests by URL pattern (case-insensitive). PURPOSE: Filter the network buffer to requests whose URL matches a substring — fast way to find a specific endpoint in a noisy app. WHEN TO USE: You know part of the URL (e.g., "/graphql", "users", a domain) and want matching requests across all devices. WORKFLOW: search_network(urlPattern="/api/") -> get_request_details(requestId="...") for full headers/body. LIMITATIONS: Matches URL only; for method/status/body filtering use get_network_requests. Bodies are only present when the SDK is installed. GOOD: search_network({ urlPattern: "/graphql" }) BAD: search_network({ urlPattern: "" }) — empty pattern matches everything; use get_network_requests instead.

NameTypeReqDescription
devicestring–RN device name (substring). Omit for all devices; see get_apps.
maxResultsnumber–Maximum number of results to return (default: 50)
querystring–Alias for `urlPattern`.
urlPatternstring–URL pattern to search for. Also matches GraphQL operation names (e.g. "GetCharacters") — use the operation name to find one GraphQL call among many sharing the same endpoint.

No output schema declared.

No examples provided.

send_feedback ~304

Report feedback about the ExecBro MCP tools THEMSELVES — a tool (tap, get_screen_layout, get_logs, etc.) that behaved incorrectly, was confusing, was missing, or could work better. This is EXCLUSIVELY about your experience operating ExecBro's debugging tools. It is NOT for bugs in the user's app under test, and NOT for the feature or task you were working on in this session — keep that out of the report entirely. Auto-collects environment info. Returns a pre-filled GitHub issue URL and formatted issue body. Ask the user to open the URL and paste the body to submit.

NameTypeReqDescription
descriptionstringyesWhat about ExecBro's tools went wrong or could be better: which tool, what you expected it to do, what it actually did. Do NOT describe the app feature or task you were debugging — only the tool's be…
titlestringyesShort summary of the ExecBro tooling issue (becomes the GitHub issue title)
typestringyesType, scoped to ExecBro tooling: "bug" = an ExecBro tool malfunctioned, "feature_request" = a missing ExecBro capability, "feedback" = general notes on using the ExecBro tools
workflow_contextstring–Which ExecBro tools were in use when the issue surfaced (e.g. "tap → get_screen_layout retry loop"). Name the tools and the debugging step — not the user's app goal.

No output schema declared.

No examples provided.

swipe ~961

Swipe gesture that auto-routes to the correct platform (iOS or Android), with pixel-diff verification. [PRIMARY INTERACTION TOOL — works on iOS and Android; prefer over ios_*/android_* siblings] PURPOSE: Single unified swipe entry point. Easiest form: swipe({ direction: "up" }) scrolls to reveal more content ("down"/"left"/"right" also work; bare swipe() defaults to "up"). Optional distance in screenshot pixels (default 33% of axis). For precise control, pass all four coordinates (startX/startY/endX/endY) — they take precedence over direction. WHEN TO USE: Scrolling lists, paging carousels, pull-to-refresh, dismissing sheets, opening drawers. Especially useful in virtualized lists (FlatList/SectionList) where off-screen items aren't mounted in the fiber tree. VERIFICATION: verify=true (default) returns `verification.meaningful`. When false, `warning` names the cause — already at top/end, not scrollable, wrong axis, no scroll view there, or (no RN connection) that it could not inspect the screen. burst=true catches transient feedback like overscroll bounce. SAFETY: Android direction swipes stay clear of the system bars; `foregroundLost` appears if the app left the foreground anyway. WORKFLOW: swipe({ direction: "up" }) -> read response.verification.meaningful. CHROMIUM: a direction is a mouse wheel (reports `scrolled`), four coordinates a drag. LIMITATIONS: iOS needs AXe (brew install cameroncooke/axe/axe) or IDB. Pass `device` to pick one (list_devices).

NameTypeReqDescription
burstboolean–Capture rapid sequential frames after the swipe to detect transient feedback (overscroll bounce, fling-then-snap-back) even when the final state is unchanged. Frames are stored in the image buffer; u…
deltanumber–iOS only — touch step size between events (driver-dependent default). Ignored on Android.
devicestring–Target device. Accepts (a) an iOS simulator UDID, (b) an Android adb serial like 'emulator-5554', (c) the iOS simulator or Android emulator/device name (substring match), or (d) a connected RN app's…
directionstring–Shorthand for a centered scroll gesture (content-scroll semantics): "up" reveals content below (finger moves bottom→top), "down" reveals content above, "left"/"right" page horizontally. A bare swipe(…
distancenumber–Travel length in screenshot pixels for the direction shorthand. Default: 33% of the relevant screen axis.
durationMsnumber–Swipe duration in milliseconds (default: 300 on Android; iOS uses driver default if omitted). On chromium: the drag duration, default 300; unused by the wheel.
endXnumber–Ending X coordinate in screenshot pixels (explicit-coordinate mode)
endYnumber–Ending Y coordinate in screenshot pixels (explicit-coordinate mode). On chromium the four coordinates are a real left-button drag (sliders, resizable panes, pointer-event drag libraries); native HTML…
screenshotboolean–Return the post-swipe image bytes in the response. Default true. Set to false to drop the PNG bytes — verification still runs (set verify=false to skip that too).
startXnumber–Starting X coordinate in screenshot pixels (explicit-coordinate mode)
startYnumber–Starting Y coordinate in screenshot pixels (explicit-coordinate mode)
verifyboolean–Compare before/after screenshots to detect whether the swipe produced a visual change. Set false to skip. When skipped, the response contains `verification: { skipped: true, skippedReason }` so calle…

No output schema declared.

No examples provided.

tap ~1,157

Tap a UI element. Automatically tries multiple strategies: fiber tree (React), accessibility tree (native), and OCR (visual). [PRIMARY INTERACTION TOOL — works on iOS and Android; prefer over ios_*/android_* siblings] PURPOSE: Single unified tap entry point — resolves text/testID/component/coordinates into a real touch event on the correct device. WHEN TO USE: Any time you need to press a button, focus an input, open a menu, or verify a handler fires. Prefer testID, then text, then component, then (x,y) from a screenshot's pressables list. WORKFLOW: screenshot -> tap(testID="...") | tap(text="...") | tap(x, y) -> screenshot again to verify. Use burst=true when meaningful=false but visual feedback looks transient. LIMITATIONS: iOS needs AXe (brew install cameroncooke/axe/axe) or IDB for accessibility/coordinate taps. Non-ASCII text skips fiber (Hermes); prefer testID. Pass `device` to target a specific simulator/emulator when multiple are available — call list_devices for the inventory. GOOD: tap({ testID: "login-btn" }); tap({ text: "Submit" }); tap({ x: 300, y: 600 }); tap({ x: 300, y: 600, native: true, device: "emulator-5554" }) LONG PRESS: tap({ testID: "row-3", duration: 800 }) holds the touch. CHROMIUM: testID matches data-testid/id; clicks via CDP. BAD: tap({ text: "" }) or tap({ x: 0, y: 0 }) — missing a target. tap({ text: "Submit" }) without first screenshotting an ambiguous screen. SOURCE: need the file:line that renders an element? inspect_at_point(x, y).

NameTypeReqDescription
burstboolean–Enable burst screenshot capture for enhanced verification. Captures 4 rapid screenshots (~150ms intervals) after the tap to detect transient visual feedback (press animations, highlights, ripples) th…
componentstring–Component name match (case-insensitive substring, e.g. 'Button', 'MenuItem').
devicestring–Target device. Accepts (a) an iOS simulator UDID, (b) an Android adb serial like 'emulator-5554', (c) the iOS simulator or Android emulator/device name (substring match), or (d) a connected RN app's…
durationnumber–Hold the touch for this many milliseconds instead of releasing it immediately — a long press. Use for context menus, drag starts, multi-select. React Native fires onLongPress at 500ms, so 800 is a sa…
indexnumber–Zero-based index when multiple elements match (default: 0).
maxTraversalDepthnumber–Max parent levels to traverse when searching by component name (default: 15). Increase if your component is deeply wrapped (e.g. inside multiple HOCs/animation wrappers).
nativeboolean–When true, tap coordinates directly via ADB/simctl without requiring a React Native connection. Useful for interacting with native UI, system dialogs, or non-RN apps. Requires x/y coordinates.
screenshotboolean–Return post-tap image bytes in the response. Default true. Set to false to drop the PNG bytes — verification still runs (set verify=false to skip that too). Combine with verify=true to get the meanin…
strategystring–"auto" (default) tries fiber -> accessibility -> OCR. Set explicitly to skip strategies you know will fail.
testIDstring–Exact match on the element's testID prop (on chromium: data-testid, data-test-id or id; text matches visible text, component a React component name, and the target is scrolled into view). Also resolv…
textstring–Visible text to match (case-insensitive substring). ASCII only for fiber strategy; OCR handles non-ASCII.
verifyboolean–Run before/after screenshot diff to detect if the tap had a meaningful visual effect. Default: true for coordinate/accessibility/ocr strategies, false for fiber. Independent of `screenshot` — verify…
xnumber–X coordinate in pixels (from screenshot). Must provide both x and y.
ynumber–Y coordinate in pixels (from screenshot). Must provide both x and y.

No output schema declared.

No examples provided.

vault_capture ~295

Read a credential out of the running app straight into the vault. The value is never returned to you. PURPOSE: Cover credentials no captured request revealed — a cold session before the app has made an authenticated call, a token the app refreshed in the background, or one held somewhere no heuristic finds (keychain, expo-secure-store, an Apollo link). WHEN TO USE: http_request says no credential is known for an origin, or list_secrets shows the entry as EXPIRED after a re-login. WORKFLOW: vault_capture({ expression: "SecureStore.getItemAsync('access_token')", origin: "https://api.acme.io" }) -> http_request({ ..., auth: { secret: "api.acme.io" } }). GOOD: vault_capture({ expression: "store.getState().auth.token", origin: "https://api.acme.io" }) BAD: reading the same value with execute_in_app — that returns it into the transcript, permanently.

NameTypeReqDescription
devicestring–Target device name (substring match). Omit for the default device.
expressionstringyesJS evaluated in the app; its result goes to the vault, not to you. An async expression is awaited.
originstringyesAbsolute URL of the API this credential authenticates, e.g. https://api.acme.io. Binds the credential: it can only be sent back to this host.

No output schema declared.

No examples provided.

Install

How do I install the ExecBro MCP server?

ExecBro runs locally as an npm package, launched with npx -y execbro. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

npm · execbro

# add to Claude Code
claude mcp add com-execbro-execbro -- npx -y execbro
Common questions

What is the ExecBro MCP server?

ExecBro is an MCP server listed in the public MCP registry as com.execbro/execbro. Gives AI agents eyes and hands into running React Native apps: logs, REPL, tap, screenshots. This page covers its npm package (execbro).

Is the ExecBro MCP server safe to use?

ExecBro scores 83 out of 100 on VerifyMCP. We recorded 1 known advisory against it as of 7 November 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

What tools does the ExecBro MCP server expose?

ExecBro exposes 68 tools: activate_license, delete_account, get_usage_guide, get_license_status, send_feedback, and 63 more. Their descriptions and schemas cost roughly 24,356 tokens of context every time the server is loaded.

Is the ExecBro MCP server still maintained?

ExecBro is still listed as active in the MCP registry. We last reached this channel on 7 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

What licence is the ExecBro MCP server under?

ExecBro declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

What did VerifyMCP test on the ExecBro MCP server?

VerifyMCP ran 15 checks on ExecBro, covering Malware scan, Known CVEs, Install scripts, Dependency health and 11 more. One item could not be completed, a gap on our side rather than a finding about ExecBro: Security disclosure. Endpoint Security and Transport & Reachability do not apply to an npm package, so they were not tested. The score does not measure everything, and the scoring documentation lists what it leaves out.