@mobilenext/mobile-mcp 0.0.33 → 0.0.35
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.
- package/README.md +41 -0
- package/lib/android.js +28 -6
- package/lib/mobilecli.js +7 -2
- package/lib/server.js +35 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -67,6 +67,47 @@ How we help to scale mobile automation:
|
|
|
67
67
|
- 📊 **Deterministic tool application**: Reduces ambiguity found in purely screenshot-based approaches by relying on structured data whenever possible.
|
|
68
68
|
- 📺 **Extract structured data**: Enables you to extract structred data from anything visible on screen.
|
|
69
69
|
|
|
70
|
+
## 🔧 Available MCP Tools
|
|
71
|
+
|
|
72
|
+
<details>
|
|
73
|
+
<summary>📱 <strong>Click to expand tool list</strong> - List of Mobile MCP tools for automation and development</summary>
|
|
74
|
+
|
|
75
|
+
> For detailed implementation and parameter specifications, see [`src/server.ts`](src/server.ts)
|
|
76
|
+
|
|
77
|
+
### Device Management
|
|
78
|
+
- **`mobile_list_available_devices`** - List all available devices (simulators, emulators, and real devices)
|
|
79
|
+
- **`mobile_get_screen_size`** - Get the screen size of the mobile device in pixels
|
|
80
|
+
- **`mobile_get_orientation`** - Get the current screen orientation of the device
|
|
81
|
+
- **`mobile_set_orientation`** - Change the screen orientation (portrait/landscape)
|
|
82
|
+
|
|
83
|
+
### App Management
|
|
84
|
+
- **`mobile_list_apps`** - List all installed apps on the device
|
|
85
|
+
- **`mobile_launch_app`** - Launch an app using its package name
|
|
86
|
+
- **`mobile_terminate_app`** - Stop and terminate a running app
|
|
87
|
+
- **`mobile_install_app`** - Install an app from file (.apk, .ipa, .app, .zip)
|
|
88
|
+
- **`mobile_uninstall_app`** - Uninstall an app using bundle ID or package name
|
|
89
|
+
|
|
90
|
+
### Screen Interaction
|
|
91
|
+
- **`mobile_take_screenshot`** - Take a screenshot to understand what's on screen
|
|
92
|
+
- **`mobile_save_screenshot`** - Save a screenshot to a file
|
|
93
|
+
- **`mobile_list_elements_on_screen`** - List UI elements with their coordinates and properties
|
|
94
|
+
- **`mobile_click_on_screen_at_coordinates`** - Click at specific x,y coordinates
|
|
95
|
+
- **`mobile_double_tap_on_screen`** - Double-tap at specific coordinates
|
|
96
|
+
- **`mobile_long_press_on_screen_at_coordinates`** - Long press at specific coordinates
|
|
97
|
+
- **`mobile_swipe_on_screen`** - Swipe in any direction (up, down, left, right)
|
|
98
|
+
|
|
99
|
+
### Input & Navigation
|
|
100
|
+
- **`mobile_type_keys`** - Type text into focused elements with optional submit
|
|
101
|
+
- **`mobile_press_button`** - Press device buttons (HOME, BACK, VOLUME_UP/DOWN, ENTER, etc.)
|
|
102
|
+
- **`mobile_open_url`** - Open URLs in the device browser
|
|
103
|
+
|
|
104
|
+
### Platform Support
|
|
105
|
+
- **iOS**: Simulators and real devices via native accessibility and WebDriverAgent
|
|
106
|
+
- **Android**: Emulators and real devices via ADB and UI Automator
|
|
107
|
+
- **Cross-platform**: Unified API works across both iOS and Android
|
|
108
|
+
|
|
109
|
+
</details>
|
|
110
|
+
|
|
70
111
|
## 🏗️ Mobile MCP Architecture
|
|
71
112
|
|
|
72
113
|
<p align="center">
|
package/lib/android.js
CHANGED
|
@@ -43,14 +43,24 @@ const node_fs_1 = require("node:fs");
|
|
|
43
43
|
const xml = __importStar(require("fast-xml-parser"));
|
|
44
44
|
const robot_1 = require("./robot");
|
|
45
45
|
const getAdbPath = () => {
|
|
46
|
+
const exeName = process.env.platform === "win32" ? "adb.exe" : "adb";
|
|
46
47
|
if (process.env.ANDROID_HOME) {
|
|
47
|
-
return node_path_1.default.join(process.env.ANDROID_HOME, "platform-tools",
|
|
48
|
+
return node_path_1.default.join(process.env.ANDROID_HOME, "platform-tools", exeName);
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
if (process.platform === "win32" && process.env.LOCALAPPDATA) {
|
|
51
|
+
const windowsAdbPath = node_path_1.default.join(process.env.LOCALAPPDATA, "Android", "Sdk", "platform-tools", "adb.exe");
|
|
52
|
+
if ((0, node_fs_1.existsSync)(windowsAdbPath)) {
|
|
53
|
+
return windowsAdbPath;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (process.platform === "darwin" && process.env.HOME) {
|
|
57
|
+
const defaultAndroidSdk = node_path_1.default.join(process.env.HOME, "Library", "Android", "sdk", "platform-tools", "adb");
|
|
58
|
+
if ((0, node_fs_1.existsSync)(defaultAndroidSdk)) {
|
|
59
|
+
return defaultAndroidSdk;
|
|
60
|
+
}
|
|
52
61
|
}
|
|
53
|
-
|
|
62
|
+
// fallthrough, hope for the best
|
|
63
|
+
return exeName;
|
|
54
64
|
};
|
|
55
65
|
const BUTTON_MAP = {
|
|
56
66
|
"BACK": "KEYCODE_BACK",
|
|
@@ -77,6 +87,13 @@ class AndroidRobot {
|
|
|
77
87
|
timeout: TIMEOUT,
|
|
78
88
|
});
|
|
79
89
|
}
|
|
90
|
+
silentAdb(...args) {
|
|
91
|
+
return (0, node_child_process_1.execFileSync)(getAdbPath(), ["-s", this.deviceId, ...args], {
|
|
92
|
+
maxBuffer: MAX_BUFFER_SIZE,
|
|
93
|
+
timeout: TIMEOUT,
|
|
94
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
95
|
+
});
|
|
96
|
+
}
|
|
80
97
|
getSystemFeatures() {
|
|
81
98
|
return this.adb("shell", "pm", "list", "features")
|
|
82
99
|
.toString()
|
|
@@ -120,7 +137,12 @@ class AndroidRobot {
|
|
|
120
137
|
.map(line => line.substring("package:".length));
|
|
121
138
|
}
|
|
122
139
|
async launchApp(packageName) {
|
|
123
|
-
|
|
140
|
+
try {
|
|
141
|
+
this.silentAdb("shell", "monkey", "-p", packageName, "-c", "android.intent.category.LAUNCHER", "1");
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
throw new robot_1.ActionableError(`Failed launching app with package name "${packageName}", please make sure it exists`);
|
|
145
|
+
}
|
|
124
146
|
}
|
|
125
147
|
async listRunningProcesses() {
|
|
126
148
|
return this.adb("shell", "ps", "-e")
|
package/lib/mobilecli.js
CHANGED
|
@@ -7,14 +7,19 @@ const getMobilecliPath = () => {
|
|
|
7
7
|
if (process.env.MOBILECLI_PATH) {
|
|
8
8
|
return process.env.MOBILECLI_PATH;
|
|
9
9
|
}
|
|
10
|
+
const arch = process.arch;
|
|
10
11
|
const platform = process.platform;
|
|
11
12
|
let binaryName = "mobilecli";
|
|
12
13
|
switch (platform) {
|
|
13
14
|
case "darwin":
|
|
14
|
-
|
|
15
|
+
if (arch === "arm64") {
|
|
16
|
+
binaryName += "-darwin-arm64";
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
binaryName += "-darwin-amd64";
|
|
20
|
+
}
|
|
15
21
|
break;
|
|
16
22
|
case "linux":
|
|
17
|
-
const arch = process.arch;
|
|
18
23
|
if (arch === "arm64") {
|
|
19
24
|
binaryName += "-linux-arm64";
|
|
20
25
|
}
|
package/lib/server.js
CHANGED
|
@@ -56,6 +56,7 @@ const createMcpServer = () => {
|
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
catch (error) {
|
|
59
|
+
posthog("tool_failed", { "ToolName": name }).then();
|
|
59
60
|
if (error instanceof robot_1.ActionableError) {
|
|
60
61
|
return {
|
|
61
62
|
content: [{ type: "text", text: `${error.message}. Please fix the issue and try again.` }],
|
|
@@ -122,6 +123,11 @@ const createMcpServer = () => {
|
|
|
122
123
|
return "failed " + error.message;
|
|
123
124
|
}
|
|
124
125
|
};
|
|
126
|
+
const getMobilecliDevices = () => {
|
|
127
|
+
const mobilecliPath = (0, mobilecli_1.getMobilecliPath)();
|
|
128
|
+
const mobilecliOutput = (0, node_child_process_1.execFileSync)(mobilecliPath, ["devices"], { encoding: "utf8" }).toString().trim();
|
|
129
|
+
return JSON.parse(mobilecliOutput);
|
|
130
|
+
};
|
|
125
131
|
const mobilecliVersion = getMobilecliVersion();
|
|
126
132
|
posthog("launch", { "MobilecliVersion": mobilecliVersion }).then();
|
|
127
133
|
const simulatorManager = new iphone_simulator_1.SimctlManager();
|
|
@@ -160,6 +166,35 @@ const createMcpServer = () => {
|
|
|
160
166
|
const iosDeviceNames = iosDevices.map(d => d.deviceId);
|
|
161
167
|
const androidTvDevices = androidDevices.filter(d => d.deviceType === "tv").map(d => d.deviceId);
|
|
162
168
|
const androidMobileDevices = androidDevices.filter(d => d.deviceType === "mobile").map(d => d.deviceId);
|
|
169
|
+
if (true) {
|
|
170
|
+
// gilm: this is new code to verify first that mobilecli detects more or equal number of devices.
|
|
171
|
+
// in an attempt to make the smoothest transition from go-ios+xcrun+adb+iproxy+sips+imagemagick+wda to
|
|
172
|
+
// a single cli tool.
|
|
173
|
+
const deviceCount = simulators.length + iosDevices.length + androidDevices.length;
|
|
174
|
+
let mobilecliDeviceCount = 0;
|
|
175
|
+
try {
|
|
176
|
+
const response = getMobilecliDevices();
|
|
177
|
+
if (response.status === "ok" && response.data && response.data.devices) {
|
|
178
|
+
mobilecliDeviceCount = response.data.devices.length;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
// if mobilecli fails, we'll just set count to 0
|
|
183
|
+
}
|
|
184
|
+
if (deviceCount === mobilecliDeviceCount) {
|
|
185
|
+
posthog("debug_mobilecli_same_number_of_devices", {
|
|
186
|
+
"DeviceCount": deviceCount,
|
|
187
|
+
"MobilecliDeviceCount": mobilecliDeviceCount,
|
|
188
|
+
}).then();
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
posthog("debug_mobilecli_different_number_of_devices", {
|
|
192
|
+
"DeviceCount": deviceCount,
|
|
193
|
+
"MobilecliDeviceCount": mobilecliDeviceCount,
|
|
194
|
+
"DeviceCountDifference": deviceCount - mobilecliDeviceCount,
|
|
195
|
+
}).then();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
163
198
|
const resp = ["Found these devices:"];
|
|
164
199
|
if (simulatorNames.length > 0) {
|
|
165
200
|
resp.push(`iOS simulators: [${simulatorNames.join(",")}]`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mobilenext/mobile-mcp",
|
|
3
3
|
"mcpName": "io.github.mobile-next/mobile-mcp",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.35",
|
|
5
5
|
"description": "Mobile MCP",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"zod-to-json-schema": "3.24.6"
|
|
32
32
|
},
|
|
33
33
|
"optionalDependencies": {
|
|
34
|
-
"@mobilenext/mobilecli": "0.0.
|
|
34
|
+
"@mobilenext/mobilecli": "0.0.33"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@eslint/eslintrc": "^3.2.0",
|