appium-mcp 1.54.0 → 1.55.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 +6 -0
- package/README.md +1 -3
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/session/geolocation.d.ts +1 -3
- package/dist/tools/session/geolocation.d.ts.map +1 -1
- package/dist/tools/session/geolocation.js +109 -168
- package/dist/tools/session/geolocation.js.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/resources/submodules.zip +0 -0
- package/src/tools/index.ts +2 -8
- package/src/tools/session/geolocation.ts +136 -184
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [1.55.0](https://github.com/appium/appium-mcp/compare/v1.54.0...v1.55.0) (2026-04-13)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* consolidate geolocation functionality into a single tool ([#256](https://github.com/appium/appium-mcp/issues/256)) ([9cdedbd](https://github.com/appium/appium-mcp/commit/9cdedbd876ee70e85be17a786cb22069af65f9c0))
|
|
6
|
+
|
|
1
7
|
## [1.54.0](https://github.com/appium/appium-mcp/compare/v1.53.1...v1.54.0) (2026-04-11)
|
|
2
8
|
|
|
3
9
|
### Features
|
package/README.md
CHANGED
|
@@ -347,9 +347,7 @@ The default regex pattern allows any URL that starts with `http://` or `https://
|
|
|
347
347
|
| `appium_swipe` | Swipe the screen in a direction (left, right, up, down) or between custom coordinates |
|
|
348
348
|
| `appium_get_page_source` | Get the page source (XML) from the current screen |
|
|
349
349
|
| `appium_orientation` | Get or set device/screen orientation with `action` = `get` or `set` (requires `orientation` for set). |
|
|
350
|
-
| `
|
|
351
|
-
| `appium_get_geolocation` | Get the current GPS coordinates (latitude, longitude, altitude) of the device. |
|
|
352
|
-
| `appium_reset_geolocation` | Reset the simulated/mocked geolocation back to the system default. On iOS, clears the simulated location. On Android real devices, removes the mock location provider. Not supported on Android emulators. |
|
|
350
|
+
| `appium_geolocation` | Get, set, or reset the device GPS coordinates with `action` = `get`, `set`, or `reset`. For `set`, provide `latitude` and `longitude` (and optional `altitude` on Android). Not supported on Android emulators for `reset`. |
|
|
353
351
|
| `appium_screen_recording` | Start or stop screen recording with `action` = `start` or `stop`. On stop, returns the saved MP4 path. |
|
|
354
352
|
| `appium_mobile_device_info` | Get device information, battery status, or current device time. Use `action` = `info` (model, OS version, locale, timezone, screen density, etc.), `battery` (level as percentage and charging state), or `time` (current device time; accepts an optional `format` moment.js string, defaults to ISO 8601). Works on both iOS and Android. |
|
|
355
353
|
|
|
@@ -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;AAuDlC,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CA4I3D"}
|
package/dist/tools/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import selectDevice from './session/select-device.js';
|
|
|
10
10
|
import openNotifications from './session/open-notifications.js';
|
|
11
11
|
import shakeDevice from './session/shake.js';
|
|
12
12
|
import { lockDevice, unlockDevice } from './session/lock.js';
|
|
13
|
-
import
|
|
13
|
+
import geolocation from './session/geolocation.js';
|
|
14
14
|
import deviceInfo from './session/device-info.js';
|
|
15
15
|
import fileTransfer from './session/file-transfer.js';
|
|
16
16
|
import driverSettings from './session/driver-settings.js';
|
|
@@ -123,9 +123,7 @@ export default function registerTools(server) {
|
|
|
123
123
|
shakeDevice(server);
|
|
124
124
|
lockDevice(server);
|
|
125
125
|
unlockDevice(server);
|
|
126
|
-
|
|
127
|
-
getGeolocation(server);
|
|
128
|
-
resetGeolocation(server);
|
|
126
|
+
geolocation(server);
|
|
129
127
|
deviceInfo(server);
|
|
130
128
|
fileTransfer(server);
|
|
131
129
|
driverSettings(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,YAAY,MAAM,kCAAkC,CAAC;AAC5D,OAAO,aAAa,MAAM,6BAA6B,CAAC;AACxD,OAAO,aAAa,MAAM,6BAA6B,CAAC;AACxD,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,aAAa,MAAM,6BAA6B,CAAC;AACxD,OAAO,gBAAgB,MAAM,+BAA+B,CAAC;AAC7D,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,iBAAiB,MAAM,iCAAiC,CAAC;AAChE,OAAO,WAAW,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import type { FastMCP } from 'fastmcp';
|
|
2
|
-
export
|
|
3
|
-
export declare function getGeolocation(server: FastMCP): void;
|
|
4
|
-
export declare function resetGeolocation(server: FastMCP): void;
|
|
2
|
+
export default function geolocation(server: FastMCP): void;
|
|
5
3
|
//# sourceMappingURL=geolocation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geolocation.d.ts","sourceRoot":"","sources":["../../../src/tools/session/geolocation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"geolocation.d.ts","sourceRoot":"","sources":["../../../src/tools/session/geolocation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AAyItD,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CA8BzD"}
|
|
@@ -1,186 +1,127 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { getDriver, getPlatformName, PLATFORM } from '../../session-store.js';
|
|
3
3
|
import { execute } from '../../command.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!driver) {
|
|
36
|
-
throw new Error('No driver found');
|
|
37
|
-
}
|
|
38
|
-
try {
|
|
39
|
-
const platform = getPlatformName(driver);
|
|
40
|
-
const { latitude, longitude, altitude } = args;
|
|
41
|
-
if (platform === PLATFORM.ios) {
|
|
42
|
-
await execute(driver, 'mobile: setSimulatedLocation', {
|
|
43
|
-
latitude,
|
|
44
|
-
longitude,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
else if (platform === PLATFORM.android) {
|
|
48
|
-
await execute(driver, 'mobile: setGeolocation', {
|
|
49
|
-
latitude,
|
|
50
|
-
longitude,
|
|
51
|
-
...(altitude !== undefined && { altitude }),
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
content: [
|
|
59
|
-
{
|
|
60
|
-
type: 'text',
|
|
61
|
-
text: `Successfully set geolocation to latitude=${latitude}, longitude=${longitude}${altitude !== undefined ? `, altitude=${altitude}` : ''}.`,
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
return {
|
|
68
|
-
content: [
|
|
69
|
-
{
|
|
70
|
-
type: 'text',
|
|
71
|
-
text: `Failed to set geolocation. Error: ${err.toString()}`,
|
|
72
|
-
},
|
|
73
|
-
],
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
});
|
|
4
|
+
const schema = z.object({
|
|
5
|
+
action: z
|
|
6
|
+
.enum(['get', 'set', 'reset'])
|
|
7
|
+
.describe('Action to perform. ' +
|
|
8
|
+
'get: read the current device geolocation. ' +
|
|
9
|
+
'set: set the device geolocation (requires latitude and longitude; optional altitude for Android). ' +
|
|
10
|
+
'reset: reset the geolocation to the default/system value. Not supported on Android emulators — use action=set instead.'),
|
|
11
|
+
latitude: z.coerce
|
|
12
|
+
.number()
|
|
13
|
+
.min(-90)
|
|
14
|
+
.max(90)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Latitude value (-90 to 90). Measurement of distance north or south of the Equator. Required for: set.'),
|
|
17
|
+
longitude: z.coerce
|
|
18
|
+
.number()
|
|
19
|
+
.min(-180)
|
|
20
|
+
.max(180)
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Longitude value (-180 to 180). Measurement of distance east or west of the prime meridian. Required for: set.'),
|
|
23
|
+
altitude: z.coerce
|
|
24
|
+
.number()
|
|
25
|
+
.optional()
|
|
26
|
+
.refine((v) => v === undefined || !isNaN(v), 'altitude must be a valid number')
|
|
27
|
+
.describe('Altitude value in meters. Android only, defaults to 0. Ignored on iOS. Used with: set.'),
|
|
28
|
+
sessionId: z
|
|
29
|
+
.string()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
32
|
+
});
|
|
33
|
+
function textResult(text) {
|
|
34
|
+
return { content: [{ type: 'text', text }] };
|
|
78
35
|
}
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
throw new Error('No driver found');
|
|
98
|
-
}
|
|
99
|
-
try {
|
|
100
|
-
const platform = getPlatformName(driver);
|
|
101
|
-
let result;
|
|
102
|
-
if (platform === PLATFORM.ios) {
|
|
103
|
-
result = await execute(driver, 'mobile: getSimulatedLocation', {});
|
|
104
|
-
}
|
|
105
|
-
else if (platform === PLATFORM.android) {
|
|
106
|
-
result = await execute(driver, 'mobile: getGeolocation', {});
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
content: [
|
|
113
|
-
{
|
|
114
|
-
type: 'text',
|
|
115
|
-
text: `Current geolocation: latitude=${result.latitude}, longitude=${result.longitude}${result.altitude !== undefined ? `, altitude=${result.altitude}` : ''}.`,
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
catch (err) {
|
|
121
|
-
return {
|
|
122
|
-
content: [
|
|
123
|
-
{
|
|
124
|
-
type: 'text',
|
|
125
|
-
text: `Failed to get geolocation. Error: ${err.toString()}`,
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
});
|
|
36
|
+
async function handleGet(args) {
|
|
37
|
+
const driver = getDriver(args.sessionId);
|
|
38
|
+
if (!driver) {
|
|
39
|
+
throw new Error('No driver found');
|
|
40
|
+
}
|
|
41
|
+
const platform = getPlatformName(driver);
|
|
42
|
+
let result;
|
|
43
|
+
if (platform === PLATFORM.ios) {
|
|
44
|
+
result = await execute(driver, 'mobile: getSimulatedLocation', {});
|
|
45
|
+
}
|
|
46
|
+
else if (platform === PLATFORM.android) {
|
|
47
|
+
result = await execute(driver, 'mobile: getGeolocation', {});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
|
|
51
|
+
}
|
|
52
|
+
const altitudeText = result.altitude !== undefined ? `, altitude=${result.altitude}` : '';
|
|
53
|
+
return textResult(`Current geolocation: latitude=${result.latitude}, longitude=${result.longitude}${altitudeText}.`);
|
|
132
54
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
55
|
+
async function handleSet(args) {
|
|
56
|
+
if (args.latitude === undefined || args.longitude === undefined) {
|
|
57
|
+
throw new Error('latitude and longitude are required for action=set');
|
|
58
|
+
}
|
|
59
|
+
const driver = getDriver(args.sessionId);
|
|
60
|
+
if (!driver) {
|
|
61
|
+
throw new Error('No driver found');
|
|
62
|
+
}
|
|
63
|
+
const platform = getPlatformName(driver);
|
|
64
|
+
const { latitude, longitude, altitude } = args;
|
|
65
|
+
if (platform === PLATFORM.ios) {
|
|
66
|
+
await execute(driver, 'mobile: setSimulatedLocation', {
|
|
67
|
+
latitude,
|
|
68
|
+
longitude,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else if (platform === PLATFORM.android) {
|
|
72
|
+
await execute(driver, 'mobile: setGeolocation', {
|
|
73
|
+
latitude,
|
|
74
|
+
longitude,
|
|
75
|
+
...(altitude !== undefined && { altitude }),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
|
|
80
|
+
}
|
|
81
|
+
const altitudeText = altitude !== undefined ? `, altitude=${altitude}` : '';
|
|
82
|
+
return textResult(`Successfully set geolocation to latitude=${latitude}, longitude=${longitude}${altitudeText}.`);
|
|
83
|
+
}
|
|
84
|
+
async function handleReset(args) {
|
|
85
|
+
const driver = getDriver(args.sessionId);
|
|
86
|
+
if (!driver) {
|
|
87
|
+
throw new Error('No driver found');
|
|
88
|
+
}
|
|
89
|
+
const platform = getPlatformName(driver);
|
|
90
|
+
if (platform === PLATFORM.ios) {
|
|
91
|
+
await execute(driver, 'mobile: resetSimulatedLocation', {});
|
|
92
|
+
}
|
|
93
|
+
else if (platform === PLATFORM.android) {
|
|
94
|
+
await execute(driver, 'mobile: resetGeolocation', {});
|
|
95
|
+
// Refresh GPS cache
|
|
96
|
+
await execute(driver, 'mobile: refreshGpsCache', {});
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
|
|
100
|
+
}
|
|
101
|
+
return textResult('Successfully reset geolocation to default.');
|
|
102
|
+
}
|
|
103
|
+
export default function geolocation(server) {
|
|
140
104
|
server.addTool({
|
|
141
|
-
name: '
|
|
142
|
-
description: '
|
|
143
|
-
parameters:
|
|
105
|
+
name: 'appium_geolocation',
|
|
106
|
+
description: 'Get, set, or reset the device geolocation (GPS coordinates). Works on both iOS (simulators and real devices) and Android (emulators and real devices with mock location enabled). Use action=get to read current coordinates, action=set with latitude/longitude (and optional altitude for Android) to simulate a location, or action=reset to restore the system default. Note: On Android emulators, reset is not supported — use action=set to manually restore coordinates instead. On Android real devices, the mocked location may persist until the GPS cache refreshes.',
|
|
107
|
+
parameters: schema,
|
|
144
108
|
annotations: {
|
|
145
109
|
readOnlyHint: false,
|
|
146
110
|
openWorldHint: false,
|
|
147
111
|
},
|
|
148
112
|
execute: async (args, _context) => {
|
|
149
|
-
const driver = getDriver(args.sessionId);
|
|
150
|
-
if (!driver) {
|
|
151
|
-
throw new Error('No driver found');
|
|
152
|
-
}
|
|
153
113
|
try {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
await execute(driver, 'mobile: refreshGpsCache', {});
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
|
|
114
|
+
switch (args.action) {
|
|
115
|
+
case 'get':
|
|
116
|
+
return await handleGet(args);
|
|
117
|
+
case 'set':
|
|
118
|
+
return await handleSet(args);
|
|
119
|
+
case 'reset':
|
|
120
|
+
return await handleReset(args);
|
|
165
121
|
}
|
|
166
|
-
return {
|
|
167
|
-
content: [
|
|
168
|
-
{
|
|
169
|
-
type: 'text',
|
|
170
|
-
text: 'Successfully reset geolocation to default.',
|
|
171
|
-
},
|
|
172
|
-
],
|
|
173
|
-
};
|
|
174
122
|
}
|
|
175
123
|
catch (err) {
|
|
176
|
-
return {
|
|
177
|
-
content: [
|
|
178
|
-
{
|
|
179
|
-
type: 'text',
|
|
180
|
-
text: `Failed to reset geolocation. Error: ${err.toString()}`,
|
|
181
|
-
},
|
|
182
|
-
],
|
|
183
|
-
};
|
|
124
|
+
return textResult(`Failed to ${args.action} geolocation. Error: ${err.toString()}`);
|
|
184
125
|
}
|
|
185
126
|
},
|
|
186
127
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geolocation.js","sourceRoot":"","sources":["../../../src/tools/session/geolocation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,
|
|
1
|
+
{"version":3,"file":"geolocation.js","sourceRoot":"","sources":["../../../src/tools/session/geolocation.ts"],"names":[],"mappings":"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"}
|
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.55.0",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "appium-mcp",
|
|
11
|
-
"version": "1.
|
|
11
|
+
"version": "1.55.0",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|
|
Binary file
|
package/src/tools/index.ts
CHANGED
|
@@ -25,11 +25,7 @@ import selectDevice from './session/select-device.js';
|
|
|
25
25
|
import openNotifications from './session/open-notifications.js';
|
|
26
26
|
import shakeDevice from './session/shake.js';
|
|
27
27
|
import { lockDevice, unlockDevice } from './session/lock.js';
|
|
28
|
-
import
|
|
29
|
-
setGeolocation,
|
|
30
|
-
getGeolocation,
|
|
31
|
-
resetGeolocation,
|
|
32
|
-
} from './session/geolocation.js';
|
|
28
|
+
import geolocation from './session/geolocation.js';
|
|
33
29
|
import deviceInfo from './session/device-info.js';
|
|
34
30
|
import fileTransfer from './session/file-transfer.js';
|
|
35
31
|
import driverSettings from './session/driver-settings.js';
|
|
@@ -148,9 +144,7 @@ export default function registerTools(server: FastMCP): void {
|
|
|
148
144
|
shakeDevice(server);
|
|
149
145
|
lockDevice(server);
|
|
150
146
|
unlockDevice(server);
|
|
151
|
-
|
|
152
|
-
getGeolocation(server);
|
|
153
|
-
resetGeolocation(server);
|
|
147
|
+
geolocation(server);
|
|
154
148
|
deviceInfo(server);
|
|
155
149
|
fileTransfer(server);
|
|
156
150
|
driverSettings(server);
|
|
@@ -3,213 +3,165 @@ import { z } from 'zod';
|
|
|
3
3
|
import { getDriver, getPlatformName, PLATFORM } from '../../session-store.js';
|
|
4
4
|
import { execute } from '../../command.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
6
|
+
const schema = z.object({
|
|
7
|
+
action: z
|
|
8
|
+
.enum(['get', 'set', 'reset'])
|
|
9
|
+
.describe(
|
|
10
|
+
'Action to perform. ' +
|
|
11
|
+
'get: read the current device geolocation. ' +
|
|
12
|
+
'set: set the device geolocation (requires latitude and longitude; optional altitude for Android). ' +
|
|
13
|
+
'reset: reset the geolocation to the default/system value. Not supported on Android emulators — use action=set instead.'
|
|
14
|
+
),
|
|
15
|
+
latitude: z.coerce
|
|
16
|
+
.number()
|
|
17
|
+
.min(-90)
|
|
18
|
+
.max(90)
|
|
19
|
+
.optional()
|
|
20
|
+
.describe(
|
|
21
|
+
'Latitude value (-90 to 90). Measurement of distance north or south of the Equator. Required for: set.'
|
|
22
|
+
),
|
|
23
|
+
longitude: z.coerce
|
|
24
|
+
.number()
|
|
25
|
+
.min(-180)
|
|
26
|
+
.max(180)
|
|
27
|
+
.optional()
|
|
28
|
+
.describe(
|
|
29
|
+
'Longitude value (-180 to 180). Measurement of distance east or west of the prime meridian. Required for: set.'
|
|
30
|
+
),
|
|
31
|
+
altitude: z.coerce
|
|
32
|
+
.number()
|
|
33
|
+
.optional()
|
|
34
|
+
.refine(
|
|
35
|
+
(v) => v === undefined || !isNaN(v),
|
|
36
|
+
'altitude must be a valid number'
|
|
37
|
+
)
|
|
38
|
+
.describe(
|
|
39
|
+
'Altitude value in meters. Android only, defaults to 0. Ignored on iOS. Used with: set.'
|
|
40
|
+
),
|
|
41
|
+
sessionId: z
|
|
42
|
+
.string()
|
|
43
|
+
.optional()
|
|
44
|
+
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
45
|
+
});
|
|
31
46
|
|
|
32
|
-
|
|
33
|
-
name: 'appium_set_geolocation',
|
|
34
|
-
description:
|
|
35
|
-
'Set the geolocation (GPS coordinates) of the device. Works on both iOS (simulators and real devices) and Android (emulators and real devices with mock location enabled).',
|
|
36
|
-
parameters: setGeolocationSchema,
|
|
37
|
-
annotations: {
|
|
38
|
-
readOnlyHint: false,
|
|
39
|
-
openWorldHint: false,
|
|
40
|
-
},
|
|
41
|
-
execute: async (
|
|
42
|
-
args: z.infer<typeof setGeolocationSchema>,
|
|
43
|
-
_context: Record<string, unknown> | undefined
|
|
44
|
-
): Promise<ContentResult> => {
|
|
45
|
-
const driver = getDriver(args.sessionId);
|
|
46
|
-
if (!driver) {
|
|
47
|
-
throw new Error('No driver found');
|
|
48
|
-
}
|
|
47
|
+
type GeolocationArgs = z.infer<typeof schema>;
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (platform === PLATFORM.ios) {
|
|
55
|
-
await execute(driver, 'mobile: setSimulatedLocation', {
|
|
56
|
-
latitude,
|
|
57
|
-
longitude,
|
|
58
|
-
});
|
|
59
|
-
} else if (platform === PLATFORM.android) {
|
|
60
|
-
await execute(driver, 'mobile: setGeolocation', {
|
|
61
|
-
latitude,
|
|
62
|
-
longitude,
|
|
63
|
-
...(altitude !== undefined && { altitude }),
|
|
64
|
-
});
|
|
65
|
-
} else {
|
|
66
|
-
throw new Error(
|
|
67
|
-
`Unsupported platform: ${platform}. Only Android and iOS are supported.`
|
|
68
|
-
);
|
|
69
|
-
}
|
|
49
|
+
function textResult(text: string): ContentResult {
|
|
50
|
+
return { content: [{ type: 'text', text }] };
|
|
51
|
+
}
|
|
70
52
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
53
|
+
async function handleGet(args: GeolocationArgs): Promise<ContentResult> {
|
|
54
|
+
const driver = getDriver(args.sessionId);
|
|
55
|
+
if (!driver) {
|
|
56
|
+
throw new Error('No driver found');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const platform = getPlatformName(driver);
|
|
60
|
+
let result: Record<string, any>;
|
|
61
|
+
|
|
62
|
+
if (platform === PLATFORM.ios) {
|
|
63
|
+
result = await execute(driver, 'mobile: getSimulatedLocation', {});
|
|
64
|
+
} else if (platform === PLATFORM.android) {
|
|
65
|
+
result = await execute(driver, 'mobile: getGeolocation', {});
|
|
66
|
+
} else {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Unsupported platform: ${platform}. Only Android and iOS are supported.`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const altitudeText =
|
|
73
|
+
result.altitude !== undefined ? `, altitude=${result.altitude}` : '';
|
|
74
|
+
return textResult(
|
|
75
|
+
`Current geolocation: latitude=${result.latitude}, longitude=${result.longitude}${altitudeText}.`
|
|
76
|
+
);
|
|
91
77
|
}
|
|
92
78
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.optional()
|
|
98
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
99
|
-
});
|
|
79
|
+
async function handleSet(args: GeolocationArgs): Promise<ContentResult> {
|
|
80
|
+
if (args.latitude === undefined || args.longitude === undefined) {
|
|
81
|
+
throw new Error('latitude and longitude are required for action=set');
|
|
82
|
+
}
|
|
100
83
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
parameters: getGeolocationSchema,
|
|
106
|
-
annotations: {
|
|
107
|
-
readOnlyHint: true,
|
|
108
|
-
openWorldHint: false,
|
|
109
|
-
},
|
|
110
|
-
execute: async (
|
|
111
|
-
args: z.infer<typeof getGeolocationSchema>,
|
|
112
|
-
_context: Record<string, unknown> | undefined
|
|
113
|
-
): Promise<ContentResult> => {
|
|
114
|
-
const driver = getDriver(args.sessionId);
|
|
115
|
-
if (!driver) {
|
|
116
|
-
throw new Error('No driver found');
|
|
117
|
-
}
|
|
84
|
+
const driver = getDriver(args.sessionId);
|
|
85
|
+
if (!driver) {
|
|
86
|
+
throw new Error('No driver found');
|
|
87
|
+
}
|
|
118
88
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
let result: Record<string, any>;
|
|
122
|
-
|
|
123
|
-
if (platform === PLATFORM.ios) {
|
|
124
|
-
result = await execute(driver, 'mobile: getSimulatedLocation', {});
|
|
125
|
-
} else if (platform === PLATFORM.android) {
|
|
126
|
-
result = await execute(driver, 'mobile: getGeolocation', {});
|
|
127
|
-
} else {
|
|
128
|
-
throw new Error(
|
|
129
|
-
`Unsupported platform: ${platform}. Only Android and iOS are supported.`
|
|
130
|
-
);
|
|
131
|
-
}
|
|
89
|
+
const platform = getPlatformName(driver);
|
|
90
|
+
const { latitude, longitude, altitude } = args;
|
|
132
91
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
92
|
+
if (platform === PLATFORM.ios) {
|
|
93
|
+
await execute(driver, 'mobile: setSimulatedLocation', {
|
|
94
|
+
latitude,
|
|
95
|
+
longitude,
|
|
96
|
+
});
|
|
97
|
+
} else if (platform === PLATFORM.android) {
|
|
98
|
+
await execute(driver, 'mobile: setGeolocation', {
|
|
99
|
+
latitude,
|
|
100
|
+
longitude,
|
|
101
|
+
...(altitude !== undefined && { altitude }),
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Unsupported platform: ${platform}. Only Android and iOS are supported.`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const altitudeText = altitude !== undefined ? `, altitude=${altitude}` : '';
|
|
110
|
+
return textResult(
|
|
111
|
+
`Successfully set geolocation to latitude=${latitude}, longitude=${longitude}${altitudeText}.`
|
|
112
|
+
);
|
|
153
113
|
}
|
|
154
114
|
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
115
|
+
async function handleReset(args: GeolocationArgs): Promise<ContentResult> {
|
|
116
|
+
const driver = getDriver(args.sessionId);
|
|
117
|
+
if (!driver) {
|
|
118
|
+
throw new Error('No driver found');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const platform = getPlatformName(driver);
|
|
122
|
+
|
|
123
|
+
if (platform === PLATFORM.ios) {
|
|
124
|
+
await execute(driver, 'mobile: resetSimulatedLocation', {});
|
|
125
|
+
} else if (platform === PLATFORM.android) {
|
|
126
|
+
await execute(driver, 'mobile: resetGeolocation', {});
|
|
127
|
+
// Refresh GPS cache
|
|
128
|
+
await execute(driver, 'mobile: refreshGpsCache', {});
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`Unsupported platform: ${platform}. Only Android and iOS are supported.`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
162
134
|
|
|
135
|
+
return textResult('Successfully reset geolocation to default.');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export default function geolocation(server: FastMCP): void {
|
|
163
139
|
server.addTool({
|
|
164
|
-
name: '
|
|
140
|
+
name: 'appium_geolocation',
|
|
165
141
|
description:
|
|
166
|
-
'
|
|
167
|
-
parameters:
|
|
142
|
+
'Get, set, or reset the device geolocation (GPS coordinates). Works on both iOS (simulators and real devices) and Android (emulators and real devices with mock location enabled). Use action=get to read current coordinates, action=set with latitude/longitude (and optional altitude for Android) to simulate a location, or action=reset to restore the system default. Note: On Android emulators, reset is not supported — use action=set to manually restore coordinates instead. On Android real devices, the mocked location may persist until the GPS cache refreshes.',
|
|
143
|
+
parameters: schema,
|
|
168
144
|
annotations: {
|
|
169
145
|
readOnlyHint: false,
|
|
170
146
|
openWorldHint: false,
|
|
171
147
|
},
|
|
172
148
|
execute: async (
|
|
173
|
-
args:
|
|
149
|
+
args: GeolocationArgs,
|
|
174
150
|
_context: Record<string, unknown> | undefined
|
|
175
151
|
): Promise<ContentResult> => {
|
|
176
|
-
const driver = getDriver(args.sessionId);
|
|
177
|
-
if (!driver) {
|
|
178
|
-
throw new Error('No driver found');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
152
|
try {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
await execute(driver, 'mobile: refreshGpsCache', {});
|
|
190
|
-
} else {
|
|
191
|
-
throw new Error(
|
|
192
|
-
`Unsupported platform: ${platform}. Only Android and iOS are supported.`
|
|
193
|
-
);
|
|
153
|
+
switch (args.action) {
|
|
154
|
+
case 'get':
|
|
155
|
+
return await handleGet(args);
|
|
156
|
+
case 'set':
|
|
157
|
+
return await handleSet(args);
|
|
158
|
+
case 'reset':
|
|
159
|
+
return await handleReset(args);
|
|
194
160
|
}
|
|
195
|
-
|
|
196
|
-
return {
|
|
197
|
-
content: [
|
|
198
|
-
{
|
|
199
|
-
type: 'text',
|
|
200
|
-
text: 'Successfully reset geolocation to default.',
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
};
|
|
204
161
|
} catch (err: any) {
|
|
205
|
-
return
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
type: 'text',
|
|
209
|
-
text: `Failed to reset geolocation. Error: ${err.toString()}`,
|
|
210
|
-
},
|
|
211
|
-
],
|
|
212
|
-
};
|
|
162
|
+
return textResult(
|
|
163
|
+
`Failed to ${args.action} geolocation. Error: ${err.toString()}`
|
|
164
|
+
);
|
|
213
165
|
}
|
|
214
166
|
},
|
|
215
167
|
});
|