Skip to content

files

Copying files between a connected device and this server's host (adb pull so far — adb push isn't implemented yet). Private app-data semantics (e.g. run-as for another app's sandboxed files) aren't handled here.

adb_automation_mcp.modules.files.tools

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

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

pull_file(ctx: Context, serial: str, remote_path: str, local_path: str) -> PullFileResult async

Copy one file from a device to this server's host: adb pull.

Uses the existing AdbBackend.pull primitive directly — no raw shell command is run. Private app-data semantics (e.g. run-as to read another app's sandboxed files) aren't handled here; remote_path is passed to adb pull exactly as given, so pulling a path the shell user can't read fails the same way a plain adb pull would.

Parameters:

Name Type Description Default
serial str

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

required
remote_path str

The device-side path to copy, e.g. "/sdcard/test.txt".

required
local_path str

Where to write the file on this server's host, relative to (or, if absolute, still required to resolve inside) the server's configured local_root.

required

Returns:

Type Description
PullFileResult

The serial, remote_path, the resolved local_path actually written, success (always True — see Error handling), and the raw adb pull output. Only returned on success.

Error handling

local_path is checked before any device round-trip: if the server has no local_root configured at all, or local_path resolves outside it (including via ".." or an absolute path elsewhere on the host), the call is refused rather than writing anywhere — there is no default local_root; an operator must set ADB_AUTOMATION_LOCAL_ROOT explicitly (POLICY_DENIED). Beyond that: an unknown serial or an unresponsive adb binary raises DEVICE_NOT_FOUND/ADB_UNAVAILABLE; a remote_path that doesn't exist on the device raises REMOTE_FILE_NOT_FOUND; a remote_path the shell user can't read raises PERMISSION_DENIED; any other adb pull failure raises a generic BACKEND_ERROR.

Example

Called with serial="emulator-5554", remote_path="/sdcard/test.txt", local_path="test.txt". A typical response:

{
  "status": "success",
  "message": "Pulled /sdcard/test.txt from emulator-5554 to /var/adb-files/test.txt.",
  "data": {
    "serial": "emulator-5554",
    "remote_path": "/sdcard/test.txt",
    "local_path": "/var/adb-files/test.txt",
    "success": true,
    "output": "/sdcard/test.txt: 1 file pulled, 0 skipped. 4.2 MB/s (1024 bytes in 0.002s)\n"
  },
  "error": null
}
Source code in src/adb_automation_mcp/modules/files/tools.py
17
18
19
20
21
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
@category("read")
async def pull_file(ctx: Context, serial: str, remote_path: str, local_path: str) -> PullFileResult:
    """Copy one file from a device to this server's host: `adb pull`.

    Uses the existing AdbBackend.pull primitive directly — no raw shell
    command is run. Private app-data semantics (e.g. `run-as` to read
    another app's sandboxed files) aren't handled here; remote_path is
    passed to `adb pull` exactly as given, so pulling a path the shell user
    can't read fails the same way a plain `adb pull` would.

    Args:
        serial: The target device's adb serial (see list_connected_devices).
        remote_path: The device-side path to copy, e.g. "/sdcard/test.txt".
        local_path: Where to write the file on this server's host, relative
            to (or, if absolute, still required to resolve inside) the
            server's configured local_root.

    Returns:
        The serial, remote_path, the resolved local_path actually written,
        success (always True — see Error handling), and the raw adb pull
        output. Only returned on success.

    Error handling:
        local_path is checked before any device round-trip: if the server
        has no local_root configured at all, or local_path resolves outside
        it (including via ".." or an absolute path elsewhere on the host),
        the call is refused rather than writing anywhere — there is no
        default local_root; an operator must set ADB_AUTOMATION_LOCAL_ROOT
        explicitly (POLICY_DENIED). Beyond that: an unknown serial or an
        unresponsive adb binary raises DEVICE_NOT_FOUND/ADB_UNAVAILABLE; a
        remote_path that doesn't exist on the device raises
        REMOTE_FILE_NOT_FOUND; a remote_path the shell user can't read
        raises PERMISSION_DENIED; any other `adb pull` failure raises a
        generic BACKEND_ERROR.

    Example:
        Called with serial="emulator-5554", remote_path="/sdcard/test.txt",
        local_path="test.txt". A typical response:

        ```json
        {
          "status": "success",
          "message": "Pulled /sdcard/test.txt from emulator-5554 to /var/adb-files/test.txt.",
          "data": {
            "serial": "emulator-5554",
            "remote_path": "/sdcard/test.txt",
            "local_path": "/var/adb-files/test.txt",
            "success": true,
            "output": "/sdcard/test.txt: 1 file pulled, 0 skipped. 4.2 MB/s (1024 bytes in 0.002s)\\n"
          },
          "error": null
        }
        ```
    """
    services = cast("dict[str, object]", ctx.lifespan_context["services"])
    files = cast(FilesService, services["files"])
    return await files.pull_file(serial, remote_path, local_path)

push_file(ctx: Context, serial: str, local_path: str, remote_path: str) -> PushFileResult async

Copy one file from this server's host to a device: adb push.

The mirror of pull_file. The host source is confined to the server's configured local_root (the same gate pull_file writes into) — reading a host path outside it, or one that doesn't exist, is refused before any device round-trip. remote_path is passed to adb push exactly as given.

Parameters:

Name Type Description Default
serial str

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

required
local_path str

The host file to copy, relative to (or, if absolute, still required to resolve inside) the server's configured local_root.

required
remote_path str

The device-side destination path, e.g. "/data/local/tmp/test.txt".

required

Returns:

Type Description
PushFileResult

The serial, the resolved local_path actually read, remote_path, success (always True — see Error handling), and the raw adb push output. Only returned on success.

Error handling

local_path is checked before any device round-trip: if the server has no local_root configured, or local_path resolves outside it, or names no existing file, the call is refused (POLICY_DENIED / INVALID_ARGUMENT) without touching the device. Beyond that: an unknown serial or unresponsive adb binary raises DEVICE_NOT_FOUND/ADB_UNAVAILABLE; a remote_path on a read-only filesystem or one the shell user can't write raises PERMISSION_DENIED; a remote_path whose parent directory doesn't exist raises REMOTE_FILE_NOT_FOUND; any other adb push failure raises BACKEND_ERROR.

Example

Called with serial="emulator-5554", local_path="test.txt", remote_path="/data/local/tmp/test.txt". A typical response:

{
  "status": "success",
  "message": "Pushed /var/adb-files/test.txt to /data/local/tmp/test.txt on emulator-5554.",
  "data": {
    "serial": "emulator-5554",
    "local_path": "/var/adb-files/test.txt",
    "remote_path": "/data/local/tmp/test.txt",
    "success": true,
    "output": "/var/adb-files/test.txt: 1 file pushed, 0 skipped. 0.0 MB/s (12 bytes in 0.001s)\n"
  },
  "error": null
}
Source code in src/adb_automation_mcp/modules/files/tools.py
 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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
@category("write")
async def push_file(ctx: Context, serial: str, local_path: str, remote_path: str) -> PushFileResult:
    """Copy one file from this server's host to a device: `adb push`.

    The mirror of pull_file. The host source is confined to the server's
    configured local_root (the same gate pull_file writes into) — reading a
    host path outside it, or one that doesn't exist, is refused before any
    device round-trip. remote_path is passed to `adb push` exactly as given.

    Args:
        serial: The target device's adb serial (see list_connected_devices).
        local_path: The host file to copy, relative to (or, if absolute, still
            required to resolve inside) the server's configured local_root.
        remote_path: The device-side destination path, e.g.
            "/data/local/tmp/test.txt".

    Returns:
        The serial, the resolved local_path actually read, remote_path,
        success (always True — see Error handling), and the raw adb push
        output. Only returned on success.

    Error handling:
        local_path is checked before any device round-trip: if the server has
        no local_root configured, or local_path resolves outside it, or names
        no existing file, the call is refused (POLICY_DENIED / INVALID_ARGUMENT)
        without touching the device. Beyond that: an unknown serial or
        unresponsive adb binary raises DEVICE_NOT_FOUND/ADB_UNAVAILABLE; a
        remote_path on a read-only filesystem or one the shell user can't write
        raises PERMISSION_DENIED; a remote_path whose parent directory doesn't
        exist raises REMOTE_FILE_NOT_FOUND; any other `adb push` failure raises
        BACKEND_ERROR.

    Example:
        Called with serial="emulator-5554", local_path="test.txt",
        remote_path="/data/local/tmp/test.txt". A typical response:

        ```json
        {
          "status": "success",
          "message": "Pushed /var/adb-files/test.txt to /data/local/tmp/test.txt on emulator-5554.",
          "data": {
            "serial": "emulator-5554",
            "local_path": "/var/adb-files/test.txt",
            "remote_path": "/data/local/tmp/test.txt",
            "success": true,
            "output": "/var/adb-files/test.txt: 1 file pushed, 0 skipped. 0.0 MB/s (12 bytes in 0.001s)\\n"
          },
          "error": null
        }
        ```
    """
    services = cast("dict[str, object]", ctx.lifespan_context["services"])
    files = cast(FilesService, services["files"])
    return await files.push_file(serial, local_path, remote_path)