uiautomator2-mcp-server 0.1.2__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
u2mcp/tools/app.py CHANGED
@@ -1,231 +1,232 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- from typing import Any
5
-
6
- from ..mcp import mcp
7
- from .device import get_device
8
-
9
- __all__ = (
10
- "app_install",
11
- "app_uninstall",
12
- "app_uninstall_all",
13
- "app_start",
14
- "app_stop",
15
- "app_stop_all",
16
- "app_clear",
17
- "app_info",
18
- "app_current",
19
- "app_list",
20
- "app_list_running",
21
- "app_auto_grant_permissions",
22
- )
23
-
24
-
25
- @mcp.tool("app_install")
26
- async def app_install(serial: str, data: str):
27
- """Install app
28
-
29
- Args:
30
- serial (str): Android device serialno
31
- data (str): APK file path or url
32
- """
33
- async with get_device(serial) as device:
34
- await asyncio.to_thread(device.app_install, data)
35
-
36
-
37
- @mcp.tool("app_uninstall")
38
- async def app_uninstall(serial: str, package_name: str) -> bool:
39
- """Uninstall an app
40
-
41
- Args:
42
- serial (str): Android device serialno
43
- package_name (str): package name
44
-
45
- Returns:
46
- bool: success
47
- """
48
- async with get_device(serial) as device:
49
- return await asyncio.to_thread(device.app_uninstall, package_name)
50
-
51
-
52
- @mcp.tool("app_uninstall_all")
53
- async def app_uninstall_all(serial: str, excludes: list[str] | None = None) -> list[str]:
54
- """Uninstall all apps
55
-
56
- Args:
57
- serial (str): Android device serialno
58
- excludes (list[str] | None): packages that do not want to uninstall
59
-
60
- Returns:
61
- list[str]: list of uninstalled apps
62
- """
63
- async with get_device(serial) as device:
64
- return await asyncio.to_thread(device.app_uninstall_all, excludes or [])
65
-
66
-
67
- @mcp.tool("app_start")
68
- async def app_start(
69
- serial: str,
70
- package_name: str,
71
- activity: str | None = None,
72
- wait: bool = False,
73
- stop: bool = False,
74
- ):
75
- """Launch application
76
-
77
- Args:
78
- serial (str): Android device serialno
79
- package_name (str): package name
80
- activity (str): app activity
81
- stop (bool): Stop app before starting the activity. (require activity)
82
- wait (bool): wait until app started. default False
83
- """
84
- async with get_device(serial) as device:
85
- await asyncio.to_thread(device.app_start, package_name, activity, wait, stop)
86
-
87
-
88
- @mcp.tool("app_wait")
89
- async def app_wait(serial: str, package_name: str, timeout: float = 20.0, front=False):
90
- """Wait until app launched
91
-
92
- Args:
93
- serial (str): Android device serialno
94
- package_name (str): package name
95
- timeout (float): maximum wait time seconds
96
- front (bool): wait until app is current app
97
- """
98
- async with get_device(serial) as device:
99
- if not await asyncio.to_thread(device.app_wait, package_name, timeout, front):
100
- raise RuntimeError(f"Failed to wait App {package_name} to launch")
101
-
102
-
103
- @mcp.tool("app_stop")
104
- async def app_stop(serial: str, package_name: str):
105
- """Stop one application
106
-
107
- Args:
108
- serial (str): Android device serialno
109
- package_name (str): package name
110
- """
111
- async with get_device(serial) as device:
112
- await asyncio.to_thread(device.app_stop, package_name)
113
-
114
-
115
- @mcp.tool("app_stop_all")
116
- async def app_stop_all(serial: str, excludes: list[str] | None = None) -> list[str]:
117
- """Stop all third party applications
118
-
119
- Args:
120
- excludes (list): apps that do now want to kill
121
-
122
- Returns:
123
- list[str]: a list of killed apps
124
- """
125
- async with get_device(serial) as device:
126
- return await asyncio.to_thread(device.app_stop_all, excludes or [])
127
-
128
-
129
- @mcp.tool("app_clear")
130
- async def app_clear(serial: str, package_name: str):
131
- """Stop and clear app data: pm clear
132
-
133
- Args:
134
- serial (str): Android device serialno
135
- package_name (str): package name
136
-
137
- Returns:
138
- bool: success
139
- """
140
- async with get_device(serial) as device:
141
- await asyncio.to_thread(device.app_clear, package_name)
142
-
143
-
144
- @mcp.tool("app_info")
145
- async def app_info(serial: str, package_name: str) -> dict[str, Any]:
146
- """
147
- Get app info
148
-
149
- Args:
150
- serial (str): Android device serialno
151
- package_name (str): package name
152
-
153
- Returns:
154
- dict[str,Any]: app info
155
-
156
- Example:
157
- {"versionName": "1.1.7", "versionCode": 1001007}
158
- """
159
- async with get_device(serial) as device:
160
- return await asyncio.to_thread(device.app_info, package_name)
161
-
162
-
163
- @mcp.tool("app_current")
164
- async def app_current(serial: str) -> dict[str, Any]:
165
- """
166
- Get current app info
167
-
168
- Args:
169
- serial (str): Android device serialno
170
-
171
- Returns:
172
- dict[str,Any]: running app info
173
- """
174
- async with get_device(serial) as device:
175
- return await asyncio.to_thread(device.app_current)
176
-
177
-
178
- @mcp.tool("app_list")
179
- async def app_list(serial: str, filter: str = "") -> list[str]:
180
- """
181
- List installed app package names
182
-
183
- Args:
184
- serial (str): Android device serialno
185
- filter (str): [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]
186
-
187
- Returns:
188
- list[str]: list of apps by filter
189
- """
190
- async with get_device(serial) as device:
191
- return await asyncio.to_thread(device.app_list, filter.strip())
192
-
193
-
194
- @mcp.tool("app_list_running")
195
- async def app_list_running(serial: str) -> list[str]:
196
- """
197
- List running apps
198
-
199
- Args:
200
- serial (str): Android device serialno
201
-
202
- Returns:
203
- list[str]: list of running apps
204
- """
205
- async with get_device(serial) as device:
206
- return await asyncio.to_thread(device.app_list_running)
207
-
208
-
209
- @mcp.tool("app_auto_grant_permissions")
210
- async def app_auto_grant_permissions(serial: str, package_name: str):
211
- """auto grant permissions
212
-
213
- Args:
214
- serial (str): Android device serialno
215
- package_name (str): package name
216
-
217
- Help of "adb shell pm":
218
- grant [--user USER_ID] PACKAGE PERMISSION
219
- revoke [--user USER_ID] PACKAGE PERMISSION
220
- These commands either grant or revoke permissions to apps. The permissions
221
- must be declared as used in the app's manifest, be runtime permissions
222
- (protection level dangerous), and the app targeting SDK greater than Lollipop MR1 (API level 22).
223
-
224
- Help of "Android official pm" see <https://developer.android.com/tools/adb#pm>
225
- Grant a permission to an app. On devices running Android 6.0 (API level 23) and higher,
226
- the permission can be any permission declared in the app manifest.
227
- On devices running Android 5.1 (API level 22) and lower,
228
- must be an optional permission defined by the app.
229
- """
230
- async with get_device(serial) as device:
231
- await asyncio.to_thread(device.app_auto_grant_permissions, package_name)
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from anyio import to_thread
6
+
7
+ from ..mcp import mcp
8
+ from .device import get_device
9
+
10
+ __all__ = (
11
+ "app_install",
12
+ "app_uninstall",
13
+ "app_uninstall_all",
14
+ "app_start",
15
+ "app_stop",
16
+ "app_stop_all",
17
+ "app_clear",
18
+ "app_info",
19
+ "app_current",
20
+ "app_list",
21
+ "app_list_running",
22
+ "app_auto_grant_permissions",
23
+ )
24
+
25
+
26
+ @mcp.tool("app_install", tags={"app:manage"})
27
+ async def app_install(serial: str, data: str):
28
+ """Install app
29
+
30
+ Args:
31
+ serial (str): Android device serialno
32
+ data (str): APK file path or url
33
+ """
34
+ async with get_device(serial) as device:
35
+ await to_thread.run_sync(device.app_install, data)
36
+
37
+
38
+ @mcp.tool("app_uninstall", tags={"app:manage"})
39
+ async def app_uninstall(serial: str, package_name: str) -> bool:
40
+ """Uninstall an app
41
+
42
+ Args:
43
+ serial (str): Android device serialno
44
+ package_name (str): package name
45
+
46
+ Returns:
47
+ bool: success
48
+ """
49
+ async with get_device(serial) as device:
50
+ return await to_thread.run_sync(device.app_uninstall, package_name)
51
+
52
+
53
+ @mcp.tool("app_uninstall_all", tags={"app:manage"})
54
+ async def app_uninstall_all(serial: str, excludes: list[str] | None = None) -> list[str]:
55
+ """Uninstall all apps
56
+
57
+ Args:
58
+ serial (str): Android device serialno
59
+ excludes (list[str] | None): packages that do not want to uninstall
60
+
61
+ Returns:
62
+ list[str]: list of uninstalled apps
63
+ """
64
+ async with get_device(serial) as device:
65
+ return await to_thread.run_sync(device.app_uninstall_all, excludes or [])
66
+
67
+
68
+ @mcp.tool("app_start", tags={"app:lifecycle"})
69
+ async def app_start(
70
+ serial: str,
71
+ package_name: str,
72
+ activity: str | None = None,
73
+ wait: bool = False,
74
+ stop: bool = False,
75
+ ):
76
+ """Launch application
77
+
78
+ Args:
79
+ serial (str): Android device serialno
80
+ package_name (str): package name
81
+ activity (str): app activity
82
+ stop (bool): Stop app before starting the activity. (require activity)
83
+ wait (bool): wait until app started. default False
84
+ """
85
+ async with get_device(serial) as device:
86
+ await to_thread.run_sync(device.app_start, package_name, activity, wait, stop)
87
+
88
+
89
+ @mcp.tool("app_wait", tags={"app:lifecycle"})
90
+ async def app_wait(serial: str, package_name: str, timeout: float = 20.0, front=False):
91
+ """Wait until app launched
92
+
93
+ Args:
94
+ serial (str): Android device serialno
95
+ package_name (str): package name
96
+ timeout (float): maximum wait time seconds
97
+ front (bool): wait until app is current app
98
+ """
99
+ async with get_device(serial) as device:
100
+ if not await to_thread.run_sync(device.app_wait, package_name, timeout, front):
101
+ raise RuntimeError(f"Failed to wait App {package_name} to launch")
102
+
103
+
104
+ @mcp.tool("app_stop", tags={"app:lifecycle"})
105
+ async def app_stop(serial: str, package_name: str):
106
+ """Stop one application
107
+
108
+ Args:
109
+ serial (str): Android device serialno
110
+ package_name (str): package name
111
+ """
112
+ async with get_device(serial) as device:
113
+ await to_thread.run_sync(device.app_stop, package_name)
114
+
115
+
116
+ @mcp.tool("app_stop_all", tags={"app:lifecycle"})
117
+ async def app_stop_all(serial: str, excludes: list[str] | None = None) -> list[str]:
118
+ """Stop all third party applications
119
+
120
+ Args:
121
+ excludes (list): apps that do now want to kill
122
+
123
+ Returns:
124
+ list[str]: a list of killed apps
125
+ """
126
+ async with get_device(serial) as device:
127
+ return await to_thread.run_sync(device.app_stop_all, excludes or [])
128
+
129
+
130
+ @mcp.tool("app_clear", tags={"app:config"})
131
+ async def app_clear(serial: str, package_name: str):
132
+ """Stop and clear app data: pm clear
133
+
134
+ Args:
135
+ serial (str): Android device serialno
136
+ package_name (str): package name
137
+
138
+ Returns:
139
+ bool: success
140
+ """
141
+ async with get_device(serial) as device:
142
+ await to_thread.run_sync(device.app_clear, package_name)
143
+
144
+
145
+ @mcp.tool("app_info", tags={"app:info"})
146
+ async def app_info(serial: str, package_name: str) -> dict[str, Any]:
147
+ """
148
+ Get app info
149
+
150
+ Args:
151
+ serial (str): Android device serialno
152
+ package_name (str): package name
153
+
154
+ Returns:
155
+ dict[str,Any]: app info
156
+
157
+ Example:
158
+ {"versionName": "1.1.7", "versionCode": 1001007}
159
+ """
160
+ async with get_device(serial) as device:
161
+ return await to_thread.run_sync(device.app_info, package_name)
162
+
163
+
164
+ @mcp.tool("app_current", tags={"app:info"})
165
+ async def app_current(serial: str) -> dict[str, Any]:
166
+ """
167
+ Get current app info
168
+
169
+ Args:
170
+ serial (str): Android device serialno
171
+
172
+ Returns:
173
+ dict[str,Any]: running app info
174
+ """
175
+ async with get_device(serial) as device:
176
+ return await to_thread.run_sync(device.app_current)
177
+
178
+
179
+ @mcp.tool("app_list", tags={"app:info"})
180
+ async def app_list(serial: str, filter: str = "") -> list[str]:
181
+ """
182
+ List installed app package names
183
+
184
+ Args:
185
+ serial (str): Android device serialno
186
+ filter (str): [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]
187
+
188
+ Returns:
189
+ list[str]: list of apps by filter
190
+ """
191
+ async with get_device(serial) as device:
192
+ return await to_thread.run_sync(device.app_list, filter.strip())
193
+
194
+
195
+ @mcp.tool("app_list_running", tags={"app:info"})
196
+ async def app_list_running(serial: str) -> list[str]:
197
+ """
198
+ List running apps
199
+
200
+ Args:
201
+ serial (str): Android device serialno
202
+
203
+ Returns:
204
+ list[str]: list of running apps
205
+ """
206
+ async with get_device(serial) as device:
207
+ return await to_thread.run_sync(device.app_list_running)
208
+
209
+
210
+ @mcp.tool("app_auto_grant_permissions", tags={"app:config"})
211
+ async def app_auto_grant_permissions(serial: str, package_name: str):
212
+ """auto grant permissions
213
+
214
+ Args:
215
+ serial (str): Android device serialno
216
+ package_name (str): package name
217
+
218
+ Help of "adb shell pm":
219
+ grant [--user USER_ID] PACKAGE PERMISSION
220
+ revoke [--user USER_ID] PACKAGE PERMISSION
221
+ These commands either grant or revoke permissions to apps. The permissions
222
+ must be declared as used in the app's manifest, be runtime permissions
223
+ (protection level dangerous), and the app targeting SDK greater than Lollipop MR1 (API level 22).
224
+
225
+ Help of "Android official pm" see <https://developer.android.com/tools/adb#pm>
226
+ Grant a permission to an app. On devices running Android 6.0 (API level 23) and higher,
227
+ the permission can be any permission declared in the app manifest.
228
+ On devices running Android 5.1 (API level 22) and lower,
229
+ must be an optional permission defined by the app.
230
+ """
231
+ async with get_device(serial) as device:
232
+ await to_thread.run_sync(device.app_auto_grant_permissions, package_name)
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from anyio import to_thread
4
+
5
+ from ..mcp import mcp
6
+ from .device import get_device
7
+
8
+ __all__ = ("read_clipboard", "write_clipboard")
9
+
10
+
11
+ @mcp.tool("read_clipboard", tags={"clipboard:read"})
12
+ async def read_clipboard(serial: str) -> str | None:
13
+ """Read clipboard from device
14
+
15
+ Args:
16
+ serial (str): Android device serialno
17
+
18
+ Returns:
19
+ str: The actual text in the clip.
20
+ None: If there is no text in the clip.
21
+ """
22
+ async with get_device(serial) as device:
23
+ return await to_thread.run_sync(lambda: device.clipboard)
24
+
25
+
26
+ @mcp.tool("write_clipboard", tags={"clipboard:write"})
27
+ async def write_clipboard(serial: str, text: str):
28
+ """Write clipboard to device
29
+
30
+ Args:
31
+ serial (str): Android device serialno
32
+ text: The actual text in the clip.
33
+ """
34
+ async with get_device(serial) as device:
35
+ await to_thread.run_sync(device.set_clipboard, text)