Skip to main content

Mobile Device Testing

Test your web application on real mobile devices during development using agnt's tunnel integration with Cloudflare or ngrok, plus optional BrowserStack automation.

The Challenge

Testing on real mobile devices during development typically requires:

  • Complex network configuration or device proxies
  • Manual URL sharing and constant re-typing
  • No visibility into mobile-specific errors
  • Separate tools for automation

agnt solves this by providing integrated tunneling with full proxy instrumentation:

  • One-command tunnel setup with automatic URL discovery
  • All proxy features work through the tunnel (error capture, screenshots, interactions)
  • Optional BrowserStack MCP integration for automated device testing

Quick Start

1. Start Your Dev Server

run {script_name: "dev"}

Wait for it to be ready:

proc {action: "output", process_id: "dev", grep: "ready", tail: 5}

2. Start the Instrumented Proxy

proxy {
action: "start",
id: "app",
target_url: "http://localhost:3000",
bind_address: "0.0.0.0"
}

Note the listen_addr from the response (e.g., 0.0.0.0:45849).

Security

Using bind_address: "0.0.0.0" exposes the proxy on all network interfaces. Only use this when you need external access (tunnels, mobile testing on local network).

3. Start the Tunnel

tunnel {
action: "start",
id: "app",
provider: "cloudflare",
local_port: 45849,
proxy_id: "app"
}

The response includes your public URL:

{
"id": "app",
"provider": "cloudflare",
"state": "connected",
"public_url": "https://random-words.trycloudflare.com",
"local_addr": "localhost:45849"
}

4. Test on Your Device

Open the public_url on your mobile device. You now have:

  • Full proxy instrumentation - The window.__devtool API works on mobile
  • Error capture - Mobile-specific JavaScript errors are logged
  • Floating indicator - Send messages from your phone to the AI agent
  • Screenshot capability - Capture mobile layouts

Checking Mobile Errors

After testing on your device:

// Check for JavaScript errors
proxylog {proxy_id: "app", types: ["error"]}

// See all HTTP traffic
proxylog {proxy_id: "app", types: ["http"], limit: 20}

// Check page sessions
currentpage {proxy_id: "app"}

BrowserStack Integration

For automated testing across many devices, combine agnt with BrowserStack's MCP server.

Setup

Add both MCP servers to your configuration:

claude_desktop_config.json
{
"mcpServers": {
"agnt": {
"command": "agnt",
"args": ["mcp"]
},
"browserstack": {
"command": "npx",
"args": ["@anthropic-ai/browserstack-mcp"],
"env": {
"BROWSERSTACK_USERNAME": "your_username",
"BROWSERSTACK_ACCESS_KEY": "your_key",
"BROWSERSTACK_LOCAL": "true"
}
}
}
}

Workflow

  1. Start your tunneled proxy (as shown above)
  2. Use BrowserStack MCP to launch tests on real devices pointing to your tunnel URL
  3. Capture results through both tools:
    • agnt: Error logs, HTTP traffic, page sessions
    • BrowserStack: Screenshots, video recordings, device logs

What Each Tool Provides

agnt CapturesBrowserStack Provides
JavaScript errors with stack tracesScreenshots on demand
All HTTP request/response dataVideo recordings of sessions
Page load performance metricsNative device logs (iOS/Android)
User interaction historyAutomated test execution
DOM mutations and changesCross-browser/device matrix
Custom diagnostic dataReal device hardware

Example: Automated Mobile Testing

// 1. Start dev server and instrumented proxy
run {script_name: "dev"}
proxy {action: "start", id: "mobile-qa", target_url: "http://localhost:3000", bind_address: "0.0.0.0"}

// 2. Start tunnel
tunnel {action: "start", id: "mobile-qa", provider: "cloudflare", local_port: 45849, proxy_id: "mobile-qa"}
// Response: {public_url: "https://abc-xyz.trycloudflare.com"}

// 3. BrowserStack runs tests on the tunnel URL across devices:
// - iPhone 15 Pro (iOS 17)
// - Samsung Galaxy S24 (Android 14)
// - iPad Pro (iPadOS 17)

// 4. After tests complete, analyze captured data:
proxylog {proxy_id: "mobile-qa", types: ["error"]}
// Shows any JS errors that occurred on any device

proxylog {proxy_id: "mobile-qa", types: ["performance"]}
// Shows load times across all devices

currentpage {proxy_id: "mobile-qa"}
// Shows page sessions from each device

Tunnel Providers

Best for: Quick testing, no signup required.

Pros:

  • Free, no account required
  • Fast and reliable
  • HTTPS by default
  • No bandwidth limits

Requirements:

# Install cloudflared
brew install cloudflare/cloudflare/cloudflared # macOS

Usage:

tunnel {action: "start", id: "app", provider: "cloudflare", local_port: 8080}

ngrok

Best for: Stable URLs, webhook testing.

Pros:

  • Stable URLs (with paid plan)
  • Request inspection dashboard
  • Webhooks and integrations

Requirements:

# Install and configure
brew install ngrok/ngrok/ngrok # macOS
ngrok config add-authtoken <your-token>

Usage:

tunnel {action: "start", id: "app", provider: "ngrok", local_port: 8080}

Tailscale Funnel

Best for: Teams using Tailscale, persistent URLs, maximum privacy.

Pros:

  • Persistent, memorable URLs (based on machine name)
  • End-to-end encrypted
  • No third-party account if you use Tailscale
  • Works with Tailscale ACLs

Requirements:

  • Tailscale installed and authenticated
  • Funnel enabled on your tailnet (admin approval required)

Usage (manual setup):

# 1. Start agnt proxy on all interfaces
proxy {action: "start", id: "app", target_url: "http://localhost:3000", bind_address: "0.0.0.0"}
# Note the port (e.g., 45849)

# 2. Start Tailscale Funnel in terminal
tailscale funnel 45849
# Output: https://your-machine.tailnet-name.ts.net

# 3. Update proxy with public URL
proxy {action: "start", id: "app", target_url: "http://localhost:3000", bind_address: "0.0.0.0", public_url: "https://your-machine.tailnet-name.ts.net"}

Debugging Device-Specific Issues

Mobile-Specific Diagnostics

When testing on mobile, use these diagnostics to catch common issues:

// Check for touch target issues (buttons too small for fingers)
proxy {action: "exec", id: "app", code: "window.__devtool.findSmallTouchTargets && window.__devtool.findSmallTouchTargets()"}

// Check responsive layout issues
proxy {action: "exec", id: "app", code: "window.__devtool.checkResponsiveRisk()"}

// Check text readability (truncation, overflow)
proxy {action: "exec", id: "app", code: "window.__devtool.checkTextFragility()"}

// Audit accessibility (critical for mobile screen readers)
proxy {action: "exec", id: "app", code: "window.__devtool.auditAccessibility()"}

Capturing Device Context

When a user reports an issue on a specific device:

// Capture complete device state
proxy {action: "exec", id: "app", code: "window.__devtool.captureState()"}
// Returns: viewport, URL, localStorage, sessionStorage, cookies, user agent

// Check what the user just interacted with
proxy {action: "exec", id: "app", code: "window.__devtool.interactions.getLastClickContext()"}
// Returns: element, position, surrounding context, mouse trail

// Check recent DOM changes
proxy {action: "exec", id: "app", code: "window.__devtool.mutations.getHistory()"}

Common Mobile Issues to Check

IssueDiagnostic Command
Buttons too smallfindSmallTouchTargets()
Text gets cut offcheckTextFragility()
Layout breaks on rotationcheckResponsiveRisk()
Forms hard to useauditAccessibility()
Slow page loadsproxylog {types: ["performance"]}
JavaScript errorsproxylog {types: ["error"]}

Troubleshooting

Tunnel Not Starting

Check if the binary is installed:

which cloudflared  # or: which ngrok

Verify the port is correct:

proxy {action: "status", id: "app"}
// Check the listen_addr in the response

Mobile Device Can't Connect

  1. Verify the tunnel is running:

    tunnel {action: "status", id: "app"}
  2. Check the public URL is accessible from another device

  3. Ensure your dev server is running and the proxy can reach it

No Errors Being Captured

The floating indicator and __devtool API should work through the tunnel. If not:

  1. Check if JavaScript is loading (view page source)
  2. Verify the proxy is correctly forwarding to your dev server
  3. Check browser console for any CORS or security errors

Best Practices

Use Separate Tunnels for Frontend and API

// Frontend
proxy {action: "start", id: "frontend", target_url: "http://localhost:3000", bind_address: "0.0.0.0"}
tunnel {action: "start", id: "frontend", provider: "cloudflare", local_port: 45849, proxy_id: "frontend"}

// API
proxy {action: "start", id: "api", target_url: "http://localhost:4000", bind_address: "0.0.0.0"}
tunnel {action: "start", id: "api", provider: "cloudflare", local_port: 45850, proxy_id: "api"}

Clean Up After Testing

tunnel {action: "stop", id: "app"}
proxy {action: "stop", id: "app"}

Monitor Tunnel Health

tunnel {action: "list"}

Check for state: "connected" on all tunnels.

Complete Example Session

// 1. Start dev server
run {script_name: "dev", mode: "background"}

// 2. Wait for ready
proc {action: "output", process_id: "dev", grep: "ready", tail: 5}

// 3. Start proxy on all interfaces
proxy {
action: "start",
id: "mobile-test",
target_url: "http://localhost:3000",
bind_address: "0.0.0.0"
}
// Response: {listen_addr: "0.0.0.0:45849"}

// 4. Start Cloudflare tunnel with proxy integration
tunnel {
action: "start",
id: "mobile-test",
provider: "cloudflare",
local_port: 45849,
proxy_id: "mobile-test"
}
// Response: {public_url: "https://random-words.trycloudflare.com"}

// 5. Test on mobile device using the public_url...

// 6. Check for errors
proxylog {proxy_id: "mobile-test", types: ["error"]}

// 7. View page sessions
currentpage {proxy_id: "mobile-test"}

// 8. Clean up
tunnel {action: "stop", id: "mobile-test"}
proxy {action: "stop", id: "mobile-test"}
proc {action: "stop", process_id: "dev"}

See Also