appium-mcp 1.56.3 → 1.57.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 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -7
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/session/device-control.d.ts +3 -0
- package/dist/tools/session/device-control.d.ts.map +1 -0
- package/dist/tools/session/device-control.js +109 -0
- package/dist/tools/session/device-control.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/resources/submodules.zip +0 -0
- package/src/tools/README.md +1 -4
- package/src/tools/index.ts +2 -7
- package/src/tools/session/device-control.ts +141 -0
- package/dist/tools/session/lock.d.ts +0 -4
- package/dist/tools/session/lock.d.ts.map +0 -1
- package/dist/tools/session/lock.js +0 -97
- package/dist/tools/session/lock.js.map +0 -1
- package/dist/tools/session/open-notifications.d.ts +0 -3
- package/dist/tools/session/open-notifications.d.ts.map +0 -1
- package/dist/tools/session/open-notifications.js +0 -67
- package/dist/tools/session/open-notifications.js.map +0 -1
- package/dist/tools/session/shake.d.ts +0 -3
- package/dist/tools/session/shake.d.ts.map +0 -1
- package/dist/tools/session/shake.js +0 -54
- package/dist/tools/session/shake.js.map +0 -1
- package/src/tools/session/lock.ts +0 -112
- package/src/tools/session/open-notifications.ts +0 -80
- package/src/tools/session/shake.ts +0 -61
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [1.57.0](https://github.com/appium/appium-mcp/compare/v1.56.3...v1.57.0) (2026-04-16)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **session:** consolidate device actions into appium_mobile_device_control ([#259](https://github.com/appium/appium-mcp/issues/259)) ([b500d0a](https://github.com/appium/appium-mcp/commit/b500d0aa5a198ffcea8c97bafcd165d09c1905d9))
|
|
6
|
+
|
|
1
7
|
## [1.56.3](https://github.com/appium/appium-mcp/compare/v1.56.2...v1.56.3) (2026-04-16)
|
|
2
8
|
|
|
3
9
|
### Miscellaneous Chores
|
package/README.md
CHANGED
|
@@ -302,7 +302,7 @@ MCP Appium provides a comprehensive set of tools organized into the following ca
|
|
|
302
302
|
| ---------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
303
303
|
| `create_session` | Create a new mobile automation session for Android, iOS, or `general` capabilities (see 'general' mode above). If a remote Appium server is referenced, `create_session` forwards the final capabilities to that server via the WebDriver `newSession` API - include device selection (e.g., `appium:udid`) in `capabilities` when targeting a remote server. |
|
|
304
304
|
| `delete_session` | Delete the current mobile session and clean up resources |
|
|
305
|
-
| `
|
|
305
|
+
| `appium_mobile_device_control` | Control device behavior: lock/unlock the screen, shake the device, or open the notifications panel (`action`: `lock` \| `unlock` \| `shake` \| `open_notifications`). `shake` is iOS only; `open_notifications` is Android only; `seconds` is optional for timed lock. |
|
|
306
306
|
| `appium_get_settings` | Read current Appium driver session settings (idle timeouts, animation-related flags, selector waits, etc.). Helps diagnose and tune flaky automation. |
|
|
307
307
|
| `appium_update_settings` | Merge key-value updates into driver session settings (driver-specific keys; use `appium_get_settings` to inspect). |
|
|
308
308
|
|
|
@@ -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;AA2ClC,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CA+H3D"}
|
package/dist/tools/index.js
CHANGED
|
@@ -7,9 +7,7 @@ import listSessions from './session/list-sessions.js';
|
|
|
7
7
|
import selectSession from './session/select-session.js';
|
|
8
8
|
import generateLocators from './test-generation/locators.js';
|
|
9
9
|
import selectDevice from './session/select-device.js';
|
|
10
|
-
import
|
|
11
|
-
import shakeDevice from './session/shake.js';
|
|
12
|
-
import { lockDevice, unlockDevice } from './session/lock.js';
|
|
10
|
+
import mobileDeviceControl from './session/device-control.js';
|
|
13
11
|
import geolocation from './session/geolocation.js';
|
|
14
12
|
import deviceInfo from './session/device-info.js';
|
|
15
13
|
import fileTransfer from './session/file-transfer.js';
|
|
@@ -109,10 +107,7 @@ export default function registerTools(server) {
|
|
|
109
107
|
listSessions(server);
|
|
110
108
|
selectSession(server);
|
|
111
109
|
deleteSession(server);
|
|
112
|
-
|
|
113
|
-
shakeDevice(server);
|
|
114
|
-
lockDevice(server);
|
|
115
|
-
unlockDevice(server);
|
|
110
|
+
mobileDeviceControl(server);
|
|
116
111
|
geolocation(server);
|
|
117
112
|
deviceInfo(server);
|
|
118
113
|
fileTransfer(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,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-control.d.ts","sourceRoot":"","sources":["../../../src/tools/session/device-control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AA4FtD,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAgDjE"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getDriver, getPlatformName, isAndroidUiautomator2DriverSession, isRemoteDriverSession, isXCUITestDriverSession, PLATFORM, } from '../../session-store.js';
|
|
3
|
+
import { execute } from '../../command.js';
|
|
4
|
+
const deviceControlSchema = z.object({
|
|
5
|
+
action: z
|
|
6
|
+
.enum(['lock', 'unlock', 'shake', 'open_notifications'])
|
|
7
|
+
.describe('Action to perform. ' +
|
|
8
|
+
'lock: lock the device (optional seconds for timed lock). ' +
|
|
9
|
+
'unlock: unlock the device. ' +
|
|
10
|
+
'shake: perform shake gesture (iOS only). ' +
|
|
11
|
+
'open_notifications: open notifications panel (Android only).'),
|
|
12
|
+
sessionId: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
16
|
+
seconds: z
|
|
17
|
+
.number()
|
|
18
|
+
.int()
|
|
19
|
+
.min(1)
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Only for action=lock: lock duration in seconds before auto-unlock. Omit to remain locked until unlock.'),
|
|
22
|
+
});
|
|
23
|
+
async function handleLock(driver, seconds) {
|
|
24
|
+
const params = {};
|
|
25
|
+
if (seconds !== undefined) {
|
|
26
|
+
params.seconds = seconds;
|
|
27
|
+
}
|
|
28
|
+
await execute(driver, 'mobile: lock', params);
|
|
29
|
+
const msg = seconds !== undefined
|
|
30
|
+
? `Device locked for ${seconds} second(s).`
|
|
31
|
+
: 'Device locked.';
|
|
32
|
+
return { content: [{ type: 'text', text: msg }] };
|
|
33
|
+
}
|
|
34
|
+
async function handleUnlock(driver) {
|
|
35
|
+
await execute(driver, 'mobile: unlock', {});
|
|
36
|
+
return { content: [{ type: 'text', text: 'Device unlocked.' }] };
|
|
37
|
+
}
|
|
38
|
+
async function handleShake(driver) {
|
|
39
|
+
if (!isXCUITestDriverSession(driver)) {
|
|
40
|
+
throw new Error('Shake is supported only with XCUITest driver sessions.');
|
|
41
|
+
}
|
|
42
|
+
await driver.mobileShake();
|
|
43
|
+
return { content: [{ type: 'text', text: 'Shake action performed.' }] };
|
|
44
|
+
}
|
|
45
|
+
async function handleOpenNotifications(driver) {
|
|
46
|
+
const platform = getPlatformName(driver);
|
|
47
|
+
if (platform !== PLATFORM.android) {
|
|
48
|
+
throw new Error(`Unsupported platform: ${platform}. Open notifications is supported on Android only.`);
|
|
49
|
+
}
|
|
50
|
+
if (isAndroidUiautomator2DriverSession(driver)) {
|
|
51
|
+
await driver.openNotifications();
|
|
52
|
+
}
|
|
53
|
+
else if (isRemoteDriverSession(driver)) {
|
|
54
|
+
await execute(driver, 'mobile: openNotifications', {});
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw new Error('Unsupported Android driver for open notifications');
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
content: [
|
|
61
|
+
{ type: 'text', text: 'Successfully opened notifications panel.' },
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export default function mobileDeviceControl(server) {
|
|
66
|
+
server.addTool({
|
|
67
|
+
name: 'appium_mobile_device_control',
|
|
68
|
+
description: 'Control device behavior: lock/unlock the screen, shake the device, or open the notifications panel. Use the action parameter to choose what to do.',
|
|
69
|
+
parameters: deviceControlSchema,
|
|
70
|
+
annotations: {
|
|
71
|
+
readOnlyHint: false,
|
|
72
|
+
openWorldHint: false,
|
|
73
|
+
},
|
|
74
|
+
execute: async (args, _context) => {
|
|
75
|
+
try {
|
|
76
|
+
const driver = getDriver(args.sessionId);
|
|
77
|
+
if (!driver) {
|
|
78
|
+
throw new Error('No driver found');
|
|
79
|
+
}
|
|
80
|
+
if (args.action !== 'lock' && args.seconds !== undefined) {
|
|
81
|
+
throw new Error('seconds is only valid when action is lock');
|
|
82
|
+
}
|
|
83
|
+
switch (args.action) {
|
|
84
|
+
case 'lock':
|
|
85
|
+
return await handleLock(driver, args.seconds);
|
|
86
|
+
case 'unlock':
|
|
87
|
+
return await handleUnlock(driver);
|
|
88
|
+
case 'shake':
|
|
89
|
+
return await handleShake(driver);
|
|
90
|
+
case 'open_notifications':
|
|
91
|
+
return await handleOpenNotifications(driver);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: `Failed device control action ${args.action}. err: ${message}`,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
isError: true,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=device-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-control.js","sourceRoot":"","sources":["../../../src/tools/session/device-control.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.57.0",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "appium-mcp",
|
|
11
|
-
"version": "1.
|
|
11
|
+
"version": "1.57.0",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|
|
Binary file
|
package/src/tools/README.md
CHANGED
|
@@ -8,10 +8,7 @@ This directory contains all MCP tools available in MCP Appium.
|
|
|
8
8
|
|
|
9
9
|
- `create-session.ts` - Create mobile automation sessions
|
|
10
10
|
- `delete-session.ts` - Clean up sessions
|
|
11
|
-
- `
|
|
12
|
-
- `shake.ts` - iOS Simulator shake via `mobile: shake` (`appium_mobile_shake`; not Android, not physical iOS)
|
|
13
|
-
- `lock.ts` - Lock device (`appium_mobile_lock`); optionally lock for N seconds (Android & iOS)
|
|
14
|
-
- `lock.ts` - Unlock device (`appium_mobile_unlock`)
|
|
11
|
+
- `device-control.ts` - Device controls in one tool (`appium_mobile_device_control`; `action=lock|unlock|shake|open_notifications`)
|
|
15
12
|
- `select-device.ts` - Discover devices and select one (auto-selects if only one found)
|
|
16
13
|
- `file-transfer.ts` - Push/pull files on device (`appium_mobile_file` with `action=push|pull`)
|
|
17
14
|
- `driver-settings.ts` - Read/update Appium driver session settings (`appium_get_settings`, `appium_update_settings`)
|
package/src/tools/index.ts
CHANGED
|
@@ -22,9 +22,7 @@ import listSessions from './session/list-sessions.js';
|
|
|
22
22
|
import selectSession from './session/select-session.js';
|
|
23
23
|
import generateLocators from './test-generation/locators.js';
|
|
24
24
|
import selectDevice from './session/select-device.js';
|
|
25
|
-
import
|
|
26
|
-
import shakeDevice from './session/shake.js';
|
|
27
|
-
import { lockDevice, unlockDevice } from './session/lock.js';
|
|
25
|
+
import mobileDeviceControl from './session/device-control.js';
|
|
28
26
|
import geolocation from './session/geolocation.js';
|
|
29
27
|
import deviceInfo from './session/device-info.js';
|
|
30
28
|
import fileTransfer from './session/file-transfer.js';
|
|
@@ -130,10 +128,7 @@ export default function registerTools(server: FastMCP): void {
|
|
|
130
128
|
listSessions(server);
|
|
131
129
|
selectSession(server);
|
|
132
130
|
deleteSession(server);
|
|
133
|
-
|
|
134
|
-
shakeDevice(server);
|
|
135
|
-
lockDevice(server);
|
|
136
|
-
unlockDevice(server);
|
|
131
|
+
mobileDeviceControl(server);
|
|
137
132
|
geolocation(server);
|
|
138
133
|
deviceInfo(server);
|
|
139
134
|
fileTransfer(server);
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { ContentResult, FastMCP } from 'fastmcp';
|
|
2
|
+
import type { AndroidUiautomator2Driver } from 'appium-uiautomator2-driver';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import {
|
|
5
|
+
getDriver,
|
|
6
|
+
getPlatformName,
|
|
7
|
+
isAndroidUiautomator2DriverSession,
|
|
8
|
+
isRemoteDriverSession,
|
|
9
|
+
isXCUITestDriverSession,
|
|
10
|
+
PLATFORM,
|
|
11
|
+
type DriverInstance,
|
|
12
|
+
} from '../../session-store.js';
|
|
13
|
+
import { execute } from '../../command.js';
|
|
14
|
+
|
|
15
|
+
const deviceControlSchema = z.object({
|
|
16
|
+
action: z
|
|
17
|
+
.enum(['lock', 'unlock', 'shake', 'open_notifications'])
|
|
18
|
+
.describe(
|
|
19
|
+
'Action to perform. ' +
|
|
20
|
+
'lock: lock the device (optional seconds for timed lock). ' +
|
|
21
|
+
'unlock: unlock the device. ' +
|
|
22
|
+
'shake: perform shake gesture (iOS only). ' +
|
|
23
|
+
'open_notifications: open notifications panel (Android only).'
|
|
24
|
+
),
|
|
25
|
+
sessionId: z
|
|
26
|
+
.string()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
29
|
+
seconds: z
|
|
30
|
+
.number()
|
|
31
|
+
.int()
|
|
32
|
+
.min(1)
|
|
33
|
+
.optional()
|
|
34
|
+
.describe(
|
|
35
|
+
'Only for action=lock: lock duration in seconds before auto-unlock. Omit to remain locked until unlock.'
|
|
36
|
+
),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
async function handleLock(
|
|
40
|
+
driver: DriverInstance,
|
|
41
|
+
seconds?: number
|
|
42
|
+
): Promise<ContentResult> {
|
|
43
|
+
const params: { seconds?: number } = {};
|
|
44
|
+
if (seconds !== undefined) {
|
|
45
|
+
params.seconds = seconds;
|
|
46
|
+
}
|
|
47
|
+
await execute(driver, 'mobile: lock', params);
|
|
48
|
+
const msg =
|
|
49
|
+
seconds !== undefined
|
|
50
|
+
? `Device locked for ${seconds} second(s).`
|
|
51
|
+
: 'Device locked.';
|
|
52
|
+
return { content: [{ type: 'text', text: msg }] };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function handleUnlock(driver: DriverInstance): Promise<ContentResult> {
|
|
56
|
+
await execute(driver, 'mobile: unlock', {});
|
|
57
|
+
return { content: [{ type: 'text', text: 'Device unlocked.' }] };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function handleShake(driver: DriverInstance): Promise<ContentResult> {
|
|
61
|
+
if (!isXCUITestDriverSession(driver)) {
|
|
62
|
+
throw new Error('Shake is supported only with XCUITest driver sessions.');
|
|
63
|
+
}
|
|
64
|
+
await (driver as any).mobileShake();
|
|
65
|
+
return { content: [{ type: 'text', text: 'Shake action performed.' }] };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function handleOpenNotifications(
|
|
69
|
+
driver: DriverInstance
|
|
70
|
+
): Promise<ContentResult> {
|
|
71
|
+
const platform = getPlatformName(driver);
|
|
72
|
+
if (platform !== PLATFORM.android) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Unsupported platform: ${platform}. Open notifications is supported on Android only.`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isAndroidUiautomator2DriverSession(driver)) {
|
|
79
|
+
await (driver as AndroidUiautomator2Driver).openNotifications();
|
|
80
|
+
} else if (isRemoteDriverSession(driver)) {
|
|
81
|
+
await execute(driver, 'mobile: openNotifications', {});
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error('Unsupported Android driver for open notifications');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
content: [
|
|
88
|
+
{ type: 'text', text: 'Successfully opened notifications panel.' },
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default function mobileDeviceControl(server: FastMCP): void {
|
|
94
|
+
server.addTool({
|
|
95
|
+
name: 'appium_mobile_device_control',
|
|
96
|
+
description:
|
|
97
|
+
'Control device behavior: lock/unlock the screen, shake the device, or open the notifications panel. Use the action parameter to choose what to do.',
|
|
98
|
+
parameters: deviceControlSchema,
|
|
99
|
+
annotations: {
|
|
100
|
+
readOnlyHint: false,
|
|
101
|
+
openWorldHint: false,
|
|
102
|
+
},
|
|
103
|
+
execute: async (
|
|
104
|
+
args: z.infer<typeof deviceControlSchema>,
|
|
105
|
+
_context: Record<string, unknown> | undefined
|
|
106
|
+
): Promise<ContentResult> => {
|
|
107
|
+
try {
|
|
108
|
+
const driver = getDriver(args.sessionId);
|
|
109
|
+
if (!driver) {
|
|
110
|
+
throw new Error('No driver found');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (args.action !== 'lock' && args.seconds !== undefined) {
|
|
114
|
+
throw new Error('seconds is only valid when action is lock');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
switch (args.action) {
|
|
118
|
+
case 'lock':
|
|
119
|
+
return await handleLock(driver, args.seconds);
|
|
120
|
+
case 'unlock':
|
|
121
|
+
return await handleUnlock(driver);
|
|
122
|
+
case 'shake':
|
|
123
|
+
return await handleShake(driver);
|
|
124
|
+
case 'open_notifications':
|
|
125
|
+
return await handleOpenNotifications(driver);
|
|
126
|
+
}
|
|
127
|
+
} catch (err: unknown) {
|
|
128
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
129
|
+
return {
|
|
130
|
+
content: [
|
|
131
|
+
{
|
|
132
|
+
type: 'text',
|
|
133
|
+
text: `Failed device control action ${args.action}. err: ${message}`,
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
isError: true,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../../src/tools/session/lock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AAKtD,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CA4DhD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CA4ClD"}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { getDriver } from '../../session-store.js';
|
|
3
|
-
import { execute } from '../../command.js';
|
|
4
|
-
export function lockDevice(server) {
|
|
5
|
-
const lockSchema = z.object({
|
|
6
|
-
sessionId: z
|
|
7
|
-
.string()
|
|
8
|
-
.optional()
|
|
9
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
10
|
-
seconds: z
|
|
11
|
-
.number()
|
|
12
|
-
.int()
|
|
13
|
-
.min(1)
|
|
14
|
-
.optional()
|
|
15
|
-
.describe('How long to lock the screen in seconds before it is automatically unlocked. Supported on both Android (UiAutomator2) and iOS (XCUITest). If omitted, the device stays locked until appium_mobile_unlock is called.'),
|
|
16
|
-
});
|
|
17
|
-
server.addTool({
|
|
18
|
-
name: 'appium_mobile_lock',
|
|
19
|
-
description: 'Lock the device. Optionally lock for a given number of seconds (both Android and iOS support automatic unlock after the timeout). If no timeout is provided, the device stays locked until appium_mobile_unlock is called. Supported on Android (UiAutomator2) and iOS (XCUITest).',
|
|
20
|
-
parameters: lockSchema,
|
|
21
|
-
annotations: {
|
|
22
|
-
readOnlyHint: false,
|
|
23
|
-
openWorldHint: false,
|
|
24
|
-
},
|
|
25
|
-
execute: async (args, _context) => {
|
|
26
|
-
const driver = getDriver(args.sessionId);
|
|
27
|
-
if (!driver) {
|
|
28
|
-
throw new Error('No driver found');
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
const params = {};
|
|
32
|
-
if (args.seconds !== undefined) {
|
|
33
|
-
params.seconds = args.seconds;
|
|
34
|
-
}
|
|
35
|
-
await execute(driver, 'mobile: lock', params);
|
|
36
|
-
const msg = args.seconds !== undefined
|
|
37
|
-
? `Device locked for ${args.seconds} second(s).`
|
|
38
|
-
: 'Device locked.';
|
|
39
|
-
return {
|
|
40
|
-
content: [{ type: 'text', text: msg }],
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
45
|
-
return {
|
|
46
|
-
content: [
|
|
47
|
-
{
|
|
48
|
-
type: 'text',
|
|
49
|
-
text: `Failed to lock device. err: ${message}`,
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
export function unlockDevice(server) {
|
|
58
|
-
const unlockSchema = z.object({
|
|
59
|
-
sessionId: z
|
|
60
|
-
.string()
|
|
61
|
-
.optional()
|
|
62
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
63
|
-
});
|
|
64
|
-
server.addTool({
|
|
65
|
-
name: 'appium_mobile_unlock',
|
|
66
|
-
description: 'Unlock the device if it is locked. No-op if already unlocked. Supported on Android (UiAutomator2) and iOS (XCUITest).',
|
|
67
|
-
parameters: unlockSchema,
|
|
68
|
-
annotations: {
|
|
69
|
-
readOnlyHint: false,
|
|
70
|
-
openWorldHint: false,
|
|
71
|
-
},
|
|
72
|
-
execute: async (args, _context) => {
|
|
73
|
-
const driver = getDriver(args.sessionId);
|
|
74
|
-
if (!driver) {
|
|
75
|
-
throw new Error('No driver found');
|
|
76
|
-
}
|
|
77
|
-
try {
|
|
78
|
-
await execute(driver, 'mobile: unlock', {});
|
|
79
|
-
return {
|
|
80
|
-
content: [{ type: 'text', text: 'Device unlocked.' }],
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
catch (err) {
|
|
84
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
85
|
-
return {
|
|
86
|
-
content: [
|
|
87
|
-
{
|
|
88
|
-
type: 'text',
|
|
89
|
-
text: `Failed to unlock device. err: ${message}`,
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
//# sourceMappingURL=lock.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../../src/tools/session/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"open-notifications.d.ts","sourceRoot":"","sources":["../../../src/tools/session/open-notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AAYtD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAmE/D"}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { getDriver, getPlatformName, isAndroidUiautomator2DriverSession, isRemoteDriverSession, PLATFORM, } from '../../session-store.js';
|
|
3
|
-
import { execute } from '../../command.js';
|
|
4
|
-
export default function openNotifications(server) {
|
|
5
|
-
const schema = z.object({
|
|
6
|
-
sessionId: z
|
|
7
|
-
.string()
|
|
8
|
-
.optional()
|
|
9
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
10
|
-
});
|
|
11
|
-
server.addTool({
|
|
12
|
-
name: 'appium_mobile_open_notifications',
|
|
13
|
-
description: 'Open the Android notifications panel using the mobile: openNotifications extension. Does nothing if the panel is already open.',
|
|
14
|
-
parameters: schema,
|
|
15
|
-
annotations: {
|
|
16
|
-
readOnlyHint: false,
|
|
17
|
-
openWorldHint: false,
|
|
18
|
-
},
|
|
19
|
-
execute: async (args, _context) => {
|
|
20
|
-
const driver = getDriver(args.sessionId);
|
|
21
|
-
if (!driver) {
|
|
22
|
-
throw new Error('No driver found');
|
|
23
|
-
}
|
|
24
|
-
const platform = getPlatformName(driver);
|
|
25
|
-
if (platform !== PLATFORM.android) {
|
|
26
|
-
return {
|
|
27
|
-
content: [
|
|
28
|
-
{
|
|
29
|
-
type: 'text',
|
|
30
|
-
text: `Unsupported platform: ${platform}. Open notifications is supported on Android only.`,
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
if (isAndroidUiautomator2DriverSession(driver)) {
|
|
37
|
-
await driver.openNotifications();
|
|
38
|
-
}
|
|
39
|
-
else if (isRemoteDriverSession(driver)) {
|
|
40
|
-
await execute(driver, 'mobile: openNotifications', {});
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
throw new Error('Unsupported Android driver for open notifications');
|
|
44
|
-
}
|
|
45
|
-
return {
|
|
46
|
-
content: [
|
|
47
|
-
{
|
|
48
|
-
type: 'text',
|
|
49
|
-
text: 'Successfully opened notifications panel.',
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
return {
|
|
56
|
-
content: [
|
|
57
|
-
{
|
|
58
|
-
type: 'text',
|
|
59
|
-
text: `Failed to open notifications panel. err: ${err.toString()}`,
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
//# sourceMappingURL=open-notifications.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"open-notifications.js","sourceRoot":"","sources":["../../../src/tools/session/open-notifications.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,SAAS,EACT,eAAe,EACf,kCAAkC,EAClC,qBAAqB,EACrB,QAAQ,GACT,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAG3C,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,MAAe;IACvD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,SAAS,EAAE,CAAC;aACT,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,4DAA4D,CAAC;KAC1E,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,kCAAkC;QACxC,WAAW,EACT,gIAAgI;QAClI,UAAU,EAAE,MAAM;QAClB,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,aAAa,EAAE,KAAK;SACrB;QACD,OAAO,EAAE,KAAK,EACZ,IAA4B,EAC5B,QAA6C,EACrB,EAAE;YAC1B,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YAED,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;YACzC,IAAI,QAAQ,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;gBAClC,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,yBAAyB,QAAQ,oDAAoD;yBAC5F;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,IAAI,CAAC;gBACH,IAAI,kCAAkC,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC/C,MAAO,MAAoC,CAAC,iBAAiB,EAAE,CAAC;gBAClE,CAAC;qBAAM,IAAI,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC;oBACzC,MAAM,OAAO,CAAC,MAAM,EAAE,2BAA2B,EAAE,EAAE,CAAC,CAAC;gBACzD,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;gBACvE,CAAC;gBAED,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,0CAA0C;yBACjD;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,4CAA4C,GAAG,CAAC,QAAQ,EAAE,EAAE;yBACnE;qBACF;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shake.d.ts","sourceRoot":"","sources":["../../../src/tools/session/shake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AAItD,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAwDzD"}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { getDriver, isXCUITestDriverSession } from '../../session-store.js';
|
|
3
|
-
export default function shakeDevice(server) {
|
|
4
|
-
const shakeSchema = z.object({
|
|
5
|
-
sessionId: z
|
|
6
|
-
.string()
|
|
7
|
-
.optional()
|
|
8
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
9
|
-
});
|
|
10
|
-
server.addTool({
|
|
11
|
-
name: 'appium_mobile_shake',
|
|
12
|
-
description: 'Perform a shake gesture via Appium `mobile: shake` using the XCUITest driver. ' +
|
|
13
|
-
'Other driver types are not supported.',
|
|
14
|
-
parameters: shakeSchema,
|
|
15
|
-
annotations: {
|
|
16
|
-
readOnlyHint: false,
|
|
17
|
-
openWorldHint: false,
|
|
18
|
-
},
|
|
19
|
-
execute: async (args, _context) => {
|
|
20
|
-
const driver = getDriver(args.sessionId);
|
|
21
|
-
if (!driver) {
|
|
22
|
-
throw new Error('No driver found');
|
|
23
|
-
}
|
|
24
|
-
if (!isXCUITestDriverSession(driver)) {
|
|
25
|
-
return {
|
|
26
|
-
content: [
|
|
27
|
-
{
|
|
28
|
-
type: 'text',
|
|
29
|
-
text: 'Shake is supported only with XCUITest driver sessions. Other driver types are not supported.',
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
await driver.mobileShake();
|
|
36
|
-
return {
|
|
37
|
-
content: [{ type: 'text', text: 'Shake action performed.' }],
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
42
|
-
return {
|
|
43
|
-
content: [
|
|
44
|
-
{
|
|
45
|
-
type: 'text',
|
|
46
|
-
text: `Failed to perform shake. err: ${message}`,
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
//# sourceMappingURL=shake.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shake.js","sourceRoot":"","sources":["../../../src/tools/session/shake.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAE5E,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,MAAe;IACjD,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;QAC3B,SAAS,EAAE,CAAC;aACT,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,4DAA4D,CAAC;KAC1E,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EACT,gFAAgF;YAChF,uCAAuC;QACzC,UAAU,EAAE,WAAW;QACvB,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,aAAa,EAAE,KAAK;SACrB;QACD,OAAO,EAAE,KAAK,EACZ,IAAiC,EACjC,QAA6C,EACrB,EAAE;YAC1B,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YAED,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrC,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,8FAA8F;yBACrG;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,IAAI,CAAC;gBACH,MAAO,MAAc,CAAC,WAAW,EAAE,CAAC;gBACpC,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,EAAE,CAAC;iBAC7D,CAAC;YACJ,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,iCAAiC,OAAO,EAAE;yBACjD;qBACF;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import type { ContentResult, FastMCP } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getDriver } from '../../session-store.js';
|
|
4
|
-
import { execute } from '../../command.js';
|
|
5
|
-
|
|
6
|
-
export function lockDevice(server: FastMCP): void {
|
|
7
|
-
const lockSchema = z.object({
|
|
8
|
-
sessionId: z
|
|
9
|
-
.string()
|
|
10
|
-
.optional()
|
|
11
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
12
|
-
seconds: z
|
|
13
|
-
.number()
|
|
14
|
-
.int()
|
|
15
|
-
.min(1)
|
|
16
|
-
.optional()
|
|
17
|
-
.describe(
|
|
18
|
-
'How long to lock the screen in seconds before it is automatically unlocked. Supported on both Android (UiAutomator2) and iOS (XCUITest). If omitted, the device stays locked until appium_mobile_unlock is called.'
|
|
19
|
-
),
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
server.addTool({
|
|
23
|
-
name: 'appium_mobile_lock',
|
|
24
|
-
description:
|
|
25
|
-
'Lock the device. Optionally lock for a given number of seconds (both Android and iOS support automatic unlock after the timeout). If no timeout is provided, the device stays locked until appium_mobile_unlock is called. Supported on Android (UiAutomator2) and iOS (XCUITest).',
|
|
26
|
-
parameters: lockSchema,
|
|
27
|
-
annotations: {
|
|
28
|
-
readOnlyHint: false,
|
|
29
|
-
openWorldHint: false,
|
|
30
|
-
},
|
|
31
|
-
execute: async (
|
|
32
|
-
args: z.infer<typeof lockSchema>,
|
|
33
|
-
_context: Record<string, unknown> | undefined
|
|
34
|
-
): Promise<ContentResult> => {
|
|
35
|
-
const driver = getDriver(args.sessionId);
|
|
36
|
-
if (!driver) {
|
|
37
|
-
throw new Error('No driver found');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const params: { seconds?: number } = {};
|
|
42
|
-
if (args.seconds !== undefined) {
|
|
43
|
-
params.seconds = args.seconds;
|
|
44
|
-
}
|
|
45
|
-
await execute(driver, 'mobile: lock', params);
|
|
46
|
-
const msg =
|
|
47
|
-
args.seconds !== undefined
|
|
48
|
-
? `Device locked for ${args.seconds} second(s).`
|
|
49
|
-
: 'Device locked.';
|
|
50
|
-
return {
|
|
51
|
-
content: [{ type: 'text', text: msg }],
|
|
52
|
-
};
|
|
53
|
-
} catch (err: unknown) {
|
|
54
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
55
|
-
return {
|
|
56
|
-
content: [
|
|
57
|
-
{
|
|
58
|
-
type: 'text',
|
|
59
|
-
text: `Failed to lock device. err: ${message}`,
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function unlockDevice(server: FastMCP): void {
|
|
69
|
-
const unlockSchema = z.object({
|
|
70
|
-
sessionId: z
|
|
71
|
-
.string()
|
|
72
|
-
.optional()
|
|
73
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
server.addTool({
|
|
77
|
-
name: 'appium_mobile_unlock',
|
|
78
|
-
description:
|
|
79
|
-
'Unlock the device if it is locked. No-op if already unlocked. Supported on Android (UiAutomator2) and iOS (XCUITest).',
|
|
80
|
-
parameters: unlockSchema,
|
|
81
|
-
annotations: {
|
|
82
|
-
readOnlyHint: false,
|
|
83
|
-
openWorldHint: false,
|
|
84
|
-
},
|
|
85
|
-
execute: async (
|
|
86
|
-
args: z.infer<typeof unlockSchema>,
|
|
87
|
-
_context: Record<string, unknown> | undefined
|
|
88
|
-
): Promise<ContentResult> => {
|
|
89
|
-
const driver = getDriver(args.sessionId);
|
|
90
|
-
if (!driver) {
|
|
91
|
-
throw new Error('No driver found');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
await execute(driver, 'mobile: unlock', {});
|
|
96
|
-
return {
|
|
97
|
-
content: [{ type: 'text', text: 'Device unlocked.' }],
|
|
98
|
-
};
|
|
99
|
-
} catch (err: unknown) {
|
|
100
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
101
|
-
return {
|
|
102
|
-
content: [
|
|
103
|
-
{
|
|
104
|
-
type: 'text',
|
|
105
|
-
text: `Failed to unlock device. err: ${message}`,
|
|
106
|
-
},
|
|
107
|
-
],
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { ContentResult, FastMCP } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import {
|
|
4
|
-
getDriver,
|
|
5
|
-
getPlatformName,
|
|
6
|
-
isAndroidUiautomator2DriverSession,
|
|
7
|
-
isRemoteDriverSession,
|
|
8
|
-
PLATFORM,
|
|
9
|
-
} from '../../session-store.js';
|
|
10
|
-
import { execute } from '../../command.js';
|
|
11
|
-
import type { AndroidUiautomator2Driver } from 'appium-uiautomator2-driver';
|
|
12
|
-
|
|
13
|
-
export default function openNotifications(server: FastMCP): void {
|
|
14
|
-
const schema = z.object({
|
|
15
|
-
sessionId: z
|
|
16
|
-
.string()
|
|
17
|
-
.optional()
|
|
18
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
server.addTool({
|
|
22
|
-
name: 'appium_mobile_open_notifications',
|
|
23
|
-
description:
|
|
24
|
-
'Open the Android notifications panel using the mobile: openNotifications extension. Does nothing if the panel is already open.',
|
|
25
|
-
parameters: schema,
|
|
26
|
-
annotations: {
|
|
27
|
-
readOnlyHint: false,
|
|
28
|
-
openWorldHint: false,
|
|
29
|
-
},
|
|
30
|
-
execute: async (
|
|
31
|
-
args: z.infer<typeof schema>,
|
|
32
|
-
_context: Record<string, unknown> | undefined
|
|
33
|
-
): Promise<ContentResult> => {
|
|
34
|
-
const driver = getDriver(args.sessionId);
|
|
35
|
-
if (!driver) {
|
|
36
|
-
throw new Error('No driver found');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const platform = getPlatformName(driver);
|
|
40
|
-
if (platform !== PLATFORM.android) {
|
|
41
|
-
return {
|
|
42
|
-
content: [
|
|
43
|
-
{
|
|
44
|
-
type: 'text',
|
|
45
|
-
text: `Unsupported platform: ${platform}. Open notifications is supported on Android only.`,
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
if (isAndroidUiautomator2DriverSession(driver)) {
|
|
53
|
-
await (driver as AndroidUiautomator2Driver).openNotifications();
|
|
54
|
-
} else if (isRemoteDriverSession(driver)) {
|
|
55
|
-
await execute(driver, 'mobile: openNotifications', {});
|
|
56
|
-
} else {
|
|
57
|
-
throw new Error('Unsupported Android driver for open notifications');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
content: [
|
|
62
|
-
{
|
|
63
|
-
type: 'text',
|
|
64
|
-
text: 'Successfully opened notifications panel.',
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
};
|
|
68
|
-
} catch (err: any) {
|
|
69
|
-
return {
|
|
70
|
-
content: [
|
|
71
|
-
{
|
|
72
|
-
type: 'text',
|
|
73
|
-
text: `Failed to open notifications panel. err: ${err.toString()}`,
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { ContentResult, FastMCP } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getDriver, isXCUITestDriverSession } from '../../session-store.js';
|
|
4
|
-
|
|
5
|
-
export default function shakeDevice(server: FastMCP): void {
|
|
6
|
-
const shakeSchema = z.object({
|
|
7
|
-
sessionId: z
|
|
8
|
-
.string()
|
|
9
|
-
.optional()
|
|
10
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
server.addTool({
|
|
14
|
-
name: 'appium_mobile_shake',
|
|
15
|
-
description:
|
|
16
|
-
'Perform a shake gesture via Appium `mobile: shake` using the XCUITest driver. ' +
|
|
17
|
-
'Other driver types are not supported.',
|
|
18
|
-
parameters: shakeSchema,
|
|
19
|
-
annotations: {
|
|
20
|
-
readOnlyHint: false,
|
|
21
|
-
openWorldHint: false,
|
|
22
|
-
},
|
|
23
|
-
execute: async (
|
|
24
|
-
args: z.infer<typeof shakeSchema>,
|
|
25
|
-
_context: Record<string, unknown> | undefined
|
|
26
|
-
): Promise<ContentResult> => {
|
|
27
|
-
const driver = getDriver(args.sessionId);
|
|
28
|
-
if (!driver) {
|
|
29
|
-
throw new Error('No driver found');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!isXCUITestDriverSession(driver)) {
|
|
33
|
-
return {
|
|
34
|
-
content: [
|
|
35
|
-
{
|
|
36
|
-
type: 'text',
|
|
37
|
-
text: 'Shake is supported only with XCUITest driver sessions. Other driver types are not supported.',
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
await (driver as any).mobileShake();
|
|
45
|
-
return {
|
|
46
|
-
content: [{ type: 'text', text: 'Shake action performed.' }],
|
|
47
|
-
};
|
|
48
|
-
} catch (err: unknown) {
|
|
49
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
50
|
-
return {
|
|
51
|
-
content: [
|
|
52
|
-
{
|
|
53
|
-
type: 'text',
|
|
54
|
-
text: `Failed to perform shake. err: ${message}`,
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
}
|