mobilerun-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| BRAVE_API_KEY | No | web_search uses Brave when set, DuckDuckGo otherwise. | |
| MOBILERUN_BIN | No | Override path to the mobilerun binary. | |
| TAVILY_API_KEY | No | web_search uses Tavily (synthesized answer) when set. | |
| MOBILERUN_DEVICE | No | Default device (adb serial, ios, cloud:<id>, or a Portal URL). Optional when exactly one device is attached to adb. | |
| MOBILERUN_ADB_BIN | No | Override path to the adb binary. | |
| MOBILERUN_MCP_POLICY | No | Safety policy: off, standard, strict. | off |
| MOBILERUN_MCP_SCOPES | No | Set to read to expose only read-only tools. | read,write |
| MOBILERUN_CREDENTIALS | No | Secrets file for type_secret (mobilerun format). | config/credentials.yaml |
| MOBILERUN_CLOUD_API_KEY | No | Mobilerun Cloud devices, tasks and the cloud platform tools. | |
| MOBILERUN_MCP_HTTP_HOST | No | Address for --http. | 127.0.0.1 |
| MOBILERUN_MCP_HTTP_PORT | No | Port for --http. | 4816 |
| MOBILERUN_DETECTOR_MODEL | No | Path to an OmniParser icon-detect .onnx file. | |
| MOBILERUN_IOS_PORTAL_URL | No | iOS portal URL for device="ios". | http://127.0.0.1:6643 |
| MOBILERUN_MCP_ENABLE_ADB | No | Set to 1 to expose the raw adb tool. | 0 |
| MOBILERUN_IOS_PORTAL_TOKEN | No | iOS portal token for device="ios". | |
| MOBILERUN_ANDROID_PORTAL_TOKEN | No | Bearer token for Portal-HTTP-only Android targets. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| logging | {} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| perceive_screenB | LOOK at the screen: an annotated screenshot plus every element and its tap point.
|
| read_screenA | Read the screen now (waits for it to stop moving first): the screen drawn as a character grid, each element a box with its som_id and label, then a table of what can be acted on: som (tap by this), in (som_id of the smallest box containing it), flg, label (only when it did not fit on the grid). Flags: * tappable, e text input (type_text, not tap), S scrollable, c toggle ON, o toggle OFF, l long-pressable, d disabled, - nothing declared (usually still tappable). N+k = som_id N plus k more elements with exactly those bounds; tap N. Header IDLE/BUSY: BUSY means it was still moving when the wait expired. Use perceive_screen instead for how something looks, when this ends with ESCALATE, or when what you need is missing. |
| get_ui_treeC | Compact accessibility tree (class, id, label, flags C/L/E/S/K/P, bounds). |
| get_screenshotC | Plain screenshot as an image. |
| screenshotC | Plain screenshot. hide_overlay hides the Portal's element overlay first. |
| screenshot_pathC | Take a screenshot, save it as a PNG file and return the path. |
| tapC | Tap at (x, y) or at the center of a numbered mark (som_id from perceive_screen / read_screen). stealth=true uses mobilerun-core's humanized tap. |
| double_tapC | Double-tap at (x, y) or a mark. |
| long_pressB | Press and hold at (x, y), a mark (som_id) or a get_state element (index). Hold time: duration_ms or ms (default 1000). |
| long_press_atC | Long press at (x, y) (mobilerun agent action). |
| swipeB | Swipe from (x1, y1) to (x2, y2) over duration_ms / ms (default 300). mobilerun agent form: coordinate=[x, y], coordinate2=[x, y], duration in seconds. |
| scroll_downB | Scroll the content down (reveal what is below): a centered swipe over half the screen (amount), or inside a scrollable mark (som_id). |
| scroll_upC | Scroll the content up (reveal what is above). |
| scroll_leftC | Scroll the content left (reveal what is to the left). |
| scroll_rightB | Scroll the content right (reveal what is to the right). |
| scrollC | Scroll the content in direction (up | down | left | right) by distance (fraction of the screen). verify=true reports whether the screen actually moved (mobilerun-core). |
| scroll_toA | Two modes. With x1, y1, x2, y2: drag the content from one point to the other (precise scroll). With text: scroll in direction until an element containing text is visible and return its mark. |
| type_textA | Type into the focused field (tap it first, or pass som_id). submit presses Enter after. |
| typeC | Type text (mobilerun). index taps that get_state element first; stealth=true types key by key like a person (mobilerun-core), at wpm words per minute. |
| press_homeC | Press the Home button. |
| press_backB | Press Back (also closes the keyboard without leaving the screen). |
| press_enterA | Press Enter (submits search bars and forms). |
| open_recent_appsC | Open the recent-apps overview. |
| keyC | Press a key by mobilerun-core name (back, home, menu, enter, delete, escape, tab, space, search, page_up, page_down, volume_up, volume_down, wakeup, media_play_pause, ...) or by Android keycode number. |
| launch_appA | Open an app by name (fuzzy) or exact package_name. An ambiguous name returns ranked candidates instead of guessing. If the app is already in the foreground it is left as is (already_foreground=true) unless force=true. |
| start_appC | Start an app by id (Android package / iOS bundle id), optionally a specific activity. |
| lookup_appC | Search installed apps by name or package; returns ranked candidates with scores. |
| list_appsA | List installed apps (user apps only unless include_system_apps=true). include_protected_apps is honoured on Mobilerun Cloud devices only (as in mobilerun-core). |
| list_app_deeplinksB | Deep links into an app, best first. source: shortcut (launcher shortcut, app-shortcut://pkg/id, open with open_deeplink) > resolved (curated and verified on this device) > catalog (curated) > discovered (declared by the app's intent filters). |
| resolve_deeplinkC | Which app would open this URI (or intent action such as android.settings.WIFI_SETTINGS)? handler_kind: app | browser_only | none. |
| open_deeplinkA | Jump straight to a screen via a URI, an app-shortcut://pkg/id from list_app_deeplinks, or an intent action. package_name / app_name pin the target app. intent:, file:, content: and javascript: URIs are refused. |
| get_device_statusB | Battery, screen power, foreground app, size, storage, network addresses, volume. |
| list_devicesA | Devices you can control. scope: local (adb devices) | cloud (Mobilerun Cloud, needs MOBILERUN_CLOUD_API_KEY) | all. Cloud filters: state (creating, assigned, ready, terminated, ...), type, name, country, page, pageSize (or a filters dict). Any listed id works as the device argument of every tool. |
| ping_deviceC | Is the device reachable? For adb devices, reports the Portal transport (http or content_provider). |
| connect_deviceB | (Re)connect adb and the Portal for a device; use after the network path came back. |
| disconnect_deviceB | Disconnect a TCP/IP adb device (adb disconnect host:port) and drop its session. |
| setup_portalC | Install and enable the Mobilerun Portal on the device (mobilerun setup); path installs a specific Portal APK. |
| doctorC | Health check of adb, the Portal and the device (mobilerun doctor). |
| request_screen_capture_permissionA | Compatibility no-op: screenshots use the Portal / adb screencap, no prompt is needed. |
| echoA | Returns text verbatim: a check that the MCP transport is alive (no device access). |
| pressA | Press home, back or enter (kept for old clients; prefer press_home/back/enter). |
| read_notificationsA | Current status-bar notifications, newest first, without touching the screen: key, app, title, text, action labels. Ongoing ones (music, navigation, downloads) only with include_ongoing=true; package_name filters to one app; limit default 20, cap 30. With the safety policy on, banking and authenticator notifications are withheld. |
| dismiss_notificationC | Dismiss one notification (by key, or package/title) or every clearable one. |
| notification_actionB | Tap one of a notification's own buttons (reply, archive, stop...); reply_text fills an inline reply field and sends it. Best-effort: it drives the notification shade. |
| get_media_sessionsC | Active media sessions (app, playback state, title/artist) and the music volume. |
| media_controlB | Control playback in any app without touching the screen: play, pause, play_pause, next, previous, stop, rewind, fast_forward. Goes to the active media session; package_name (from get_media_sessions) is checked against it and reported. |
| volume_upC | Raise the music volume by |
| volume_downC | Lower the music volume by |
| muteB | Toggle mute on the media stream (muted=true/false forces a state). Muting remembers the previous level for unmute. |
| find_filesA | Search the device's media index by name, newest first: images, videos, audio and documents (downloads included). kind: image | video | audio | document | any. Returns content:// URIs for open_file (limit default 10, cap 25). With path, searches that shared-storage folder by file name instead (any file type). |
| open_fileB | Open a file in its default viewer. uri: a content://media/... URI from find_files (never build one by hand); path: a file under shared storage. |
| system_intentA | One-call Android actions (action = the verb; verb= is accepted too). Verbs: set_alarm(hour, minute, label), set_timer(seconds, label), dial(phone_number), compose_sms(phone_number, body), add_calendar_event(title, start, end, location, notes; ISO datetimes), share_text(text, subject), navigate( destination, mode drive|walk|bike|transit). dial/compose_sms only prefill; the user sends. |
| resolve_contactA | Find contacts by (partial) name and return their phone numbers. |
| wait_forA | LONG waits only (downloads, uploads, processing, status changes); gestures already settle. With text / package / activity: wait until that is on screen (gone=true: until it disappears). Otherwise wait until loading finishes (no progress bar or "Loading" text) and the screen is still; condition is echoed back, you judge the returned state. Timeouts: timeout_ms (default 5000, max 30000) / poll_interval_ms (default 500, min 100), or timeout / interval in seconds. |
| watch_device_eventsA | Collect device events for up to timeout_seconds (default 10, max 30), returning early once max_events (default 50) arrive: foreground app, keyboard, screen content, notifications posted/removed. kinds filters: foreground, keyboard, screen, notifications. |
| validate_actionB | Pre-check a planned action against the safety policy (and, for our action set, that its target exists) without doing it. gesture_type is the action (tap, type_text, launch_app, open_deeplink, ...); target is the app name/package, deep-link URI or text you plan to use. Returns allowed=false with a category for blocked apps and text. |
| verify_actionC | Check an outcome against the live screen. kind: text (visible), gone (not visible), app (foreground package or name), activity, changed (the last action changed the screen). |
| web_searchA | Search the web for how to do something in an app ('how to in android'). Returns ranked sources (title, url, snippet) and, with TAVILY_API_KEY set, a synthesized answer. max_results 1-10 (default 5); topic general | news. The screen overrules results. |
| set_planC | Start a plan checklist. target_count > 0 means 'N items must be recorded' before end_session(success) is allowed. With 3+ steps and a search_query, the first web search rides along in the reply. |
| mark_stepA | Update a plan step: pending | in_progress | done | skipped | failed. Put facts you read off the screen in note; the pixels are gone next turn. |
| record_findingB | Record one item you found. |
| end_sessionA | Mark the end of the task (the server keeps listening; the next call starts fresh). reason: short summary of what was done. outcome: success (goal state verified) | partial | failure. goal_type: play_media | send_message | send_email | purchase | post | open_app | search | navigate | other. For send_message, send_email, purchase and post, success is refused unless you looked at the screen (read_screen / perceive_screen) after your last action. Success is also refused while fewer findings than the plan's target_count are recorded. failure is never refused. |
| get_usage_guideB | How to use this server well. Topics: overview, shortcuts, text_entry, failures, ledger, safety, stop, efficiency, full. |
| uiC | Raw UI snapshot (a11y_tree, phone_state, device_context, ...), as Device.ui(). |
| ui_jsonC | The UI snapshot serialized as JSON text. |
| ui_with_recoveryC | UI snapshot that retries past a dead or empty accessibility tree. |
| capabilitiesC | Backend, platform and the actions this device supports. |
| supportsC | Whether this device supports a Device action (e.g. execute_script, get_clipboard). |
| screen_sizeD | [width, height] in pixels. |
| current_app_idC | Package / bundle id of the foreground app. |
| timeD | The device clock. |
| find_nodesA | Nodes matching every given filter (exact text/desc/resource_id/class_name, or *_contains substrings), including off-screen ones. |
| find_nodes_on_screenC | Like find_nodes, limited to nodes inside the visible screen. |
| tap_textC | Tap the first on-screen node whose text/description contains text. |
| tap_nodeB | Tap the center of a node returned by find_nodes / find_nodes_on_screen. |
| tap_and_waitB | Tap a text (or node) and wait until the UI has been idle for idle seconds. |
| scroll_untilC | Scroll until a matching node is on screen; result is the node (or null). |
| clear_inputC | Clear the focused text field. |
| assert_onC | Fail unless app_id is in the foreground. |
| assert_text_visibleB | Fail unless text becomes visible on screen within timeout seconds. |
| wait_for_appC | Wait until app_id is in the foreground. |
| wait_for_idleC | Wait until the UI stops changing. |
| wait_for_screen_changeC | Wait until the UI differs from now. |
| wait_for_textC | Wait until a node containing text exists (off-screen nodes count). |
| wait_for_nodesC | Poll find_nodes until something matches (or timeout, returning []). |
| open_and_settleB | Start an app and wait until it is in front and idle. |
| stop_appB | Force-stop an app; clear_data also wipes its data. |
| install_appC | Install an APK (host path) on the device. |
| uninstall_appD | Uninstall an app. |
| grant_permissionC | Grant a runtime permission (android.permission.*) to an app. |
| open_deep_linkC | Dispatch a deep link / intent (default action VIEW), optionally pinned to a package. |
| execute_scriptC | Run JavaScript in the foreground browser page and return its JSON result. |
| get_clipboardC | The clipboard's text (Android needs the Mobilerun Keyboard as the active IME). |
| set_clipboardC | Put text on the clipboard. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| automate_task | Plan and run a phone-automation task end-to-end with the right tools, safely. |
| open_app | Open an app by name the reliable way: resolve, policy-check, launch or deep-link, confirm. |
| perceive_act_verify | Work toward a goal on the device one verified step at a time. |
| research_then_act | Look up the flow first, then execute it against the live screen. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| guide_resource | Usage guide for this server. |
| policy_resource | Safety policy boundaries and the active mode. |
| ledger_resource | The current plan and recorded findings for the default device. |
| snapshot_resource | A live text snapshot of the default device's screen. |
TDQS
Scored across 94 tools
Many tools overlap heavily: ui/ui_json/ui_with_recovery/get_ui_tree/perceive_screen/read_screen all return screen state, screenshot/screenshot_path/get_screenshot are near-duplicates, and tap/tap_text/tap_node/tap_and_wait plus press/press_home/press_back/press_enter/key blur action boundaries. The descriptions do hint at distinctions (raw vs annotated, coordinate vs text), but the sheer number of near-synonyms with legacy aliases ('kept for old clients', 'prefer ...') makes misselection likely.
Almost everything follows a snake_case verb_noun convention (get_screenshot, launch_app, list_devices, press_home). Deviations are minor: bare nouns (ui, capabilities, time, echo), a single-word verb 'press' vs compound 'press_home', and a few legacy synonyms. Overall predictable and readable.
94 tools is far beyond what a device-control surface needs, and much of the count comes from redundant aliases and overlapping variants (multiple screenshot tools, multiple screen-read tools, multiple tap/wait/key variants). This is an extreme mismatch that burdens selection.
Coverage is exhaustive: input gestures, UI perception, app lifecycle (install/uninstall/stop/grant), files, notifications, media, clipboard, device management, deep links, safety validation, and a task-planning/verification loop. Nearly every lifecycle operation for the domain is present with no obvious dead ends.