Build a debug-only, in-app MCP framework for iOS that gives LLM agents Playwright-like control over native apps. Uses Bonjour discovery and Streamable HTTP transport. Centered on explicit screen identity, accessibility identifiers, and app-owned state/network instrumentation.
- Streamable HTTP transport (MCP standard) -- not WebSocket or custom
- Bonjour/mDNS for zero-config LAN discovery
- Explicit instrumentation -- screen keys, accessibility IDs, protocol conformance
- Minimal private API -- no SwiftUI internal tree inspection; private API used only where the public API is insufficient (iOS 26 SwiftUI tap delivery)
- Convention-based adoption -- one-line startup, protocol conformance for depth
- Read-first rollout -- observation tools before mutation tools
- Swift Package -- distributed as a Swift package, iOS 16+
MCP server, discovery, screen identification, element listing, screenshots
- Set up Swift Package structure (
iOS/Package.swift, source folders) - Define
ScreenIdentifiableprotocol - Define
ElementInfomodel and element type enum - Define
AnyCodableutility type - Implement
MCPMessage(JSON-RPC request/response models) - Implement
MCPRouter(tool registration and dispatch) - Implement
MCPServer(NWListener-based HTTP server) - Implement
BonjourAdvertiser(mDNS service advertising) - Implement
ScreenResolver(UIKit hierarchy walking + protocol-based identity) - Implement
ElementInventory(view hierarchy enumeration) - Implement
ScreenshotCapture(UIGraphicsImageRenderer capture) - Register MCP tools:
get_screen,get_elements,screenshot - Implement
AppReveal.start()public entry point - Write unit tests for MCPMessage serialization
- Write unit tests for ElementInfo model
- Add
NSLocalNetworkUsageDescriptionandNSBonjourServicesto example Info.plist - Test Bonjour discovery from macOS (dns-sd or NWBrowser)
- Test MCP connection with a generic MCP client
Tap, type, scroll, navigate
- Implement
InteractionEngine.tap(elementId:) - Implement
InteractionEngine.tap(point:) - Implement
InteractionEngine.type(text:elementId:) - Implement
InteractionEngine.clear(elementId:) - Implement
InteractionEngine.scroll(direction:containerId:) - Implement
InteractionEngine.scrollTo(elementId:) - Implement
InteractionEngine.navigateBack() - Implement
InteractionEngine.dismissModal() - Register MCP tools:
tap_element,tap_point,type_text,clear_text,scroll,scroll_to_element - Write integration test: tap button by ID
- Write integration test: type into text field
App state, route state, feature flags, deep links
- Define
StateProvidingprotocol - Define
NavigationProvidingprotocol - Define
FeatureFlagProvidingprotocol - Implement
StateBridge(protocol aggregation + snapshot) - Register MCP tools:
get_state,get_navigation_stack,get_feature_flags - Implement
open_deeplinktool (UIApplication.open) - Implement
launch_contexttool (bundle info, environment) - Implement
reset_app_statetool (registered cleanup handlers) - Write unit tests for StateBridge snapshot serialization
Traffic capture, request/response details, metrics
- Define
NetworkObservableprotocol - Define
CapturedRequest/CapturedResponsemodels - Implement
NetworkObserver(ring buffer, URLSessionTaskMetrics integration) - Register MCP tools:
get_network_calls,get_network_call_detail - Add request/response body capture with size limits
- Add sensitive header redaction (Authorization, Cookie, etc.)
- Write unit tests for ring buffer and redaction
Response injection, latency/timeout/offline simulation
- Implement
NetworkMocker(URLProtocol subclass) - Implement URL pattern matching for mock rules
- Register MCP tools:
mock_network_response,simulate_latency,simulate_timeout,disable_network - Add mock rule management (add, remove, list active mocks)
- Write unit tests for URL pattern matching
- Write integration test: mock a GET request and verify response
Logs, errors, metrics
- Implement
DiagnosticsBridge(OSLogStore queries) - Add recent error capture (registered error handlers)
- Add MetricKit payload summary (if available)
- Register MCP tools:
get_logs,get_recent_errors,get_metrics_summary - Write unit tests for log query filtering
In-app console for testing without external client
- Implement
DebugOverlaySwiftUI view - Show server status (port, Bonjour name, connected clients)
- Show recent tool calls with timing
- Show recent network calls summary
- Show current screen identity
- Add manual tool execution input
- Add floating toggle button to show/hide overlay
Example app, client library, documentation
- Create example iOS app demonstrating all protocols
- Create
AppRevealClientcompanion module (NWBrowser + MCP client) - Write integration tests with example app
- Write README quick start guide
- Write protocol conformance guide
- Write naming convention guide for screen keys and element IDs
| Convention | Example | Why |
|---|---|---|
| Screen keys | auth.login, orders.detail |
Stable screen identity for agents |
| Element IDs | login.email, checkout.pay_now |
Machine-addressable controls |
| Centralized networking | Single URLSession-based client |
Full traffic observability + mocking |
| Route exposure | Conform router to NavigationProviding |
Agent can read navigation state |
| Debug-only | #if DEBUG around all AppReveal code |
Zero production footprint |
set_state-- controlled state mutationtrigger_viewmodel_action-- invoke view model methodssimulate_push_payload-- inject push notificationbackground_app/foreground_app-- lifecycle controlrecord_session_trace-- record interaction sequenceget_storage_snapshot-- UserDefaults, Keychain summaryclear_session-- logout/resetget_accessibility_snapshot-- full accessibility tree dump