appium-mcp 1.67.0 → 1.68.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 +16 -0
- package/README.md +1 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/gestures/handlers/pinch.d.ts +13 -0
- package/dist/tools/gestures/handlers/pinch.d.ts.map +1 -1
- package/dist/tools/gestures/handlers/pinch.js +129 -10
- package/dist/tools/gestures/handlers/pinch.js.map +1 -1
- package/dist/tools/gestures/schema.d.ts.map +1 -1
- package/dist/tools/gestures/schema.js +4 -2
- package/dist/tools/gestures/schema.js.map +1 -1
- package/dist/tools/interactions/clipboard.d.ts +0 -10
- package/dist/tools/interactions/clipboard.d.ts.map +1 -1
- package/dist/tools/interactions/clipboard.js +49 -68
- package/dist/tools/interactions/clipboard.js.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/index.ts +2 -2
- package/src/resources/submodules.zip +0 -0
- package/src/tools/gestures/handlers/pinch.ts +184 -10
- package/src/tools/gestures/schema.ts +4 -2
- package/src/tools/interactions/clipboard.ts +60 -78
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [1.68.0](https://github.com/appium/appium-mcp/compare/v1.67.1...v1.68.0) (2026-04-29)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **tools:** single appium_mobile_clipboard tool ([#292](https://github.com/appium/appium-mcp/issues/292)) ([ce7c7dc](https://github.com/appium/appium-mcp/commit/ce7c7dc6e20f0dd43b0ebf3f56651065af55fa8c))
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* loading mcp server to start ([#298](https://github.com/appium/appium-mcp/issues/298)) ([82414d6](https://github.com/appium/appium-mcp/commit/82414d6dbec351887758aae9414ac9adfc4de22d))
|
|
10
|
+
|
|
11
|
+
## [1.67.1](https://github.com/appium/appium-mcp/compare/v1.67.0...v1.67.1) (2026-04-27)
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* support zoom in/out via custom coords ([#291](https://github.com/appium/appium-mcp/issues/291)) ([251e144](https://github.com/appium/appium-mcp/commit/251e144f9a21ca0909df93ba52bc8b748e4e20b4))
|
|
16
|
+
|
|
1
17
|
## [1.67.0](https://github.com/appium/appium-mcp/compare/v1.66.0...v1.67.0) (2026-04-23)
|
|
2
18
|
|
|
3
19
|
### Features
|
package/README.md
CHANGED
|
@@ -343,8 +343,7 @@ The default regex pattern allows any URL that starts with `http://` or `https://
|
|
|
343
343
|
| `appium_mobile_hide_keyboard` | Dismiss the on-screen keyboard (`mobile: hideKeyboard`) |
|
|
344
344
|
| `appium_mobile_is_keyboard_shown` | Whether the on-screen keyboard is visible (`mobile: isKeyboardShown`) |
|
|
345
345
|
| `appium_get_text` | Get text content from an element |
|
|
346
|
-
| `
|
|
347
|
-
| `appium_set_clipboard` | Set the device clipboard to the provided plain text |
|
|
346
|
+
| `appium_mobile_clipboard` | Read or set device clipboard plain text. `action=get` \| `set` (`content` required for set). |
|
|
348
347
|
| `appium_alert` | Handle alerts with `action` = `accept`, `dismiss`, or `get_text` (optional `buttonLabel`) |
|
|
349
348
|
|
|
350
349
|
### Screen & Navigation
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ async function startServer() {
|
|
|
9
9
|
log.info('Starting MCP Appium MCP Server...');
|
|
10
10
|
try {
|
|
11
11
|
if (useHttpStream) {
|
|
12
|
-
server.start({
|
|
12
|
+
await server.start({
|
|
13
13
|
transportType: 'httpStream',
|
|
14
14
|
httpStream: {
|
|
15
15
|
endpoint: '/sse',
|
|
@@ -21,7 +21,7 @@ async function startServer() {
|
|
|
21
21
|
}
|
|
22
22
|
else {
|
|
23
23
|
// Start with stdio transport
|
|
24
|
-
server.start({
|
|
24
|
+
await server.start({
|
|
25
25
|
transportType: 'stdio',
|
|
26
26
|
});
|
|
27
27
|
log.info('Server started with stdio transport');
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,GAAG,MAAM,aAAa,CAAC;AAE9B,+BAA+B;AAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AACpD,MAAM,IAAI,GACR,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;AAEzE,KAAK,UAAU,WAAW;IACxB,GAAG,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IAE9C,IAAI,CAAC;QACH,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,CAAC,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,GAAG,MAAM,aAAa,CAAC;AAE9B,+BAA+B;AAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AACpD,MAAM,IAAI,GACR,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;AAEzE,KAAK,UAAU,WAAW;IACxB,GAAG,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IAE9C,IAAI,CAAC;QACH,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,MAAM,CAAC,KAAK,CAAC;gBACjB,aAAa,EAAE,YAAY;gBAC3B,UAAU,EAAE;oBACV,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;iBACzB;aACF,CAAC,CAAC;YAEH,GAAG,CAAC,IAAI,CACN,gEAAgE,IAAI,MAAM,CAC3E,CAAC;YACF,GAAG,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,6BAA6B;YAC7B,MAAM,MAAM,CAAC,KAAK,CAAC;gBACjB,aAAa,EAAE,OAAO;aACvB,CAAC,CAAC;YAEH,GAAG,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YAChD,GAAG,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,GAAG,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;QAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,mBAAmB;AACnB,WAAW,EAAE,CAAC"}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { ContentResult } from 'fastmcp';
|
|
2
2
|
import type { DriverInstance } from '../../../session-store.js';
|
|
3
3
|
import type { GestureArgs } from '../schema.js';
|
|
4
|
+
type RectLike = {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
};
|
|
10
|
+
type PinchTarget = {
|
|
11
|
+
cx: number;
|
|
12
|
+
cy: number;
|
|
13
|
+
spread: number;
|
|
14
|
+
};
|
|
15
|
+
export declare function resolveElementPinchTarget(elementRect: RectLike, windowRect: RectLike): PinchTarget;
|
|
4
16
|
export declare function handlePinchZoom(driver: DriverInstance, args: GestureArgs): Promise<ContentResult>;
|
|
17
|
+
export {};
|
|
5
18
|
//# sourceMappingURL=pinch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pinch.d.ts","sourceRoot":"","sources":["../../../../src/tools/gestures/handlers/pinch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAahE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"pinch.d.ts","sourceRoot":"","sources":["../../../../src/tools/gestures/handlers/pinch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAahE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMhD,KAAK,QAAQ,GAAG;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAmBF,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,QAAQ,EACrB,UAAU,EAAE,QAAQ,GACnB,WAAW,CAyCb;AAwCD,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,aAAa,CAAC,CA6JxB"}
|
|
@@ -2,12 +2,79 @@ import { getPlatformName, PLATFORM } from '../../../session-store.js';
|
|
|
2
2
|
import { execute, getElementRect, getWindowRect, performActions, } from '../../../command.js';
|
|
3
3
|
import { errorResult, textResult, toolErrorMessage, } from '../../tool-response.js';
|
|
4
4
|
const DEFAULT_VELOCITY = 2.2;
|
|
5
|
+
const DEFAULT_PINCH_SPREAD_RATIO = 0.3;
|
|
6
|
+
const MIN_ELEMENT_PINCH_SPREAD_RATIO = 0.15;
|
|
7
|
+
function clamp(value, min, max) {
|
|
8
|
+
return Math.min(max, Math.max(min, value));
|
|
9
|
+
}
|
|
10
|
+
function maxSpreadForCenter(cx, cy, windowRect) {
|
|
11
|
+
const left = windowRect.x ?? 0;
|
|
12
|
+
const top = windowRect.y ?? 0;
|
|
13
|
+
const right = left + windowRect.width - 1;
|
|
14
|
+
const bottom = top + windowRect.height - 1;
|
|
15
|
+
return Math.max(1, Math.min(cx - left, right - cx, cy - top, bottom - cy));
|
|
16
|
+
}
|
|
17
|
+
export function resolveElementPinchTarget(elementRect, windowRect) {
|
|
18
|
+
const windowLeft = windowRect.x ?? 0;
|
|
19
|
+
const windowTop = windowRect.y ?? 0;
|
|
20
|
+
const windowRight = windowLeft + windowRect.width;
|
|
21
|
+
const windowBottom = windowTop + windowRect.height;
|
|
22
|
+
const visibleLeft = Math.max(windowLeft, elementRect.x);
|
|
23
|
+
const visibleTop = Math.max(windowTop, elementRect.y);
|
|
24
|
+
const visibleRight = Math.min(windowRight, elementRect.x + elementRect.width);
|
|
25
|
+
const visibleBottom = Math.min(windowBottom, elementRect.y + elementRect.height);
|
|
26
|
+
const rawCx = visibleRight > visibleLeft
|
|
27
|
+
? visibleLeft + (visibleRight - visibleLeft) / 2
|
|
28
|
+
: elementRect.x + elementRect.width / 2;
|
|
29
|
+
const rawCy = visibleBottom > visibleTop
|
|
30
|
+
? visibleTop + (visibleBottom - visibleTop) / 2
|
|
31
|
+
: elementRect.y + elementRect.height / 2;
|
|
32
|
+
const cx = Math.floor(clamp(rawCx, windowLeft, windowRight - 1));
|
|
33
|
+
const cy = Math.floor(clamp(rawCy, windowTop, windowBottom - 1));
|
|
34
|
+
const windowBase = Math.min(windowRect.width, windowRect.height);
|
|
35
|
+
const elementBase = Math.min(elementRect.width, elementRect.height);
|
|
36
|
+
const desired = Math.max(Math.floor(elementBase * DEFAULT_PINCH_SPREAD_RATIO), Math.floor(windowBase * MIN_ELEMENT_PINCH_SPREAD_RATIO));
|
|
37
|
+
const cappedDesired = Math.min(desired, Math.floor(windowBase * DEFAULT_PINCH_SPREAD_RATIO));
|
|
38
|
+
return {
|
|
39
|
+
cx,
|
|
40
|
+
cy,
|
|
41
|
+
spread: Math.min(cappedDesired, maxSpreadForCenter(cx, cy, windowRect)),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function resolveWindowPinchTarget(windowRect) {
|
|
45
|
+
const cx = Math.floor((windowRect.x ?? 0) + windowRect.width / 2);
|
|
46
|
+
const cy = Math.floor((windowRect.y ?? 0) + windowRect.height / 2);
|
|
47
|
+
return {
|
|
48
|
+
cx,
|
|
49
|
+
cy,
|
|
50
|
+
spread: Math.floor(Math.min(windowRect.width, windowRect.height) * DEFAULT_PINCH_SPREAD_RATIO),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function resolveCoordinatePinchTarget(x, y, windowRect) {
|
|
54
|
+
const left = windowRect.x ?? 0;
|
|
55
|
+
const top = windowRect.y ?? 0;
|
|
56
|
+
const right = left + windowRect.width;
|
|
57
|
+
const bottom = top + windowRect.height;
|
|
58
|
+
if (x < left || x >= right || y < top || y >= bottom) {
|
|
59
|
+
return `pinch_zoom coordinates (${x}, ${y}) are outside window bounds (${windowRect.width}x${windowRect.height}).`;
|
|
60
|
+
}
|
|
61
|
+
const desired = Math.floor(Math.min(windowRect.width, windowRect.height) * DEFAULT_PINCH_SPREAD_RATIO);
|
|
62
|
+
return {
|
|
63
|
+
cx: x,
|
|
64
|
+
cy: y,
|
|
65
|
+
spread: Math.min(desired, maxSpreadForCenter(x, y, windowRect)),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
5
68
|
export async function handlePinchZoom(driver, args) {
|
|
6
69
|
if (args.scale === undefined) {
|
|
7
70
|
return errorResult('pinch_zoom requires a scale value (e.g. 0.5 to zoom out, 2.0 to zoom in).');
|
|
8
71
|
}
|
|
9
72
|
const scale = args.scale;
|
|
10
73
|
const velocity = args.velocity ?? DEFAULT_VELOCITY;
|
|
74
|
+
if (!args.elementUUID && (args.x !== undefined) !== (args.y !== undefined)) {
|
|
75
|
+
return errorResult('pinch_zoom requires both x and y when using custom coordinates.');
|
|
76
|
+
}
|
|
77
|
+
const useCustomCoords = !args.elementUUID && args.x !== undefined && args.y !== undefined;
|
|
11
78
|
try {
|
|
12
79
|
const platform = getPlatformName(driver);
|
|
13
80
|
let cx;
|
|
@@ -15,19 +82,24 @@ export async function handlePinchZoom(driver, args) {
|
|
|
15
82
|
let spread;
|
|
16
83
|
let windowRect = null;
|
|
17
84
|
if (args.elementUUID) {
|
|
85
|
+
windowRect = await getWindowRect(driver);
|
|
18
86
|
const rect = await getElementRect(driver, args.elementUUID);
|
|
19
|
-
cx =
|
|
20
|
-
|
|
21
|
-
|
|
87
|
+
({ cx, cy, spread } = resolveElementPinchTarget(rect, windowRect));
|
|
88
|
+
}
|
|
89
|
+
else if (useCustomCoords) {
|
|
90
|
+
windowRect = await getWindowRect(driver);
|
|
91
|
+
const target = resolveCoordinatePinchTarget(args.x, args.y, windowRect);
|
|
92
|
+
if (typeof target === 'string') {
|
|
93
|
+
return errorResult(target);
|
|
94
|
+
}
|
|
95
|
+
({ cx, cy, spread } = target);
|
|
22
96
|
}
|
|
23
97
|
else {
|
|
24
98
|
windowRect = await getWindowRect(driver);
|
|
25
|
-
cx =
|
|
26
|
-
cy = Math.floor(windowRect.height / 2);
|
|
27
|
-
spread = Math.floor(Math.min(windowRect.width, windowRect.height) * 0.3);
|
|
99
|
+
({ cx, cy, spread } = resolveWindowPinchTarget(windowRect));
|
|
28
100
|
}
|
|
29
101
|
if (scale < 1) {
|
|
30
|
-
// Zoom out:
|
|
102
|
+
// Zoom out: W3C Actions on all platforms; fingers move together.
|
|
31
103
|
const startSpread = spread;
|
|
32
104
|
const endSpread = Math.max(1, Math.floor(spread * scale));
|
|
33
105
|
const duration = Math.floor((1 / Math.abs(velocity)) * 1000);
|
|
@@ -56,6 +128,49 @@ export async function handlePinchZoom(driver, args) {
|
|
|
56
128
|
},
|
|
57
129
|
]);
|
|
58
130
|
}
|
|
131
|
+
else if (useCustomCoords && platform === PLATFORM.android) {
|
|
132
|
+
// Zoom in at a custom center on Android: use the native pinchOpenGesture
|
|
133
|
+
// with a region centered at (cx, cy). spread is pre-clamped so the region
|
|
134
|
+
// fits within the window.
|
|
135
|
+
const percent = Math.min(0.99, 1 - 1 / scale);
|
|
136
|
+
await execute(driver, 'mobile: pinchOpenGesture', {
|
|
137
|
+
left: cx - spread,
|
|
138
|
+
top: cy - spread,
|
|
139
|
+
width: 2 * spread,
|
|
140
|
+
height: 2 * spread,
|
|
141
|
+
percent,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else if (useCustomCoords) {
|
|
145
|
+
// Zoom in at a custom center on iOS using W3C Actions
|
|
146
|
+
const startSpread = Math.max(1, Math.floor(spread / scale));
|
|
147
|
+
const endSpread = spread;
|
|
148
|
+
const duration = Math.floor((1 / Math.abs(velocity)) * 1000);
|
|
149
|
+
await performActions(driver, [
|
|
150
|
+
{
|
|
151
|
+
type: 'pointer',
|
|
152
|
+
id: 'finger1',
|
|
153
|
+
parameters: { pointerType: 'touch' },
|
|
154
|
+
actions: [
|
|
155
|
+
{ type: 'pointerMove', duration: 0, x: cx - startSpread, y: cy },
|
|
156
|
+
{ type: 'pointerDown', button: 0 },
|
|
157
|
+
{ type: 'pointerMove', duration, x: cx - endSpread, y: cy },
|
|
158
|
+
{ type: 'pointerUp', button: 0 },
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
type: 'pointer',
|
|
163
|
+
id: 'finger2',
|
|
164
|
+
parameters: { pointerType: 'touch' },
|
|
165
|
+
actions: [
|
|
166
|
+
{ type: 'pointerMove', duration: 0, x: cx + startSpread, y: cy },
|
|
167
|
+
{ type: 'pointerDown', button: 0 },
|
|
168
|
+
{ type: 'pointerMove', duration, x: cx + endSpread, y: cy },
|
|
169
|
+
{ type: 'pointerUp', button: 0 },
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
59
174
|
else if (platform === PLATFORM.ios) {
|
|
60
175
|
const params = {
|
|
61
176
|
scale,
|
|
@@ -67,8 +182,8 @@ export async function handlePinchZoom(driver, args) {
|
|
|
67
182
|
await execute(driver, 'mobile: pinch', params);
|
|
68
183
|
}
|
|
69
184
|
else if (platform === PLATFORM.android) {
|
|
70
|
-
// Convert scale factor to percent (0
|
|
71
|
-
// scale=2
|
|
185
|
+
// Convert scale factor to percent (0-1) for pinchOpenGesture.
|
|
186
|
+
// scale=2 -> 0.5, scale=4 -> 0.75, scale=10 -> 0.9. Capped at 0.99.
|
|
72
187
|
const percent = Math.min(0.99, 1 - 1 / scale);
|
|
73
188
|
const params = { percent };
|
|
74
189
|
if (args.elementUUID) {
|
|
@@ -87,7 +202,11 @@ export async function handlePinchZoom(driver, args) {
|
|
|
87
202
|
return errorResult(`pinch_zoom is not supported on platform '${platform}'. Supported: iOS, Android.`);
|
|
88
203
|
}
|
|
89
204
|
const direction = scale < 1 ? 'out' : 'in';
|
|
90
|
-
const target = args.elementUUID
|
|
205
|
+
const target = args.elementUUID
|
|
206
|
+
? `element ${args.elementUUID}`
|
|
207
|
+
: useCustomCoords
|
|
208
|
+
? `coordinates (${cx}, ${cy})`
|
|
209
|
+
: 'screen';
|
|
91
210
|
return textResult(`Successfully pinched ${direction} (scale=${scale}) on ${target}.`);
|
|
92
211
|
}
|
|
93
212
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pinch.js","sourceRoot":"","sources":["../../../../src/tools/gestures/handlers/pinch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EACL,OAAO,EACP,cAAc,EACd,aAAa,EACb,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,WAAW,EACX,UAAU,EACV,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,gBAAgB,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"pinch.js","sourceRoot":"","sources":["../../../../src/tools/gestures/handlers/pinch.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/tools/gestures/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,eAAe,oGAQlB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAO,MAAM,YAAY,qCAAsC,CAAC;AAChE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,eAAO,MAAM,uBAAuB,uCAAwC,CAAC;AAC7E,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,eAAO,MAAM,kBAAkB,yJAUrB,CAAC;AAEX,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/tools/gestures/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,eAAe,oGAQlB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAO,MAAM,YAAY,qCAAsC,CAAC;AAChE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,eAAO,MAAM,uBAAuB,uCAAwC,CAAC;AAC7E,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,eAAO,MAAM,kBAAkB,yJAUrB,CAAC;AAEX,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoJxB,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC"}
|
|
@@ -47,7 +47,8 @@ export const gestureSchema = z.object({
|
|
|
47
47
|
.optional()
|
|
48
48
|
.describe(`X coordinate. ` +
|
|
49
49
|
`For tap/double_tap/long_press: tap location (alternative to elementUUID). ` +
|
|
50
|
-
`For scroll/swipe: starting X for custom-coordinate mode (requires y, endX, endY)
|
|
50
|
+
`For scroll/swipe: starting X for custom-coordinate mode (requires y, endX, endY). ` +
|
|
51
|
+
`For pinch_zoom: center X of the pinch. Requires y. Ignored if elementUUID is set.`),
|
|
51
52
|
y: z
|
|
52
53
|
.number()
|
|
53
54
|
.int()
|
|
@@ -55,7 +56,8 @@ export const gestureSchema = z.object({
|
|
|
55
56
|
.optional()
|
|
56
57
|
.describe(`Y coordinate. ` +
|
|
57
58
|
`For tap/double_tap/long_press: tap location. ` +
|
|
58
|
-
`For scroll/swipe: starting Y for custom-coordinate mode
|
|
59
|
+
`For scroll/swipe: starting Y for custom-coordinate mode. ` +
|
|
60
|
+
`For pinch_zoom: center Y of the pinch. Requires x. Ignored if elementUUID is set.`),
|
|
59
61
|
endX: z
|
|
60
62
|
.number()
|
|
61
63
|
.int()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/tools/gestures/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,KAAK;IACL,YAAY;IACZ,YAAY;IACZ,QAAQ;IACR,OAAO;IACP,YAAY;IACZ,mBAAmB;CACX,CAAC;AAIX,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAC;AAGhE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC;AAG7E,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,OAAO;IACP,IAAI;IACJ,MAAM;IACN,YAAY;IACZ,kBAAkB;IAClB,cAAc;IACd,sBAAsB;IACtB,uBAAuB;IACvB,kBAAkB;CACV,CAAC;AAEX,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,eAAe,CAAC;SACrB,QAAQ,CACP,sBAAsB;QACpB,uCAAuC;QACvC,uFAAuF;QACvF,sEAAsE;QACtE,0DAA0D;QAC1D,2GAA2G;QAC3G,4FAA4F;QAC5F,6GAA6G;QAC7G,yIAAyI;QACzI,gFAAgF,CACnF;IAEH,WAAW,EAAE,iBAAiB;SAC3B,QAAQ,EAAE;SACV,QAAQ,CACP,6FAA6F;QAC3F,oDAAoD;QACpD,iIAAiI,CACpI;IAEH,CAAC,EAAE,CAAC;SACD,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,QAAQ,CACP,gBAAgB;QACd,4EAA4E;QAC5E,mFAAmF,CACtF;IACH,CAAC,EAAE,CAAC;SACD,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,QAAQ,CACP,gBAAgB;QACd,+CAA+C;QAC/C,
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/tools/gestures/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,3 @@
|
|
|
1
1
|
import type { FastMCP } from 'fastmcp';
|
|
2
|
-
/**
|
|
3
|
-
* Register clipboard read/write tools.
|
|
4
|
-
*
|
|
5
|
-
* - appium_get_clipboard: reads the current clipboard content as plain text
|
|
6
|
-
* - appium_set_clipboard: writes plain text to the clipboard
|
|
7
|
-
*
|
|
8
|
-
* Both tools rely on the `mobile: getClipboard` / `mobile: setClipboard`
|
|
9
|
-
* Appium execute commands and work on Android, iOS, and remote WebDriver
|
|
10
|
-
* sessions.
|
|
11
|
-
*/
|
|
12
2
|
export default function clipboard(server: FastMCP): void;
|
|
13
3
|
//# sourceMappingURL=clipboard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../../src/tools/interactions/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../../src/tools/interactions/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AA0DtD,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAiCvD"}
|
|
@@ -1,84 +1,65 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { getClipboard, setClipboard } from '../../command.js';
|
|
3
3
|
import { resolveDriver, textResult, errorResult, toolErrorMessage, } from '../tool-response.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
const schema = z.object({
|
|
5
|
+
action: z
|
|
6
|
+
.enum(['get', 'set'])
|
|
7
|
+
.describe('get: read device clipboard as plain text. set: write plain text to the clipboard.'),
|
|
8
|
+
content: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Required when action is set. Plain text to put on the clipboard.'),
|
|
12
|
+
sessionId: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
16
|
+
});
|
|
17
|
+
async function handleGet(sessionId) {
|
|
18
|
+
const resolved = resolveDriver(sessionId);
|
|
19
|
+
if (!resolved.ok) {
|
|
20
|
+
return resolved.result;
|
|
21
|
+
}
|
|
22
|
+
const { driver } = resolved;
|
|
23
|
+
const text = await getClipboard(driver);
|
|
24
|
+
if (!text) {
|
|
25
|
+
return textResult('Clipboard is empty.');
|
|
26
|
+
}
|
|
27
|
+
return textResult(`Clipboard content: ${text}`);
|
|
28
|
+
}
|
|
29
|
+
async function handleSet(sessionId, content) {
|
|
30
|
+
const resolved = resolveDriver(sessionId);
|
|
31
|
+
if (!resolved.ok) {
|
|
32
|
+
return resolved.result;
|
|
33
|
+
}
|
|
34
|
+
const { driver } = resolved;
|
|
35
|
+
await setClipboard(driver, content);
|
|
36
|
+
return textResult(`Successfully set clipboard content to: ${content}`);
|
|
37
|
+
}
|
|
14
38
|
export default function clipboard(server) {
|
|
15
|
-
// ─── Get Clipboard ────────────────────────────────────────────────────────
|
|
16
|
-
server.addTool({
|
|
17
|
-
name: 'appium_mobile_get_clipboard',
|
|
18
|
-
description: 'Get the current clipboard content as plain text from the device. ' +
|
|
19
|
-
'Works on Android (UiAutomator2) and iOS (XCUITest). ' +
|
|
20
|
-
'Returns an empty string if the clipboard is empty.',
|
|
21
|
-
parameters: z.object({
|
|
22
|
-
sessionId: z
|
|
23
|
-
.string()
|
|
24
|
-
.optional()
|
|
25
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
26
|
-
}),
|
|
27
|
-
annotations: {
|
|
28
|
-
readOnlyHint: true,
|
|
29
|
-
openWorldHint: false,
|
|
30
|
-
},
|
|
31
|
-
execute: async (args, _context) => {
|
|
32
|
-
const resolved = resolveDriver(args.sessionId);
|
|
33
|
-
if (!resolved.ok) {
|
|
34
|
-
return resolved.result;
|
|
35
|
-
}
|
|
36
|
-
const { driver } = resolved;
|
|
37
|
-
try {
|
|
38
|
-
const content = await getClipboard(driver);
|
|
39
|
-
if (!content) {
|
|
40
|
-
return textResult('Clipboard is empty.');
|
|
41
|
-
}
|
|
42
|
-
return textResult(`Clipboard content: ${content}`);
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
return errorResult(`Failed to get clipboard content. err: ${toolErrorMessage(err)}`);
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
// ─── Set Clipboard ────────────────────────────────────────────────────────
|
|
50
|
-
const setClipboardSchema = z.object({
|
|
51
|
-
content: z
|
|
52
|
-
.string()
|
|
53
|
-
.describe('The plain text content to write to the device clipboard'),
|
|
54
|
-
sessionId: z
|
|
55
|
-
.string()
|
|
56
|
-
.optional()
|
|
57
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
58
|
-
});
|
|
59
39
|
server.addTool({
|
|
60
|
-
name: '
|
|
61
|
-
description: '
|
|
62
|
-
'
|
|
63
|
-
|
|
64
|
-
'or for injecting long strings without typing them character by character.',
|
|
65
|
-
parameters: setClipboardSchema,
|
|
40
|
+
name: 'appium_mobile_clipboard',
|
|
41
|
+
description: 'Read or set the device clipboard as plain text (Android UiAutomator2 / iOS XCUITest). ' +
|
|
42
|
+
'action=get returns current text; action=set requires content.',
|
|
43
|
+
parameters: schema,
|
|
66
44
|
annotations: {
|
|
67
45
|
readOnlyHint: false,
|
|
68
46
|
openWorldHint: false,
|
|
69
47
|
},
|
|
70
48
|
execute: async (args, _context) => {
|
|
71
|
-
const resolved = resolveDriver(args.sessionId);
|
|
72
|
-
if (!resolved.ok) {
|
|
73
|
-
return resolved.result;
|
|
74
|
-
}
|
|
75
|
-
const { driver } = resolved;
|
|
76
49
|
try {
|
|
77
|
-
|
|
78
|
-
|
|
50
|
+
switch (args.action) {
|
|
51
|
+
case 'get':
|
|
52
|
+
return await handleGet(args.sessionId);
|
|
53
|
+
case 'set': {
|
|
54
|
+
if (args.content === undefined) {
|
|
55
|
+
return errorResult('content is required for set action');
|
|
56
|
+
}
|
|
57
|
+
return await handleSet(args.sessionId, args.content);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
79
60
|
}
|
|
80
61
|
catch (err) {
|
|
81
|
-
return errorResult(`Failed to
|
|
62
|
+
return errorResult(`Failed to ${args.action} clipboard. err: ${toolErrorMessage(err)}`);
|
|
82
63
|
}
|
|
83
64
|
},
|
|
84
65
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../../src/tools/interactions/clipboard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,aAAa,EACb,UAAU,EACV,WAAW,EACX,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAE7B
|
|
1
|
+
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../../src/tools/interactions/clipboard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,aAAa,EACb,UAAU,EACV,WAAW,EACX,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;SACpB,QAAQ,CACP,mFAAmF,CACpF;IACH,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,kEAAkE,CACnE;IACH,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,4DAA4D,CAAC;CAC1E,CAAC,CAAC;AAIH,KAAK,UAAU,SAAS,CAAC,SAAkB;IACzC,MAAM,QAAQ,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,QAAQ,CAAC,MAAM,CAAC;IACzB,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;IAE5B,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,UAAU,CAAC,qBAAqB,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,UAAU,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,SAA6B,EAC7B,OAAe;IAEf,MAAM,QAAQ,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,QAAQ,CAAC,MAAM,CAAC;IACzB,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;IAE5B,MAAM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,UAAU,CAAC,0CAA0C,OAAO,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,MAAe;IAC/C,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EACT,wFAAwF;YACxF,+DAA+D;QACjE,UAAU,EAAE,MAAM;QAClB,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,aAAa,EAAE,KAAK;SACrB;QACD,OAAO,EAAE,KAAK,EACZ,IAAmB,EACnB,QAA6C,EACrB,EAAE;YAC1B,IAAI,CAAC;gBACH,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;oBACpB,KAAK,KAAK;wBACR,OAAO,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACzC,KAAK,KAAK,CAAC,CAAC,CAAC;wBACX,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;4BAC/B,OAAO,WAAW,CAAC,oCAAoC,CAAC,CAAC;wBAC3D,CAAC;wBACD,OAAO,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;oBACvD,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,OAAO,WAAW,CAChB,aAAa,IAAI,CAAC,MAAM,oBAAoB,gBAAgB,CAAC,GAAG,CAAC,EAAE,CACpE,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
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.68.0",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "appium-mcp",
|
|
11
|
-
"version": "1.
|
|
11
|
+
"version": "1.68.0",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ async function startServer(): Promise<void> {
|
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
16
|
if (useHttpStream) {
|
|
17
|
-
server.start({
|
|
17
|
+
await server.start({
|
|
18
18
|
transportType: 'httpStream',
|
|
19
19
|
httpStream: {
|
|
20
20
|
endpoint: '/sse',
|
|
@@ -28,7 +28,7 @@ async function startServer(): Promise<void> {
|
|
|
28
28
|
log.info('Waiting for client connections...');
|
|
29
29
|
} else {
|
|
30
30
|
// Start with stdio transport
|
|
31
|
-
server.start({
|
|
31
|
+
await server.start({
|
|
32
32
|
transportType: 'stdio',
|
|
33
33
|
});
|
|
34
34
|
|
|
Binary file
|
|
@@ -15,6 +15,122 @@ import {
|
|
|
15
15
|
import type { GestureArgs } from '../schema.js';
|
|
16
16
|
|
|
17
17
|
const DEFAULT_VELOCITY = 2.2;
|
|
18
|
+
const DEFAULT_PINCH_SPREAD_RATIO = 0.3;
|
|
19
|
+
const MIN_ELEMENT_PINCH_SPREAD_RATIO = 0.15;
|
|
20
|
+
|
|
21
|
+
type RectLike = {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type PinchTarget = {
|
|
29
|
+
cx: number;
|
|
30
|
+
cy: number;
|
|
31
|
+
spread: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function clamp(value: number, min: number, max: number): number {
|
|
35
|
+
return Math.min(max, Math.max(min, value));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function maxSpreadForCenter(
|
|
39
|
+
cx: number,
|
|
40
|
+
cy: number,
|
|
41
|
+
windowRect: RectLike
|
|
42
|
+
): number {
|
|
43
|
+
const left = windowRect.x ?? 0;
|
|
44
|
+
const top = windowRect.y ?? 0;
|
|
45
|
+
const right = left + windowRect.width - 1;
|
|
46
|
+
const bottom = top + windowRect.height - 1;
|
|
47
|
+
|
|
48
|
+
return Math.max(1, Math.min(cx - left, right - cx, cy - top, bottom - cy));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function resolveElementPinchTarget(
|
|
52
|
+
elementRect: RectLike,
|
|
53
|
+
windowRect: RectLike
|
|
54
|
+
): PinchTarget {
|
|
55
|
+
const windowLeft = windowRect.x ?? 0;
|
|
56
|
+
const windowTop = windowRect.y ?? 0;
|
|
57
|
+
const windowRight = windowLeft + windowRect.width;
|
|
58
|
+
const windowBottom = windowTop + windowRect.height;
|
|
59
|
+
|
|
60
|
+
const visibleLeft = Math.max(windowLeft, elementRect.x);
|
|
61
|
+
const visibleTop = Math.max(windowTop, elementRect.y);
|
|
62
|
+
const visibleRight = Math.min(windowRight, elementRect.x + elementRect.width);
|
|
63
|
+
const visibleBottom = Math.min(
|
|
64
|
+
windowBottom,
|
|
65
|
+
elementRect.y + elementRect.height
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const rawCx =
|
|
69
|
+
visibleRight > visibleLeft
|
|
70
|
+
? visibleLeft + (visibleRight - visibleLeft) / 2
|
|
71
|
+
: elementRect.x + elementRect.width / 2;
|
|
72
|
+
const rawCy =
|
|
73
|
+
visibleBottom > visibleTop
|
|
74
|
+
? visibleTop + (visibleBottom - visibleTop) / 2
|
|
75
|
+
: elementRect.y + elementRect.height / 2;
|
|
76
|
+
|
|
77
|
+
const cx = Math.floor(clamp(rawCx, windowLeft, windowRight - 1));
|
|
78
|
+
const cy = Math.floor(clamp(rawCy, windowTop, windowBottom - 1));
|
|
79
|
+
const windowBase = Math.min(windowRect.width, windowRect.height);
|
|
80
|
+
const elementBase = Math.min(elementRect.width, elementRect.height);
|
|
81
|
+
const desired = Math.max(
|
|
82
|
+
Math.floor(elementBase * DEFAULT_PINCH_SPREAD_RATIO),
|
|
83
|
+
Math.floor(windowBase * MIN_ELEMENT_PINCH_SPREAD_RATIO)
|
|
84
|
+
);
|
|
85
|
+
const cappedDesired = Math.min(
|
|
86
|
+
desired,
|
|
87
|
+
Math.floor(windowBase * DEFAULT_PINCH_SPREAD_RATIO)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
cx,
|
|
92
|
+
cy,
|
|
93
|
+
spread: Math.min(cappedDesired, maxSpreadForCenter(cx, cy, windowRect)),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveWindowPinchTarget(windowRect: RectLike): PinchTarget {
|
|
98
|
+
const cx = Math.floor((windowRect.x ?? 0) + windowRect.width / 2);
|
|
99
|
+
const cy = Math.floor((windowRect.y ?? 0) + windowRect.height / 2);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
cx,
|
|
103
|
+
cy,
|
|
104
|
+
spread: Math.floor(
|
|
105
|
+
Math.min(windowRect.width, windowRect.height) * DEFAULT_PINCH_SPREAD_RATIO
|
|
106
|
+
),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function resolveCoordinatePinchTarget(
|
|
111
|
+
x: number,
|
|
112
|
+
y: number,
|
|
113
|
+
windowRect: RectLike
|
|
114
|
+
): PinchTarget | string {
|
|
115
|
+
const left = windowRect.x ?? 0;
|
|
116
|
+
const top = windowRect.y ?? 0;
|
|
117
|
+
const right = left + windowRect.width;
|
|
118
|
+
const bottom = top + windowRect.height;
|
|
119
|
+
|
|
120
|
+
if (x < left || x >= right || y < top || y >= bottom) {
|
|
121
|
+
return `pinch_zoom coordinates (${x}, ${y}) are outside window bounds (${windowRect.width}x${windowRect.height}).`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const desired = Math.floor(
|
|
125
|
+
Math.min(windowRect.width, windowRect.height) * DEFAULT_PINCH_SPREAD_RATIO
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
cx: x,
|
|
130
|
+
cy: y,
|
|
131
|
+
spread: Math.min(desired, maxSpreadForCenter(x, y, windowRect)),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
18
134
|
|
|
19
135
|
export async function handlePinchZoom(
|
|
20
136
|
driver: DriverInstance,
|
|
@@ -28,6 +144,14 @@ export async function handlePinchZoom(
|
|
|
28
144
|
const scale = args.scale;
|
|
29
145
|
const velocity = args.velocity ?? DEFAULT_VELOCITY;
|
|
30
146
|
|
|
147
|
+
if (!args.elementUUID && (args.x !== undefined) !== (args.y !== undefined)) {
|
|
148
|
+
return errorResult(
|
|
149
|
+
'pinch_zoom requires both x and y when using custom coordinates.'
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
const useCustomCoords =
|
|
153
|
+
!args.elementUUID && args.x !== undefined && args.y !== undefined;
|
|
154
|
+
|
|
31
155
|
try {
|
|
32
156
|
const platform = getPlatformName(driver);
|
|
33
157
|
|
|
@@ -37,23 +161,69 @@ export async function handlePinchZoom(
|
|
|
37
161
|
let windowRect: Awaited<ReturnType<typeof getWindowRect>> | null = null;
|
|
38
162
|
|
|
39
163
|
if (args.elementUUID) {
|
|
164
|
+
windowRect = await getWindowRect(driver);
|
|
40
165
|
const rect = await getElementRect(driver, args.elementUUID);
|
|
41
|
-
cx =
|
|
42
|
-
|
|
43
|
-
|
|
166
|
+
({ cx, cy, spread } = resolveElementPinchTarget(rect, windowRect));
|
|
167
|
+
} else if (useCustomCoords) {
|
|
168
|
+
windowRect = await getWindowRect(driver);
|
|
169
|
+
const target = resolveCoordinatePinchTarget(args.x!, args.y!, windowRect);
|
|
170
|
+
if (typeof target === 'string') {
|
|
171
|
+
return errorResult(target);
|
|
172
|
+
}
|
|
173
|
+
({ cx, cy, spread } = target);
|
|
44
174
|
} else {
|
|
45
175
|
windowRect = await getWindowRect(driver);
|
|
46
|
-
cx =
|
|
47
|
-
cy = Math.floor(windowRect.height / 2);
|
|
48
|
-
spread = Math.floor(Math.min(windowRect.width, windowRect.height) * 0.3);
|
|
176
|
+
({ cx, cy, spread } = resolveWindowPinchTarget(windowRect));
|
|
49
177
|
}
|
|
50
178
|
|
|
51
179
|
if (scale < 1) {
|
|
52
|
-
// Zoom out:
|
|
180
|
+
// Zoom out: W3C Actions on all platforms; fingers move together.
|
|
53
181
|
const startSpread = spread;
|
|
54
182
|
const endSpread = Math.max(1, Math.floor(spread * scale));
|
|
55
183
|
const duration = Math.floor((1 / Math.abs(velocity)) * 1000);
|
|
56
184
|
|
|
185
|
+
await performActions(driver, [
|
|
186
|
+
{
|
|
187
|
+
type: 'pointer',
|
|
188
|
+
id: 'finger1',
|
|
189
|
+
parameters: { pointerType: 'touch' },
|
|
190
|
+
actions: [
|
|
191
|
+
{ type: 'pointerMove', duration: 0, x: cx - startSpread, y: cy },
|
|
192
|
+
{ type: 'pointerDown', button: 0 },
|
|
193
|
+
{ type: 'pointerMove', duration, x: cx - endSpread, y: cy },
|
|
194
|
+
{ type: 'pointerUp', button: 0 },
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
type: 'pointer',
|
|
199
|
+
id: 'finger2',
|
|
200
|
+
parameters: { pointerType: 'touch' },
|
|
201
|
+
actions: [
|
|
202
|
+
{ type: 'pointerMove', duration: 0, x: cx + startSpread, y: cy },
|
|
203
|
+
{ type: 'pointerDown', button: 0 },
|
|
204
|
+
{ type: 'pointerMove', duration, x: cx + endSpread, y: cy },
|
|
205
|
+
{ type: 'pointerUp', button: 0 },
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
} else if (useCustomCoords && platform === PLATFORM.android) {
|
|
210
|
+
// Zoom in at a custom center on Android: use the native pinchOpenGesture
|
|
211
|
+
// with a region centered at (cx, cy). spread is pre-clamped so the region
|
|
212
|
+
// fits within the window.
|
|
213
|
+
const percent = Math.min(0.99, 1 - 1 / scale);
|
|
214
|
+
await execute(driver, 'mobile: pinchOpenGesture', {
|
|
215
|
+
left: cx - spread,
|
|
216
|
+
top: cy - spread,
|
|
217
|
+
width: 2 * spread,
|
|
218
|
+
height: 2 * spread,
|
|
219
|
+
percent,
|
|
220
|
+
});
|
|
221
|
+
} else if (useCustomCoords) {
|
|
222
|
+
// Zoom in at a custom center on iOS using W3C Actions
|
|
223
|
+
const startSpread = Math.max(1, Math.floor(spread / scale));
|
|
224
|
+
const endSpread = spread;
|
|
225
|
+
const duration = Math.floor((1 / Math.abs(velocity)) * 1000);
|
|
226
|
+
|
|
57
227
|
await performActions(driver, [
|
|
58
228
|
{
|
|
59
229
|
type: 'pointer',
|
|
@@ -88,8 +258,8 @@ export async function handlePinchZoom(
|
|
|
88
258
|
}
|
|
89
259
|
await execute(driver, 'mobile: pinch', params);
|
|
90
260
|
} else if (platform === PLATFORM.android) {
|
|
91
|
-
// Convert scale factor to percent (0
|
|
92
|
-
// scale=2
|
|
261
|
+
// Convert scale factor to percent (0-1) for pinchOpenGesture.
|
|
262
|
+
// scale=2 -> 0.5, scale=4 -> 0.75, scale=10 -> 0.9. Capped at 0.99.
|
|
93
263
|
const percent = Math.min(0.99, 1 - 1 / scale);
|
|
94
264
|
const params: Record<string, unknown> = { percent };
|
|
95
265
|
if (args.elementUUID) {
|
|
@@ -109,7 +279,11 @@ export async function handlePinchZoom(
|
|
|
109
279
|
}
|
|
110
280
|
|
|
111
281
|
const direction = scale < 1 ? 'out' : 'in';
|
|
112
|
-
const target = args.elementUUID
|
|
282
|
+
const target = args.elementUUID
|
|
283
|
+
? `element ${args.elementUUID}`
|
|
284
|
+
: useCustomCoords
|
|
285
|
+
? `coordinates (${cx}, ${cy})`
|
|
286
|
+
: 'screen';
|
|
113
287
|
return textResult(
|
|
114
288
|
`Successfully pinched ${direction} (scale=${scale}) on ${target}.`
|
|
115
289
|
);
|
|
@@ -63,7 +63,8 @@ export const gestureSchema = z.object({
|
|
|
63
63
|
.describe(
|
|
64
64
|
`X coordinate. ` +
|
|
65
65
|
`For tap/double_tap/long_press: tap location (alternative to elementUUID). ` +
|
|
66
|
-
`For scroll/swipe: starting X for custom-coordinate mode (requires y, endX, endY)
|
|
66
|
+
`For scroll/swipe: starting X for custom-coordinate mode (requires y, endX, endY). ` +
|
|
67
|
+
`For pinch_zoom: center X of the pinch. Requires y. Ignored if elementUUID is set.`
|
|
67
68
|
),
|
|
68
69
|
y: z
|
|
69
70
|
.number()
|
|
@@ -73,7 +74,8 @@ export const gestureSchema = z.object({
|
|
|
73
74
|
.describe(
|
|
74
75
|
`Y coordinate. ` +
|
|
75
76
|
`For tap/double_tap/long_press: tap location. ` +
|
|
76
|
-
`For scroll/swipe: starting Y for custom-coordinate mode
|
|
77
|
+
`For scroll/swipe: starting Y for custom-coordinate mode. ` +
|
|
78
|
+
`For pinch_zoom: center Y of the pinch. Requires x. Ignored if elementUUID is set.`
|
|
77
79
|
),
|
|
78
80
|
endX: z
|
|
79
81
|
.number()
|
|
@@ -8,101 +8,83 @@ import {
|
|
|
8
8
|
toolErrorMessage,
|
|
9
9
|
} from '../tool-response.js';
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
const schema = z.object({
|
|
12
|
+
action: z
|
|
13
|
+
.enum(['get', 'set'])
|
|
14
|
+
.describe(
|
|
15
|
+
'get: read device clipboard as plain text. set: write plain text to the clipboard.'
|
|
16
|
+
),
|
|
17
|
+
content: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe(
|
|
21
|
+
'Required when action is set. Plain text to put on the clipboard.'
|
|
22
|
+
),
|
|
23
|
+
sessionId: z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
27
|
+
});
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
name: 'appium_mobile_get_clipboard',
|
|
26
|
-
description:
|
|
27
|
-
'Get the current clipboard content as plain text from the device. ' +
|
|
28
|
-
'Works on Android (UiAutomator2) and iOS (XCUITest). ' +
|
|
29
|
-
'Returns an empty string if the clipboard is empty.',
|
|
30
|
-
parameters: z.object({
|
|
31
|
-
sessionId: z
|
|
32
|
-
.string()
|
|
33
|
-
.optional()
|
|
34
|
-
.describe('Session ID to target. If omitted, uses the active session.'),
|
|
35
|
-
}),
|
|
36
|
-
annotations: {
|
|
37
|
-
readOnlyHint: true,
|
|
38
|
-
openWorldHint: false,
|
|
39
|
-
},
|
|
40
|
-
execute: async (
|
|
41
|
-
args: { sessionId?: string },
|
|
42
|
-
_context: Record<string, unknown> | undefined
|
|
43
|
-
): Promise<ContentResult> => {
|
|
44
|
-
const resolved = resolveDriver(args.sessionId);
|
|
45
|
-
if (!resolved.ok) {
|
|
46
|
-
return resolved.result;
|
|
47
|
-
}
|
|
48
|
-
const { driver } = resolved;
|
|
29
|
+
type ClipboardArgs = z.infer<typeof schema>;
|
|
49
30
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} catch (err: unknown) {
|
|
57
|
-
return errorResult(
|
|
58
|
-
`Failed to get clipboard content. err: ${toolErrorMessage(err)}`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
});
|
|
31
|
+
async function handleGet(sessionId?: string): Promise<ContentResult> {
|
|
32
|
+
const resolved = resolveDriver(sessionId);
|
|
33
|
+
if (!resolved.ok) {
|
|
34
|
+
return resolved.result;
|
|
35
|
+
}
|
|
36
|
+
const { driver } = resolved;
|
|
63
37
|
|
|
64
|
-
|
|
38
|
+
const text = await getClipboard(driver);
|
|
39
|
+
if (!text) {
|
|
40
|
+
return textResult('Clipboard is empty.');
|
|
41
|
+
}
|
|
42
|
+
return textResult(`Clipboard content: ${text}`);
|
|
43
|
+
}
|
|
65
44
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
45
|
+
async function handleSet(
|
|
46
|
+
sessionId: string | undefined,
|
|
47
|
+
content: string
|
|
48
|
+
): Promise<ContentResult> {
|
|
49
|
+
const resolved = resolveDriver(sessionId);
|
|
50
|
+
if (!resolved.ok) {
|
|
51
|
+
return resolved.result;
|
|
52
|
+
}
|
|
53
|
+
const { driver } = resolved;
|
|
75
54
|
|
|
55
|
+
await setClipboard(driver, content);
|
|
56
|
+
return textResult(`Successfully set clipboard content to: ${content}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default function clipboard(server: FastMCP): void {
|
|
76
60
|
server.addTool({
|
|
77
|
-
name: '
|
|
61
|
+
name: 'appium_mobile_clipboard',
|
|
78
62
|
description:
|
|
79
|
-
'
|
|
80
|
-
'
|
|
81
|
-
|
|
82
|
-
'or for injecting long strings without typing them character by character.',
|
|
83
|
-
parameters: setClipboardSchema,
|
|
63
|
+
'Read or set the device clipboard as plain text (Android UiAutomator2 / iOS XCUITest). ' +
|
|
64
|
+
'action=get returns current text; action=set requires content.',
|
|
65
|
+
parameters: schema,
|
|
84
66
|
annotations: {
|
|
85
67
|
readOnlyHint: false,
|
|
86
68
|
openWorldHint: false,
|
|
87
69
|
},
|
|
88
70
|
execute: async (
|
|
89
|
-
args:
|
|
71
|
+
args: ClipboardArgs,
|
|
90
72
|
_context: Record<string, unknown> | undefined
|
|
91
73
|
): Promise<ContentResult> => {
|
|
92
|
-
const resolved = resolveDriver(args.sessionId);
|
|
93
|
-
if (!resolved.ok) {
|
|
94
|
-
return resolved.result;
|
|
95
|
-
}
|
|
96
|
-
const { driver } = resolved;
|
|
97
|
-
|
|
98
74
|
try {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
75
|
+
switch (args.action) {
|
|
76
|
+
case 'get':
|
|
77
|
+
return await handleGet(args.sessionId);
|
|
78
|
+
case 'set': {
|
|
79
|
+
if (args.content === undefined) {
|
|
80
|
+
return errorResult('content is required for set action');
|
|
81
|
+
}
|
|
82
|
+
return await handleSet(args.sessionId, args.content);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
103
85
|
} catch (err: unknown) {
|
|
104
86
|
return errorResult(
|
|
105
|
-
`Failed to
|
|
87
|
+
`Failed to ${args.action} clipboard. err: ${toolErrorMessage(err)}`
|
|
106
88
|
);
|
|
107
89
|
}
|
|
108
90
|
},
|