@mobilenext/mobile-mcp 0.0.22 → 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 +43 -4
- 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
|
@@ -190,8 +190,42 @@ class AndroidRobot {
|
|
|
190
190
|
}
|
|
191
191
|
this.adb("shell", "input", "swipe", `${x0}`, `${y0}`, `${x1}`, `${y1}`, "1000");
|
|
192
192
|
}
|
|
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
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
193
221
|
async getScreenshot() {
|
|
194
|
-
|
|
222
|
+
if (this.getDisplayCount() <= 1) {
|
|
223
|
+
// backward compatibility for android 10 and below, and for single display devices
|
|
224
|
+
return this.adb("exec-out", "screencap", "-p");
|
|
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}`);
|
|
195
229
|
}
|
|
196
230
|
collectElements(node) {
|
|
197
231
|
const elements = [];
|
|
@@ -274,16 +308,21 @@ class AndroidRobot {
|
|
|
274
308
|
if (!BUTTON_MAP[button]) {
|
|
275
309
|
throw new robot_1.ActionableError(`Button "${button}" is not supported`);
|
|
276
310
|
}
|
|
277
|
-
|
|
311
|
+
const mapped = BUTTON_MAP[button];
|
|
312
|
+
this.adb("shell", "input", "keyevent", mapped);
|
|
278
313
|
}
|
|
279
314
|
async tap(x, y) {
|
|
280
315
|
this.adb("shell", "input", "tap", `${x}`, `${y}`);
|
|
281
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
|
+
}
|
|
282
321
|
async setOrientation(orientation) {
|
|
283
|
-
const
|
|
322
|
+
const value = orientation === "portrait" ? 0 : 1;
|
|
284
323
|
// disable auto-rotation prior to setting the orientation
|
|
285
324
|
this.adb("shell", "settings", "put", "system", "accelerometer_rotation", "0");
|
|
286
|
-
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}`);
|
|
287
326
|
}
|
|
288
327
|
async getOrientation() {
|
|
289
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
|
}
|