appium-mcp 1.67.1 → 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 +10 -0
- package/README.md +1 -2
- package/dist/index.js +2 -2
- package/dist/index.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/interactions/clipboard.ts +60 -78
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
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
|
+
|
|
1
11
|
## [1.67.1](https://github.com/appium/appium-mcp/compare/v1.67.0...v1.67.1) (2026-04-27)
|
|
2
12
|
|
|
3
13
|
### Bug Fixes
|
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,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
|
|
@@ -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
|
},
|