appium-mcp 1.81.4 → 1.81.5
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/dist/tools/session/create-session.d.ts +3 -2
- package/dist/tools/session/create-session.d.ts.map +1 -1
- package/dist/tools/session/create-session.js +34 -9
- package/dist/tools/session/create-session.js.map +1 -1
- package/dist/tools/session/select-device.d.ts.map +1 -1
- package/dist/tools/session/select-device.js +56 -29
- package/dist/tools/session/select-device.js.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/resources/submodules.zip +0 -0
- package/src/tools/session/create-session.ts +60 -16
- package/src/tools/session/select-device.ts +96 -50
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [1.81.5](https://github.com/appium/appium-mcp/compare/v1.81.4...v1.81.5) (2026-06-02)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* **session:** return errorResult from select_device and session create failures ([#377](https://github.com/appium/appium-mcp/issues/377)) ([7c5c6f9](https://github.com/appium/appium-mcp/commit/7c5c6f9c302482bda1b64f29888f83f4583c6811))
|
|
6
|
+
|
|
1
7
|
## [1.81.4](https://github.com/appium/appium-mcp/compare/v1.81.3...v1.81.4) (2026-05-30)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getPortFromUrl } from '../../utils/url.js';
|
|
2
|
+
import type { ContentResult } from 'fastmcp';
|
|
2
3
|
interface Capabilities {
|
|
3
4
|
platformName: string;
|
|
4
5
|
'appium:automationName': string;
|
|
@@ -50,12 +51,12 @@ export declare function validateRemoteServerUrl(remoteServerUrl: string, regexRu
|
|
|
50
51
|
* - text: Success message with session ID and device details
|
|
51
52
|
* - ui: Interactive session dashboard UI component
|
|
52
53
|
*
|
|
53
|
-
*
|
|
54
|
+
* Returns a tool-execution error result (isError: true) on failure.
|
|
54
55
|
*/
|
|
55
56
|
export declare function createSessionAction(args: {
|
|
56
57
|
platform: 'ios' | 'android' | 'general';
|
|
57
58
|
capabilities?: Record<string, any>;
|
|
58
59
|
remoteServerUrl?: string;
|
|
59
|
-
}): Promise<
|
|
60
|
+
}): Promise<ContentResult>;
|
|
60
61
|
export { getPortFromUrl };
|
|
61
62
|
//# sourceMappingURL=create-session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-session.d.ts","sourceRoot":"","sources":["../../../src/tools/session/create-session.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"create-session.d.ts","sourceRoot":"","sources":["../../../src/tools/session/create-session.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAiBpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C,UAAU,YAAY;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,MAAM,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AASD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,YAAY,GACzB,YAAY,CAQd;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,EAC3C,cAAc,EAAE,OAAO,GACtB,YAAY,CA8Bd;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,GACtC,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,EAC3C,cAAc,EAAE,OAAO,GACtB,OAAO,CAAC,YAAY,CAAC,CAuDvB;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,EACvB,SAAS,CAAC,EAAE,MAAM,GACjB,IAAI,CAKN;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC,aAAa,CAAC,CA2HzB;AA2FD,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -9,7 +9,7 @@ import { getSelectedDevice, getSelectedDeviceType, getSelectedDeviceInfo, clearS
|
|
|
9
9
|
import { IOSManager } from '../../devicemanager/ios-manager.js';
|
|
10
10
|
import log from '../../logger.js';
|
|
11
11
|
import { createUIResource, createSessionDashboardUI, addUIResourceToResponse, } from '../../ui/mcp-ui-utils.js';
|
|
12
|
-
import { textResult, toolErrorMessage } from '../tool-response.js';
|
|
12
|
+
import { errorResult, textResult, toolErrorMessage } from '../tool-response.js';
|
|
13
13
|
import WebDriver from 'webdriver';
|
|
14
14
|
/**
|
|
15
15
|
* Remove empty string values from capabilities object
|
|
@@ -64,7 +64,7 @@ export async function validateIOSDeviceSelection(deviceType) {
|
|
|
64
64
|
if (devices.length > 1) {
|
|
65
65
|
const selectedDevice = getSelectedDevice();
|
|
66
66
|
if (!selectedDevice) {
|
|
67
|
-
throw new Error(`Multiple iOS ${deviceType === 'simulator' ? 'simulators' : 'devices'} found (${devices.length}).
|
|
67
|
+
throw new Error(`Multiple iOS ${deviceType === 'simulator' ? 'simulators' : 'devices'} found (${devices.length}). Use select_device with platform=ios and iosDeviceType=${deviceType} to choose one, then call appium_session_management with action=create.`);
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -150,13 +150,13 @@ export function validateRemoteServerUrl(remoteServerUrl, regexRule) {
|
|
|
150
150
|
* - text: Success message with session ID and device details
|
|
151
151
|
* - ui: Interactive session dashboard UI component
|
|
152
152
|
*
|
|
153
|
-
*
|
|
153
|
+
* Returns a tool-execution error result (isError: true) on failure.
|
|
154
154
|
*/
|
|
155
155
|
export async function createSessionAction(args) {
|
|
156
|
+
let finalCapabilities;
|
|
156
157
|
try {
|
|
157
158
|
const { platform, capabilities: customCapabilities, remoteServerUrl, } = args;
|
|
158
159
|
const configCapabilities = await loadCapabilitiesConfig();
|
|
159
|
-
let finalCapabilities;
|
|
160
160
|
if (platform === 'android') {
|
|
161
161
|
finalCapabilities = buildAndroidCapabilities(configCapabilities.android, customCapabilities, !!remoteServerUrl);
|
|
162
162
|
}
|
|
@@ -172,7 +172,12 @@ export async function createSessionAction(args) {
|
|
|
172
172
|
log.info(`Creating new ${platform.toUpperCase()} session with capabilities:`, JSON.stringify(finalCapabilities, null, 2));
|
|
173
173
|
let sessionId;
|
|
174
174
|
if (remoteServerUrl) {
|
|
175
|
-
|
|
175
|
+
try {
|
|
176
|
+
validateRemoteServerUrl(remoteServerUrl, process.env.REMOTE_SERVER_URL_ALLOW_REGEX);
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
return errorResult(`Invalid remoteServerUrl "${remoteServerUrl}". ${toolErrorMessage(err)} Pass a valid http(s) URL, or omit remoteServerUrl to use the local embedded driver.`);
|
|
180
|
+
}
|
|
176
181
|
const remoteUrl = new URL(remoteServerUrl);
|
|
177
182
|
const protocol = remoteUrl.protocol.replace(':', '');
|
|
178
183
|
const port = getPortFromUrl(remoteUrl);
|
|
@@ -196,7 +201,7 @@ export async function createSessionAction(args) {
|
|
|
196
201
|
}
|
|
197
202
|
else {
|
|
198
203
|
if (platform === 'general') {
|
|
199
|
-
|
|
204
|
+
return errorResult('platform=general requires remoteServerUrl.');
|
|
200
205
|
}
|
|
201
206
|
const driver = createDriverForPlatform(platform);
|
|
202
207
|
log.info(`Sending session with ${driver.constructor.name}`);
|
|
@@ -221,10 +226,30 @@ export async function createSessionAction(args) {
|
|
|
221
226
|
}
|
|
222
227
|
catch (error) {
|
|
223
228
|
log.error('Error creating session:', error);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
229
|
+
return errorResult(buildCreateSessionFailureMessage(error, {
|
|
230
|
+
platform: args.platform,
|
|
231
|
+
remoteServerUrl: args.remoteServerUrl,
|
|
232
|
+
finalCapabilities,
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function buildCreateSessionFailureMessage(error, ctx) {
|
|
237
|
+
const detail = toolErrorMessage(error);
|
|
238
|
+
const base = `Failed to create session. ${detail}`;
|
|
239
|
+
if (ctx.remoteServerUrl) {
|
|
240
|
+
return `${base} remoteServerUrl="${ctx.remoteServerUrl}".`;
|
|
241
|
+
}
|
|
242
|
+
if (/select_device/i.test(detail)) {
|
|
243
|
+
return base;
|
|
244
|
+
}
|
|
245
|
+
const caps = ctx.finalCapabilities ?? {};
|
|
246
|
+
const hasDeviceTarget = Boolean(caps['appium:udid'] || caps['appium:deviceName']) ||
|
|
247
|
+
Boolean(getSelectedDevice());
|
|
248
|
+
if (!hasDeviceTarget &&
|
|
249
|
+
(ctx.platform === 'ios' || ctx.platform === 'android')) {
|
|
250
|
+
return `${base} For local sessions without appium:udid (or a prior select_device), use select_device with a matching platform or pass target device capabilities, then action=create.`;
|
|
227
251
|
}
|
|
252
|
+
return base;
|
|
228
253
|
}
|
|
229
254
|
/**
|
|
230
255
|
* Load capabilities configuration from file if specified in environment
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-session.js","sourceRoot":"","sources":["../../../src/tools/session/create-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,GAAG,MAAM,iBAAiB,CAAC;AAClC,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"create-session.js","sourceRoot":"","sources":["../../../src/tools/session/create-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-device.d.ts","sourceRoot":"","sources":["../../../src/tools/session/select-device.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"select-device.d.ts","sourceRoot":"","sources":["../../../src/tools/session/select-device.ts"],"names":[],"mappings":"AAoBA,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,qBAAqB,IAAI,WAAW,GAAG,MAAM,GAAG,IAAI,CAEnE;AAED,wBAAgB,qBAAqB,IAAI,GAAG,CAE3C;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAqEtD"}
|
|
@@ -6,7 +6,7 @@ import { IOSManager } from '../../devicemanager/ios-manager.js';
|
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
import log from '../../logger.js';
|
|
8
8
|
import { createUIResource, createDevicePickerUI, addUIResourceToResponse, } from '../../ui/mcp-ui-utils.js';
|
|
9
|
-
import { textResult } from '../tool-response.js';
|
|
9
|
+
import { errorResult, textResult, toolErrorMessage } from '../tool-response.js';
|
|
10
10
|
// Store selected device globally
|
|
11
11
|
let selectedDeviceUdid = null;
|
|
12
12
|
let selectedDeviceType = null;
|
|
@@ -69,18 +69,14 @@ export default function selectDevice(server) {
|
|
|
69
69
|
if (platform === 'android') {
|
|
70
70
|
return await handleAndroidDeviceSelection(deviceUdid);
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
if (platform === 'ios') {
|
|
73
73
|
return await handleIOSDeviceSelection(iosDeviceType, deviceUdid);
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
throw new Error(`Invalid platform: ${platform}. Please choose 'android' or 'ios'.`);
|
|
77
|
-
}
|
|
75
|
+
return errorResult(`Invalid platform '${String(platform)}'. Use platform='android' or platform='ios'.`);
|
|
78
76
|
}
|
|
79
77
|
catch (error) {
|
|
80
78
|
log.error('Error selecting device:', error);
|
|
81
|
-
|
|
82
|
-
cause: error,
|
|
83
|
-
});
|
|
79
|
+
return errorResult(`Failed to select device. ${toolErrorMessage(error)}`);
|
|
84
80
|
}
|
|
85
81
|
},
|
|
86
82
|
});
|
|
@@ -92,9 +88,12 @@ async function getAndroidDevices() {
|
|
|
92
88
|
const adb = await ADBManager.getInstance().initialize();
|
|
93
89
|
const devices = await adb.getConnectedDevices();
|
|
94
90
|
if (devices.length === 0) {
|
|
95
|
-
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
result: errorResult('No Android devices or emulators found. Connect a USB device with USB debugging enabled, or start an Android emulator, then call select_device again with platform=android.'),
|
|
94
|
+
};
|
|
96
95
|
}
|
|
97
|
-
return devices;
|
|
96
|
+
return { ok: true, devices };
|
|
98
97
|
}
|
|
99
98
|
/**
|
|
100
99
|
* Validate and select Android device by UDID
|
|
@@ -102,10 +101,11 @@ async function getAndroidDevices() {
|
|
|
102
101
|
function selectAndroidDevice(deviceUdid, devices) {
|
|
103
102
|
const selectedDevice = devices.find((d) => d.udid === deviceUdid);
|
|
104
103
|
if (!selectedDevice) {
|
|
105
|
-
|
|
104
|
+
return errorResult(`Device with UDID "${deviceUdid}" not found. Available devices: ${devices.map((d) => d.udid).join(', ')}. Call select_device again with deviceUdid set to one of these values.`);
|
|
106
105
|
}
|
|
107
106
|
selectedDeviceUdid = deviceUdid;
|
|
108
107
|
log.info(`Device selected: ${deviceUdid}`);
|
|
108
|
+
return undefined;
|
|
109
109
|
}
|
|
110
110
|
/**
|
|
111
111
|
* Format device selection response for Android
|
|
@@ -137,8 +137,9 @@ function formatAndroidListResponse(devices) {
|
|
|
137
137
|
*/
|
|
138
138
|
function validateIOSDeviceType(iosDeviceType) {
|
|
139
139
|
if (!iosDeviceType) {
|
|
140
|
-
|
|
140
|
+
return errorResult("iosDeviceType is required when platform=ios. Pass iosDeviceType='simulator' or iosDeviceType='real'.");
|
|
141
141
|
}
|
|
142
|
+
return undefined;
|
|
142
143
|
}
|
|
143
144
|
/**
|
|
144
145
|
* Get and validate iOS devices by type
|
|
@@ -147,24 +148,27 @@ async function getIOSDevices(iosDeviceType) {
|
|
|
147
148
|
const iosManager = IOSManager.getInstance();
|
|
148
149
|
const devices = await iosManager.getDevicesByType(iosDeviceType);
|
|
149
150
|
if (devices.length === 0) {
|
|
150
|
-
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
result: errorResult(`No iOS ${iosDeviceType === 'simulator' ? 'simulators' : 'devices'} found. Start a simulator in Xcode, or connect a real device with Developer Mode enabled, then call select_device again with platform=ios and iosDeviceType=${iosDeviceType}.`),
|
|
154
|
+
};
|
|
151
155
|
}
|
|
152
|
-
return devices;
|
|
156
|
+
return { ok: true, devices };
|
|
153
157
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Validate and select iOS device by UDID
|
|
156
|
-
*/
|
|
157
158
|
function selectIOSDevice(deviceUdid, devices, iosDeviceType) {
|
|
158
159
|
const selectedDevice = devices.find((d) => d.udid === deviceUdid);
|
|
159
160
|
if (!selectedDevice) {
|
|
160
161
|
const deviceList = devices.map((d) => `${d.name} (${d.udid})`).join(', ');
|
|
161
|
-
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
result: errorResult(`Device with UDID "${deviceUdid}" not found. Available devices: ${deviceList}. Call select_device again with deviceUdid set to one of these values.`),
|
|
165
|
+
};
|
|
162
166
|
}
|
|
163
167
|
selectedDeviceUdid = deviceUdid;
|
|
164
168
|
selectedDeviceType = iosDeviceType;
|
|
165
169
|
selectedDeviceInfo = selectedDevice;
|
|
166
170
|
log.info(`iOS ${iosDeviceType} selected: ${selectedDevice.name} (${deviceUdid})`);
|
|
167
|
-
return selectedDevice;
|
|
171
|
+
return { ok: true, device: selectedDevice };
|
|
168
172
|
}
|
|
169
173
|
/**
|
|
170
174
|
* Format device selection response for iOS
|
|
@@ -195,14 +199,24 @@ function formatIOSListResponse(devices, iosDeviceType) {
|
|
|
195
199
|
* Handle Android device selection
|
|
196
200
|
*/
|
|
197
201
|
async function handleAndroidDeviceSelection(deviceUdid) {
|
|
198
|
-
const
|
|
202
|
+
const listed = await getAndroidDevices();
|
|
203
|
+
if (!listed.ok) {
|
|
204
|
+
return listed.result;
|
|
205
|
+
}
|
|
206
|
+
const { devices } = listed;
|
|
199
207
|
if (deviceUdid) {
|
|
200
|
-
selectAndroidDevice(deviceUdid, devices);
|
|
208
|
+
const selectionError = selectAndroidDevice(deviceUdid, devices);
|
|
209
|
+
if (selectionError) {
|
|
210
|
+
return selectionError;
|
|
211
|
+
}
|
|
201
212
|
return formatAndroidSelectionResponse(deviceUdid);
|
|
202
213
|
}
|
|
203
214
|
// Auto-select when only one device is available
|
|
204
215
|
if (devices.length === 1) {
|
|
205
|
-
selectAndroidDevice(devices[0].udid, devices);
|
|
216
|
+
const selectionError = selectAndroidDevice(devices[0].udid, devices);
|
|
217
|
+
if (selectionError) {
|
|
218
|
+
return selectionError;
|
|
219
|
+
}
|
|
206
220
|
return formatAndroidSelectionResponse(devices[0].udid);
|
|
207
221
|
}
|
|
208
222
|
return formatAndroidListResponse(devices);
|
|
@@ -213,18 +227,31 @@ async function handleAndroidDeviceSelection(deviceUdid) {
|
|
|
213
227
|
async function handleIOSDeviceSelection(iosDeviceType, deviceUdid) {
|
|
214
228
|
const iosManager = IOSManager.getInstance();
|
|
215
229
|
if (!iosManager.isMac()) {
|
|
216
|
-
|
|
230
|
+
return errorResult('iOS device selection requires macOS with Xcode installed.');
|
|
231
|
+
}
|
|
232
|
+
const typeError = validateIOSDeviceType(iosDeviceType);
|
|
233
|
+
if (typeError) {
|
|
234
|
+
return typeError;
|
|
235
|
+
}
|
|
236
|
+
const listed = await getIOSDevices(iosDeviceType);
|
|
237
|
+
if (!listed.ok) {
|
|
238
|
+
return listed.result;
|
|
217
239
|
}
|
|
218
|
-
|
|
219
|
-
const devices = await getIOSDevices(iosDeviceType);
|
|
240
|
+
const { devices } = listed;
|
|
220
241
|
if (deviceUdid) {
|
|
221
|
-
const
|
|
222
|
-
|
|
242
|
+
const selected = selectIOSDevice(deviceUdid, devices, iosDeviceType);
|
|
243
|
+
if (!selected.ok) {
|
|
244
|
+
return selected.result;
|
|
245
|
+
}
|
|
246
|
+
return formatIOSSelectionResponse(selected.device.name, deviceUdid);
|
|
223
247
|
}
|
|
224
248
|
// Auto-select when only one device is available
|
|
225
249
|
if (devices.length === 1) {
|
|
226
|
-
const
|
|
227
|
-
|
|
250
|
+
const selected = selectIOSDevice(devices[0].udid, devices, iosDeviceType);
|
|
251
|
+
if (!selected.ok) {
|
|
252
|
+
return selected.result;
|
|
253
|
+
}
|
|
254
|
+
return formatIOSSelectionResponse(selected.device.name, devices[0].udid);
|
|
228
255
|
}
|
|
229
256
|
return formatIOSListResponse(devices, iosDeviceType);
|
|
230
257
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-device.js","sourceRoot":"","sources":["../../../src/tools/session/select-device.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,GAAG,MAAM,iBAAiB,CAAC;AAClC,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"select-device.js","sourceRoot":"","sources":["../../../src/tools/session/select-device.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.81.
|
|
6
|
+
"version": "1.81.5",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "appium-mcp",
|
|
11
|
-
"version": "1.81.
|
|
11
|
+
"version": "1.81.5",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|
|
Binary file
|
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
createSessionDashboardUI,
|
|
19
19
|
addUIResourceToResponse,
|
|
20
20
|
} from '../../ui/mcp-ui-utils.js';
|
|
21
|
-
import {
|
|
21
|
+
import type { ContentResult } from 'fastmcp';
|
|
22
|
+
import { errorResult, textResult, toolErrorMessage } from '../tool-response.js';
|
|
22
23
|
import WebDriver from 'webdriver';
|
|
23
24
|
|
|
24
25
|
// Define capabilities type
|
|
@@ -107,7 +108,7 @@ export async function validateIOSDeviceSelection(
|
|
|
107
108
|
const selectedDevice = getSelectedDevice();
|
|
108
109
|
if (!selectedDevice) {
|
|
109
110
|
throw new Error(
|
|
110
|
-
`Multiple iOS ${deviceType === 'simulator' ? 'simulators' : 'devices'} found (${devices.length}).
|
|
111
|
+
`Multiple iOS ${deviceType === 'simulator' ? 'simulators' : 'devices'} found (${devices.length}). Use select_device with platform=ios and iosDeviceType=${deviceType} to choose one, then call appium_session_management with action=create.`
|
|
111
112
|
);
|
|
112
113
|
}
|
|
113
114
|
}
|
|
@@ -215,13 +216,15 @@ export function validateRemoteServerUrl(
|
|
|
215
216
|
* - text: Success message with session ID and device details
|
|
216
217
|
* - ui: Interactive session dashboard UI component
|
|
217
218
|
*
|
|
218
|
-
*
|
|
219
|
+
* Returns a tool-execution error result (isError: true) on failure.
|
|
219
220
|
*/
|
|
220
221
|
export async function createSessionAction(args: {
|
|
221
222
|
platform: 'ios' | 'android' | 'general';
|
|
222
223
|
capabilities?: Record<string, any>;
|
|
223
224
|
remoteServerUrl?: string;
|
|
224
|
-
}): Promise<
|
|
225
|
+
}): Promise<ContentResult> {
|
|
226
|
+
let finalCapabilities: Capabilities | undefined;
|
|
227
|
+
|
|
225
228
|
try {
|
|
226
229
|
const {
|
|
227
230
|
platform,
|
|
@@ -230,7 +233,6 @@ export async function createSessionAction(args: {
|
|
|
230
233
|
} = args;
|
|
231
234
|
|
|
232
235
|
const configCapabilities = await loadCapabilitiesConfig();
|
|
233
|
-
let finalCapabilities: Capabilities;
|
|
234
236
|
if (platform === 'android') {
|
|
235
237
|
finalCapabilities = buildAndroidCapabilities(
|
|
236
238
|
configCapabilities.android,
|
|
@@ -256,10 +258,16 @@ export async function createSessionAction(args: {
|
|
|
256
258
|
);
|
|
257
259
|
let sessionId;
|
|
258
260
|
if (remoteServerUrl) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
try {
|
|
262
|
+
validateRemoteServerUrl(
|
|
263
|
+
remoteServerUrl,
|
|
264
|
+
process.env.REMOTE_SERVER_URL_ALLOW_REGEX
|
|
265
|
+
);
|
|
266
|
+
} catch (err: unknown) {
|
|
267
|
+
return errorResult(
|
|
268
|
+
`Invalid remoteServerUrl "${remoteServerUrl}". ${toolErrorMessage(err)} Pass a valid http(s) URL, or omit remoteServerUrl to use the local embedded driver.`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
263
271
|
|
|
264
272
|
const remoteUrl = new URL(remoteServerUrl);
|
|
265
273
|
const protocol = remoteUrl.protocol.replace(':', '');
|
|
@@ -291,9 +299,7 @@ export async function createSessionAction(args: {
|
|
|
291
299
|
);
|
|
292
300
|
} else {
|
|
293
301
|
if (platform === 'general') {
|
|
294
|
-
|
|
295
|
-
'platform="general" requires a remoteServerUrl — local drivers are not supported for general sessions.'
|
|
296
|
-
);
|
|
302
|
+
return errorResult('platform=general requires remoteServerUrl.');
|
|
297
303
|
}
|
|
298
304
|
const driver = createDriverForPlatform(platform);
|
|
299
305
|
log.info(`Sending session with ${driver.constructor.name}`);
|
|
@@ -329,14 +335,52 @@ export async function createSessionAction(args: {
|
|
|
329
335
|
);
|
|
330
336
|
|
|
331
337
|
return addUIResourceToResponse(textResponse, uiResource);
|
|
332
|
-
} catch (error:
|
|
338
|
+
} catch (error: unknown) {
|
|
333
339
|
log.error('Error creating session:', error);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
340
|
+
return errorResult(
|
|
341
|
+
buildCreateSessionFailureMessage(error, {
|
|
342
|
+
platform: args.platform,
|
|
343
|
+
remoteServerUrl: args.remoteServerUrl,
|
|
344
|
+
finalCapabilities,
|
|
345
|
+
})
|
|
346
|
+
);
|
|
337
347
|
}
|
|
338
348
|
}
|
|
339
349
|
|
|
350
|
+
function buildCreateSessionFailureMessage(
|
|
351
|
+
error: unknown,
|
|
352
|
+
ctx: {
|
|
353
|
+
platform: 'ios' | 'android' | 'general';
|
|
354
|
+
remoteServerUrl?: string;
|
|
355
|
+
finalCapabilities?: Capabilities;
|
|
356
|
+
}
|
|
357
|
+
): string {
|
|
358
|
+
const detail = toolErrorMessage(error);
|
|
359
|
+
const base = `Failed to create session. ${detail}`;
|
|
360
|
+
|
|
361
|
+
if (ctx.remoteServerUrl) {
|
|
362
|
+
return `${base} remoteServerUrl="${ctx.remoteServerUrl}".`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (/select_device/i.test(detail)) {
|
|
366
|
+
return base;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const caps: Record<string, any> = ctx.finalCapabilities ?? {};
|
|
370
|
+
const hasDeviceTarget =
|
|
371
|
+
Boolean(caps['appium:udid'] || caps['appium:deviceName']) ||
|
|
372
|
+
Boolean(getSelectedDevice());
|
|
373
|
+
|
|
374
|
+
if (
|
|
375
|
+
!hasDeviceTarget &&
|
|
376
|
+
(ctx.platform === 'ios' || ctx.platform === 'android')
|
|
377
|
+
) {
|
|
378
|
+
return `${base} For local sessions without appium:udid (or a prior select_device), use select_device with a matching platform or pass target device capabilities, then action=create.`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return base;
|
|
382
|
+
}
|
|
383
|
+
|
|
340
384
|
/**
|
|
341
385
|
* Load capabilities configuration from file if specified in environment
|
|
342
386
|
*/
|
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
createDevicePickerUI,
|
|
11
11
|
addUIResourceToResponse,
|
|
12
12
|
} from '../../ui/mcp-ui-utils.js';
|
|
13
|
-
import {
|
|
13
|
+
import type { ContentResult } from 'fastmcp';
|
|
14
|
+
import { errorResult, textResult, toolErrorMessage } from '../tool-response.js';
|
|
14
15
|
|
|
15
16
|
// Store selected device globally
|
|
16
17
|
let selectedDeviceUdid: string | null = null;
|
|
@@ -83,62 +84,74 @@ export default function selectDevice(server: any): void {
|
|
|
83
84
|
readOnlyHint: false,
|
|
84
85
|
openWorldHint: false,
|
|
85
86
|
},
|
|
86
|
-
execute: async (args: any, _context: any): Promise<
|
|
87
|
+
execute: async (args: any, _context: any): Promise<ContentResult> => {
|
|
87
88
|
try {
|
|
88
89
|
const { platform, iosDeviceType, deviceUdid } = args;
|
|
89
90
|
|
|
90
91
|
if (platform === 'android') {
|
|
91
92
|
return await handleAndroidDeviceSelection(deviceUdid);
|
|
92
|
-
}
|
|
93
|
+
}
|
|
94
|
+
if (platform === 'ios') {
|
|
93
95
|
return await handleIOSDeviceSelection(iosDeviceType, deviceUdid);
|
|
94
|
-
} else {
|
|
95
|
-
throw new Error(
|
|
96
|
-
`Invalid platform: ${platform}. Please choose 'android' or 'ios'.`
|
|
97
|
-
);
|
|
98
96
|
}
|
|
99
|
-
|
|
97
|
+
return errorResult(
|
|
98
|
+
`Invalid platform '${String(platform)}'. Use platform='android' or platform='ios'.`
|
|
99
|
+
);
|
|
100
|
+
} catch (error: unknown) {
|
|
100
101
|
log.error('Error selecting device:', error);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
return errorResult(
|
|
103
|
+
`Failed to select device. ${toolErrorMessage(error)}`
|
|
104
|
+
);
|
|
104
105
|
}
|
|
105
106
|
},
|
|
106
107
|
});
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
type DevicesOk = { ok: true; devices: any[] };
|
|
111
|
+
type DevicesFail = { ok: false; result: ContentResult };
|
|
112
|
+
|
|
109
113
|
/**
|
|
110
114
|
* Get and validate Android devices
|
|
111
115
|
*/
|
|
112
|
-
async function getAndroidDevices(): Promise<
|
|
116
|
+
async function getAndroidDevices(): Promise<DevicesOk | DevicesFail> {
|
|
113
117
|
const adb = await ADBManager.getInstance().initialize();
|
|
114
118
|
const devices = await adb.getConnectedDevices();
|
|
115
119
|
|
|
116
120
|
if (devices.length === 0) {
|
|
117
|
-
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
result: errorResult(
|
|
124
|
+
'No Android devices or emulators found. Connect a USB device with USB debugging enabled, or start an Android emulator, then call select_device again with platform=android.'
|
|
125
|
+
),
|
|
126
|
+
};
|
|
118
127
|
}
|
|
119
128
|
|
|
120
|
-
return devices;
|
|
129
|
+
return { ok: true, devices };
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
/**
|
|
124
133
|
* Validate and select Android device by UDID
|
|
125
134
|
*/
|
|
126
|
-
function selectAndroidDevice(
|
|
135
|
+
function selectAndroidDevice(
|
|
136
|
+
deviceUdid: string,
|
|
137
|
+
devices: any[]
|
|
138
|
+
): ContentResult | undefined {
|
|
127
139
|
const selectedDevice = devices.find((d) => d.udid === deviceUdid);
|
|
128
140
|
if (!selectedDevice) {
|
|
129
|
-
|
|
130
|
-
`Device with UDID "${deviceUdid}" not found. Available devices: ${devices.map((d) => d.udid).join(', ')}
|
|
141
|
+
return errorResult(
|
|
142
|
+
`Device with UDID "${deviceUdid}" not found. Available devices: ${devices.map((d) => d.udid).join(', ')}. Call select_device again with deviceUdid set to one of these values.`
|
|
131
143
|
);
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
selectedDeviceUdid = deviceUdid;
|
|
135
147
|
log.info(`Device selected: ${deviceUdid}`);
|
|
148
|
+
return undefined;
|
|
136
149
|
}
|
|
137
150
|
|
|
138
151
|
/**
|
|
139
152
|
* Format device selection response for Android
|
|
140
153
|
*/
|
|
141
|
-
function formatAndroidSelectionResponse(deviceUdid: string):
|
|
154
|
+
function formatAndroidSelectionResponse(deviceUdid: string): ContentResult {
|
|
142
155
|
return textResult(
|
|
143
156
|
JSON.stringify(
|
|
144
157
|
{
|
|
@@ -159,7 +172,7 @@ function formatAndroidSelectionResponse(deviceUdid: string): any {
|
|
|
159
172
|
/**
|
|
160
173
|
* Format device list response for Android
|
|
161
174
|
*/
|
|
162
|
-
function formatAndroidListResponse(devices: any[]):
|
|
175
|
+
function formatAndroidListResponse(devices: any[]): ContentResult {
|
|
163
176
|
const deviceList = devices
|
|
164
177
|
.map((device, index) => ` ${index + 1}. ${device.udid}`)
|
|
165
178
|
.join('\n');
|
|
@@ -182,12 +195,13 @@ function formatAndroidListResponse(devices: any[]): any {
|
|
|
182
195
|
*/
|
|
183
196
|
function validateIOSDeviceType(
|
|
184
197
|
iosDeviceType: 'simulator' | 'real' | undefined
|
|
185
|
-
):
|
|
198
|
+
): ContentResult | undefined {
|
|
186
199
|
if (!iosDeviceType) {
|
|
187
|
-
|
|
188
|
-
"
|
|
200
|
+
return errorResult(
|
|
201
|
+
"iosDeviceType is required when platform=ios. Pass iosDeviceType='simulator' or iosDeviceType='real'."
|
|
189
202
|
);
|
|
190
203
|
}
|
|
204
|
+
return undefined;
|
|
191
205
|
}
|
|
192
206
|
|
|
193
207
|
/**
|
|
@@ -195,33 +209,42 @@ function validateIOSDeviceType(
|
|
|
195
209
|
*/
|
|
196
210
|
async function getIOSDevices(
|
|
197
211
|
iosDeviceType: 'simulator' | 'real'
|
|
198
|
-
): Promise<
|
|
212
|
+
): Promise<DevicesOk | DevicesFail> {
|
|
199
213
|
const iosManager = IOSManager.getInstance();
|
|
200
214
|
const devices = await iosManager.getDevicesByType(iosDeviceType);
|
|
201
215
|
|
|
202
216
|
if (devices.length === 0) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
217
|
+
return {
|
|
218
|
+
ok: false,
|
|
219
|
+
result: errorResult(
|
|
220
|
+
`No iOS ${iosDeviceType === 'simulator' ? 'simulators' : 'devices'} found. Start a simulator in Xcode, or connect a real device with Developer Mode enabled, then call select_device again with platform=ios and iosDeviceType=${iosDeviceType}.`
|
|
221
|
+
),
|
|
222
|
+
};
|
|
206
223
|
}
|
|
207
224
|
|
|
208
|
-
return devices;
|
|
225
|
+
return { ok: true, devices };
|
|
209
226
|
}
|
|
210
227
|
|
|
211
228
|
/**
|
|
212
229
|
* Validate and select iOS device by UDID
|
|
213
230
|
*/
|
|
231
|
+
type SelectIOSOk = { ok: true; device: any };
|
|
232
|
+
type SelectIOSFail = { ok: false; result: ContentResult };
|
|
233
|
+
|
|
214
234
|
function selectIOSDevice(
|
|
215
235
|
deviceUdid: string,
|
|
216
236
|
devices: any[],
|
|
217
237
|
iosDeviceType: 'simulator' | 'real'
|
|
218
|
-
):
|
|
238
|
+
): SelectIOSOk | SelectIOSFail {
|
|
219
239
|
const selectedDevice = devices.find((d) => d.udid === deviceUdid);
|
|
220
240
|
if (!selectedDevice) {
|
|
221
241
|
const deviceList = devices.map((d) => `${d.name} (${d.udid})`).join(', ');
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
242
|
+
return {
|
|
243
|
+
ok: false,
|
|
244
|
+
result: errorResult(
|
|
245
|
+
`Device with UDID "${deviceUdid}" not found. Available devices: ${deviceList}. Call select_device again with deviceUdid set to one of these values.`
|
|
246
|
+
),
|
|
247
|
+
};
|
|
225
248
|
}
|
|
226
249
|
|
|
227
250
|
selectedDeviceUdid = deviceUdid;
|
|
@@ -231,7 +254,7 @@ function selectIOSDevice(
|
|
|
231
254
|
`iOS ${iosDeviceType} selected: ${selectedDevice.name} (${deviceUdid})`
|
|
232
255
|
);
|
|
233
256
|
|
|
234
|
-
return selectedDevice;
|
|
257
|
+
return { ok: true, device: selectedDevice };
|
|
235
258
|
}
|
|
236
259
|
|
|
237
260
|
/**
|
|
@@ -240,7 +263,7 @@ function selectIOSDevice(
|
|
|
240
263
|
function formatIOSSelectionResponse(
|
|
241
264
|
deviceName: string,
|
|
242
265
|
deviceUdid: string
|
|
243
|
-
):
|
|
266
|
+
): ContentResult {
|
|
244
267
|
return textResult(
|
|
245
268
|
JSON.stringify(
|
|
246
269
|
{
|
|
@@ -264,7 +287,7 @@ function formatIOSSelectionResponse(
|
|
|
264
287
|
function formatIOSListResponse(
|
|
265
288
|
devices: any[],
|
|
266
289
|
iosDeviceType: 'simulator' | 'real'
|
|
267
|
-
):
|
|
290
|
+
): ContentResult {
|
|
268
291
|
const deviceList = devices
|
|
269
292
|
.map(
|
|
270
293
|
(device, index) =>
|
|
@@ -288,17 +311,29 @@ function formatIOSListResponse(
|
|
|
288
311
|
/**
|
|
289
312
|
* Handle Android device selection
|
|
290
313
|
*/
|
|
291
|
-
async function handleAndroidDeviceSelection(
|
|
292
|
-
|
|
314
|
+
async function handleAndroidDeviceSelection(
|
|
315
|
+
deviceUdid?: string
|
|
316
|
+
): Promise<ContentResult> {
|
|
317
|
+
const listed = await getAndroidDevices();
|
|
318
|
+
if (!listed.ok) {
|
|
319
|
+
return listed.result;
|
|
320
|
+
}
|
|
321
|
+
const { devices } = listed;
|
|
293
322
|
|
|
294
323
|
if (deviceUdid) {
|
|
295
|
-
selectAndroidDevice(deviceUdid, devices);
|
|
324
|
+
const selectionError = selectAndroidDevice(deviceUdid, devices);
|
|
325
|
+
if (selectionError) {
|
|
326
|
+
return selectionError;
|
|
327
|
+
}
|
|
296
328
|
return formatAndroidSelectionResponse(deviceUdid);
|
|
297
329
|
}
|
|
298
330
|
|
|
299
331
|
// Auto-select when only one device is available
|
|
300
332
|
if (devices.length === 1) {
|
|
301
|
-
selectAndroidDevice(devices[0].udid, devices);
|
|
333
|
+
const selectionError = selectAndroidDevice(devices[0].udid, devices);
|
|
334
|
+
if (selectionError) {
|
|
335
|
+
return selectionError;
|
|
336
|
+
}
|
|
302
337
|
return formatAndroidSelectionResponse(devices[0].udid);
|
|
303
338
|
}
|
|
304
339
|
|
|
@@ -311,29 +346,40 @@ async function handleAndroidDeviceSelection(deviceUdid?: string): Promise<any> {
|
|
|
311
346
|
async function handleIOSDeviceSelection(
|
|
312
347
|
iosDeviceType: 'simulator' | 'real' | undefined,
|
|
313
348
|
deviceUdid?: string
|
|
314
|
-
): Promise<
|
|
349
|
+
): Promise<ContentResult> {
|
|
315
350
|
const iosManager = IOSManager.getInstance();
|
|
316
351
|
if (!iosManager.isMac()) {
|
|
317
|
-
|
|
352
|
+
return errorResult(
|
|
353
|
+
'iOS device selection requires macOS with Xcode installed.'
|
|
354
|
+
);
|
|
318
355
|
}
|
|
319
356
|
|
|
320
|
-
validateIOSDeviceType(iosDeviceType);
|
|
357
|
+
const typeError = validateIOSDeviceType(iosDeviceType);
|
|
358
|
+
if (typeError) {
|
|
359
|
+
return typeError;
|
|
360
|
+
}
|
|
321
361
|
|
|
322
|
-
const
|
|
362
|
+
const listed = await getIOSDevices(iosDeviceType!);
|
|
363
|
+
if (!listed.ok) {
|
|
364
|
+
return listed.result;
|
|
365
|
+
}
|
|
366
|
+
const { devices } = listed;
|
|
323
367
|
|
|
324
368
|
if (deviceUdid) {
|
|
325
|
-
const
|
|
326
|
-
|
|
369
|
+
const selected = selectIOSDevice(deviceUdid, devices, iosDeviceType!);
|
|
370
|
+
if (!selected.ok) {
|
|
371
|
+
return selected.result;
|
|
372
|
+
}
|
|
373
|
+
return formatIOSSelectionResponse(selected.device.name, deviceUdid);
|
|
327
374
|
}
|
|
328
375
|
|
|
329
376
|
// Auto-select when only one device is available
|
|
330
377
|
if (devices.length === 1) {
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
);
|
|
336
|
-
return formatIOSSelectionResponse(selectedDevice.name, devices[0].udid);
|
|
378
|
+
const selected = selectIOSDevice(devices[0].udid, devices, iosDeviceType!);
|
|
379
|
+
if (!selected.ok) {
|
|
380
|
+
return selected.result;
|
|
381
|
+
}
|
|
382
|
+
return formatIOSSelectionResponse(selected.device.name, devices[0].udid);
|
|
337
383
|
}
|
|
338
384
|
|
|
339
385
|
return formatIOSListResponse(devices, iosDeviceType!);
|