@nbakka/mcp-appium 4.0.1 → 4.0.3

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 +43 -18
  2. package/package.json +1 -1
package/lib/server.js CHANGED
@@ -171,13 +171,22 @@ tool(
171
171
  }
172
172
  );
173
173
 
174
+ tool(
175
+ "guidelines_to_write_code",
176
+ "Read production-grade coding guidelines from the bundled mcp-production-grade.json file and use them when writing code. This ensures code follows established patterns and standards.",
177
+ {},
178
+ async () => {
179
+ return "Read production-grade coding guidelines from the bundled mcp-production-grade.json file and use them when writing code. This ensures code follows established patterns and standards.";
180
+ }
181
+ );
182
+
174
183
  tool("mobile_list_elements_on_screen", "List elements on screen in a simplified, flattened structure. Returns only useful attributes: text, class, id (resource-id), accessibilityId (content-desc), and bounds. Do not cache this result.", {}, async ({}) => {
175
184
  requireRobot();
176
185
  const elements = await robot.getSimplifiedElements();
177
186
  return `Screen elements (${elements.length} found): ${JSON.stringify(elements, null, 2)}`;
178
187
  });
179
188
 
180
- toool("mobile_press_button", "Press a button on device", {
189
+ tool("mobile_press_button", "Press a button on device", {
181
190
  button: zod_1.z.string().describe("The button to press. Supported buttons: BACK (android only), HOME, VOLUME_UP, VOLUME_DOWN, ENTER, DPAD_CENTER (android tv only), DPAD_UP (android tv only), DPAD_DOWN (android tv only), DPAD_LEFT (android tv only), DPAD_RIGHT (android tv only)"),
182
191
  }, async ({ button }) => {
183
192
  requireRobot();
@@ -185,7 +194,7 @@ tool(
185
194
  return `Pressed the button: ${button}`;
186
195
  });
187
196
 
188
- tool("mobile_open_url", "Open a URL in browser on device", {
197
+ tool("mobile_open_deeplink", "Open a deeplink in app on device", {
189
198
  url: zod_1.z.string().describe("The URL to open"),
190
199
  }, async ({ url }) => {
191
200
  requireRobot();
@@ -230,33 +239,49 @@ tool(
230
239
 
231
240
  tool(
232
241
  "mobile_tap_by_text",
233
- "Tap an element on screen by its displayed text or accessibility label using ADB tap",
242
+ "Tap an element on screen by any attribute: text, accessibilityId (content-desc), or id (resource-id). Searches all attributes automatically.",
234
243
  {
235
- text: zod_1.z.string().describe("The exact text or label of the element to tap"),
244
+ value: zod_1.z.string().describe("The value to search for - can be text, accessibilityId, or id"),
236
245
  },
237
- async ({ text }) => {
238
- if (!text) throw new Error("Input text is required");
246
+ async ({ value }) => {
247
+ if (!value) throw new Error("Input value is required");
239
248
 
240
249
  requireRobot(); // ensure robot instance available
241
- const elements = await robot.getElementsOnScreen();
250
+ const elements = await robot.getSimplifiedElements();
242
251
 
243
- // Find element by exact match on text or label
252
+ // Find element by matching any attribute (text, accessibilityId, or id)
244
253
  const element = elements.find(
245
- el => el.text === text || el.label === text
254
+ el => el.text === value || el.accessibilityId === value || el.id === value
246
255
  );
247
256
 
248
- if (!element) throw new Error(`Element with text or label "${text}" not found`);
249
-
257
+ if (!element) throw new Error(`Element with value "${value}" not found in text, accessibilityId, or id`);
258
+
259
+ // Parse bounds to get coordinates
260
+ // Bounds format: "[x1,y1][x2,y2]"
261
+ if (!element.bounds) throw new Error("Element has no bounds information");
262
+
263
+ const boundsMatch = element.bounds.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
264
+ if (!boundsMatch) throw new Error("Invalid bounds format");
265
+
266
+ const x1 = parseInt(boundsMatch[1]);
267
+ const y1 = parseInt(boundsMatch[2]);
268
+ const x2 = parseInt(boundsMatch[3]);
269
+ const y2 = parseInt(boundsMatch[4]);
270
+
250
271
  // Calculate center coordinates
251
- const rect = element.rect;
252
- const x = Math.floor(rect.x + rect.width / 2);
253
- const y = Math.floor(rect.y + rect.height / 2);
272
+ const x = Math.floor((x1 + x2) / 2);
273
+ const y = Math.floor((y1 + y2) / 2);
274
+
275
+ // Execute tap using robot (works for both Android and iOS)
276
+ await robot.tap(x, y);
254
277
 
255
- // Execute adb tap
256
- const { execSync } = require("child_process");
257
- execSync(`adb shell input tap ${x} ${y}`);
278
+ // Determine which attribute matched
279
+ let matchedBy = '';
280
+ if (element.text === value) matchedBy = 'text';
281
+ else if (element.accessibilityId === value) matchedBy = 'accessibilityId';
282
+ else if (element.id === value) matchedBy = 'id';
258
283
 
259
- return `Tapped element with text/label "${text}" at (${x},${y})`;
284
+ return `Tapped element by ${matchedBy} "${value}" at (${x},${y})`;
260
285
  }
261
286
  );
262
287
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"