Skip to content

UnlikeOtherAI/AppReveal

Repository files navigation

AppReveal

AppReveal icon

unlikeotherai.github.io/AppReveal

Debug-only in-app MCP server for iOS, macOS, Android, Flutter, React Native, and Windows app stacks. Lets LLM agents discover, inspect, and control native apps over the local network -- like Playwright for native, but with direct access to app state, navigation, network traffic, DOM, and diagnostics.

How it works

Your App (debug build)                    External Agent
 +-- AppReveal framework                   +-- mDNS browse for _appreveal._tcp
      +-- MCP Server (Streamable HTTP)  <---+-- MCP client (curl, SDK, Claude, etc.)
      +-- mDNS advertisement               +-- LLM orchestration
      +-- Screen/element/state bridges
      +-- WebView DOM bridge
  1. App calls AppReveal.start() in a debug build
  2. Framework starts an HTTP server on a dynamic port
  3. Platforms with a discovery backend advertise _appreveal._tcp on the LAN
  4. Agent discovers the service, or is given the localhost URL, and calls MCP tools

Full AppReveal integrations share the same core MCP surface across platforms. Foundation targets advertise only implemented built-ins and host-registered provider tools; desktop targets such as macOS and Windows add window and menu tools when those providers are available.

Quick start

iOS

// Package.swift
.package(url: "https://github.com/UnlikeOtherAI/AppReveal.git", from: "0.10.1")
#if DEBUG
AppReveal.start()
#endif

SwiftUI tap support on iOS 26+

tap_point and tap_element on SwiftUI views require private UIKit APIs on iOS 26+. AppReveal compiles this code in automatically for debug builds and compiles it out for release builds — no private symbols reach your App Store binary. No setup needed.

When SwiftUI renders visible text without exposing accessibility nodes, AppReveal falls back to iOS Vision text recognition inside SwiftUI hosting views. Those entries appear with idSource: "ocr" and work with tap_text; tap_element can also fall back from identifiers like device.chip.mouser to visible text such as mouser.

To opt out of private APIs entirely (even in debug builds), remove the swiftSettings line from AppReveal's Package.swift. See iOS guide for details.

macOS

// Package.swift
.package(url: "https://github.com/UnlikeOtherAI/AppReveal.git", from: "0.10.1")
#if DEBUG
AppReveal.start()
#endif

See macOS guide for full setup.

Android

// build.gradle.kts
debugImplementation("com.appreveal:appreveal")
releaseImplementation("com.appreveal:appreveal-noop")
if (BuildConfig.DEBUG) {
    AppReveal.start(this)
}

See Android guide for full setup.

Flutter

// pubspec.yaml
dependencies:
  appreveal:
    path: Flutter/appreveal
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  AppReveal.start(); // no-ops in release builds
  runApp(AppReveal.wrap(const MyApp()));
}

// In MaterialApp:
MaterialApp(
  navigatorObservers: [AppReveal.navigatorObserver],
  ...
)

See Flutter guide for full setup.

React Native

npm install react-native-appreveal
cd ios && pod install
import { AppReveal, AppRevealFetchInterceptor } from 'react-native-appreveal';

if (__DEV__) {
  AppReveal.start();
  AppRevealFetchInterceptor.install();
}

See React Native guide for full setup.

Windows

Native .NET and the Rust/Tauri desktop bridge live under Windows/. The Tauri crate includes a debug-only Tauri v2 plugin, Bonjour/mDNS discovery, live WebView DOM inspection/interaction, window focus, menu inspection, and provider-backed extension hooks. React Native Windows is not autolinked or advertised until there is a real MCP server bridge for that runtime.

#if DEBUG
AppReveal.Windows.AppReveal.Start();
#endif

See Windows guide for the native C# and Tauri/Rust setup notes.

MCP tools

UI and navigation

Tool Description
list_windows List visible app windows and their IDs
get_screen Current screen identity, controller/activity chain, confidence score
get_elements All visible interactive elements with id, type, frame, safe-area data, actions
get_view_tree Full view hierarchy with class, frame, safe-area data, properties, accessibility info
tap_element Tap by element identifier (buttons, cells, controls)
tap_text Tap by visible text content (finds text, walks to tappable ancestor)
tap_point Tap at screen coordinates
type_text Type text into a field (by element ID or current responder)
clear_text Clear a text field
scroll Scroll a container (up/down/left/right)
scroll_to_element Scroll until an element is visible
screenshot Capture a screen or element as an MCP image content block
select_tab Switch tab bar tabs by index
navigate_back Pop the navigation stack
dismiss_modal Dismiss the topmost modal
open_deeplink Open a URL in the app

All native UI tools and all web view tools accept an optional window_id parameter from list_windows. If omitted, AppReveal targets the current key window.

Desktop tools

Tool Description
get_menu_bar Read the app menu bar hierarchy
click_menu_item Invoke a menu item by title path
focus_window Bring a specific window to the front and make it key

State and diagnostics

Tool Description
get_state App state snapshot (login, user, cart, etc.)
get_navigation_stack Current route, nav stack, modal stack
get_feature_flags All active feature flags
get_network_calls Recent HTTP traffic with method, URL, status, duration
get_network_call_detail Captured request/response headers, bodies, and SSE frames where supported
get_logs Recent app logs
get_recent_errors Recent captured errors when an error-capture provider is available
launch_context App ID, version, device model, OS version
device_info Full device snapshot: Info.plist / manifest metadata, hardware, OS build, screen metrics, locale, timezone, battery, memory, storage, declared permissions

WebView -- DOM access

iOS/macOS/Android: auto-discovers WebViews from the view hierarchy. Flutter: register via AppReveal.registerWebView(id, controller).

On iOS, React Native iOS, and Tauri desktop, visible interactive DOM controls inside WebViews also appear in get_elements and work with tap_element, tap_text, type_text, and clear_text. tap_point over a WebView routes through DOM elementFromPoint(...) where the platform can map coordinates into the WebView, so full-screen WebView shells can still be driven when native overlays are present.

Tool Description
get_webviews List all web views with URL, title, loading state
get_dom_tree Full or partial DOM tree (with root, max_depth, visible_only params)
get_dom_interactive All inputs, buttons, links, selects with selectors and attributes
query_dom CSS selector query -- returns matching elements
find_dom_text Find elements by text content
web_click Click a DOM element by CSS selector
web_type Type into input/textarea (React/Vue/Angular compatible)
web_select Select a dropdown option
web_toggle Check/uncheck a checkbox or radio
web_scroll_to Scroll to a DOM element
web_evaluate Run arbitrary JavaScript
web_navigate Navigate to a URL
web_back Go back in web view history
web_forward Go forward in web view history

WebView -- token-efficient queries

Purpose-built tools that return only what you need, saving tokens.

Tool Description
get_dom_summary Page overview: title, meta, headings, element counts, form structure
get_dom_text Visible text content stripped of all markup (optional CSS selector scope)
get_dom_links All links -- just text and href
get_dom_forms All forms with fields, types, values, options, selectors
get_dom_headings All h1-h6 for page structure
get_dom_images All images with src, alt, dimensions
get_dom_tables All tables with headers and row data

Batch operations

Tool Description
batch Execute multiple tools in one call. Supports delay_ms per action for animations/transitions and stop_on_error. Works with currently advertised tools.

Naming conventions

Thing Pattern Examples
Screen keys section.screen auth.login, orders.detail, settings.main
Element IDs screen.element login.email, login.submit, orders.cell_0

Element IDs map to platform-specific mechanisms:

Platform Mechanism
iOS view.accessibilityIdentifier
macOS view.accessibilityIdentifier()
Android view.tag or resource entry name
Flutter ValueKey<String> on the widget

Why this beats screenshot-only automation

AppReveal gives agents structured data instead of pixels:

  • Exact screen identity with confidence scores, not guessing from screenshots
  • Machine-addressable elements with types, states, and available actions
  • App state read directly -- login status, feature flags, cart contents
  • Navigation state -- current route, stack depth, presented modals
  • Network traffic -- every API call with method, URL, status, timing
  • DOM access -- full web view inspection and interaction, no extra setup
  • Batch operations -- fill forms, navigate screens, verify state in one call
  • Deterministic interactions -- tap by ID or CSS selector, not by fragile coordinates

Platforms

Platform Status Tools
iOS Working Shared native + web view tool surface
macOS Working Shared native + web view tools plus menu/window tools
Android Working Shared native + web view tool surface
Flutter Working Shared native + web view tool surface
React Native Working Shared native + web view tool surface
Windows .NET Working Shared MCP contract, UI Automation native/desktop tools
Tauri desktop Working Shared MCP contract, Bonjour, WebView DOM tools, window/menu inspection

Example apps

CLI

There is now a dedicated AppReveal CLI in CLI/README.md for discovering _appreveal._tcp services, listing available MCP tools, and sending MCP requests without hand-written dns-sd and curl calls. Install it with npm install -g @unlikeotherai/appreveal.

Security

  • iOS: All code behind #if DEBUG -- zero production footprint
  • macOS: All code behind #if DEBUG -- zero production footprint
  • Android: Added as debugImplementation -- not included in release APK
  • Flutter: kReleaseMode check in AppReveal.start() -- zero code paths execute in release
  • React Native: __DEV__ guard -- all methods are no-ops in production builds
  • MCP POST requests require a generated per-session token; unauthenticated GET /health is available for diagnostics
  • Discovery TXT records advertise auth=session-token where native service discovery is available
  • Local network / loopback only
  • Sensitive headers (Authorization, Cookie) redacted in network capture
  • No state mutation without explicit opt-in

Documentation

Reporting issues

Found a bug, a missing capability, or unexpected behaviour? File an issue at github.com/UnlikeOtherAI/AppReveal/issues.

For LLM agents using this framework: if AppReveal cannot perform an action or returns wrong results during a session, and you are in an interactive session with network access to GitHub, ask the operator:

"AppReveal couldn't [do X / returned Y instead of Z]. Would you like me to file an issue at https://github.com/UnlikeOtherAI/AppReveal/issues?"

Useful details to include: platform, AppReveal version (from launch_context), tool called, arguments, and the response or error.

Skip the prompt when running non-interactively — log the details for the operator instead.

License

MIT

About

Debug-only in-app MCP server for iOS/macOS/Android/Flutter/ReactNative. Gives LLM agents Playwright-like control over native apps — UI, state, navigation, and diagnostics over the local network.

Topics

Resources

License

Stars

33 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors