@mobilenext/mobile-mcp 0.0.23 → 0.0.24
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/lib/android.js +41 -15
- package/lib/ios.js +4 -0
- package/lib/iphone-simulator.js +4 -0
- package/lib/server.js +8 -0
- package/lib/webdriver-agent.js +26 -0
- package/package.json +1 -1
package/lib/android.js
CHANGED
|
@@ -73,11 +73,6 @@ class AndroidRobot {
|
|
|
73
73
|
timeout: TIMEOUT,
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
|
-
getFirstDisplayId() {
|
|
77
|
-
const output = this.adb("shell", "dumpsys", "SurfaceFlinger", "--display-id").toString();
|
|
78
|
-
const match = output.match(/Display (\d+) \(/);
|
|
79
|
-
return match ? match[1] : null;
|
|
80
|
-
}
|
|
81
76
|
getSystemFeatures() {
|
|
82
77
|
return this.adb("shell", "pm", "list", "features")
|
|
83
78
|
.toString()
|
|
@@ -195,16 +190,42 @@ class AndroidRobot {
|
|
|
195
190
|
}
|
|
196
191
|
this.adb("shell", "input", "swipe", `${x0}`, `${y0}`, `${x1}`, `${y1}`, "1000");
|
|
197
192
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
193
|
+
getDisplayCount() {
|
|
194
|
+
return this.adb("shell", "dumpsys", "SurfaceFlinger", "--display-id")
|
|
195
|
+
.toString()
|
|
196
|
+
.split("\n")
|
|
197
|
+
.filter(s => s.startsWith("Display "))
|
|
198
|
+
.length;
|
|
199
|
+
}
|
|
200
|
+
getFirstDisplayId() {
|
|
201
|
+
const displays = this.adb("shell", "cmd", "display", "get-displays")
|
|
202
|
+
.toString()
|
|
203
|
+
.split("\n")
|
|
204
|
+
.filter(s => s.startsWith("Display id "))
|
|
205
|
+
// filter for state ON even though get-displays only returns turned on displays
|
|
206
|
+
.filter(s => s.indexOf(", state ON,") >= 0)
|
|
207
|
+
// another paranoia check
|
|
208
|
+
.filter(s => s.indexOf(", uniqueId ") >= 0);
|
|
209
|
+
if (displays.length > 0) {
|
|
210
|
+
const m = displays[0].match(/uniqueId \"([^\"]+)\"/);
|
|
211
|
+
if (m !== null) {
|
|
212
|
+
const displayId = m[1];
|
|
213
|
+
if (displayId.indexOf("local:") === 0) {
|
|
214
|
+
return displayId.split(":")[1];
|
|
215
|
+
}
|
|
216
|
+
return displayId;
|
|
217
|
+
}
|
|
203
218
|
}
|
|
204
|
-
|
|
205
|
-
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
async getScreenshot() {
|
|
222
|
+
if (this.getDisplayCount() <= 1) {
|
|
223
|
+
// backward compatibility for android 10 and below, and for single display devices
|
|
206
224
|
return this.adb("exec-out", "screencap", "-p");
|
|
207
225
|
}
|
|
226
|
+
// find the first display that is turned on, and capture that one
|
|
227
|
+
const displayId = this.getFirstDisplayId();
|
|
228
|
+
return this.adb("exec-out", "screencap", "-p", "-d", `${displayId}`);
|
|
208
229
|
}
|
|
209
230
|
collectElements(node) {
|
|
210
231
|
const elements = [];
|
|
@@ -287,16 +308,21 @@ class AndroidRobot {
|
|
|
287
308
|
if (!BUTTON_MAP[button]) {
|
|
288
309
|
throw new robot_1.ActionableError(`Button "${button}" is not supported`);
|
|
289
310
|
}
|
|
290
|
-
|
|
311
|
+
const mapped = BUTTON_MAP[button];
|
|
312
|
+
this.adb("shell", "input", "keyevent", mapped);
|
|
291
313
|
}
|
|
292
314
|
async tap(x, y) {
|
|
293
315
|
this.adb("shell", "input", "tap", `${x}`, `${y}`);
|
|
294
316
|
}
|
|
317
|
+
async longPress(x, y) {
|
|
318
|
+
// a long press is a swipe with no movement and a long duration
|
|
319
|
+
this.adb("shell", "input", "swipe", `${x}`, `${y}`, `${x}`, `${y}`, "500");
|
|
320
|
+
}
|
|
295
321
|
async setOrientation(orientation) {
|
|
296
|
-
const
|
|
322
|
+
const value = orientation === "portrait" ? 0 : 1;
|
|
297
323
|
// disable auto-rotation prior to setting the orientation
|
|
298
324
|
this.adb("shell", "settings", "put", "system", "accelerometer_rotation", "0");
|
|
299
|
-
this.adb("shell", "content", "insert", "--uri", "content://settings/system", "--bind", "name:s:user_rotation", "--bind", `value:i:${
|
|
325
|
+
this.adb("shell", "content", "insert", "--uri", "content://settings/system", "--bind", "name:s:user_rotation", "--bind", `value:i:${value}`);
|
|
300
326
|
}
|
|
301
327
|
async getOrientation() {
|
|
302
328
|
const rotation = this.adb("shell", "settings", "get", "system", "user_rotation").toString().trim();
|
package/lib/ios.js
CHANGED
|
@@ -117,6 +117,10 @@ class IosRobot {
|
|
|
117
117
|
const wda = await this.wda();
|
|
118
118
|
await wda.tap(x, y);
|
|
119
119
|
}
|
|
120
|
+
async longPress(x, y) {
|
|
121
|
+
const wda = await this.wda();
|
|
122
|
+
await wda.longPress(x, y);
|
|
123
|
+
}
|
|
120
124
|
async getElementsOnScreen() {
|
|
121
125
|
const wda = await this.wda();
|
|
122
126
|
return await wda.getElementsOnScreen();
|
package/lib/iphone-simulator.js
CHANGED
|
@@ -104,6 +104,10 @@ class Simctl {
|
|
|
104
104
|
const wda = await this.wda();
|
|
105
105
|
return wda.tap(x, y);
|
|
106
106
|
}
|
|
107
|
+
async longPress(x, y) {
|
|
108
|
+
const wda = await this.wda();
|
|
109
|
+
return wda.longPress(x, y);
|
|
110
|
+
}
|
|
107
111
|
async pressButton(button) {
|
|
108
112
|
const wda = await this.wda();
|
|
109
113
|
return wda.pressButton(button);
|
package/lib/server.js
CHANGED
|
@@ -229,6 +229,14 @@ const createMcpServer = () => {
|
|
|
229
229
|
await robot.tap(x, y);
|
|
230
230
|
return `Clicked on screen at coordinates: ${x}, ${y}`;
|
|
231
231
|
});
|
|
232
|
+
tool("mobile_long_press_on_screen_at_coordinates", "Long press on the screen at given x,y coordinates. If long pressing on an element, use the list_elements_on_screen tool to find the coordinates.", {
|
|
233
|
+
x: zod_1.z.number().describe("The x coordinate to long press on the screen, in pixels"),
|
|
234
|
+
y: zod_1.z.number().describe("The y coordinate to long press on the screen, in pixels"),
|
|
235
|
+
}, async ({ x, y }) => {
|
|
236
|
+
requireRobot();
|
|
237
|
+
await robot.longPress(x, y);
|
|
238
|
+
return `Long pressed on screen at coordinates: ${x}, ${y}`;
|
|
239
|
+
});
|
|
232
240
|
tool("mobile_list_elements_on_screen", "List elements on screen and their coordinates, with display text or accessibility label. Do not cache this result.", {
|
|
233
241
|
noParams
|
|
234
242
|
}, async ({}) => {
|
package/lib/webdriver-agent.js
CHANGED
|
@@ -142,6 +142,32 @@ class WebDriverAgent {
|
|
|
142
142
|
});
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
|
+
async longPress(x, y) {
|
|
146
|
+
await this.withinSession(async (sessionUrl) => {
|
|
147
|
+
const url = `${sessionUrl}/actions`;
|
|
148
|
+
await fetch(url, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: {
|
|
151
|
+
"Content-Type": "application/json",
|
|
152
|
+
},
|
|
153
|
+
body: JSON.stringify({
|
|
154
|
+
actions: [
|
|
155
|
+
{
|
|
156
|
+
type: "pointer",
|
|
157
|
+
id: "finger1",
|
|
158
|
+
parameters: { pointerType: "touch" },
|
|
159
|
+
actions: [
|
|
160
|
+
{ type: "pointerMove", duration: 0, x, y },
|
|
161
|
+
{ type: "pointerDown", button: 0 },
|
|
162
|
+
{ type: "pause", duration: 500 },
|
|
163
|
+
{ type: "pointerUp", button: 0 }
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}),
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
145
171
|
isVisible(rect) {
|
|
146
172
|
return rect.x >= 0 && rect.y >= 0;
|
|
147
173
|
}
|