appium-mcp 1.13.0 → 1.15.0
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/CHANGELOG.md +12 -0
- package/README.md +23 -0
- package/dist/command.d.ts +1 -1
- package/dist/command.d.ts.map +1 -1
- package/dist/command.js +10 -1
- package/dist/command.js.map +1 -1
- package/dist/tests/screenshot.test.js +43 -13
- package/dist/tests/screenshot.test.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interactions/handle-alert.d.ts +11 -0
- package/dist/tools/interactions/handle-alert.d.ts.map +1 -0
- package/dist/tools/interactions/handle-alert.js +134 -0
- package/dist/tools/interactions/handle-alert.js.map +1 -0
- package/dist/tools/interactions/screenshot.d.ts +6 -2
- package/dist/tools/interactions/screenshot.d.ts.map +1 -1
- package/dist/tools/interactions/screenshot.js +24 -4
- package/dist/tools/interactions/screenshot.js.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/command.ts +13 -1
- package/src/tests/screenshot.test.ts +67 -14
- package/src/tools/index.ts +4 -1
- package/src/tools/interactions/handle-alert.ts +168 -0
- package/src/tools/interactions/screenshot.ts +32 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [1.15.0](https://github.com/appium/appium-mcp/compare/v1.14.0...v1.15.0) (2026-02-15)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **interactions:** add handle-alert tool ([#57](https://github.com/appium/appium-mcp/issues/57)) ([966d035](https://github.com/appium/appium-mcp/commit/966d035455c2767cc027e78bda592a01a3847c17))
|
|
6
|
+
|
|
7
|
+
## [1.14.0](https://github.com/appium/appium-mcp/compare/v1.13.0...v1.14.0) (2026-02-15)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* add element screenshot support ([#165](https://github.com/appium/appium-mcp/issues/165)) ([b6e42c7](https://github.com/appium/appium-mcp/commit/b6e42c75085dec12d1ef96229c3f53947c7ec652))
|
|
12
|
+
|
|
1
13
|
## [1.13.0](https://github.com/appium/appium-mcp/compare/v1.12.4...v1.13.0) (2026-02-14)
|
|
2
14
|
|
|
3
15
|
### Features
|
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ MCP Appium is an intelligent MCP (Model Context Protocol) server designed to emp
|
|
|
25
25
|
- **Automated Test Generation**: Generate Java/TestNG test code from natural language descriptions.
|
|
26
26
|
- **Page Object Model Support**: Utilize built-in templates that follow industry best practices.
|
|
27
27
|
- **Flexible Configuration**: Customize capabilities and settings for different environments.
|
|
28
|
+
- **Multilingual Support**: Use your native language - AI handles all interactions naturally in any language (English, Spanish, Chinese, Japanese, Korean, etc.).
|
|
28
29
|
|
|
29
30
|
## 📋 Prerequisites
|
|
30
31
|
|
|
@@ -264,12 +265,14 @@ MCP Appium provides a comprehensive set of tools organized into the following ca
|
|
|
264
265
|
| `appium_drag_and_drop` | Perform a drag and drop gesture from a source location to a target location (supports element-to-element, element-to-coordinates, coordinates-to-element, and coordinates-to-coordinates) |
|
|
265
266
|
| `appium_set_value` | Enter text into an input field |
|
|
266
267
|
| `appium_get_text` | Get text content from an element |
|
|
268
|
+
| `appium_handle_alert` | Accept or dismiss system/permission alerts, or click a dialog button by label |
|
|
267
269
|
|
|
268
270
|
### Screen & Navigation
|
|
269
271
|
|
|
270
272
|
| Tool | Description |
|
|
271
273
|
| -------------------------- | ------------------------------------------------------- |
|
|
272
274
|
| `appium_screenshot` | Take a screenshot of the current screen and save as PNG |
|
|
275
|
+
| `appium_element_screenshot` | Take a screenshot of a specific element by its UUID and save as PNG |
|
|
273
276
|
| `appium_scroll` | Scroll the screen vertically (up or down) |
|
|
274
277
|
| `appium_scroll_to_element` | Scroll until a specific element becomes visible |
|
|
275
278
|
| `appium_swipe` | Swipe the screen in a direction (left, right, up, down) or between custom coordinates |
|
|
@@ -309,6 +312,26 @@ Open Amazon mobile app, search for "iPhone 15 Pro", select the first search resu
|
|
|
309
312
|
|
|
310
313
|
This example demonstrates a complete e-commerce checkout flow that can be automated using MCP Appium's intelligent locator generation and test creation capabilities.
|
|
311
314
|
|
|
315
|
+
### Working in Your Native Language
|
|
316
|
+
|
|
317
|
+
**MCP Appium works seamlessly in any language** - you don't need to know English! The AI assistant understands and responds in your native language. Simply describe what you want to do in your preferred language:
|
|
318
|
+
|
|
319
|
+
**Examples in different languages:**
|
|
320
|
+
|
|
321
|
+
🇪🇸 **Spanish**: "Abre la aplicación de Amazon, busca 'iPhone 15 Pro' y agrégalo al carrito"
|
|
322
|
+
|
|
323
|
+
🇨🇳 **Chinese**: "打开Amazon应用,搜索'iPhone 15 Pro'并添加到购物车"
|
|
324
|
+
|
|
325
|
+
🇯🇵 **Japanese**: "Amazonアプリを開いて、'iPhone 15 Pro'を検索してカートに追加する"
|
|
326
|
+
|
|
327
|
+
🇰🇷 **Korean**: "Amazon 앱을 열고 'iPhone 15 Pro'를 검색한 후 장바구니에 추가"
|
|
328
|
+
|
|
329
|
+
🇫🇷 **French**: "Ouvre l'application Amazon, recherche 'iPhone 15 Pro' et ajoute-le au panier"
|
|
330
|
+
|
|
331
|
+
🇩🇪 **German**: "Öffne die Amazon App, suche nach 'iPhone 15 Pro' und füge es zum Warenkorb hinzu"
|
|
332
|
+
|
|
333
|
+
The AI will handle your requests naturally and generate the appropriate test code, regardless of the language you use.
|
|
334
|
+
|
|
312
335
|
## 🙌 Contributing
|
|
313
336
|
|
|
314
337
|
Contributions are welcome! Please feel free to submit a pull request or open an issue to discuss any changes.
|
package/dist/command.d.ts
CHANGED
|
@@ -95,7 +95,7 @@ export declare function getPageSource(driver: DriverInstance): Promise<string>;
|
|
|
95
95
|
* @param driver - The driver instance to capture from.
|
|
96
96
|
* @returns Base64-encoded PNG string.
|
|
97
97
|
*/
|
|
98
|
-
export declare function getScreenshot(driver: DriverInstance): Promise<string>;
|
|
98
|
+
export declare function getScreenshot(driver: DriverInstance, elementId?: string): Promise<string>;
|
|
99
99
|
/**
|
|
100
100
|
* Get the visible text from an element.
|
|
101
101
|
*
|
package/dist/command.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,GAAG,CAAC,CAQd;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAO3E;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,iBAQb;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,eAAe,EAAE,IAAI,CAAC,CAOvC;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,eAAe,EAAE,IAAI,CAAC,CAOvC;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,eAAe,EAAE,cAAc,EAAE,GACxE,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAO3E;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,GAAG,CAAC,CAQd;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAO3E;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,iBAQb;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,eAAe,EAAE,IAAI,CAAC,CAOvC;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,eAAe,EAAE,IAAI,CAAC,CAOvC;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,eAAe,EAAE,cAAc,EAAE,GACxE,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAO3E;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAOjB"}
|
package/dist/command.js
CHANGED
|
@@ -184,7 +184,16 @@ export async function getPageSource(driver) {
|
|
|
184
184
|
* @param driver - The driver instance to capture from.
|
|
185
185
|
* @returns Base64-encoded PNG string.
|
|
186
186
|
*/
|
|
187
|
-
export async function getScreenshot(driver) {
|
|
187
|
+
export async function getScreenshot(driver, elementId) {
|
|
188
|
+
if (elementId) {
|
|
189
|
+
if (isAndroidUiautomator2DriverSession(driver)) {
|
|
190
|
+
return await driver.getElementScreenshot(elementId);
|
|
191
|
+
}
|
|
192
|
+
else if (isXCUITestDriverSession(driver)) {
|
|
193
|
+
return await driver.getElementScreenshot(elementId);
|
|
194
|
+
}
|
|
195
|
+
return await driver.takeElementScreenshot(elementId);
|
|
196
|
+
}
|
|
188
197
|
if (isAndroidUiautomator2DriverSession(driver)) {
|
|
189
198
|
return await driver.getScreenshot();
|
|
190
199
|
}
|
package/dist/command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kCAAkC,EAClC,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAI5B;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,MAAsB,EACtB,GAAW,EACX,MAAW;IAEX,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,OAAO,MAAO,MAAiB,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAsB,EACtB,KAAa;IAEb,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,MAAO,MAAiB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAsB;IAEtB,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,iBAAiB,EAAE,CAAC;IAC1C,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,iBAAiB,EAAE,CAAC;IAC1C,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAsB;IACtD,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,CAAC,MAAM,MAAM,CAAC,WAAW,EAAE,CAAa,CAAC;IAClD,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;AAClD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAsB,EACtB,IAAa;IAEb,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,UAAU,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAsB,EACtB,WAAmB,EACnB,IAAY;IAEZ,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAClD,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,MAAO,MAAiB,CAAC,eAAe,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AACrE,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAsB,EACtB,WAAmB;IAEnB,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,MAAM,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAsB,EACtB,WAAmB;IAEnB,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,MAAM,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;AAClD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAsB;IAEtB,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;IACtC,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAsB,EACtB,SAAyE;IAEzE,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,cAAc,CAChC,SAAqD,CACtD,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAsB;IACxD,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;IACtC,CAAC;SAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,OAAO,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,13 +20,21 @@ function resolveScreenshotDir() {
|
|
|
20
20
|
* Local implementation of executeScreenshot for testing.
|
|
21
21
|
* This mirrors the implementation in screenshot.ts.
|
|
22
22
|
*/
|
|
23
|
-
async function executeScreenshot(
|
|
23
|
+
async function executeScreenshot(opts = {}) {
|
|
24
|
+
const defaultDeps = {
|
|
25
|
+
getDriver: () => null,
|
|
26
|
+
writeFile: async () => { },
|
|
27
|
+
mkdir: async () => { },
|
|
28
|
+
resolveScreenshotDir: () => '',
|
|
29
|
+
dateNow: () => 0,
|
|
30
|
+
};
|
|
31
|
+
const { deps = defaultDeps, elementId } = opts;
|
|
24
32
|
const driver = deps.getDriver();
|
|
25
33
|
if (!driver) {
|
|
26
34
|
throw new Error('No driver found');
|
|
27
35
|
}
|
|
28
36
|
try {
|
|
29
|
-
const screenshotBase64 = await driver.getScreenshot();
|
|
37
|
+
const screenshotBase64 = await driver.getScreenshot(elementId);
|
|
30
38
|
const screenshotBuffer = Buffer.from(screenshotBase64, 'base64');
|
|
31
39
|
const timestamp = deps.dateNow();
|
|
32
40
|
const filename = `screenshot_${timestamp}.png`;
|
|
@@ -34,11 +42,12 @@ async function executeScreenshot(deps) {
|
|
|
34
42
|
await deps.mkdir(screenshotDir, { recursive: true });
|
|
35
43
|
const filepath = join(screenshotDir, filename);
|
|
36
44
|
await deps.writeFile(filepath, screenshotBuffer);
|
|
45
|
+
// Return text response (UI resource is added by addUIResourceToResponse in actual implementation)
|
|
37
46
|
return {
|
|
38
47
|
content: [
|
|
39
48
|
{
|
|
40
49
|
type: 'text',
|
|
41
|
-
text: `Screenshot saved successfully to: ${
|
|
50
|
+
text: `Screenshot saved successfully to: ${filepath}`,
|
|
42
51
|
},
|
|
43
52
|
],
|
|
44
53
|
};
|
|
@@ -114,7 +123,7 @@ describe('executeScreenshot', () => {
|
|
|
114
123
|
function createMockDeps(overrides = {}) {
|
|
115
124
|
return {
|
|
116
125
|
getDriver: jest.fn(() => ({
|
|
117
|
-
getScreenshot: jest.fn(() => Promise.resolve(mockBase64)),
|
|
126
|
+
getScreenshot: jest.fn((elementId) => Promise.resolve(mockBase64)),
|
|
118
127
|
})),
|
|
119
128
|
writeFile: jest.fn(() => Promise.resolve()),
|
|
120
129
|
mkdir: jest.fn(() => Promise.resolve()),
|
|
@@ -127,16 +136,16 @@ describe('executeScreenshot', () => {
|
|
|
127
136
|
const deps = createMockDeps({
|
|
128
137
|
getDriver: jest.fn(() => null),
|
|
129
138
|
});
|
|
130
|
-
await expect(executeScreenshot(deps)).rejects.toThrow('No driver found');
|
|
139
|
+
await expect(executeScreenshot({ deps })).rejects.toThrow('No driver found');
|
|
131
140
|
});
|
|
132
141
|
test('should return success content with filename', async () => {
|
|
133
142
|
const deps = createMockDeps();
|
|
134
|
-
const result = await executeScreenshot(deps);
|
|
143
|
+
const result = await executeScreenshot({ deps });
|
|
135
144
|
expect(result).toEqual({
|
|
136
145
|
content: [
|
|
137
146
|
{
|
|
138
147
|
type: 'text',
|
|
139
|
-
text: `Screenshot saved successfully to: screenshot_${mockTimestamp}.png`,
|
|
148
|
+
text: `Screenshot saved successfully to: /mock/screenshots/screenshot_${mockTimestamp}.png`,
|
|
140
149
|
},
|
|
141
150
|
],
|
|
142
151
|
});
|
|
@@ -146,20 +155,20 @@ describe('executeScreenshot', () => {
|
|
|
146
155
|
const deps = createMockDeps({
|
|
147
156
|
resolveScreenshotDir: jest.fn(() => customDir),
|
|
148
157
|
});
|
|
149
|
-
await executeScreenshot(deps);
|
|
158
|
+
await executeScreenshot({ deps });
|
|
150
159
|
expect(deps.mkdir).toHaveBeenCalledWith(customDir, { recursive: true });
|
|
151
160
|
expect(deps.writeFile).toHaveBeenCalledWith(join(customDir, `screenshot_${mockTimestamp}.png`), expect.any(Buffer));
|
|
152
161
|
});
|
|
153
162
|
test('should create directory with recursive option', async () => {
|
|
154
163
|
const deps = createMockDeps();
|
|
155
|
-
await executeScreenshot(deps);
|
|
164
|
+
await executeScreenshot({ deps });
|
|
156
165
|
expect(deps.mkdir).toHaveBeenCalledWith('/mock/screenshots', {
|
|
157
166
|
recursive: true,
|
|
158
167
|
});
|
|
159
168
|
});
|
|
160
169
|
test('should write screenshot buffer to correct filepath', async () => {
|
|
161
170
|
const deps = createMockDeps();
|
|
162
|
-
await executeScreenshot(deps);
|
|
171
|
+
await executeScreenshot({ deps });
|
|
163
172
|
expect(deps.writeFile).toHaveBeenCalledWith(`/mock/screenshots/screenshot_${mockTimestamp}.png`, Buffer.from(mockBase64, 'base64'));
|
|
164
173
|
});
|
|
165
174
|
test('should return error content when screenshot fails', async () => {
|
|
@@ -169,7 +178,7 @@ describe('executeScreenshot', () => {
|
|
|
169
178
|
getScreenshot: jest.fn(() => Promise.reject(new Error(errorMessage))),
|
|
170
179
|
})),
|
|
171
180
|
});
|
|
172
|
-
const result = await executeScreenshot(deps);
|
|
181
|
+
const result = await executeScreenshot({ deps });
|
|
173
182
|
expect(result).toEqual({
|
|
174
183
|
content: [
|
|
175
184
|
{
|
|
@@ -184,7 +193,7 @@ describe('executeScreenshot', () => {
|
|
|
184
193
|
const deps = createMockDeps({
|
|
185
194
|
mkdir: jest.fn(() => Promise.reject(new Error(errorMessage))),
|
|
186
195
|
});
|
|
187
|
-
const result = await executeScreenshot(deps);
|
|
196
|
+
const result = await executeScreenshot({ deps });
|
|
188
197
|
expect(result).toEqual({
|
|
189
198
|
content: [
|
|
190
199
|
{
|
|
@@ -199,7 +208,7 @@ describe('executeScreenshot', () => {
|
|
|
199
208
|
const deps = createMockDeps({
|
|
200
209
|
writeFile: jest.fn(() => Promise.reject(new Error(errorMessage))),
|
|
201
210
|
});
|
|
202
|
-
const result = await executeScreenshot(deps);
|
|
211
|
+
const result = await executeScreenshot({ deps });
|
|
203
212
|
expect(result).toEqual({
|
|
204
213
|
content: [
|
|
205
214
|
{
|
|
@@ -209,5 +218,26 @@ describe('executeScreenshot', () => {
|
|
|
209
218
|
],
|
|
210
219
|
});
|
|
211
220
|
});
|
|
221
|
+
test('should pass elementId to getScreenshot when provided', async () => {
|
|
222
|
+
const elementId = 'element-uuid-123';
|
|
223
|
+
const mockScreenshot = jest.fn((elementId) => Promise.resolve(mockBase64));
|
|
224
|
+
const deps = createMockDeps({
|
|
225
|
+
getDriver: jest.fn(() => ({
|
|
226
|
+
getScreenshot: mockScreenshot,
|
|
227
|
+
})),
|
|
228
|
+
});
|
|
229
|
+
await executeScreenshot({ deps, elementId });
|
|
230
|
+
expect(mockScreenshot).toHaveBeenCalledWith(elementId);
|
|
231
|
+
});
|
|
232
|
+
test('should pass undefined elementId to getScreenshot when not provided', async () => {
|
|
233
|
+
const mockScreenshot = jest.fn((elementId) => Promise.resolve(mockBase64));
|
|
234
|
+
const deps = createMockDeps({
|
|
235
|
+
getDriver: jest.fn(() => ({
|
|
236
|
+
getScreenshot: mockScreenshot,
|
|
237
|
+
})),
|
|
238
|
+
});
|
|
239
|
+
await executeScreenshot({ deps });
|
|
240
|
+
expect(mockScreenshot).toHaveBeenCalledWith(undefined);
|
|
241
|
+
});
|
|
212
242
|
});
|
|
213
243
|
//# sourceMappingURL=screenshot.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.test.js","sourceRoot":"","sources":["../../src/tests/screenshot.test.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,UAAU,EACV,SAAS,EACT,IAAI,GACL,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B;;;;GAIG;AACH,SAAS,oBAAoB;IAC3B,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAElD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC;IACrB,CAAC;IAED,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC,CAAC;AAC5C,CAAC;
|
|
1
|
+
{"version":3,"file":"screenshot.test.js","sourceRoot":"","sources":["../../src/tests/screenshot.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAiClC,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAkH3D"}
|
package/dist/tools/index.js
CHANGED
|
@@ -20,7 +20,8 @@ import dragAndDrop from './interactions/drag-and-drop.js';
|
|
|
20
20
|
import setValue from './interactions/set-value.js';
|
|
21
21
|
import getText from './interactions/get-text.js';
|
|
22
22
|
import getPageSource from './interactions/get-page-source.js';
|
|
23
|
-
import
|
|
23
|
+
import handleAlert from './interactions/handle-alert.js';
|
|
24
|
+
import { screenshot, elementScreenshot } from './interactions/screenshot.js';
|
|
24
25
|
import activateApp from './app-management/activate-app.js';
|
|
25
26
|
import installApp from './app-management/install-app.js';
|
|
26
27
|
import uninstallApp from './app-management/uninstall-app.js';
|
|
@@ -112,7 +113,9 @@ export default function registerTools(server) {
|
|
|
112
113
|
setValue(server);
|
|
113
114
|
getText(server);
|
|
114
115
|
getPageSource(server);
|
|
116
|
+
handleAlert(server);
|
|
115
117
|
screenshot(server);
|
|
118
|
+
elementScreenshot(server);
|
|
116
119
|
// App Management
|
|
117
120
|
activateApp(server);
|
|
118
121
|
installApp(server);
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAeA,OAAO,GAAG,MAAM,cAAc,CAAC;AAC/B,OAAO,YAAY,MAAM,kCAAkC,CAAC;AAC5D,OAAO,aAAa,MAAM,6BAA6B,CAAC;AACxD,OAAO,aAAa,MAAM,6BAA6B,CAAC;AACxD,OAAO,gBAAgB,MAAM,+BAA+B,CAAC;AAC7D,OAAO,cAAc,MAAM,8BAA8B,CAAC;AAC1D,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,aAAa,MAAM,yBAAyB,CAAC;AACpD,OAAO,QAAQ,MAAM,oBAAoB,CAAC;AAC1C,OAAO,UAAU,MAAM,sBAAsB,CAAC;AAC9C,OAAO,YAAY,MAAM,qCAAqC,CAAC;AAC/D,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAC7C,OAAO,eAAe,MAAM,oCAAoC,CAAC;AACjE,OAAO,KAAK,MAAM,wBAAwB,CAAC;AAC3C,OAAO,WAAW,MAAM,wBAAwB,CAAC;AACjD,OAAO,YAAY,MAAM,yBAAyB,CAAC;AACnD,OAAO,SAAS,MAAM,8BAA8B,CAAC;AACrD,OAAO,SAAS,MAAM,8BAA8B,CAAC;AACrD,OAAO,WAAW,MAAM,iCAAiC,CAAC;AAC1D,OAAO,QAAQ,MAAM,6BAA6B,CAAC;AACnD,OAAO,OAAO,MAAM,4BAA4B,CAAC;AACjD,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,UAAU,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FastMCP } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const handleAlertSchema: z.ZodObject<{
|
|
4
|
+
action: z.ZodEnum<{
|
|
5
|
+
accept: "accept";
|
|
6
|
+
dismiss: "dismiss";
|
|
7
|
+
}>;
|
|
8
|
+
buttonLabel: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export default function handleAlert(server: FastMCP): void;
|
|
11
|
+
//# sourceMappingURL=handle-alert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-alert.d.ts","sourceRoot":"","sources":["../../../src/tools/interactions/handle-alert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,iBAAiB;;;;;;iBAe5B,CAAC;AAsFH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CA6DzD"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { generateAllElementLocators } from '../../locators/generate-all-locators.js';
|
|
3
|
+
import { getDriver, getPlatformName, PLATFORM } from '../../session-store.js';
|
|
4
|
+
export const handleAlertSchema = z.object({
|
|
5
|
+
action: z
|
|
6
|
+
.enum(['accept', 'dismiss'])
|
|
7
|
+
.describe('Action to perform on the alert: accept or dismiss'),
|
|
8
|
+
buttonLabel: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe(`Optional label of the button to click. Common permission dialog buttons:
|
|
12
|
+
Android: "While using the app", "Only this time", "Don't allow"
|
|
13
|
+
iOS: "Always" or "Allow Always", "Once" or "Allow Once", "Don't allow"
|
|
14
|
+
Standard: "OK", "Cancel", "Allow", "Deny"
|
|
15
|
+
If not provided, uses default button based on action.
|
|
16
|
+
Use appium_get_page_source or generate_locators to inspect the screen and discover exact labels.`),
|
|
17
|
+
});
|
|
18
|
+
const ANDROID_LOCATOR_STRATEGY_ORDER = [
|
|
19
|
+
'id',
|
|
20
|
+
'accessibility id',
|
|
21
|
+
'xpath',
|
|
22
|
+
'-android uiautomator',
|
|
23
|
+
'class name',
|
|
24
|
+
];
|
|
25
|
+
async function handleAndroidAlert(driver, action, buttonLabel) {
|
|
26
|
+
if (buttonLabel) {
|
|
27
|
+
const pageSource = await driver.getPageSource();
|
|
28
|
+
const elements = generateAllElementLocators(pageSource, true, 'uiautomator2', {
|
|
29
|
+
fetchableOnly: true,
|
|
30
|
+
});
|
|
31
|
+
const normalizedLabel = buttonLabel.trim();
|
|
32
|
+
const match = elements.find((el) => (el.text?.trim() === normalizedLabel ||
|
|
33
|
+
el.contentDesc?.trim() === normalizedLabel) &&
|
|
34
|
+
el.clickable) ??
|
|
35
|
+
elements.find((el) => el.text?.trim() === normalizedLabel ||
|
|
36
|
+
el.contentDesc?.trim() === normalizedLabel);
|
|
37
|
+
if (!match) {
|
|
38
|
+
throw new Error(`No element found with text or content-desc "${buttonLabel}"`);
|
|
39
|
+
}
|
|
40
|
+
let button = null;
|
|
41
|
+
for (const strategy of ANDROID_LOCATOR_STRATEGY_ORDER) {
|
|
42
|
+
const selector = match.locators[strategy];
|
|
43
|
+
if (!selector) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
button = await driver.findElement(strategy, selector);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!button) {
|
|
55
|
+
throw new Error('Could not find element with any generated locator; it may have disappeared');
|
|
56
|
+
}
|
|
57
|
+
const buttonUUID = button.ELEMENT || button;
|
|
58
|
+
await driver.click(buttonUUID);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
if (action === 'accept') {
|
|
62
|
+
await driver.execute('mobile: acceptAlert', {});
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
await driver.execute('mobile: dismissAlert', {});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function handleiOSAlert(driver, action, buttonLabel) {
|
|
70
|
+
const params = { action };
|
|
71
|
+
if (buttonLabel) {
|
|
72
|
+
params.buttonLabel = buttonLabel;
|
|
73
|
+
}
|
|
74
|
+
await driver.execute('mobile: alert', params);
|
|
75
|
+
}
|
|
76
|
+
export default function handleAlert(server) {
|
|
77
|
+
server.addTool({
|
|
78
|
+
name: 'appium_handle_alert',
|
|
79
|
+
description: `Handle system alerts or dialogs that do not belong to the app.
|
|
80
|
+
Use this to dismiss or accept alerts programmatically instead of using autoDismissAlerts capability.
|
|
81
|
+
Supports permission dialogs with buttons like:
|
|
82
|
+
- Android: "While using the app", "Only this time", "Don't allow"
|
|
83
|
+
- iOS: "Always", "Allow Once", "Don't allow"
|
|
84
|
+
For iOS: Uses mobile: alert execute command.
|
|
85
|
+
For Android: Uses mobile: acceptAlert/dismissAlert or searches the current page source for an element whose text or content-desc matches the label, then uses generated locators to find and click it (no hardcoded resource IDs or XPaths).
|
|
86
|
+
If no alert is present, the error is caught and returned gracefully.
|
|
87
|
+
To discover button labels and screen structure first, use appium_get_page_source (XML hierarchy) or generate_locators (interactable elements with text/content-desc).`,
|
|
88
|
+
parameters: handleAlertSchema,
|
|
89
|
+
annotations: {
|
|
90
|
+
readOnlyHint: false,
|
|
91
|
+
openWorldHint: false,
|
|
92
|
+
},
|
|
93
|
+
execute: async (args, context) => {
|
|
94
|
+
const driver = getDriver();
|
|
95
|
+
if (!driver) {
|
|
96
|
+
throw new Error('No driver found');
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const platform = getPlatformName(driver);
|
|
100
|
+
if (platform === PLATFORM.android) {
|
|
101
|
+
await handleAndroidAlert(driver, args.action, args.buttonLabel);
|
|
102
|
+
}
|
|
103
|
+
else if (platform === PLATFORM.ios) {
|
|
104
|
+
await handleiOSAlert(driver, args.action, args.buttonLabel);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: 'text',
|
|
113
|
+
text: `Successfully ${args.action}ed alert${args.buttonLabel ? ` with button "${args.buttonLabel}"` : ''}`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
const contextStr = args.buttonLabel
|
|
120
|
+
? `action=${args.action}, buttonLabel="${args.buttonLabel}"`
|
|
121
|
+
: `action=${args.action}`;
|
|
122
|
+
return {
|
|
123
|
+
content: [
|
|
124
|
+
{
|
|
125
|
+
type: 'text',
|
|
126
|
+
text: `Failed to handle alert (${contextStr}). err: ${err.toString()}`,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=handle-alert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-alert.js","sourceRoot":"","sources":["../../../src/tools/interactions/handle-alert.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAE9E,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;SAC3B,QAAQ,CAAC,mDAAmD,CAAC;IAChE,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP;;;;;iGAK2F,CAC5F;CACJ,CAAC,CAAC;AAEH,MAAM,8BAA8B,GAAG;IACrC,IAAI;IACJ,kBAAkB;IAClB,OAAO;IACP,sBAAsB;IACtB,YAAY;CACb,CAAC;AAEF,KAAK,UAAU,kBAAkB,CAC/B,MAAW,EACX,MAAc,EACd,WAAoB;IAEpB,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;QAChD,MAAM,QAAQ,GAAG,0BAA0B,CACzC,UAAU,EACV,IAAI,EACJ,cAAc,EACd;YACE,aAAa,EAAE,IAAI;SACpB,CACF,CAAC;QACF,MAAM,eAAe,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;QAC3C,MAAM,KAAK,GACT,QAAQ,CAAC,IAAI,CACX,CAAC,EAAE,EAAE,EAAE,CACL,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,eAAe;YAClC,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,KAAK,eAAe,CAAC;YAC7C,EAAE,CAAC,SAAS,CACf;YACD,QAAQ,CAAC,IAAI,CACX,CAAC,EAAE,EAAE,EAAE,CACL,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,eAAe;gBACnC,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,KAAK,eAAe,CAC7C,CAAC;QAEJ,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACb,+CAA+C,WAAW,GAAG,CAC9D,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,GAAQ,IAAI,CAAC;QACvB,KAAK,MAAM,QAAQ,IAAI,8BAA8B,EAAE,CAAC;YACtD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBACtD,MAAM;YACR,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,4EAA4E,CAC7E,CAAC;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;QAC5C,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxB,MAAM,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,MAAW,EACX,MAAc,EACd,WAAoB;IAEpB,MAAM,MAAM,GAAQ,EAAE,MAAM,EAAE,CAAC;IAC/B,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,CAAC,WAAW,GAAG,WAAW,CAAC;IACnC,CAAC;IACD,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,MAAe;IACjD,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EAAE;;;;;;;;sKAQqJ;QAClK,UAAU,EAAE,iBAAiB;QAC7B,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,aAAa,EAAE,KAAK;SACrB;QACD,OAAO,EAAE,KAAK,EAAE,IAAS,EAAE,OAAY,EAAgB,EAAE;YACvD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBAEzC,IAAI,QAAQ,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;oBAClC,MAAM,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;gBAClE,CAAC;qBAAM,IAAI,QAAQ,KAAK,QAAQ,CAAC,GAAG,EAAE,CAAC;oBACrC,MAAM,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;gBAC9D,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,KAAK,CACb,yBAAyB,QAAQ,uCAAuC,CACzE,CAAC;gBACJ,CAAC;gBAED,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,gBAAgB,IAAI,CAAC,MAAM,WAC/B,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,EAC5D,EAAE;yBACH;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW;oBACjC,CAAC,CAAC,UAAU,IAAI,CAAC,MAAM,kBAAkB,IAAI,CAAC,WAAW,GAAG;oBAC5D,CAAC,CAAC,UAAU,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC5B,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,2BAA2B,UAAU,WAAW,GAAG,CAAC,QAAQ,EAAE,EAAE;yBACvE;qBACF;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -15,6 +15,10 @@ export interface ScreenshotDeps {
|
|
|
15
15
|
resolveScreenshotDir: typeof resolveScreenshotDir;
|
|
16
16
|
dateNow: () => number;
|
|
17
17
|
}
|
|
18
|
-
export declare function executeScreenshot(
|
|
19
|
-
|
|
18
|
+
export declare function executeScreenshot(opts: {
|
|
19
|
+
deps?: ScreenshotDeps;
|
|
20
|
+
elementId?: any;
|
|
21
|
+
}): Promise<any>;
|
|
22
|
+
export declare function screenshot(server: FastMCP): void;
|
|
23
|
+
export declare function elementScreenshot(server: FastMCP): void;
|
|
20
24
|
//# sourceMappingURL=screenshot.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../../src/tools/interactions/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../../src/tools/interactions/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAWpD;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAY7C;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,sBAAsB,CAAC;IACxC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAClD,OAAO,EAAE,MAAM,MAAM,CAAC;CACvB;AAUD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,MAAC;CACZ,GAAG,OAAO,CAAC,GAAG,CAAC,CAqDf;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAWhD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAmBvD"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { getDriver } from '../../session-store.js';
|
|
2
|
+
import { elementUUIDScheme } from '../../schema.js';
|
|
2
3
|
import { writeFile, mkdir } from 'node:fs/promises';
|
|
3
4
|
import { join, isAbsolute } from 'node:path';
|
|
4
5
|
import * as os from 'node:os';
|
|
5
6
|
import { createUIResource, createScreenshotViewerUI, addUIResourceToResponse, } from '../../ui/mcp-ui-utils.js';
|
|
6
7
|
import { getScreenshot } from '../../command.js';
|
|
8
|
+
import z from 'zod';
|
|
7
9
|
/**
|
|
8
10
|
* Resolves the screenshot directory path.
|
|
9
11
|
* - If SCREENSHOTS_DIR is not set, returns process.cwd()
|
|
@@ -27,13 +29,14 @@ const defaultDeps = {
|
|
|
27
29
|
resolveScreenshotDir,
|
|
28
30
|
dateNow: () => Date.now(),
|
|
29
31
|
};
|
|
30
|
-
export async function executeScreenshot(
|
|
32
|
+
export async function executeScreenshot(opts) {
|
|
33
|
+
const { deps = defaultDeps, elementId } = opts;
|
|
31
34
|
const driver = deps.getDriver();
|
|
32
35
|
if (!driver) {
|
|
33
36
|
throw new Error('No driver found');
|
|
34
37
|
}
|
|
35
38
|
try {
|
|
36
|
-
const screenshotBase64 = await getScreenshot(driver);
|
|
39
|
+
const screenshotBase64 = await getScreenshot(driver, elementId);
|
|
37
40
|
// Convert base64 to buffer
|
|
38
41
|
const screenshotBuffer = Buffer.from(screenshotBase64, 'base64');
|
|
39
42
|
// Generate filename with timestamp
|
|
@@ -68,7 +71,7 @@ export async function executeScreenshot(deps = defaultDeps) {
|
|
|
68
71
|
};
|
|
69
72
|
}
|
|
70
73
|
}
|
|
71
|
-
export
|
|
74
|
+
export function screenshot(server) {
|
|
72
75
|
server.addTool({
|
|
73
76
|
name: 'appium_screenshot',
|
|
74
77
|
description: 'Take a screenshot of the current screen and return as PNG image',
|
|
@@ -76,7 +79,24 @@ export default function screenshot(server) {
|
|
|
76
79
|
readOnlyHint: false,
|
|
77
80
|
openWorldHint: false,
|
|
78
81
|
},
|
|
79
|
-
execute: async () => executeScreenshot(),
|
|
82
|
+
execute: async () => executeScreenshot({}),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export function elementScreenshot(server) {
|
|
86
|
+
const elementScreenshotSchema = z.object({
|
|
87
|
+
elementUUID: elementUUIDScheme,
|
|
88
|
+
});
|
|
89
|
+
server.addTool({
|
|
90
|
+
name: 'appium_element_screenshot',
|
|
91
|
+
description: 'Take a screenshot of the given element uuid and return as PNG image',
|
|
92
|
+
parameters: elementScreenshotSchema,
|
|
93
|
+
annotations: {
|
|
94
|
+
readOnlyHint: false,
|
|
95
|
+
openWorldHint: false,
|
|
96
|
+
},
|
|
97
|
+
execute: async (args) => executeScreenshot({
|
|
98
|
+
elementId: args.elementUUID,
|
|
99
|
+
}),
|
|
80
100
|
});
|
|
81
101
|
}
|
|
82
102
|
//# sourceMappingURL=screenshot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../../src/tools/interactions/screenshot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../../src/tools/interactions/screenshot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAElD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC;IACrB,CAAC;IAED,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC,CAAC;AAC5C,CAAC;AAUD,MAAM,WAAW,GAAmB;IAClC,SAAS;IACT,SAAS;IACT,KAAK;IACL,oBAAoB;IACpB,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;CAC1B,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAGvC;IACC,MAAM,EAAE,IAAI,GAAG,WAAW,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAE/C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,gBAAgB,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAEhE,2BAA2B;QAC3B,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QAEjE,mCAAmC;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,cAAc,SAAS,MAAM,CAAC;QAC/C,MAAM,aAAa,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAElD,yCAAyC;QACzC,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAErD,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QAE/C,0BAA0B;QAC1B,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;QAEjD,MAAM,YAAY,GAAG;YACnB,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,qCAAqC,QAAQ,EAAE;iBACtD;aACF;SACF,CAAC;QAEF,uCAAuC;QACvC,MAAM,UAAU,GAAG,gBAAgB,CACjC,qCAAqC,IAAI,CAAC,GAAG,EAAE,EAAE,EACjD,wBAAwB,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CACrD,CAAC;QAEF,OAAO,uBAAuB,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC3D,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,mCAAmC,GAAG,CAAC,QAAQ,EAAE,EAAE;iBAC1D;aACF;SACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAe;IACxC,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,mBAAmB;QACzB,WAAW,EACT,iEAAiE;QACnE,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,aAAa,EAAE,KAAK;SACrB;QACD,OAAO,EAAE,KAAK,IAAkB,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;KACzD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAe;IAC/C,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;QACvC,WAAW,EAAE,iBAAiB;KAC/B,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,2BAA2B;QACjC,WAAW,EACT,qEAAqE;QACvE,UAAU,EAAE,uBAAuB;QACnC,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,aAAa,EAAE,KAAK;SACrB;QACD,OAAO,EAAE,KAAK,EAAE,IAAS,EAAgB,EAAE,CACzC,iBAAiB,CAAC;YAChB,SAAS,EAAE,IAAI,CAAC,WAAW;SAC5B,CAAC;KACL,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"name": "io.github.appium/appium-mcp",
|
|
4
4
|
"title": "MCP Appium - Mobile Development and Automation Server",
|
|
5
5
|
"description": "MCP server for Appium mobile automation on iOS and Android devices with test creation tools.",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.15.0",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "appium-mcp",
|
|
11
|
-
"version": "1.
|
|
11
|
+
"version": "1.15.0",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|
package/src/command.ts
CHANGED
|
@@ -219,7 +219,19 @@ export async function getPageSource(driver: DriverInstance): Promise<string> {
|
|
|
219
219
|
* @param driver - The driver instance to capture from.
|
|
220
220
|
* @returns Base64-encoded PNG string.
|
|
221
221
|
*/
|
|
222
|
-
export async function getScreenshot(
|
|
222
|
+
export async function getScreenshot(
|
|
223
|
+
driver: DriverInstance,
|
|
224
|
+
elementId?: string
|
|
225
|
+
): Promise<string> {
|
|
226
|
+
if (elementId) {
|
|
227
|
+
if (isAndroidUiautomator2DriverSession(driver)) {
|
|
228
|
+
return await driver.getElementScreenshot(elementId);
|
|
229
|
+
} else if (isXCUITestDriverSession(driver)) {
|
|
230
|
+
return await driver.getElementScreenshot(elementId);
|
|
231
|
+
}
|
|
232
|
+
return await driver.takeElementScreenshot(elementId);
|
|
233
|
+
}
|
|
234
|
+
|
|
223
235
|
if (isAndroidUiautomator2DriverSession(driver)) {
|
|
224
236
|
return await driver.getScreenshot();
|
|
225
237
|
} else if (isXCUITestDriverSession(driver)) {
|
|
@@ -32,7 +32,9 @@ function resolveScreenshotDir(): string {
|
|
|
32
32
|
* Interface for screenshot dependencies (mirrors screenshot.ts).
|
|
33
33
|
*/
|
|
34
34
|
interface ScreenshotDeps {
|
|
35
|
-
getDriver: () => {
|
|
35
|
+
getDriver: () => {
|
|
36
|
+
getScreenshot: (elementId?: string) => Promise<string>;
|
|
37
|
+
} | null;
|
|
36
38
|
writeFile: (path: string, data: Buffer) => Promise<void>;
|
|
37
39
|
mkdir: (path: string, options: { recursive: boolean }) => Promise<void>;
|
|
38
40
|
resolveScreenshotDir: () => string;
|
|
@@ -43,14 +45,29 @@ interface ScreenshotDeps {
|
|
|
43
45
|
* Local implementation of executeScreenshot for testing.
|
|
44
46
|
* This mirrors the implementation in screenshot.ts.
|
|
45
47
|
*/
|
|
46
|
-
async function executeScreenshot(
|
|
48
|
+
async function executeScreenshot(
|
|
49
|
+
opts: {
|
|
50
|
+
deps?: ScreenshotDeps;
|
|
51
|
+
elementId?: string;
|
|
52
|
+
} = {}
|
|
53
|
+
): Promise<any> {
|
|
54
|
+
const defaultDeps: ScreenshotDeps = {
|
|
55
|
+
getDriver: () => null as any,
|
|
56
|
+
writeFile: async () => {},
|
|
57
|
+
mkdir: async () => {},
|
|
58
|
+
resolveScreenshotDir: () => '',
|
|
59
|
+
dateNow: () => 0,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const { deps = defaultDeps, elementId } = opts;
|
|
63
|
+
|
|
47
64
|
const driver = deps.getDriver();
|
|
48
65
|
if (!driver) {
|
|
49
66
|
throw new Error('No driver found');
|
|
50
67
|
}
|
|
51
68
|
|
|
52
69
|
try {
|
|
53
|
-
const screenshotBase64 = await driver.getScreenshot();
|
|
70
|
+
const screenshotBase64 = await driver.getScreenshot(elementId);
|
|
54
71
|
const screenshotBuffer = Buffer.from(screenshotBase64, 'base64');
|
|
55
72
|
const timestamp = deps.dateNow();
|
|
56
73
|
const filename = `screenshot_${timestamp}.png`;
|
|
@@ -60,11 +77,12 @@ async function executeScreenshot(deps: ScreenshotDeps): Promise<any> {
|
|
|
60
77
|
const filepath = join(screenshotDir, filename);
|
|
61
78
|
await deps.writeFile(filepath, screenshotBuffer);
|
|
62
79
|
|
|
80
|
+
// Return text response (UI resource is added by addUIResourceToResponse in actual implementation)
|
|
63
81
|
return {
|
|
64
82
|
content: [
|
|
65
83
|
{
|
|
66
84
|
type: 'text',
|
|
67
|
-
text: `Screenshot saved successfully to: ${
|
|
85
|
+
text: `Screenshot saved successfully to: ${filepath}`,
|
|
68
86
|
},
|
|
69
87
|
],
|
|
70
88
|
};
|
|
@@ -152,7 +170,9 @@ describe('executeScreenshot', () => {
|
|
|
152
170
|
): ScreenshotDeps {
|
|
153
171
|
return {
|
|
154
172
|
getDriver: jest.fn(() => ({
|
|
155
|
-
getScreenshot: jest.fn(() =>
|
|
173
|
+
getScreenshot: jest.fn((elementId?: string) =>
|
|
174
|
+
Promise.resolve(mockBase64)
|
|
175
|
+
),
|
|
156
176
|
})) as any,
|
|
157
177
|
writeFile: jest.fn(() => Promise.resolve()) as any,
|
|
158
178
|
mkdir: jest.fn(() => Promise.resolve()) as any,
|
|
@@ -167,19 +187,21 @@ describe('executeScreenshot', () => {
|
|
|
167
187
|
getDriver: jest.fn(() => null) as any,
|
|
168
188
|
});
|
|
169
189
|
|
|
170
|
-
await expect(executeScreenshot(deps)).rejects.toThrow(
|
|
190
|
+
await expect(executeScreenshot({ deps })).rejects.toThrow(
|
|
191
|
+
'No driver found'
|
|
192
|
+
);
|
|
171
193
|
});
|
|
172
194
|
|
|
173
195
|
test('should return success content with filename', async () => {
|
|
174
196
|
const deps = createMockDeps();
|
|
175
197
|
|
|
176
|
-
const result = await executeScreenshot(deps);
|
|
198
|
+
const result = await executeScreenshot({ deps });
|
|
177
199
|
|
|
178
200
|
expect(result).toEqual({
|
|
179
201
|
content: [
|
|
180
202
|
{
|
|
181
203
|
type: 'text',
|
|
182
|
-
text: `Screenshot saved successfully to: screenshot_${mockTimestamp}.png`,
|
|
204
|
+
text: `Screenshot saved successfully to: /mock/screenshots/screenshot_${mockTimestamp}.png`,
|
|
183
205
|
},
|
|
184
206
|
],
|
|
185
207
|
});
|
|
@@ -191,7 +213,7 @@ describe('executeScreenshot', () => {
|
|
|
191
213
|
resolveScreenshotDir: jest.fn(() => customDir) as any,
|
|
192
214
|
});
|
|
193
215
|
|
|
194
|
-
await executeScreenshot(deps);
|
|
216
|
+
await executeScreenshot({ deps });
|
|
195
217
|
|
|
196
218
|
expect(deps.mkdir).toHaveBeenCalledWith(customDir, { recursive: true });
|
|
197
219
|
expect(deps.writeFile).toHaveBeenCalledWith(
|
|
@@ -203,7 +225,7 @@ describe('executeScreenshot', () => {
|
|
|
203
225
|
test('should create directory with recursive option', async () => {
|
|
204
226
|
const deps = createMockDeps();
|
|
205
227
|
|
|
206
|
-
await executeScreenshot(deps);
|
|
228
|
+
await executeScreenshot({ deps });
|
|
207
229
|
|
|
208
230
|
expect(deps.mkdir).toHaveBeenCalledWith('/mock/screenshots', {
|
|
209
231
|
recursive: true,
|
|
@@ -213,7 +235,7 @@ describe('executeScreenshot', () => {
|
|
|
213
235
|
test('should write screenshot buffer to correct filepath', async () => {
|
|
214
236
|
const deps = createMockDeps();
|
|
215
237
|
|
|
216
|
-
await executeScreenshot(deps);
|
|
238
|
+
await executeScreenshot({ deps });
|
|
217
239
|
|
|
218
240
|
expect(deps.writeFile).toHaveBeenCalledWith(
|
|
219
241
|
`/mock/screenshots/screenshot_${mockTimestamp}.png`,
|
|
@@ -229,7 +251,7 @@ describe('executeScreenshot', () => {
|
|
|
229
251
|
})) as any,
|
|
230
252
|
});
|
|
231
253
|
|
|
232
|
-
const result = await executeScreenshot(deps);
|
|
254
|
+
const result = await executeScreenshot({ deps });
|
|
233
255
|
|
|
234
256
|
expect(result).toEqual({
|
|
235
257
|
content: [
|
|
@@ -247,7 +269,7 @@ describe('executeScreenshot', () => {
|
|
|
247
269
|
mkdir: jest.fn(() => Promise.reject(new Error(errorMessage))) as any,
|
|
248
270
|
});
|
|
249
271
|
|
|
250
|
-
const result = await executeScreenshot(deps);
|
|
272
|
+
const result = await executeScreenshot({ deps });
|
|
251
273
|
|
|
252
274
|
expect(result).toEqual({
|
|
253
275
|
content: [
|
|
@@ -265,7 +287,7 @@ describe('executeScreenshot', () => {
|
|
|
265
287
|
writeFile: jest.fn(() => Promise.reject(new Error(errorMessage))) as any,
|
|
266
288
|
});
|
|
267
289
|
|
|
268
|
-
const result = await executeScreenshot(deps);
|
|
290
|
+
const result = await executeScreenshot({ deps });
|
|
269
291
|
|
|
270
292
|
expect(result).toEqual({
|
|
271
293
|
content: [
|
|
@@ -276,4 +298,35 @@ describe('executeScreenshot', () => {
|
|
|
276
298
|
],
|
|
277
299
|
});
|
|
278
300
|
});
|
|
301
|
+
|
|
302
|
+
test('should pass elementId to getScreenshot when provided', async () => {
|
|
303
|
+
const elementId = 'element-uuid-123';
|
|
304
|
+
const mockScreenshot = jest.fn<(elementId?: string) => Promise<string>>(
|
|
305
|
+
(elementId?: string) => Promise.resolve(mockBase64)
|
|
306
|
+
);
|
|
307
|
+
const deps = createMockDeps({
|
|
308
|
+
getDriver: jest.fn(() => ({
|
|
309
|
+
getScreenshot: mockScreenshot as any,
|
|
310
|
+
})) as any,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
await executeScreenshot({ deps, elementId });
|
|
314
|
+
|
|
315
|
+
expect(mockScreenshot).toHaveBeenCalledWith(elementId);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test('should pass undefined elementId to getScreenshot when not provided', async () => {
|
|
319
|
+
const mockScreenshot = jest.fn<(elementId?: string) => Promise<string>>(
|
|
320
|
+
(elementId?: string) => Promise.resolve(mockBase64)
|
|
321
|
+
);
|
|
322
|
+
const deps = createMockDeps({
|
|
323
|
+
getDriver: jest.fn(() => ({
|
|
324
|
+
getScreenshot: mockScreenshot as any,
|
|
325
|
+
})) as any,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
await executeScreenshot({ deps });
|
|
329
|
+
|
|
330
|
+
expect(mockScreenshot).toHaveBeenCalledWith(undefined);
|
|
331
|
+
});
|
|
279
332
|
});
|
package/src/tools/index.ts
CHANGED
|
@@ -35,7 +35,8 @@ import dragAndDrop from './interactions/drag-and-drop.js';
|
|
|
35
35
|
import setValue from './interactions/set-value.js';
|
|
36
36
|
import getText from './interactions/get-text.js';
|
|
37
37
|
import getPageSource from './interactions/get-page-source.js';
|
|
38
|
-
import
|
|
38
|
+
import handleAlert from './interactions/handle-alert.js';
|
|
39
|
+
import { screenshot, elementScreenshot } from './interactions/screenshot.js';
|
|
39
40
|
import activateApp from './app-management/activate-app.js';
|
|
40
41
|
import installApp from './app-management/install-app.js';
|
|
41
42
|
import uninstallApp from './app-management/uninstall-app.js';
|
|
@@ -136,7 +137,9 @@ export default function registerTools(server: FastMCP): void {
|
|
|
136
137
|
setValue(server);
|
|
137
138
|
getText(server);
|
|
138
139
|
getPageSource(server);
|
|
140
|
+
handleAlert(server);
|
|
139
141
|
screenshot(server);
|
|
142
|
+
elementScreenshot(server);
|
|
140
143
|
|
|
141
144
|
// App Management
|
|
142
145
|
activateApp(server);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { FastMCP } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { generateAllElementLocators } from '../../locators/generate-all-locators.js';
|
|
4
|
+
import { getDriver, getPlatformName, PLATFORM } from '../../session-store.js';
|
|
5
|
+
|
|
6
|
+
export const handleAlertSchema = z.object({
|
|
7
|
+
action: z
|
|
8
|
+
.enum(['accept', 'dismiss'])
|
|
9
|
+
.describe('Action to perform on the alert: accept or dismiss'),
|
|
10
|
+
buttonLabel: z
|
|
11
|
+
.string()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe(
|
|
14
|
+
`Optional label of the button to click. Common permission dialog buttons:
|
|
15
|
+
Android: "While using the app", "Only this time", "Don't allow"
|
|
16
|
+
iOS: "Always" or "Allow Always", "Once" or "Allow Once", "Don't allow"
|
|
17
|
+
Standard: "OK", "Cancel", "Allow", "Deny"
|
|
18
|
+
If not provided, uses default button based on action.
|
|
19
|
+
Use appium_get_page_source or generate_locators to inspect the screen and discover exact labels.`
|
|
20
|
+
),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const ANDROID_LOCATOR_STRATEGY_ORDER = [
|
|
24
|
+
'id',
|
|
25
|
+
'accessibility id',
|
|
26
|
+
'xpath',
|
|
27
|
+
'-android uiautomator',
|
|
28
|
+
'class name',
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
async function handleAndroidAlert(
|
|
32
|
+
driver: any,
|
|
33
|
+
action: string,
|
|
34
|
+
buttonLabel?: string
|
|
35
|
+
): Promise<void> {
|
|
36
|
+
if (buttonLabel) {
|
|
37
|
+
const pageSource = await driver.getPageSource();
|
|
38
|
+
const elements = generateAllElementLocators(
|
|
39
|
+
pageSource,
|
|
40
|
+
true,
|
|
41
|
+
'uiautomator2',
|
|
42
|
+
{
|
|
43
|
+
fetchableOnly: true,
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
const normalizedLabel = buttonLabel.trim();
|
|
47
|
+
const match =
|
|
48
|
+
elements.find(
|
|
49
|
+
(el) =>
|
|
50
|
+
(el.text?.trim() === normalizedLabel ||
|
|
51
|
+
el.contentDesc?.trim() === normalizedLabel) &&
|
|
52
|
+
el.clickable
|
|
53
|
+
) ??
|
|
54
|
+
elements.find(
|
|
55
|
+
(el) =>
|
|
56
|
+
el.text?.trim() === normalizedLabel ||
|
|
57
|
+
el.contentDesc?.trim() === normalizedLabel
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (!match) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`No element found with text or content-desc "${buttonLabel}"`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let button: any = null;
|
|
67
|
+
for (const strategy of ANDROID_LOCATOR_STRATEGY_ORDER) {
|
|
68
|
+
const selector = match.locators[strategy];
|
|
69
|
+
if (!selector) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
button = await driver.findElement(strategy, selector);
|
|
74
|
+
break;
|
|
75
|
+
} catch {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!button) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
'Could not find element with any generated locator; it may have disappeared'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const buttonUUID = button.ELEMENT || button;
|
|
85
|
+
await driver.click(buttonUUID);
|
|
86
|
+
} else {
|
|
87
|
+
if (action === 'accept') {
|
|
88
|
+
await driver.execute('mobile: acceptAlert', {});
|
|
89
|
+
} else {
|
|
90
|
+
await driver.execute('mobile: dismissAlert', {});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function handleiOSAlert(
|
|
96
|
+
driver: any,
|
|
97
|
+
action: string,
|
|
98
|
+
buttonLabel?: string
|
|
99
|
+
): Promise<void> {
|
|
100
|
+
const params: any = { action };
|
|
101
|
+
if (buttonLabel) {
|
|
102
|
+
params.buttonLabel = buttonLabel;
|
|
103
|
+
}
|
|
104
|
+
await driver.execute('mobile: alert', params);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export default function handleAlert(server: FastMCP): void {
|
|
108
|
+
server.addTool({
|
|
109
|
+
name: 'appium_handle_alert',
|
|
110
|
+
description: `Handle system alerts or dialogs that do not belong to the app.
|
|
111
|
+
Use this to dismiss or accept alerts programmatically instead of using autoDismissAlerts capability.
|
|
112
|
+
Supports permission dialogs with buttons like:
|
|
113
|
+
- Android: "While using the app", "Only this time", "Don't allow"
|
|
114
|
+
- iOS: "Always", "Allow Once", "Don't allow"
|
|
115
|
+
For iOS: Uses mobile: alert execute command.
|
|
116
|
+
For Android: Uses mobile: acceptAlert/dismissAlert or searches the current page source for an element whose text or content-desc matches the label, then uses generated locators to find and click it (no hardcoded resource IDs or XPaths).
|
|
117
|
+
If no alert is present, the error is caught and returned gracefully.
|
|
118
|
+
To discover button labels and screen structure first, use appium_get_page_source (XML hierarchy) or generate_locators (interactable elements with text/content-desc).`,
|
|
119
|
+
parameters: handleAlertSchema,
|
|
120
|
+
annotations: {
|
|
121
|
+
readOnlyHint: false,
|
|
122
|
+
openWorldHint: false,
|
|
123
|
+
},
|
|
124
|
+
execute: async (args: any, context: any): Promise<any> => {
|
|
125
|
+
const driver = getDriver();
|
|
126
|
+
if (!driver) {
|
|
127
|
+
throw new Error('No driver found');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const platform = getPlatformName(driver);
|
|
132
|
+
|
|
133
|
+
if (platform === PLATFORM.android) {
|
|
134
|
+
await handleAndroidAlert(driver, args.action, args.buttonLabel);
|
|
135
|
+
} else if (platform === PLATFORM.ios) {
|
|
136
|
+
await handleiOSAlert(driver, args.action, args.buttonLabel);
|
|
137
|
+
} else {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Unsupported platform: ${platform}. Only Android and iOS are supported.`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: 'text',
|
|
147
|
+
text: `Successfully ${args.action}ed alert${
|
|
148
|
+
args.buttonLabel ? ` with button "${args.buttonLabel}"` : ''
|
|
149
|
+
}`,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
};
|
|
153
|
+
} catch (err: any) {
|
|
154
|
+
const contextStr = args.buttonLabel
|
|
155
|
+
? `action=${args.action}, buttonLabel="${args.buttonLabel}"`
|
|
156
|
+
: `action=${args.action}`;
|
|
157
|
+
return {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: 'text',
|
|
161
|
+
text: `Failed to handle alert (${contextStr}). err: ${err.toString()}`,
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FastMCP } from 'fastmcp';
|
|
2
2
|
import { getDriver } from '../../session-store.js';
|
|
3
|
+
import { elementUUIDScheme } from '../../schema.js';
|
|
3
4
|
import type { NullableDriverInstance } from '../../session-store.js';
|
|
4
5
|
import { writeFile, mkdir } from 'node:fs/promises';
|
|
5
6
|
import { join, isAbsolute } from 'node:path';
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
addUIResourceToResponse,
|
|
11
12
|
} from '../../ui/mcp-ui-utils.js';
|
|
12
13
|
import { getScreenshot } from '../../command.js';
|
|
14
|
+
import z from 'zod';
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Resolves the screenshot directory path.
|
|
@@ -47,16 +49,19 @@ const defaultDeps: ScreenshotDeps = {
|
|
|
47
49
|
dateNow: () => Date.now(),
|
|
48
50
|
};
|
|
49
51
|
|
|
50
|
-
export async function executeScreenshot(
|
|
51
|
-
deps
|
|
52
|
-
|
|
52
|
+
export async function executeScreenshot(opts: {
|
|
53
|
+
deps?: ScreenshotDeps;
|
|
54
|
+
elementId?;
|
|
55
|
+
}): Promise<any> {
|
|
56
|
+
const { deps = defaultDeps, elementId } = opts;
|
|
57
|
+
|
|
53
58
|
const driver = deps.getDriver();
|
|
54
59
|
if (!driver) {
|
|
55
60
|
throw new Error('No driver found');
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
try {
|
|
59
|
-
const screenshotBase64 = await getScreenshot(driver);
|
|
64
|
+
const screenshotBase64 = await getScreenshot(driver, elementId);
|
|
60
65
|
|
|
61
66
|
// Convert base64 to buffer
|
|
62
67
|
const screenshotBuffer = Buffer.from(screenshotBase64, 'base64');
|
|
@@ -102,7 +107,7 @@ export async function executeScreenshot(
|
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
|
|
105
|
-
export
|
|
110
|
+
export function screenshot(server: FastMCP): void {
|
|
106
111
|
server.addTool({
|
|
107
112
|
name: 'appium_screenshot',
|
|
108
113
|
description:
|
|
@@ -111,6 +116,27 @@ export default function screenshot(server: FastMCP): void {
|
|
|
111
116
|
readOnlyHint: false,
|
|
112
117
|
openWorldHint: false,
|
|
113
118
|
},
|
|
114
|
-
execute: async (): Promise<any> => executeScreenshot(),
|
|
119
|
+
execute: async (): Promise<any> => executeScreenshot({}),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function elementScreenshot(server: FastMCP): void {
|
|
124
|
+
const elementScreenshotSchema = z.object({
|
|
125
|
+
elementUUID: elementUUIDScheme,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
server.addTool({
|
|
129
|
+
name: 'appium_element_screenshot',
|
|
130
|
+
description:
|
|
131
|
+
'Take a screenshot of the given element uuid and return as PNG image',
|
|
132
|
+
parameters: elementScreenshotSchema,
|
|
133
|
+
annotations: {
|
|
134
|
+
readOnlyHint: false,
|
|
135
|
+
openWorldHint: false,
|
|
136
|
+
},
|
|
137
|
+
execute: async (args: any): Promise<any> =>
|
|
138
|
+
executeScreenshot({
|
|
139
|
+
elementId: args.elementUUID,
|
|
140
|
+
}),
|
|
115
141
|
});
|
|
116
142
|
}
|