@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.
Files changed (2) hide show
  1. package/lib/server.js +36 -59
  2. 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
- "tap_by_text",
218
- "tap an element by passing the text visible on screen",
219
- { text: zod_1.z.string().describe("The exact text to search for in the UI element"), },
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
- // 1. Dump UI Automator XML to stdout
227
- let dump;
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
- if (node.node) {
249
- if (Array.isArray(node.node)) {
250
- for (const child of node.node) {
251
- const found = findElement(child);
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
- const element = findElement(xmlObj.hierarchy.node);
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
- // 4. Parse bounds: format "[left,top][right,bottom]"
266
- const bounds = element.bounds;
267
- if (!bounds) throw new Error("Element bounds not found");
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
- const coords = bounds.match(/\d+/g).map(Number);
270
- if (coords.length !== 4) throw new Error("Invalid bounds format");
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.10",
3
+ "version": "2.0.12",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"