@nbakka/mcp-appium 2.0.10 → 2.0.12
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/server.js +36 -59
- package/package.json +1 -1
package/lib/server.js
CHANGED
|
@@ -142,6 +142,22 @@ const createMcpServer = () => {
|
|
|
142
142
|
const screenSize = await robot.getScreenSize();
|
|
143
143
|
return `Screen size is ${screenSize.width}x${screenSize.height} pixels`;
|
|
144
144
|
});
|
|
145
|
+
tool(
|
|
146
|
+
"mobile_learn_current_app_context",
|
|
147
|
+
"Capture and store contextual knowledge about the current app screen or deeplink to inform intelligent automation decisions. This should be the first step after launching the app and before performing any tests.",
|
|
148
|
+
{},
|
|
149
|
+
async () => {
|
|
150
|
+
const context = {
|
|
151
|
+
notes: ["mira_road_east_buy_srp - https://housing.com/in/buy/mumbai/mira_road_east",
|
|
152
|
+
"Filters on the filter screen may initially be offscreen and require scrolling to view.",
|
|
153
|
+
"This context enables dynamic decisions during automation without hardcoded actions.",
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
return `App context learned`;
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
|
|
145
161
|
|
|
146
162
|
tool("mobile_list_elements_on_screen", "List elements on screen and their coordinates, with display text or accessibility label. Do not cache this result.", {}, async ({}) => {
|
|
147
163
|
requireRobot();
|
|
@@ -214,73 +230,34 @@ await new Promise(resolve => setTimeout(resolve, 5000));
|
|
|
214
230
|
return `Current device orientation is ${orientation}`;
|
|
215
231
|
});
|
|
216
232
|
tool(
|
|
217
|
-
"
|
|
218
|
-
"
|
|
219
|
-
{
|
|
233
|
+
"mobile_tap_by_text",
|
|
234
|
+
"Tap an element on screen by its displayed text or accessibility label using ADB tap",
|
|
235
|
+
{
|
|
236
|
+
text: zod_1.z.string().describe("The exact text or label of the element to tap"),
|
|
237
|
+
},
|
|
220
238
|
async ({ text }) => {
|
|
221
|
-
const { execSync } = require("child_process");
|
|
222
|
-
const { XMLParser } = require("fast-xml-parser");
|
|
223
|
-
|
|
224
239
|
if (!text) throw new Error("Input text is required");
|
|
225
240
|
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
try {
|
|
229
|
-
dump = execSync("adb shell uiautomator dump /dev/tty", { maxBuffer: 10 * 1024 * 1024 }).toString();
|
|
230
|
-
} catch (e) {
|
|
231
|
-
throw new Error("Failed to dump UI Automator XML via adb");
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// 2. Parse XML
|
|
235
|
-
const parser = new XMLParser({
|
|
236
|
-
ignoreAttributes: false,
|
|
237
|
-
attributeNamePrefix: "",
|
|
238
|
-
});
|
|
239
|
-
const xmlObj = parser.parse(dump);
|
|
240
|
-
|
|
241
|
-
// 3. Recursive traversal to find matching element by text/content-desc/hint
|
|
242
|
-
function findElement(node) {
|
|
243
|
-
if (!node) return null;
|
|
244
|
-
|
|
245
|
-
const matchText = node.text || node["content-desc"] || node.hint || "";
|
|
246
|
-
if (matchText === text) return node;
|
|
241
|
+
requireRobot(); // ensure robot instance available
|
|
242
|
+
const elements = await robot.getElementsOnScreen();
|
|
247
243
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (found) return found;
|
|
253
|
-
}
|
|
254
|
-
} else {
|
|
255
|
-
return findElement(node.node);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
244
|
+
// Find element by exact match on text or label
|
|
245
|
+
const element = elements.find(
|
|
246
|
+
el => el.text === text || el.label === text
|
|
247
|
+
);
|
|
261
248
|
|
|
262
|
-
|
|
263
|
-
if (!element) throw new Error(`Element with text "${text}" not found`);
|
|
249
|
+
if (!element) throw new Error(`Element with text or label "${text}" not found`);
|
|
264
250
|
|
|
265
|
-
//
|
|
266
|
-
const
|
|
267
|
-
|
|
251
|
+
// Calculate center coordinates
|
|
252
|
+
const rect = element.rect;
|
|
253
|
+
const x = Math.floor(rect.x + rect.width / 2);
|
|
254
|
+
const y = Math.floor(rect.y + rect.height / 2);
|
|
268
255
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const [left, top, right, bottom] = coords;
|
|
273
|
-
const x = Math.floor((left + right) / 2);
|
|
274
|
-
const y = Math.floor((top + bottom) / 2);
|
|
275
|
-
|
|
276
|
-
// 5. Tap via adb
|
|
277
|
-
try {
|
|
278
|
-
execSync(`adb shell input tap ${x} ${y}`);
|
|
279
|
-
} catch (e) {
|
|
280
|
-
throw new Error("Failed to perform tap via adb");
|
|
281
|
-
}
|
|
256
|
+
// Execute adb tap
|
|
257
|
+
const { execSync } = require("child_process");
|
|
258
|
+
execSync(`adb shell input tap ${x} ${y}`);
|
|
282
259
|
|
|
283
|
-
return `Tapped element with text "${text}" at (${x},${y})`;
|
|
260
|
+
return `Tapped element with text/label "${text}" at (${x},${y})`;
|
|
284
261
|
}
|
|
285
262
|
);
|
|
286
263
|
// async check for latest agent version
|