appium-mcp 1.2.0 → 1.4.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 +8 -0
- package/dist/session-store.d.ts +4 -0
- package/dist/session-store.js +6 -2
- package/dist/session-store.js.map +1 -1
- package/dist/tools/app-management/install-app.js +1 -1
- package/dist/tools/app-management/install-app.js.map +1 -1
- package/dist/tools/app-management/terminate-app.js +1 -1
- package/dist/tools/app-management/terminate-app.js.map +1 -1
- package/dist/tools/app-management/uninstall-app.js +1 -1
- package/dist/tools/app-management/uninstall-app.js.map +1 -1
- package/dist/tools/context/get-contexts.d.ts +2 -0
- package/dist/tools/context/get-contexts.js +55 -0
- package/dist/tools/context/get-contexts.js.map +1 -0
- package/dist/tools/context/switch-context.d.ts +2 -0
- package/dist/tools/context/switch-context.js +85 -0
- package/dist/tools/context/switch-context.js.map +1 -0
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interactions/long-press.d.ts +2 -0
- package/dist/tools/interactions/long-press.js +101 -0
- package/dist/tools/interactions/long-press.js.map +1 -0
- package/package.json +1 -1
- package/src/session-store.ts +7 -2
- package/src/tools/README.md +1 -0
- package/src/tools/app-management/install-app.ts +1 -1
- package/src/tools/app-management/terminate-app.ts +1 -1
- package/src/tools/app-management/uninstall-app.ts +1 -1
- package/src/tools/context/get-contexts.ts +59 -0
- package/src/tools/context/switch-context.ts +95 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/interactions/long-press.ts +108 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [1.4.0](https://github.com/appium/appium-mcp/compare/v1.3.0...v1.4.0) (2025-12-07)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **interactions:** add long press tool for press and hold gestures ([#53](https://github.com/appium/appium-mcp/issues/53)) ([ef6092e](https://github.com/appium/appium-mcp/commit/ef6092e0e100dd0535f1d6bf371ad794969e863d))
|
|
6
|
+
|
|
7
|
+
## [1.3.0](https://github.com/appium/appium-mcp/compare/v1.2.0...v1.3.0) (2025-12-05)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* add new tools to get active contexts and switch context ([#51](https://github.com/appium/appium-mcp/issues/51)) ([e6ffad0](https://github.com/appium/appium-mcp/commit/e6ffad0a73522f0df2d6a8550fd0a751f797cc40))
|
|
12
|
+
|
|
1
13
|
## [1.2.0](https://github.com/appium/appium-mcp/compare/v1.1.17...v1.2.0) (2025-12-01)
|
|
2
14
|
|
|
3
15
|
### Features
|
package/README.md
CHANGED
|
@@ -178,6 +178,13 @@ MCP Appium provides a comprehensive set of tools organized into the following ca
|
|
|
178
178
|
| `create_session` | Create a new mobile automation session for Android or iOS |
|
|
179
179
|
| `delete_session` | Delete the current mobile session and clean up resources |
|
|
180
180
|
|
|
181
|
+
### Context Management
|
|
182
|
+
|
|
183
|
+
| Tool | Description |
|
|
184
|
+
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
185
|
+
| `appium_get_contexts` | Get all available contexts in the current Appium session. Returns a list of context names including NATIVE_APP and any webview contexts (e.g., WEBVIEW_<id> or WEBVIEW_<package>). |
|
|
186
|
+
| `appium_switch_context` | Switch to a specific context in the Appium session. Use this to switch between native app context (NATIVE_APP) and webview contexts (WEBVIEW_<id> or WEBVIEW_<package>). Use appium_get_contexts to see available contexts first. |
|
|
187
|
+
|
|
181
188
|
### Element Discovery & Interaction
|
|
182
189
|
|
|
183
190
|
| Tool | Description |
|
|
@@ -185,6 +192,7 @@ MCP Appium provides a comprehensive set of tools organized into the following ca
|
|
|
185
192
|
| `appium_find_element` | Find a specific element using various locator strategies (xpath, id, accessibility id, etc.) |
|
|
186
193
|
| `appium_click` | Click on an element |
|
|
187
194
|
| `appium_double_tap` | Perform double tap on an element |
|
|
195
|
+
| `appium_long_press` | Perform a long press (press and hold) gesture on an element |
|
|
188
196
|
| `appium_set_value` | Enter text into an input field |
|
|
189
197
|
| `appium_get_text` | Get text content from an element |
|
|
190
198
|
|
package/dist/session-store.d.ts
CHANGED
package/dist/session-store.js
CHANGED
|
@@ -4,6 +4,10 @@ import log from './logger.js';
|
|
|
4
4
|
let driver = null;
|
|
5
5
|
let sessionId = null;
|
|
6
6
|
let isDeletingSession = false; // Lock to prevent concurrent deletion
|
|
7
|
+
export const PLATFORM = {
|
|
8
|
+
android: 'Android',
|
|
9
|
+
ios: 'iOS',
|
|
10
|
+
};
|
|
7
11
|
export function setSession(d, id) {
|
|
8
12
|
driver = d;
|
|
9
13
|
sessionId = id;
|
|
@@ -57,9 +61,9 @@ export async function safeDeleteSession() {
|
|
|
57
61
|
}
|
|
58
62
|
export const getPlatformName = (driver) => {
|
|
59
63
|
if (driver instanceof AndroidUiautomator2Driver)
|
|
60
|
-
return
|
|
64
|
+
return PLATFORM.android;
|
|
61
65
|
if (driver instanceof XCUITestDriver)
|
|
62
|
-
return
|
|
66
|
+
return PLATFORM.ios;
|
|
63
67
|
throw new Error('Unknown driver type');
|
|
64
68
|
};
|
|
65
69
|
//# sourceMappingURL=session-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,GAAG,MAAM,aAAa,CAAC;AAE9B,IAAI,MAAM,GAAQ,IAAI,CAAC;AACvB,IAAI,SAAS,GAAkB,IAAI,CAAC;AACpC,IAAI,iBAAiB,GAAG,KAAK,CAAC,CAAC,sCAAsC;AAErE,MAAM,UAAU,UAAU,CAAC,CAAM,EAAE,EAAiB;IAClD,MAAM,GAAG,CAAC,CAAC;IACX,SAAS,GAAG,EAAE,CAAC;IACf,iDAAiD;IACjD,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACZ,iBAAiB,GAAG,KAAK,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,2BAA2B;IACzC,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,MAAM,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,IAAI,CAAC,iBAAiB,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,wCAAwC;IACxC,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1B,GAAG,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QACzC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,2CAA2C;IAC3C,IAAI,iBAAiB,EAAE,CAAC;QACtB,GAAG,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;QAC9D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,WAAW;IACX,iBAAiB,GAAG,IAAI,CAAC;IAEzB,IAAI,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACrC,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;QAE7B,+BAA+B;QAC/B,MAAM,GAAG,IAAI,CAAC;QACd,SAAS,GAAG,IAAI,CAAC;QAEjB,GAAG,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QAC5C,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,sBAAsB;QACtB,iBAAiB,GAAG,KAAK,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAW,EAAU,EAAE;IACrD,IAAI,MAAM,YAAY,yBAAyB;QAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,GAAG,MAAM,aAAa,CAAC;AAE9B,IAAI,MAAM,GAAQ,IAAI,CAAC;AACvB,IAAI,SAAS,GAAkB,IAAI,CAAC;AACpC,IAAI,iBAAiB,GAAG,KAAK,CAAC,CAAC,sCAAsC;AAErE,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,OAAO,EAAE,SAAS;IAClB,GAAG,EAAE,KAAK;CACX,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,CAAM,EAAE,EAAiB;IAClD,MAAM,GAAG,CAAC,CAAC;IACX,SAAS,GAAG,EAAE,CAAC;IACf,iDAAiD;IACjD,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACZ,iBAAiB,GAAG,KAAK,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,2BAA2B;IACzC,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,MAAM,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,IAAI,CAAC,iBAAiB,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,wCAAwC;IACxC,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1B,GAAG,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QACzC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,2CAA2C;IAC3C,IAAI,iBAAiB,EAAE,CAAC;QACtB,GAAG,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;QAC9D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,WAAW;IACX,iBAAiB,GAAG,IAAI,CAAC;IAEzB,IAAI,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACrC,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;QAE7B,+BAA+B;QAC/B,MAAM,GAAG,IAAI,CAAC;QACd,SAAS,GAAG,IAAI,CAAC;QAEjB,GAAG,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QAC5C,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,sBAAsB;QACtB,iBAAiB,GAAG,KAAK,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAW,EAAU,EAAE;IACrD,IAAI,MAAM,YAAY,yBAAyB;QAAE,OAAO,QAAQ,CAAC,OAAO,CAAC;IACzE,IAAI,MAAM,YAAY,cAAc;QAAE,OAAO,QAAQ,CAAC,GAAG,CAAC;IAC1D,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;AACzC,CAAC,CAAC"}
|
|
@@ -5,7 +5,7 @@ export default function installApp(server) {
|
|
|
5
5
|
path: z.string().describe('Path to the app file to install'),
|
|
6
6
|
});
|
|
7
7
|
server.addTool({
|
|
8
|
-
name: '
|
|
8
|
+
name: 'appium_install_app',
|
|
9
9
|
description: 'Install an app on the device from a file path.',
|
|
10
10
|
parameters: schema,
|
|
11
11
|
execute: async (args) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-app.js","sourceRoot":"","sources":["../../../src/tools/app-management/install-app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,MAAe;IAChD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;KAC7D,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"install-app.js","sourceRoot":"","sources":["../../../src/tools/app-management/install-app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,MAAe;IAChD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;KAC7D,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,gDAAgD;QAC7D,UAAU,EAAE,MAAM;QAClB,OAAO,EAAE,KAAK,EAAE,IAA4B,EAAE,EAAE;YAC9C,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;YACtB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBACzC,MAAM,MAAM,GACV,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;gBAC7D,MAAO,MAAc,CAAC,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;gBAC5D,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,4BAA4B;yBACnC;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,+BAA+B,GAAG,CAAC,QAAQ,EAAE,EAAE;yBACtD;qBACF;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -7,7 +7,7 @@ export default function terminateApp(server) {
|
|
|
7
7
|
.describe('App identifier (package name for Android, bundle ID for iOS)'),
|
|
8
8
|
});
|
|
9
9
|
server.addTool({
|
|
10
|
-
name: '
|
|
10
|
+
name: 'appium_terminate_app',
|
|
11
11
|
description: 'Terminate an app on the device.',
|
|
12
12
|
parameters: schema,
|
|
13
13
|
execute: async (args) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminate-app.js","sourceRoot":"","sources":["../../../src/tools/app-management/terminate-app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,MAAe;IAClD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,EAAE,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,CAAC,8DAA8D,CAAC;KAC5E,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"terminate-app.js","sourceRoot":"","sources":["../../../src/tools/app-management/terminate-app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,MAAe;IAClD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,EAAE,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,CAAC,8DAA8D,CAAC;KAC5E,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,iCAAiC;QAC9C,UAAU,EAAE,MAAM;QAClB,OAAO,EAAE,KAAK,EAAE,IAA4B,EAAE,EAAE;YAC9C,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;YACpB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBACzC,MAAM,MAAM,GACV,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;gBAC5D,MAAO,MAAc,CAAC,OAAO,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;gBAC9D,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,6BAA6B;yBACpC;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,iCAAiC,GAAG,CAAC,QAAQ,EAAE,EAAE;yBACxD;qBACF;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -7,7 +7,7 @@ export default function uninstallApp(server) {
|
|
|
7
7
|
.describe('App identifier (package name for Android, bundle ID for iOS)'),
|
|
8
8
|
});
|
|
9
9
|
server.addTool({
|
|
10
|
-
name: '
|
|
10
|
+
name: 'appium_uninstall_app',
|
|
11
11
|
description: 'Uninstall an app from the device.',
|
|
12
12
|
parameters: schema,
|
|
13
13
|
execute: async (args) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uninstall-app.js","sourceRoot":"","sources":["../../../src/tools/app-management/uninstall-app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,MAAe;IAClD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,EAAE,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,CAAC,8DAA8D,CAAC;KAC5E,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"uninstall-app.js","sourceRoot":"","sources":["../../../src/tools/app-management/uninstall-app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,MAAe;IAClD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,EAAE,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,CAAC,8DAA8D,CAAC;KAC5E,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,mCAAmC;QAChD,UAAU,EAAE,MAAM;QAClB,OAAO,EAAE,KAAK,EAAE,IAA4B,EAAE,EAAE;YAC9C,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;YACpB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBACzC,MAAM,MAAM,GACV,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;gBAC5D,MAAO,MAAc,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;gBAC3D,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,8BAA8B;yBACrC;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,iCAAiC,GAAG,CAAC,QAAQ,EAAE,EAAE;yBACxD;qBACF;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDriver } from '../../session-store.js';
|
|
3
|
+
export default function getContexts(server) {
|
|
4
|
+
server.addTool({
|
|
5
|
+
name: 'appium_get_contexts',
|
|
6
|
+
description: 'Get all available contexts in the current Appium session. Returns a list of context names including NATIVE_APP and any webview contexts (e.g., WEBVIEW_<id> or WEBVIEW_<package>).',
|
|
7
|
+
parameters: z.object({}),
|
|
8
|
+
annotations: {
|
|
9
|
+
readOnlyHint: true,
|
|
10
|
+
openWorldHint: false,
|
|
11
|
+
},
|
|
12
|
+
execute: async (args, context) => {
|
|
13
|
+
const driver = getDriver();
|
|
14
|
+
if (!driver) {
|
|
15
|
+
throw new Error('No driver found. Please create a session first.');
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const [currentContext, contexts] = await Promise.all([
|
|
19
|
+
driver.getCurrentContext().catch(() => null),
|
|
20
|
+
driver.getContexts().catch(() => []),
|
|
21
|
+
]);
|
|
22
|
+
if (!contexts || contexts.length === 0) {
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: 'text',
|
|
27
|
+
text: 'No contexts available.',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: 'text',
|
|
36
|
+
text: `Available contexts: ${JSON.stringify(contexts, null, 2)}\nCurrent context: ${currentContext || 'N/A'}`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return {
|
|
43
|
+
content: [
|
|
44
|
+
{
|
|
45
|
+
type: 'text',
|
|
46
|
+
text: `Failed to get contexts. Error: ${err.toString()}`,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
isError: true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=get-contexts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-contexts.js","sourceRoot":"","sources":["../../../src/tools/context/get-contexts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,MAAe;IACjD,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EACT,oLAAoL;QACtL,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,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,iDAAiD,CAAC,CAAC;YACrE,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;oBACnD,MAAM,CAAC,iBAAiB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;oBAC5C,MAAM,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;iBACrC,CAAC,CAAC;gBAEH,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACvC,OAAO;wBACL,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,wBAAwB;6BAC/B;yBACF;qBACF,CAAC;gBACJ,CAAC;gBAED,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,uBAAuB,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,sBAAsB,cAAc,IAAI,KAAK,EAAE;yBAC9G;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,kCAAkC,GAAG,CAAC,QAAQ,EAAE,EAAE;yBACzD;qBACF;oBACD,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDriver } from '../../session-store.js';
|
|
3
|
+
export default function switchContext(server) {
|
|
4
|
+
const schema = z.object({
|
|
5
|
+
context: z
|
|
6
|
+
.string()
|
|
7
|
+
.describe('The name of the context to switch to. Common values: "NATIVE_APP" for native context, or "WEBVIEW_<id>" / "WEBVIEW_<package>" for webview contexts.'),
|
|
8
|
+
});
|
|
9
|
+
server.addTool({
|
|
10
|
+
name: 'appium_switch_context',
|
|
11
|
+
description: 'Switch to a specific context in the Appium session. Use this to switch between native app context (NATIVE_APP) and webview contexts (WEBVIEW_<id> or WEBVIEW_<package>). Use appium_get_contexts to see available contexts first.',
|
|
12
|
+
parameters: schema,
|
|
13
|
+
annotations: {
|
|
14
|
+
readOnlyHint: false,
|
|
15
|
+
openWorldHint: false,
|
|
16
|
+
},
|
|
17
|
+
execute: async (args, context) => {
|
|
18
|
+
const driver = getDriver();
|
|
19
|
+
if (!driver) {
|
|
20
|
+
throw new Error('No driver found. Please create a session first.');
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const [currentContext, availableContexts] = await Promise.all([
|
|
24
|
+
driver.getCurrentContext().catch(() => null),
|
|
25
|
+
driver.getContexts().catch(() => []),
|
|
26
|
+
]);
|
|
27
|
+
if (currentContext === args.context) {
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: 'text',
|
|
32
|
+
text: `Already on context "${args.context}".`,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (!availableContexts || availableContexts.length === 0) {
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: 'text',
|
|
42
|
+
text: 'No contexts available. Cannot switch context.',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
isError: true,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (!availableContexts.includes(args.context)) {
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
text: `Context "${args.context}" not found. Available contexts: ${JSON.stringify(availableContexts, null, 2)}`,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
await driver.switchContext(args.context);
|
|
60
|
+
// Verify the switch was successful
|
|
61
|
+
const newContext = await driver.getCurrentContext();
|
|
62
|
+
return {
|
|
63
|
+
content: [
|
|
64
|
+
{
|
|
65
|
+
type: 'text',
|
|
66
|
+
text: `Successfully switched context from "${currentContext || 'N/A'}" to "${newContext}".`,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
return {
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: 'text',
|
|
76
|
+
text: `Failed to switch context. Error: ${err.toString()}`,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
isError: true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=switch-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"switch-context.js","sourceRoot":"","sources":["../../../src/tools/context/switch-context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAA6B,MAAM,wBAAwB,CAAC;AAE9E,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,MAAe;IACnD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,QAAQ,CACP,qJAAqJ,CACtJ;KACJ,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EACT,mOAAmO;QACrO,UAAU,EAAE,MAAM;QAClB,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,iDAAiD,CAAC,CAAC;YACrE,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;oBAC5D,MAAM,CAAC,iBAAiB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;oBAC5C,MAAM,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;iBACrC,CAAC,CAAC;gBAEH,IAAI,cAAc,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;oBACpC,OAAO;wBACL,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,uBAAuB,IAAI,CAAC,OAAO,IAAI;6BAC9C;yBACF;qBACF,CAAC;gBACJ,CAAC;gBAED,IAAI,CAAC,iBAAiB,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACzD,OAAO;wBACL,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,+CAA+C;6BACtD;yBACF;wBACD,OAAO,EAAE,IAAI;qBACd,CAAC;gBACJ,CAAC;gBAED,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC9C,OAAO;wBACL,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,YAAY,IAAI,CAAC,OAAO,oCAAoC,IAAI,CAAC,SAAS,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;6BAC/G;yBACF;wBACD,OAAO,EAAE,IAAI;qBACd,CAAC;gBACJ,CAAC;gBACD,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAEzC,mCAAmC;gBACnC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,iBAAiB,EAAE,CAAC;gBAEpD,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,uCAAuC,cAAc,IAAI,KAAK,SAAS,UAAU,IAAI;yBAC5F;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,oCAAoC,GAAG,CAAC,QAAQ,EAAE,EAAE;yBAC3D;qBACF;oBACD,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
package/dist/tools/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import swipe from './navigations/swipe.js';
|
|
|
15
15
|
import findElement from './interactions/find.js';
|
|
16
16
|
import clickElement from './interactions/click.js';
|
|
17
17
|
import doubleTap from './interactions/double-tap.js';
|
|
18
|
+
import longPress from './interactions/long-press.js';
|
|
18
19
|
import setValue from './interactions/set-value.js';
|
|
19
20
|
import getText from './interactions/get-text.js';
|
|
20
21
|
import getPageSource from './interactions/get-page-source.js';
|
|
@@ -24,6 +25,8 @@ import installApp from './app-management/install-app.js';
|
|
|
24
25
|
import uninstallApp from './app-management/uninstall-app.js';
|
|
25
26
|
import terminateApp from './app-management/terminate-app.js';
|
|
26
27
|
import listApps from './app-management/list-apps.js';
|
|
28
|
+
import getContexts from './context/get-contexts.js';
|
|
29
|
+
import switchContext from './context/switch-context.js';
|
|
27
30
|
export default function registerTools(server) {
|
|
28
31
|
// Wrap addTool to inject logging around tool execution
|
|
29
32
|
const originalAddTool = server.addTool.bind(server);
|
|
@@ -103,6 +106,7 @@ export default function registerTools(server) {
|
|
|
103
106
|
findElement(server);
|
|
104
107
|
clickElement(server);
|
|
105
108
|
doubleTap(server);
|
|
109
|
+
longPress(server);
|
|
106
110
|
setValue(server);
|
|
107
111
|
getText(server);
|
|
108
112
|
getPageSource(server);
|
|
@@ -113,6 +117,9 @@ export default function registerTools(server) {
|
|
|
113
117
|
uninstallApp(server);
|
|
114
118
|
terminateApp(server);
|
|
115
119
|
listApps(server);
|
|
120
|
+
// Context Management
|
|
121
|
+
getContexts(server);
|
|
122
|
+
switchContext(server);
|
|
116
123
|
// Test Generation
|
|
117
124
|
generateLocators(server);
|
|
118
125
|
generateTest(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,QAAQ,MAAM,6BAA6B,CAAC;AACnD,OAAO,OAAO,MAAM,4BAA4B,CAAC;AACjD,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,UAAU,MAAM,8BAA8B,CAAC;AACtD,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAC3D,OAAO,UAAU,MAAM,iCAAiC,CAAC;AACzD,OAAO,YAAY,MAAM,mCAAmC,CAAC;AAC7D,OAAO,YAAY,MAAM,mCAAmC,CAAC;AAC7D,OAAO,QAAQ,MAAM,+BAA+B,CAAC;
|
|
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,QAAQ,MAAM,6BAA6B,CAAC;AACnD,OAAO,OAAO,MAAM,4BAA4B,CAAC;AACjD,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,UAAU,MAAM,8BAA8B,CAAC;AACtD,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAC3D,OAAO,UAAU,MAAM,iCAAiC,CAAC;AACzD,OAAO,YAAY,MAAM,mCAAmC,CAAC;AAC7D,OAAO,YAAY,MAAM,mCAAmC,CAAC;AAC7D,OAAO,QAAQ,MAAM,+BAA+B,CAAC;AACrD,OAAO,WAAW,MAAM,2BAA2B,CAAC;AACpD,OAAO,aAAa,MAAM,6BAA6B,CAAC;AAExD,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,MAAe;IACnD,uDAAuD;IACvD,MAAM,eAAe,GAAI,MAAc,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5D,MAAc,CAAC,OAAO,GAAG,CAAC,OAAY,EAAE,EAAE;QACzC,MAAM,QAAQ,GAAG,OAAO,EAAE,IAAI,IAAI,cAAc,CAAC;QACjD,MAAM,eAAe,GAAG,OAAO,EAAE,OAAO,CAAC;QACzC,IAAI,OAAO,eAAe,KAAK,UAAU,EAAE,CAAC;YAC1C,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,MAAM,cAAc,GAAG;YACrB,UAAU;YACV,OAAO;YACP,aAAa;YACb,eAAe;YACf,QAAQ;YACR,QAAQ;YACR,QAAQ;YACR,cAAc;SACf,CAAC;QACF,MAAM,UAAU,GAAG,CAAC,GAAQ,EAAE,EAAE;YAC9B,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CACf,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;oBACjC,IACE,GAAG;wBACH,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EACvD,CAAC;wBACD,OAAO,YAAY,CAAC;oBACtB,CAAC;oBACD,gDAAgD;oBAChD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;wBAC9D,OAAO,WAAW,KAAK,CAAC,MAAM,GAAG,CAAC;oBACpC,CAAC;oBACD,IACE,KAAK;wBACL,OAAO,MAAM,KAAK,WAAW;wBAC7B,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EACtB,CAAC;wBACD,OAAO,WAAY,KAAgB,CAAC,MAAM,GAAG,CAAC;oBAChD,CAAC;oBACD,OAAO,KAAK,CAAC;gBACf,CAAC,CAAC,CACH,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,uBAAuB,CAAC;YACjC,CAAC;QACH,CAAC,CAAC;QACF,OAAO,eAAe,CAAC;YACrB,GAAG,OAAO;YACV,OAAO,EAAE,KAAK,EAAE,IAAS,EAAE,OAAY,EAAE,EAAE;gBACzC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACzB,GAAG,CAAC,IAAI,CAAC,gBAAgB,QAAQ,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;gBACvD,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;oBACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;oBACpC,GAAG,CAAC,IAAI,CAAC,cAAc,QAAQ,KAAK,QAAQ,KAAK,CAAC,CAAC;oBACnD,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAAC,OAAO,GAAQ,EAAE,CAAC;oBAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;oBACpC,MAAM,GAAG,GAAG,GAAG,EAAE,KAAK,IAAI,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;oBACtD,GAAG,CAAC,KAAK,CAAC,gBAAgB,QAAQ,KAAK,QAAQ,QAAQ,GAAG,EAAE,CAAC,CAAC;oBAC9D,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,qBAAqB;IACrB,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,aAAa,CAAC,MAAM,CAAC,CAAC;IAEtB,YAAY;IACZ,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjB,UAAU,CAAC,MAAM,CAAC,CAAC;IAEnB,aAAa;IACb,MAAM,CAAC,MAAM,CAAC,CAAC;IACf,eAAe,CAAC,MAAM,CAAC,CAAC;IACxB,KAAK,CAAC,MAAM,CAAC,CAAC;IAEd,uBAAuB;IACvB,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjB,OAAO,CAAC,MAAM,CAAC,CAAC;IAChB,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,UAAU,CAAC,MAAM,CAAC,CAAC;IAEnB,iBAAiB;IACjB,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,UAAU,CAAC,MAAM,CAAC,CAAC;IACnB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEjB,qBAAqB;IACrB,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,aAAa,CAAC,MAAM,CAAC,CAAC;IAEtB,kBAAkB;IAClB,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,YAAY,CAAC,MAAM,CAAC,CAAC;IAErB,gBAAgB;IAChB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDriver, getPlatformName } from '../../session-store.js';
|
|
3
|
+
import { elementUUIDScheme } from '../../schema.js';
|
|
4
|
+
export default function longPress(server) {
|
|
5
|
+
const longPressSchema = z.object({
|
|
6
|
+
elementUUID: elementUUIDScheme,
|
|
7
|
+
duration: z
|
|
8
|
+
.number()
|
|
9
|
+
.int()
|
|
10
|
+
.min(500)
|
|
11
|
+
.max(10000)
|
|
12
|
+
.default(2000)
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Duration of the long press in milliseconds. Default is 2000ms.'),
|
|
15
|
+
});
|
|
16
|
+
server.addTool({
|
|
17
|
+
name: 'appium_long_press',
|
|
18
|
+
description: 'Perform a long press (press and hold) gesture on an element',
|
|
19
|
+
parameters: longPressSchema,
|
|
20
|
+
annotations: {
|
|
21
|
+
readOnlyHint: false,
|
|
22
|
+
openWorldHint: false,
|
|
23
|
+
},
|
|
24
|
+
execute: async (args, context) => {
|
|
25
|
+
const driver = getDriver();
|
|
26
|
+
if (!driver) {
|
|
27
|
+
throw new Error('No driver found');
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const platform = getPlatformName(driver);
|
|
31
|
+
const duration = args.duration || 2000;
|
|
32
|
+
if (platform === 'Android') {
|
|
33
|
+
const rect = await driver.getElementRect(args.elementUUID);
|
|
34
|
+
const x = Math.floor(rect.x + rect.width / 2);
|
|
35
|
+
const y = Math.floor(rect.y + rect.height / 2);
|
|
36
|
+
await driver.performActions([
|
|
37
|
+
{
|
|
38
|
+
type: 'pointer',
|
|
39
|
+
id: 'finger1',
|
|
40
|
+
parameters: { pointerType: 'touch' },
|
|
41
|
+
actions: [
|
|
42
|
+
{ type: 'pointerMove', duration: 0, x, y },
|
|
43
|
+
{ type: 'pointerDown', button: 0 },
|
|
44
|
+
{ type: 'pause', duration: duration },
|
|
45
|
+
{ type: 'pointerUp', button: 0 },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
50
|
+
else if (platform === 'iOS') {
|
|
51
|
+
try {
|
|
52
|
+
await driver.execute('mobile: touchAndHold', {
|
|
53
|
+
elementId: args.elementUUID,
|
|
54
|
+
duration: duration / 1000,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (touchAndHoldError) {
|
|
58
|
+
const rect = await driver.getElementRect(args.elementUUID);
|
|
59
|
+
const x = Math.floor(rect.x + rect.width / 2);
|
|
60
|
+
const y = Math.floor(rect.y + rect.height / 2);
|
|
61
|
+
await driver.performActions([
|
|
62
|
+
{
|
|
63
|
+
type: 'pointer',
|
|
64
|
+
id: 'finger1',
|
|
65
|
+
parameters: { pointerType: 'touch' },
|
|
66
|
+
actions: [
|
|
67
|
+
{ type: 'pointerMove', duration: 0, x, y },
|
|
68
|
+
{ type: 'pointerDown', button: 0 },
|
|
69
|
+
{ type: 'pause', duration: duration },
|
|
70
|
+
{ type: 'pointerUp', button: 0 },
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
content: [
|
|
81
|
+
{
|
|
82
|
+
type: 'text',
|
|
83
|
+
text: `Successfully performed long press on element ${args.elementUUID}`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
return {
|
|
90
|
+
content: [
|
|
91
|
+
{
|
|
92
|
+
type: 'text',
|
|
93
|
+
text: `Failed to perform long press on element ${args.elementUUID}. err: ${err.toString()}`,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=long-press.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"long-press.js","sourceRoot":"","sources":["../../../src/tools/interactions/long-press.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/session-store.ts
CHANGED
|
@@ -6,6 +6,11 @@ let driver: any = null;
|
|
|
6
6
|
let sessionId: string | null = null;
|
|
7
7
|
let isDeletingSession = false; // Lock to prevent concurrent deletion
|
|
8
8
|
|
|
9
|
+
export const PLATFORM = {
|
|
10
|
+
android: 'Android',
|
|
11
|
+
ios: 'iOS',
|
|
12
|
+
};
|
|
13
|
+
|
|
9
14
|
export function setSession(d: any, id: string | null) {
|
|
10
15
|
driver = d;
|
|
11
16
|
sessionId = id;
|
|
@@ -67,7 +72,7 @@ export async function safeDeleteSession(): Promise<boolean> {
|
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
export const getPlatformName = (driver: any): string => {
|
|
70
|
-
if (driver instanceof AndroidUiautomator2Driver) return
|
|
71
|
-
if (driver instanceof XCUITestDriver) return
|
|
75
|
+
if (driver instanceof AndroidUiautomator2Driver) return PLATFORM.android;
|
|
76
|
+
if (driver instanceof XCUITestDriver) return PLATFORM.ios;
|
|
72
77
|
throw new Error('Unknown driver type');
|
|
73
78
|
};
|
package/src/tools/README.md
CHANGED
|
@@ -28,6 +28,7 @@ This directory contains all MCP tools available in MCP Appium.
|
|
|
28
28
|
- `find.ts` - Find elements
|
|
29
29
|
- `click.ts` - Click elements
|
|
30
30
|
- `double-tap.ts` - Double tap elements
|
|
31
|
+
- `long-press.ts` - Long press (press and hold) elements
|
|
31
32
|
- `set-value.ts` - Enter text
|
|
32
33
|
- `get-text.ts` - Get element text
|
|
33
34
|
- `get-page-source.ts` - Get page source (XML) from current screen
|
|
@@ -8,7 +8,7 @@ export default function installApp(server: FastMCP): void {
|
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
server.addTool({
|
|
11
|
-
name: '
|
|
11
|
+
name: 'appium_install_app',
|
|
12
12
|
description: 'Install an app on the device from a file path.',
|
|
13
13
|
parameters: schema,
|
|
14
14
|
execute: async (args: z.infer<typeof schema>) => {
|
|
@@ -10,7 +10,7 @@ export default function terminateApp(server: FastMCP): void {
|
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
server.addTool({
|
|
13
|
-
name: '
|
|
13
|
+
name: 'appium_terminate_app',
|
|
14
14
|
description: 'Terminate an app on the device.',
|
|
15
15
|
parameters: schema,
|
|
16
16
|
execute: async (args: z.infer<typeof schema>) => {
|
|
@@ -10,7 +10,7 @@ export default function uninstallApp(server: FastMCP): void {
|
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
server.addTool({
|
|
13
|
-
name: '
|
|
13
|
+
name: 'appium_uninstall_app',
|
|
14
14
|
description: 'Uninstall an app from the device.',
|
|
15
15
|
parameters: schema,
|
|
16
16
|
execute: async (args: z.infer<typeof schema>) => {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { FastMCP } from 'fastmcp/dist/FastMCP.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getDriver } from '../../session-store.js';
|
|
4
|
+
|
|
5
|
+
export default function getContexts(server: FastMCP): void {
|
|
6
|
+
server.addTool({
|
|
7
|
+
name: 'appium_get_contexts',
|
|
8
|
+
description:
|
|
9
|
+
'Get all available contexts in the current Appium session. Returns a list of context names including NATIVE_APP and any webview contexts (e.g., WEBVIEW_<id> or WEBVIEW_<package>).',
|
|
10
|
+
parameters: z.object({}),
|
|
11
|
+
annotations: {
|
|
12
|
+
readOnlyHint: true,
|
|
13
|
+
openWorldHint: false,
|
|
14
|
+
},
|
|
15
|
+
execute: async (args: any, context: any): Promise<any> => {
|
|
16
|
+
const driver = getDriver();
|
|
17
|
+
if (!driver) {
|
|
18
|
+
throw new Error('No driver found. Please create a session first.');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const [currentContext, contexts] = await Promise.all([
|
|
23
|
+
driver.getCurrentContext().catch(() => null),
|
|
24
|
+
driver.getContexts().catch(() => []),
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
if (!contexts || contexts.length === 0) {
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: 'text',
|
|
32
|
+
text: 'No contexts available.',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: 'text',
|
|
42
|
+
text: `Available contexts: ${JSON.stringify(contexts, null, 2)}\nCurrent context: ${currentContext || 'N/A'}`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
} catch (err: any) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: `Failed to get contexts. Error: ${err.toString()}`,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
isError: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { FastMCP } from 'fastmcp/dist/FastMCP.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getDriver, getPlatformName, PLATFORM } from '../../session-store.js';
|
|
4
|
+
|
|
5
|
+
export default function switchContext(server: FastMCP): void {
|
|
6
|
+
const schema = z.object({
|
|
7
|
+
context: z
|
|
8
|
+
.string()
|
|
9
|
+
.describe(
|
|
10
|
+
'The name of the context to switch to. Common values: "NATIVE_APP" for native context, or "WEBVIEW_<id>" / "WEBVIEW_<package>" for webview contexts.'
|
|
11
|
+
),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
server.addTool({
|
|
15
|
+
name: 'appium_switch_context',
|
|
16
|
+
description:
|
|
17
|
+
'Switch to a specific context in the Appium session. Use this to switch between native app context (NATIVE_APP) and webview contexts (WEBVIEW_<id> or WEBVIEW_<package>). Use appium_get_contexts to see available contexts first.',
|
|
18
|
+
parameters: schema,
|
|
19
|
+
annotations: {
|
|
20
|
+
readOnlyHint: false,
|
|
21
|
+
openWorldHint: false,
|
|
22
|
+
},
|
|
23
|
+
execute: async (args: any, context: any): Promise<any> => {
|
|
24
|
+
const driver = getDriver();
|
|
25
|
+
if (!driver) {
|
|
26
|
+
throw new Error('No driver found. Please create a session first.');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const [currentContext, availableContexts] = await Promise.all([
|
|
31
|
+
driver.getCurrentContext().catch(() => null),
|
|
32
|
+
driver.getContexts().catch(() => []),
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
if (currentContext === args.context) {
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: 'text',
|
|
40
|
+
text: `Already on context "${args.context}".`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!availableContexts || availableContexts.length === 0) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: 'No contexts available. Cannot switch context.',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
isError: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!availableContexts.includes(args.context)) {
|
|
59
|
+
return {
|
|
60
|
+
content: [
|
|
61
|
+
{
|
|
62
|
+
type: 'text',
|
|
63
|
+
text: `Context "${args.context}" not found. Available contexts: ${JSON.stringify(availableContexts, null, 2)}`,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
isError: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
await driver.switchContext(args.context);
|
|
70
|
+
|
|
71
|
+
// Verify the switch was successful
|
|
72
|
+
const newContext = await driver.getCurrentContext();
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
content: [
|
|
76
|
+
{
|
|
77
|
+
type: 'text',
|
|
78
|
+
text: `Successfully switched context from "${currentContext || 'N/A'}" to "${newContext}".`,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
} catch (err: any) {
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: 'text',
|
|
87
|
+
text: `Failed to switch context. Error: ${err.toString()}`,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
isError: true,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
package/src/tools/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ import swipe from './navigations/swipe.js';
|
|
|
30
30
|
import findElement from './interactions/find.js';
|
|
31
31
|
import clickElement from './interactions/click.js';
|
|
32
32
|
import doubleTap from './interactions/double-tap.js';
|
|
33
|
+
import longPress from './interactions/long-press.js';
|
|
33
34
|
import setValue from './interactions/set-value.js';
|
|
34
35
|
import getText from './interactions/get-text.js';
|
|
35
36
|
import getPageSource from './interactions/get-page-source.js';
|
|
@@ -39,6 +40,8 @@ import installApp from './app-management/install-app.js';
|
|
|
39
40
|
import uninstallApp from './app-management/uninstall-app.js';
|
|
40
41
|
import terminateApp from './app-management/terminate-app.js';
|
|
41
42
|
import listApps from './app-management/list-apps.js';
|
|
43
|
+
import getContexts from './context/get-contexts.js';
|
|
44
|
+
import switchContext from './context/switch-context.js';
|
|
42
45
|
|
|
43
46
|
export default function registerTools(server: FastMCP): void {
|
|
44
47
|
// Wrap addTool to inject logging around tool execution
|
|
@@ -127,6 +130,7 @@ export default function registerTools(server: FastMCP): void {
|
|
|
127
130
|
findElement(server);
|
|
128
131
|
clickElement(server);
|
|
129
132
|
doubleTap(server);
|
|
133
|
+
longPress(server);
|
|
130
134
|
setValue(server);
|
|
131
135
|
getText(server);
|
|
132
136
|
getPageSource(server);
|
|
@@ -139,6 +143,10 @@ export default function registerTools(server: FastMCP): void {
|
|
|
139
143
|
terminateApp(server);
|
|
140
144
|
listApps(server);
|
|
141
145
|
|
|
146
|
+
// Context Management
|
|
147
|
+
getContexts(server);
|
|
148
|
+
switchContext(server);
|
|
149
|
+
|
|
142
150
|
// Test Generation
|
|
143
151
|
generateLocators(server);
|
|
144
152
|
generateTest(server);
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { FastMCP } from 'fastmcp/dist/FastMCP.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getDriver, getPlatformName } from '../../session-store.js';
|
|
4
|
+
import { elementUUIDScheme } from '../../schema.js';
|
|
5
|
+
|
|
6
|
+
export default function longPress(server: FastMCP): void {
|
|
7
|
+
const longPressSchema = z.object({
|
|
8
|
+
elementUUID: elementUUIDScheme,
|
|
9
|
+
duration: z
|
|
10
|
+
.number()
|
|
11
|
+
.int()
|
|
12
|
+
.min(500)
|
|
13
|
+
.max(10000)
|
|
14
|
+
.default(2000)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe(
|
|
17
|
+
'Duration of the long press in milliseconds. Default is 2000ms.'
|
|
18
|
+
),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
server.addTool({
|
|
22
|
+
name: 'appium_long_press',
|
|
23
|
+
description: 'Perform a long press (press and hold) gesture on an element',
|
|
24
|
+
parameters: longPressSchema,
|
|
25
|
+
annotations: {
|
|
26
|
+
readOnlyHint: false,
|
|
27
|
+
openWorldHint: false,
|
|
28
|
+
},
|
|
29
|
+
execute: async (args: any, context: any): Promise<any> => {
|
|
30
|
+
const driver = getDriver();
|
|
31
|
+
if (!driver) {
|
|
32
|
+
throw new Error('No driver found');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const platform = getPlatformName(driver);
|
|
37
|
+
const duration = args.duration || 2000;
|
|
38
|
+
|
|
39
|
+
if (platform === 'Android') {
|
|
40
|
+
const rect = await driver.getElementRect(args.elementUUID);
|
|
41
|
+
const x = Math.floor(rect.x + rect.width / 2);
|
|
42
|
+
const y = Math.floor(rect.y + rect.height / 2);
|
|
43
|
+
|
|
44
|
+
await driver.performActions([
|
|
45
|
+
{
|
|
46
|
+
type: 'pointer',
|
|
47
|
+
id: 'finger1',
|
|
48
|
+
parameters: { pointerType: 'touch' },
|
|
49
|
+
actions: [
|
|
50
|
+
{ type: 'pointerMove', duration: 0, x, y },
|
|
51
|
+
{ type: 'pointerDown', button: 0 },
|
|
52
|
+
{ type: 'pause', duration: duration },
|
|
53
|
+
{ type: 'pointerUp', button: 0 },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
} else if (platform === 'iOS') {
|
|
58
|
+
try {
|
|
59
|
+
await driver.execute('mobile: touchAndHold', {
|
|
60
|
+
elementId: args.elementUUID,
|
|
61
|
+
duration: duration / 1000,
|
|
62
|
+
});
|
|
63
|
+
} catch (touchAndHoldError) {
|
|
64
|
+
const rect = await driver.getElementRect(args.elementUUID);
|
|
65
|
+
const x = Math.floor(rect.x + rect.width / 2);
|
|
66
|
+
const y = Math.floor(rect.y + rect.height / 2);
|
|
67
|
+
|
|
68
|
+
await driver.performActions([
|
|
69
|
+
{
|
|
70
|
+
type: 'pointer',
|
|
71
|
+
id: 'finger1',
|
|
72
|
+
parameters: { pointerType: 'touch' },
|
|
73
|
+
actions: [
|
|
74
|
+
{ type: 'pointerMove', duration: 0, x, y },
|
|
75
|
+
{ type: 'pointerDown', button: 0 },
|
|
76
|
+
{ type: 'pause', duration: duration },
|
|
77
|
+
{ type: 'pointerUp', button: 0 },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Unsupported platform: ${platform}. Only Android and iOS are supported.`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: 'text',
|
|
92
|
+
text: `Successfully performed long press on element ${args.elementUUID}`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
} catch (err: any) {
|
|
97
|
+
return {
|
|
98
|
+
content: [
|
|
99
|
+
{
|
|
100
|
+
type: 'text',
|
|
101
|
+
text: `Failed to perform long press on element ${args.elementUUID}. err: ${err.toString()}`,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|