Skip to content

activities

Launching Android activities (adb shell am start) on a connected device. Only an explicit-component launch is implemented so far; other Intent options (action, extras, flags, data URI) and interacting with already-running activities aren't in scope for this module yet.

adb_automation_mcp.modules.activities.tools

Module-level, statically-introspectable tool functions for the activities module.

Kept as plain top-level functions, never closures, so that documentation tooling and the registry meta-test can both introspect them directly.

get_foreground_activity(ctx: Context, serial: str) -> ForegroundActivitySnapshot async

Report the currently resumed/top Activity: adb shell dumpsys activity activities.

The way to confirm what's actually in front after a start_activity (or a tap, or a back press) — parses the resumed-activity markers out of dumpsys instead of returning the raw dump.

Parameters:

Name Type Description Default
serial str

The target device's adb serial (see list_connected_devices).

required

Returns:

Type Description
ForegroundActivitySnapshot

resolved (False when nothing is resumed anywhere — screen off, all apps stopped — which is a valid answer, not an error), and when resolved: component ("package/class"), package_name, activity_class, user_id, display_id and task_id of the globally-focused Activity, plus per_display — the resumed Activity of each display that has one (more than one entry only on a multi-display device).

Error handling

An unknown serial raises DEVICE_NOT_FOUND; an unreachable adb binary raises ADB_UNAVAILABLE. Output whose format this build's dumpsys doesn't match parses to resolved=false rather than raising.

Example

Called with serial="emulator-5554". A typical response:

{
  "status": "success",
  "message": "com.android.car.carlauncher/.CarLauncher is foreground on emulator-5554.",
  "data": {
    "serial": "emulator-5554",
    "resolved": true,
    "component": "com.android.car.carlauncher/.CarLauncher",
    "package_name": "com.android.car.carlauncher",
    "activity_class": ".CarLauncher",
    "user_id": 10,
    "display_id": 0,
    "task_id": 1000004,
    "per_display": [
      {
        "display_id": 0,
        "component": "com.android.car.carlauncher/.CarLauncher",
        "package_name": "com.android.car.carlauncher",
        "activity_class": ".CarLauncher",
        "user_id": 10,
        "task_id": 1000004
      }
    ]
  },
  "error": null
}
Source code in src/adb_automation_mcp/modules/activities/tools.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
@category("read")
async def get_foreground_activity(ctx: Context, serial: str) -> ForegroundActivitySnapshot:
    """Report the currently resumed/top Activity: `adb shell dumpsys activity activities`.

    The way to confirm what's actually in front after a start_activity (or a
    tap, or a back press) — parses the resumed-activity markers out of dumpsys
    instead of returning the raw dump.

    Args:
        serial: The target device's adb serial (see list_connected_devices).

    Returns:
        resolved (False when nothing is resumed anywhere — screen off, all apps
        stopped — which is a valid answer, not an error), and when resolved:
        component ("package/class"), package_name, activity_class, user_id,
        display_id and task_id of the globally-focused Activity, plus
        per_display — the resumed Activity of each display that has one (more
        than one entry only on a multi-display device).

    Error handling:
        An unknown serial raises DEVICE_NOT_FOUND; an unreachable adb binary
        raises ADB_UNAVAILABLE. Output whose format this build's dumpsys
        doesn't match parses to resolved=false rather than raising.

    Example:
        Called with serial="emulator-5554". A typical response:

        ```json
        {
          "status": "success",
          "message": "com.android.car.carlauncher/.CarLauncher is foreground on emulator-5554.",
          "data": {
            "serial": "emulator-5554",
            "resolved": true,
            "component": "com.android.car.carlauncher/.CarLauncher",
            "package_name": "com.android.car.carlauncher",
            "activity_class": ".CarLauncher",
            "user_id": 10,
            "display_id": 0,
            "task_id": 1000004,
            "per_display": [
              {
                "display_id": 0,
                "component": "com.android.car.carlauncher/.CarLauncher",
                "package_name": "com.android.car.carlauncher",
                "activity_class": ".CarLauncher",
                "user_id": 10,
                "task_id": 1000004
              }
            ]
          },
          "error": null
        }
        ```
    """
    services = cast("dict[str, object]", ctx.lifespan_context["services"])
    activities = cast(ActivitiesService, services["activities"])
    return await activities.get_foreground_activity(serial)

resolve_activity(ctx: Context, serial: str, action: str | None = None, data_uri: str | None = None, mime_type: str | None = None, categories: list[str] | None = None, component: str | None = None, package_name: str | None = None, user_id: int | None = None) -> ResolvedActivity async

Resolve which Activity would handle an Intent, without launching it: cmd package resolve-activity.

The safe way to find out what start_activity (or the system) would pick for a given Intent — nothing is started. Describe the Intent with the typed fields below (at least one is required); they map to the same -a/-d/ -t/-c/-n/-p options Android's own tooling uses.

Parameters:

Name Type Description Default
serial str

The target device's adb serial (see list_connected_devices).

required
action str | None

Intent action, e.g. "android.intent.action.VIEW" (-a).

None
data_uri str | None

Intent data URI, e.g. "https://example.com" (-d).

None
mime_type str | None

Explicit MIME type, e.g. "text/plain" (-t).

None
categories list[str] | None

Intent categories, e.g. ["android.intent.category.HOME"] (-c, repeatable).

None
component str | None

An explicit "package/class" component to resolve (-n) — resolution still confirms it exists and is enabled.

None
package_name str | None

Constrain resolution to one package (-p).

None
user_id int | None

Resolve as one Android user (--user, see list_users).

None

Returns:

Type Description
ResolvedActivity

resolved (False for the normal "no activity handles this" outcome, not an error), and when resolved: component ("package/class"), its package_name and activity_class, is_default (whether the winner is a registered default handler vs. the system resolver), the match hex, and priority.

Error handling

Specifying no Intent fields at all, or a negative user_id, raises INVALID_ARGUMENT before any adb call. A malformed component string (not "package/class" shape) also raises INVALID_ARGUMENT — Android's Intent parser rejects it. An unknown serial raises DEVICE_NOT_FOUND; an unreachable adb binary raises ADB_UNAVAILABLE. A build whose resolve-activity lacks an option used here raises BACKEND_ERROR.

Example

Called with serial="emulator-5554", action="android.intent.action.MAIN", categories=["android.intent.category.HOME"]. A typical response:

{
  "status": "success",
  "message": "com.android.car.carlauncher/.CarLauncher resolves that intent on emulator-5554 (default).",
  "data": {
    "serial": "emulator-5554",
    "resolved": true,
    "component": "com.android.car.carlauncher/.CarLauncher",
    "package_name": "com.android.car.carlauncher",
    "activity_class": ".CarLauncher",
    "is_default": true,
    "match": "0x108000",
    "priority": 0
  },
  "error": null
}
Source code in src/adb_automation_mcp/modules/activities/tools.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
@category("read")
async def resolve_activity(
    ctx: Context,
    serial: str,
    action: str | None = None,
    data_uri: str | None = None,
    mime_type: str | None = None,
    categories: list[str] | None = None,
    component: str | None = None,
    package_name: str | None = None,
    user_id: int | None = None,
) -> ResolvedActivity:
    """Resolve which Activity would handle an Intent, without launching it: `cmd package resolve-activity`.

    The safe way to find out what `start_activity` (or the system) would pick
    for a given Intent — nothing is started. Describe the Intent with the typed
    fields below (at least one is required); they map to the same `-a`/`-d`/
    `-t`/`-c`/`-n`/`-p` options Android's own tooling uses.

    Args:
        serial: The target device's adb serial (see list_connected_devices).
        action: Intent action, e.g. "android.intent.action.VIEW" (`-a`).
        data_uri: Intent data URI, e.g. "https://example.com" (`-d`).
        mime_type: Explicit MIME type, e.g. "text/plain" (`-t`).
        categories: Intent categories, e.g. ["android.intent.category.HOME"]
            (`-c`, repeatable).
        component: An explicit "package/class" component to resolve (`-n`) —
            resolution still confirms it exists and is enabled.
        package_name: Constrain resolution to one package (`-p`).
        user_id: Resolve as one Android user (`--user`, see list_users).

    Returns:
        resolved (False for the normal "no activity handles this" outcome, not
        an error), and when resolved: component ("package/class"), its
        package_name and activity_class, is_default (whether the winner is a
        registered default handler vs. the system resolver), the match hex, and
        priority.

    Error handling:
        Specifying no Intent fields at all, or a negative user_id, raises
        INVALID_ARGUMENT before any adb call. A malformed component string
        (not "package/class" shape) also raises INVALID_ARGUMENT — Android's
        Intent parser rejects it. An unknown serial raises DEVICE_NOT_FOUND;
        an unreachable adb binary raises ADB_UNAVAILABLE. A build whose
        `resolve-activity` lacks an option used here raises BACKEND_ERROR.

    Example:
        Called with serial="emulator-5554",
        action="android.intent.action.MAIN",
        categories=["android.intent.category.HOME"]. A typical response:

        ```json
        {
          "status": "success",
          "message": "com.android.car.carlauncher/.CarLauncher resolves that intent on emulator-5554 (default).",
          "data": {
            "serial": "emulator-5554",
            "resolved": true,
            "component": "com.android.car.carlauncher/.CarLauncher",
            "package_name": "com.android.car.carlauncher",
            "activity_class": ".CarLauncher",
            "is_default": true,
            "match": "0x108000",
            "priority": 0
          },
          "error": null
        }
        ```
    """
    services = cast("dict[str, object]", ctx.lifespan_context["services"])
    activities = cast(ActivitiesService, services["activities"])
    return await activities.resolve_activity(
        serial,
        action=action,
        data_uri=data_uri,
        mime_type=mime_type,
        categories=categories,
        component=component,
        package_name=package_name,
        user_id=user_id,
    )

start_activity(ctx: Context, serial: str, component: str, user_id: int | None = None, display_id: int | None = None, wait_for_launch: bool = False) -> StartActivityResult async

Launch an Android activity on a device: adb shell am start.

Models the launch target semantically (an explicit component) rather than accepting a raw am command string — see the module docs for why this server never exposes arbitrary shell arguments. Only an explicit component launch is supported so far; other Intent options (action, extras, flags, data URI) aren't implemented yet, and this tool never starts broadcasts or services (see the broadcasts/android_services modules for those).

Parameters:

Name Type Description Default
serial str

The target device's adb serial (see list_connected_devices).

required
component str

The activity to launch, in "package/class" form, e.g. "com.example.app/.MainActivity" (-n). A relative class name (starting with ".") is resolved against the package.

required
user_id int | None

Launch the activity as one specific Android user (--user, see list_users). Omit to use am's default user.

None
display_id int | None

Launch the activity on one specific display (--display, see list_displays). Omit to use the default display.

None
wait_for_launch bool

Wait for the launch to complete and report detailed status (-W): populates status/launch_state/total_time_ms/ wait_time_ms/activity. Without it (the default), am start is fire-and-forget — success=True only means the request wasn't immediately rejected, not that the activity finished launching.

False

Returns:

Type Description
StartActivityResult

Whether the launch succeeded, the component requested, and — only when wait_for_launch=True and available — the ActivityManager- confirmed activity and launch timing/status detail. On a launch failure that isn't a bad request (success=False), error_type/ error_message carry ActivityManager's own error text.

Error handling

Propagates the same way most tools do (unlike check_adb_available): if the adb binary itself can't be found or is unresponsive, or the serial doesn't match a connected device, that surfaces as an actual tool error. A malformed component string (not "package/class" shape) raises COMPONENT_NOT_FOUND; a protected activity the caller isn't allowed to start raises PERMISSION_DENIED; any other ActivityManager transport failure raises a generic BACKEND_ERROR. A well-formed component that ActivityManager can't resolve or launch (e.g. a class that doesn't exist) is NOT a tool error — it's a normal response with success=False and error_type/error_message populated, since that's a genuine launch outcome rather than a bad call.

Example

Called with serial="emulator-5554", component="com.example.app/.MainActivity". A typical response:

{
  "status": "success",
  "message": "Launched com.example.app/.MainActivity on emulator-5554.",
  "data": {
    "serial": "emulator-5554",
    "component": "com.example.app/.MainActivity",
    "user_id": null,
    "display_id": null,
    "wait_for_launch": false,
    "success": true,
    "activity": null,
    "status": null,
    "launch_state": null,
    "total_time_ms": null,
    "wait_time_ms": null,
    "error_type": null,
    "error_message": null,
    "output": "Starting: Intent { cmp=com.example.app/.MainActivity }\n"
  },
  "error": null
}
Source code in src/adb_automation_mcp/modules/activities/tools.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
@category("write")
async def start_activity(
    ctx: Context,
    serial: str,
    component: str,
    user_id: int | None = None,
    display_id: int | None = None,
    wait_for_launch: bool = False,
) -> StartActivityResult:
    """Launch an Android activity on a device: `adb shell am start`.

    Models the launch target semantically (an explicit component) rather
    than accepting a raw `am` command string — see the module docs for why
    this server never exposes arbitrary shell arguments. Only an explicit
    component launch is supported so far; other Intent options (action,
    extras, flags, data URI) aren't implemented yet, and this tool never
    starts broadcasts or services (see the broadcasts/android_services
    modules for those).

    Args:
        serial: The target device's adb serial (see list_connected_devices).
        component: The activity to launch, in "package/class" form, e.g.
            "com.example.app/.MainActivity" (`-n`). A relative class name
            (starting with ".") is resolved against the package.
        user_id: Launch the activity as one specific Android user (`--user`,
            see list_users). Omit to use am's default user.
        display_id: Launch the activity on one specific display (`--display`,
            see list_displays). Omit to use the default display.
        wait_for_launch: Wait for the launch to complete and report detailed
            status (`-W`): populates status/launch_state/total_time_ms/
            wait_time_ms/activity. Without it (the default), `am start` is
            fire-and-forget — success=True only means the request wasn't
            immediately rejected, not that the activity finished launching.

    Returns:
        Whether the launch succeeded, the component requested, and — only
        when wait_for_launch=True and available — the ActivityManager-
        confirmed activity and launch timing/status detail. On a launch
        failure that isn't a bad request (success=False), error_type/
        error_message carry ActivityManager's own error text.

    Error handling:
        Propagates the same way most tools do (unlike check_adb_available): if
        the adb binary itself can't be found or is unresponsive, or the
        serial doesn't match a connected device, that surfaces as an actual
        tool error. A malformed component string (not "package/class" shape)
        raises COMPONENT_NOT_FOUND; a protected activity the caller isn't
        allowed to start raises PERMISSION_DENIED; any other ActivityManager
        transport failure raises a generic BACKEND_ERROR. A well-formed
        component that ActivityManager can't resolve or launch (e.g. a class
        that doesn't exist) is NOT a tool error — it's a normal response with
        success=False and error_type/error_message populated, since that's a
        genuine launch outcome rather than a bad call.

    Example:
        Called with serial="emulator-5554",
        component="com.example.app/.MainActivity". A typical response:

        ```json
        {
          "status": "success",
          "message": "Launched com.example.app/.MainActivity on emulator-5554.",
          "data": {
            "serial": "emulator-5554",
            "component": "com.example.app/.MainActivity",
            "user_id": null,
            "display_id": null,
            "wait_for_launch": false,
            "success": true,
            "activity": null,
            "status": null,
            "launch_state": null,
            "total_time_ms": null,
            "wait_time_ms": null,
            "error_type": null,
            "error_message": null,
            "output": "Starting: Intent { cmp=com.example.app/.MainActivity }\\n"
          },
          "error": null
        }
        ```
    """
    services = cast("dict[str, object]", ctx.lifespan_context["services"])
    activities = cast(ActivitiesService, services["activities"])
    return await activities.start_activity(
        serial,
        component,
        user_id=user_id,
        display_id=display_id,
        wait_for_launch=wait_for_launch,
    )