appium-mcp 1.1.17 → 1.2.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 CHANGED
@@ -1,3 +1,9 @@
1
+ ## [1.2.0](https://github.com/appium/appium-mcp/compare/v1.1.17...v1.2.0) (2025-12-01)
2
+
3
+ ### Features
4
+
5
+ * **navigation:** add swipe tool for horizontal and vertical swiping ([#49](https://github.com/appium/appium-mcp/issues/49)) ([5c163b7](https://github.com/appium/appium-mcp/commit/5c163b75896999a33c0db098ca6fb3973a390313))
6
+
1
7
  ## [1.1.17](https://github.com/appium/appium-mcp/compare/v1.1.16...v1.1.17) (2025-11-21)
2
8
 
3
9
  ### Miscellaneous Chores
package/README.md CHANGED
@@ -195,6 +195,7 @@ MCP Appium provides a comprehensive set of tools organized into the following ca
195
195
  | `appium_screenshot` | Take a screenshot of the current screen and save as PNG |
196
196
  | `appium_scroll` | Scroll the screen vertically (up or down) |
197
197
  | `appium_scroll_to_element` | Scroll until a specific element becomes visible |
198
+ | `appium_swipe` | Swipe the screen in a direction (left, right, up, down) or between custom coordinates |
198
199
  | `appium_get_page_source` | Get the page source (XML) from the current screen |
199
200
 
200
201
  ### App Management
@@ -11,6 +11,7 @@ import installWDA from './ios/install-wda.js';
11
11
  import generateTest from './test-generation/generate-tests.js';
12
12
  import scroll from './navigations/scroll.js';
13
13
  import scrollToElement from './navigations/scroll-to-element.js';
14
+ import swipe from './navigations/swipe.js';
14
15
  import findElement from './interactions/find.js';
15
16
  import clickElement from './interactions/click.js';
16
17
  import doubleTap from './interactions/double-tap.js';
@@ -97,6 +98,7 @@ export default function registerTools(server) {
97
98
  // Navigation
98
99
  scroll(server);
99
100
  scrollToElement(server);
101
+ swipe(server);
100
102
  // Element Interactions
101
103
  findElement(server);
102
104
  clickElement(server);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export default function swipe(server: any): void;
@@ -0,0 +1,295 @@
1
+ import { z } from 'zod';
2
+ import { getDriver, getPlatformName } from '../../session-store.js';
3
+ import log from '../../logger.js';
4
+ import { elementUUIDScheme } from '../../schema.js';
5
+ function calculateSwipeCoordinates(direction, width, height) {
6
+ const centerX = Math.floor(width / 2);
7
+ const centerY = Math.floor(height / 2);
8
+ switch (direction) {
9
+ case 'left':
10
+ return {
11
+ startX: Math.floor(width * 0.8),
12
+ startY: centerY,
13
+ endX: Math.floor(width * 0.2),
14
+ endY: centerY,
15
+ };
16
+ case 'right':
17
+ return {
18
+ startX: Math.floor(width * 0.2),
19
+ startY: centerY,
20
+ endX: Math.floor(width * 0.8),
21
+ endY: centerY,
22
+ };
23
+ case 'up':
24
+ return {
25
+ startX: centerX,
26
+ startY: Math.floor(height * 0.8),
27
+ endX: centerX,
28
+ endY: Math.floor(height * 0.2),
29
+ };
30
+ case 'down':
31
+ return {
32
+ startX: centerX,
33
+ startY: Math.floor(height * 0.2),
34
+ endX: centerX,
35
+ endY: Math.floor(height * 0.8),
36
+ };
37
+ default:
38
+ throw new Error(`Invalid direction: ${direction}`);
39
+ }
40
+ }
41
+ async function performAndroidSwipe(driver, startX, startY, endX, endY, duration) {
42
+ await driver.performActions([
43
+ {
44
+ type: 'pointer',
45
+ id: 'finger1',
46
+ parameters: { pointerType: 'touch' },
47
+ actions: [
48
+ { type: 'pointerMove', duration: 0, x: startX, y: startY },
49
+ { type: 'pointerDown', button: 0 },
50
+ { type: 'pause', duration: 250 },
51
+ { type: 'pointerMove', duration: duration, x: endX, y: endY },
52
+ { type: 'pointerUp', button: 0 },
53
+ ],
54
+ },
55
+ ]);
56
+ }
57
+ async function performiOSSwipe(driver, startX, startY, endX, endY, duration) {
58
+ try {
59
+ await driver.execute('mobile: dragFromToForDuration', {
60
+ fromX: startX,
61
+ fromY: startY,
62
+ toX: endX,
63
+ toY: endY,
64
+ duration: duration / 1000,
65
+ });
66
+ log.info('iOS swipe completed using mobile: dragFromToForDuration');
67
+ }
68
+ catch (dragError) {
69
+ log.info('mobile: dragFromToForDuration failed, trying performActions');
70
+ await driver.performActions([
71
+ {
72
+ type: 'pointer',
73
+ id: 'finger1',
74
+ parameters: { pointerType: 'touch' },
75
+ actions: [
76
+ { type: 'pointerMove', duration: 0, x: startX, y: startY },
77
+ { type: 'pointerDown', button: 0 },
78
+ { type: 'pause', duration: 200 },
79
+ { type: 'pointerMove', duration: duration, x: endX, y: endY },
80
+ { type: 'pause', duration: 50 },
81
+ { type: 'pointerUp', button: 0 },
82
+ ],
83
+ },
84
+ ]);
85
+ log.info('iOS swipe completed using performActions');
86
+ }
87
+ }
88
+ export default function swipe(server) {
89
+ server.addTool({
90
+ name: 'appium_swipe',
91
+ description: `Swipe on the current screen in a specified direction or between custom coordinates.
92
+ Supports four directions: left, right, up, down.
93
+ Can also perform custom coordinate-based swipes for precise control.
94
+ This is useful for navigating carousels, switching tabs, dismissing elements, or navigating between screens.`,
95
+ parameters: z.object({
96
+ direction: z
97
+ .enum(['left', 'right', 'up', 'down'])
98
+ .optional()
99
+ .describe('Direction to swipe. If provided, coordinates will be calculated automatically based on screen size or, when elementUUID is set, relative to that element. Either direction OR custom coordinates must be provided.'),
100
+ elementUUID: elementUUIDScheme
101
+ .optional()
102
+ .describe('Optional element to base the swipe on. When provided with direction, the swipe is calculated relative to this element instead of the whole screen.'),
103
+ startX: z
104
+ .number()
105
+ .int()
106
+ .min(0)
107
+ .optional()
108
+ .describe('Starting X coordinate for custom swipe. Required if direction is not provided.'),
109
+ startY: z
110
+ .number()
111
+ .int()
112
+ .min(0)
113
+ .optional()
114
+ .describe('Starting Y coordinate for custom swipe. Required if direction is not provided.'),
115
+ endX: z
116
+ .number()
117
+ .int()
118
+ .min(0)
119
+ .optional()
120
+ .describe('Ending X coordinate for custom swipe. Required if direction is not provided.'),
121
+ endY: z
122
+ .number()
123
+ .int()
124
+ .min(0)
125
+ .optional()
126
+ .describe('Ending Y coordinate for custom swipe. Required if direction is not provided.'),
127
+ duration: z
128
+ .number()
129
+ .int()
130
+ .min(0)
131
+ .max(5000)
132
+ .default(600)
133
+ .optional()
134
+ .describe('Duration of the swipe gesture in milliseconds. Default is 600ms. Higher values create slower swipes.'),
135
+ }),
136
+ annotations: {
137
+ readOnlyHint: false,
138
+ openWorldHint: false,
139
+ },
140
+ execute: async (args, context) => {
141
+ const driver = getDriver();
142
+ if (!driver) {
143
+ throw new Error('No active driver session. Please create a session first.');
144
+ }
145
+ try {
146
+ const platform = getPlatformName(driver);
147
+ let startX, startY, endX, endY;
148
+ if (args.direction) {
149
+ if (args.elementUUID) {
150
+ const rect = await driver.getElementRect(args.elementUUID);
151
+ const elementCenterX = Math.floor(rect.x + rect.width / 2);
152
+ const elementCenterY = Math.floor(rect.y + rect.height / 2);
153
+ switch (args.direction) {
154
+ case 'left':
155
+ startX = Math.floor(rect.x + rect.width * 0.8);
156
+ startY = elementCenterY;
157
+ endX = Math.floor(rect.x + rect.width * 0.2);
158
+ endY = elementCenterY;
159
+ break;
160
+ case 'right':
161
+ startX = Math.floor(rect.x + rect.width * 0.2);
162
+ startY = elementCenterY;
163
+ endX = Math.floor(rect.x + rect.width * 0.8);
164
+ endY = elementCenterY;
165
+ break;
166
+ case 'up':
167
+ startX = elementCenterX;
168
+ startY = Math.floor(rect.y + rect.height * 0.8);
169
+ endX = elementCenterX;
170
+ endY = Math.floor(rect.y + rect.height * 0.2);
171
+ break;
172
+ case 'down':
173
+ startX = elementCenterX;
174
+ startY = Math.floor(rect.y + rect.height * 0.2);
175
+ endX = elementCenterX;
176
+ endY = Math.floor(rect.y + rect.height * 0.8);
177
+ break;
178
+ default:
179
+ throw new Error(`Invalid direction: ${args.direction}`);
180
+ }
181
+ log.info('Calculated element-based swipe coordinates:', {
182
+ elementUUID: args.elementUUID,
183
+ startX,
184
+ startY,
185
+ endX,
186
+ endY,
187
+ });
188
+ }
189
+ else {
190
+ const { width, height } = await driver.getWindowSize();
191
+ log.info('Device screen size:', { width, height });
192
+ const coords = calculateSwipeCoordinates(args.direction, width, height);
193
+ startX = coords.startX;
194
+ startY = coords.startY;
195
+ endX = coords.endX;
196
+ endY = coords.endY;
197
+ }
198
+ }
199
+ else if (args.startX !== undefined &&
200
+ args.startY !== undefined &&
201
+ args.endX !== undefined &&
202
+ args.endY !== undefined) {
203
+ startX = args.startX;
204
+ startY = args.startY;
205
+ endX = args.endX;
206
+ endY = args.endY;
207
+ }
208
+ else {
209
+ throw new Error('Either direction or all custom coordinates (startX, startY, endX, endY) must be provided.');
210
+ }
211
+ const duration = args.duration || 600;
212
+ log.info('Swipe coordinates:', {
213
+ startX,
214
+ startY,
215
+ endX,
216
+ endY,
217
+ duration,
218
+ });
219
+ if (platform === 'Android') {
220
+ if (startX !== endX && Math.abs(startY - endY) < 50) {
221
+ const swipeDuration = Math.min(duration, 400);
222
+ await driver.performActions([
223
+ {
224
+ type: 'pointer',
225
+ id: 'finger1',
226
+ parameters: { pointerType: 'touch' },
227
+ actions: [
228
+ { type: 'pointerMove', duration: 0, x: startX, y: startY },
229
+ { type: 'pointerDown', button: 0 },
230
+ { type: 'pause', duration: 200 },
231
+ {
232
+ type: 'pointerMove',
233
+ duration: swipeDuration,
234
+ x: endX,
235
+ y: endY,
236
+ },
237
+ { type: 'pause', duration: 50 },
238
+ { type: 'pointerUp', button: 0 },
239
+ ],
240
+ },
241
+ ]);
242
+ log.info('Android horizontal swipe completed');
243
+ }
244
+ else {
245
+ await performAndroidSwipe(driver, startX, startY, endX, endY, duration);
246
+ }
247
+ log.info('Android swipe action completed successfully.');
248
+ }
249
+ else if (platform === 'iOS') {
250
+ if (args.direction) {
251
+ try {
252
+ await driver.execute('mobile: swipe', {
253
+ direction: args.direction,
254
+ });
255
+ log.info(`iOS swipe completed using mobile: swipe (${args.direction})`);
256
+ }
257
+ catch (swipeError) {
258
+ log.info('mobile: swipe failed, trying dragFromToForDuration');
259
+ await performiOSSwipe(driver, startX, startY, endX, endY, duration);
260
+ }
261
+ }
262
+ else {
263
+ await performiOSSwipe(driver, startX, startY, endX, endY, duration);
264
+ }
265
+ log.info('iOS swipe action completed successfully.');
266
+ }
267
+ else {
268
+ throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
269
+ }
270
+ const directionText = args.direction
271
+ ? ` ${args.direction}`
272
+ : ` from (${startX}, ${startY}) to (${endX}, ${endY})`;
273
+ return {
274
+ content: [
275
+ {
276
+ type: 'text',
277
+ text: `Swiped${directionText} successfully.`,
278
+ },
279
+ ],
280
+ };
281
+ }
282
+ catch (err) {
283
+ return {
284
+ content: [
285
+ {
286
+ type: 'text',
287
+ text: `Failed to perform swipe. Error: ${err.toString()}`,
288
+ },
289
+ ],
290
+ };
291
+ }
292
+ },
293
+ });
294
+ }
295
+ //# sourceMappingURL=swipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swipe.js","sourceRoot":"","sources":["../../../src/tools/navigations/swipe.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appium-mcp",
3
3
  "mcpName": "io.github.appium/appium-mcp",
4
- "version": "1.1.17",
4
+ "version": "1.2.0",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -21,6 +21,7 @@ This directory contains all MCP tools available in MCP Appium.
21
21
 
22
22
  - `scroll.ts` - Scroll screens
23
23
  - `scroll-to-element.ts` - Scroll until element found
24
+ - `swipe.ts` - Swipe screens in any direction or between custom coordinates
24
25
 
25
26
  ### Element Interactions (`interactions/`)
26
27
 
@@ -26,6 +26,7 @@ import installWDA from './ios/install-wda.js';
26
26
  import generateTest from './test-generation/generate-tests.js';
27
27
  import scroll from './navigations/scroll.js';
28
28
  import scrollToElement from './navigations/scroll-to-element.js';
29
+ import swipe from './navigations/swipe.js';
29
30
  import findElement from './interactions/find.js';
30
31
  import clickElement from './interactions/click.js';
31
32
  import doubleTap from './interactions/double-tap.js';
@@ -120,6 +121,7 @@ export default function registerTools(server: FastMCP): void {
120
121
  // Navigation
121
122
  scroll(server);
122
123
  scrollToElement(server);
124
+ swipe(server);
123
125
 
124
126
  // Element Interactions
125
127
  findElement(server);
@@ -0,0 +1,357 @@
1
+ import { z } from 'zod';
2
+ import { getDriver, getPlatformName } from '../../session-store.js';
3
+ import log from '../../logger.js';
4
+ import { elementUUIDScheme } from '../../schema.js';
5
+
6
+ function calculateSwipeCoordinates(
7
+ direction: 'left' | 'right' | 'up' | 'down',
8
+ width: number,
9
+ height: number
10
+ ): { startX: number; startY: number; endX: number; endY: number } {
11
+ const centerX = Math.floor(width / 2);
12
+ const centerY = Math.floor(height / 2);
13
+
14
+ switch (direction) {
15
+ case 'left':
16
+ return {
17
+ startX: Math.floor(width * 0.8),
18
+ startY: centerY,
19
+ endX: Math.floor(width * 0.2),
20
+ endY: centerY,
21
+ };
22
+ case 'right':
23
+ return {
24
+ startX: Math.floor(width * 0.2),
25
+ startY: centerY,
26
+ endX: Math.floor(width * 0.8),
27
+ endY: centerY,
28
+ };
29
+ case 'up':
30
+ return {
31
+ startX: centerX,
32
+ startY: Math.floor(height * 0.8),
33
+ endX: centerX,
34
+ endY: Math.floor(height * 0.2),
35
+ };
36
+ case 'down':
37
+ return {
38
+ startX: centerX,
39
+ startY: Math.floor(height * 0.2),
40
+ endX: centerX,
41
+ endY: Math.floor(height * 0.8),
42
+ };
43
+ default:
44
+ throw new Error(`Invalid direction: ${direction}`);
45
+ }
46
+ }
47
+
48
+ async function performAndroidSwipe(
49
+ driver: any,
50
+ startX: number,
51
+ startY: number,
52
+ endX: number,
53
+ endY: number,
54
+ duration: number
55
+ ): Promise<void> {
56
+ await driver.performActions([
57
+ {
58
+ type: 'pointer',
59
+ id: 'finger1',
60
+ parameters: { pointerType: 'touch' },
61
+ actions: [
62
+ { type: 'pointerMove', duration: 0, x: startX, y: startY },
63
+ { type: 'pointerDown', button: 0 },
64
+ { type: 'pause', duration: 250 },
65
+ { type: 'pointerMove', duration: duration, x: endX, y: endY },
66
+ { type: 'pointerUp', button: 0 },
67
+ ],
68
+ },
69
+ ]);
70
+ }
71
+
72
+ async function performiOSSwipe(
73
+ driver: any,
74
+ startX: number,
75
+ startY: number,
76
+ endX: number,
77
+ endY: number,
78
+ duration: number
79
+ ): Promise<void> {
80
+ try {
81
+ await driver.execute('mobile: dragFromToForDuration', {
82
+ fromX: startX,
83
+ fromY: startY,
84
+ toX: endX,
85
+ toY: endY,
86
+ duration: duration / 1000,
87
+ });
88
+ log.info('iOS swipe completed using mobile: dragFromToForDuration');
89
+ } catch (dragError) {
90
+ log.info('mobile: dragFromToForDuration failed, trying performActions');
91
+ await driver.performActions([
92
+ {
93
+ type: 'pointer',
94
+ id: 'finger1',
95
+ parameters: { pointerType: 'touch' },
96
+ actions: [
97
+ { type: 'pointerMove', duration: 0, x: startX, y: startY },
98
+ { type: 'pointerDown', button: 0 },
99
+ { type: 'pause', duration: 200 },
100
+ { type: 'pointerMove', duration: duration, x: endX, y: endY },
101
+ { type: 'pause', duration: 50 },
102
+ { type: 'pointerUp', button: 0 },
103
+ ],
104
+ },
105
+ ]);
106
+ log.info('iOS swipe completed using performActions');
107
+ }
108
+ }
109
+
110
+ export default function swipe(server: any): void {
111
+ server.addTool({
112
+ name: 'appium_swipe',
113
+ description: `Swipe on the current screen in a specified direction or between custom coordinates.
114
+ Supports four directions: left, right, up, down.
115
+ Can also perform custom coordinate-based swipes for precise control.
116
+ This is useful for navigating carousels, switching tabs, dismissing elements, or navigating between screens.`,
117
+ parameters: z.object({
118
+ direction: z
119
+ .enum(['left', 'right', 'up', 'down'])
120
+ .optional()
121
+ .describe(
122
+ 'Direction to swipe. If provided, coordinates will be calculated automatically based on screen size or, when elementUUID is set, relative to that element. Either direction OR custom coordinates must be provided.'
123
+ ),
124
+ elementUUID: elementUUIDScheme
125
+ .optional()
126
+ .describe(
127
+ 'Optional element to base the swipe on. When provided with direction, the swipe is calculated relative to this element instead of the whole screen.'
128
+ ),
129
+ startX: z
130
+ .number()
131
+ .int()
132
+ .min(0)
133
+ .optional()
134
+ .describe(
135
+ 'Starting X coordinate for custom swipe. Required if direction is not provided.'
136
+ ),
137
+ startY: z
138
+ .number()
139
+ .int()
140
+ .min(0)
141
+ .optional()
142
+ .describe(
143
+ 'Starting Y coordinate for custom swipe. Required if direction is not provided.'
144
+ ),
145
+ endX: z
146
+ .number()
147
+ .int()
148
+ .min(0)
149
+ .optional()
150
+ .describe(
151
+ 'Ending X coordinate for custom swipe. Required if direction is not provided.'
152
+ ),
153
+ endY: z
154
+ .number()
155
+ .int()
156
+ .min(0)
157
+ .optional()
158
+ .describe(
159
+ 'Ending Y coordinate for custom swipe. Required if direction is not provided.'
160
+ ),
161
+ duration: z
162
+ .number()
163
+ .int()
164
+ .min(0)
165
+ .max(5000)
166
+ .default(600)
167
+ .optional()
168
+ .describe(
169
+ 'Duration of the swipe gesture in milliseconds. Default is 600ms. Higher values create slower swipes.'
170
+ ),
171
+ }),
172
+ annotations: {
173
+ readOnlyHint: false,
174
+ openWorldHint: false,
175
+ },
176
+ execute: async (args: any, context: any): Promise<any> => {
177
+ const driver = getDriver();
178
+ if (!driver) {
179
+ throw new Error(
180
+ 'No active driver session. Please create a session first.'
181
+ );
182
+ }
183
+
184
+ try {
185
+ const platform = getPlatformName(driver);
186
+ let startX: number, startY: number, endX: number, endY: number;
187
+
188
+ if (args.direction) {
189
+ if (args.elementUUID) {
190
+ const rect = await (driver as any).getElementRect(args.elementUUID);
191
+ const elementCenterX = Math.floor(rect.x + rect.width / 2);
192
+ const elementCenterY = Math.floor(rect.y + rect.height / 2);
193
+
194
+ switch (args.direction) {
195
+ case 'left':
196
+ startX = Math.floor(rect.x + rect.width * 0.8);
197
+ startY = elementCenterY;
198
+ endX = Math.floor(rect.x + rect.width * 0.2);
199
+ endY = elementCenterY;
200
+ break;
201
+ case 'right':
202
+ startX = Math.floor(rect.x + rect.width * 0.2);
203
+ startY = elementCenterY;
204
+ endX = Math.floor(rect.x + rect.width * 0.8);
205
+ endY = elementCenterY;
206
+ break;
207
+ case 'up':
208
+ startX = elementCenterX;
209
+ startY = Math.floor(rect.y + rect.height * 0.8);
210
+ endX = elementCenterX;
211
+ endY = Math.floor(rect.y + rect.height * 0.2);
212
+ break;
213
+ case 'down':
214
+ startX = elementCenterX;
215
+ startY = Math.floor(rect.y + rect.height * 0.2);
216
+ endX = elementCenterX;
217
+ endY = Math.floor(rect.y + rect.height * 0.8);
218
+ break;
219
+ default:
220
+ throw new Error(`Invalid direction: ${args.direction}`);
221
+ }
222
+ log.info('Calculated element-based swipe coordinates:', {
223
+ elementUUID: args.elementUUID,
224
+ startX,
225
+ startY,
226
+ endX,
227
+ endY,
228
+ });
229
+ } else {
230
+ const { width, height } = await driver.getWindowSize();
231
+ log.info('Device screen size:', { width, height });
232
+ const coords = calculateSwipeCoordinates(
233
+ args.direction,
234
+ width,
235
+ height
236
+ );
237
+ startX = coords.startX;
238
+ startY = coords.startY;
239
+ endX = coords.endX;
240
+ endY = coords.endY;
241
+ }
242
+ } else if (
243
+ args.startX !== undefined &&
244
+ args.startY !== undefined &&
245
+ args.endX !== undefined &&
246
+ args.endY !== undefined
247
+ ) {
248
+ startX = args.startX;
249
+ startY = args.startY;
250
+ endX = args.endX;
251
+ endY = args.endY;
252
+ } else {
253
+ throw new Error(
254
+ 'Either direction or all custom coordinates (startX, startY, endX, endY) must be provided.'
255
+ );
256
+ }
257
+
258
+ const duration = args.duration || 600;
259
+
260
+ log.info('Swipe coordinates:', {
261
+ startX,
262
+ startY,
263
+ endX,
264
+ endY,
265
+ duration,
266
+ });
267
+
268
+ if (platform === 'Android') {
269
+ if (startX !== endX && Math.abs(startY - endY) < 50) {
270
+ const swipeDuration = Math.min(duration, 400);
271
+ await driver.performActions([
272
+ {
273
+ type: 'pointer',
274
+ id: 'finger1',
275
+ parameters: { pointerType: 'touch' },
276
+ actions: [
277
+ { type: 'pointerMove', duration: 0, x: startX, y: startY },
278
+ { type: 'pointerDown', button: 0 },
279
+ { type: 'pause', duration: 200 },
280
+ {
281
+ type: 'pointerMove',
282
+ duration: swipeDuration,
283
+ x: endX,
284
+ y: endY,
285
+ },
286
+ { type: 'pause', duration: 50 },
287
+ { type: 'pointerUp', button: 0 },
288
+ ],
289
+ },
290
+ ]);
291
+ log.info('Android horizontal swipe completed');
292
+ } else {
293
+ await performAndroidSwipe(
294
+ driver,
295
+ startX,
296
+ startY,
297
+ endX,
298
+ endY,
299
+ duration
300
+ );
301
+ }
302
+ log.info('Android swipe action completed successfully.');
303
+ } else if (platform === 'iOS') {
304
+ if (args.direction) {
305
+ try {
306
+ await driver.execute('mobile: swipe', {
307
+ direction: args.direction,
308
+ });
309
+ log.info(
310
+ `iOS swipe completed using mobile: swipe (${args.direction})`
311
+ );
312
+ } catch (swipeError) {
313
+ log.info('mobile: swipe failed, trying dragFromToForDuration');
314
+ await performiOSSwipe(
315
+ driver,
316
+ startX,
317
+ startY,
318
+ endX,
319
+ endY,
320
+ duration
321
+ );
322
+ }
323
+ } else {
324
+ await performiOSSwipe(driver, startX, startY, endX, endY, duration);
325
+ }
326
+ log.info('iOS swipe action completed successfully.');
327
+ } else {
328
+ throw new Error(
329
+ `Unsupported platform: ${platform}. Only Android and iOS are supported.`
330
+ );
331
+ }
332
+
333
+ const directionText = args.direction
334
+ ? ` ${args.direction}`
335
+ : ` from (${startX}, ${startY}) to (${endX}, ${endY})`;
336
+
337
+ return {
338
+ content: [
339
+ {
340
+ type: 'text',
341
+ text: `Swiped${directionText} successfully.`,
342
+ },
343
+ ],
344
+ };
345
+ } catch (err: any) {
346
+ return {
347
+ content: [
348
+ {
349
+ type: 'text',
350
+ text: `Failed to perform swipe. Error: ${err.toString()}`,
351
+ },
352
+ ],
353
+ };
354
+ }
355
+ },
356
+ });
357
+ }