appium-mcp 1.32.0 → 1.33.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 CHANGED
@@ -1,3 +1,9 @@
1
+ ## [1.33.0](https://github.com/appium/appium-mcp/compare/v1.32.0...v1.33.0) (2026-03-22)
2
+
3
+ ### Features
4
+
5
+ * **session:** add appium_mobile_push_file and appium_mobile_pull_file ([#222](https://github.com/appium/appium-mcp/issues/222)) ([8eb1f8d](https://github.com/appium/appium-mcp/commit/8eb1f8dab342b844a54aa90f0744f7586a1d9dc7))
6
+
1
7
  ## [1.32.0](https://github.com/appium/appium-mcp/compare/v1.31.0...v1.32.0) (2026-03-21)
2
8
 
3
9
  ### Features
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=file-transfer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-transfer.test.d.ts","sourceRoot":"","sources":["../../../../src/tests/tools/session/file-transfer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,87 @@
1
+ import { describe, test, expect, jest, beforeEach } from '@jest/globals';
2
+ jest.unstable_mockModule('../../../session-store', () => ({
3
+ getDriver: jest.fn(),
4
+ getPlatformName: jest.fn(),
5
+ PLATFORM: { ios: 'iOS', android: 'Android' },
6
+ }));
7
+ jest.unstable_mockModule('../../../command', () => ({
8
+ execute: jest.fn(),
9
+ }));
10
+ jest.unstable_mockModule('../../../logger', () => ({
11
+ default: { debug: () => { }, info: () => { }, warn: () => { }, error: () => { } },
12
+ }));
13
+ const { getDriver, getPlatformName, PLATFORM } = await import('../../../session-store.js');
14
+ const { execute } = await import('../../../command.js');
15
+ const mockGetDriver = getDriver;
16
+ const mockGetPlatformName = getPlatformName;
17
+ const mockExecute = execute;
18
+ describe('appium_mobile_push_file / appium_mobile_pull_file', () => {
19
+ const mockServer = { addTool: jest.fn() };
20
+ beforeEach(() => {
21
+ mockServer.addTool.mockClear();
22
+ mockExecute.mockReset();
23
+ });
24
+ test('push: throws when no driver is active', async () => {
25
+ const { pushFile } = await import('../../../tools/session/file-transfer.js');
26
+ mockGetDriver.mockReturnValue(null);
27
+ pushFile(mockServer);
28
+ const tool = mockServer.addTool.mock.calls.at(-1)?.[0];
29
+ await expect(tool.execute({ remotePath: '/sdcard/x.txt', payloadBase64: 'YQ==' }, undefined)).rejects.toThrow('No driver found');
30
+ });
31
+ test('push: Android uses path and data', async () => {
32
+ const { pushFile } = await import('../../../tools/session/file-transfer.js');
33
+ mockGetDriver.mockReturnValue({});
34
+ mockGetPlatformName.mockReturnValue(PLATFORM.android);
35
+ mockExecute.mockResolvedValue(undefined);
36
+ pushFile(mockServer);
37
+ const tool = mockServer.addTool.mock.calls.at(-1)?.[0];
38
+ await tool.execute({ remotePath: '/data/local/tmp/a.txt', payloadBase64: 'SGVsbG8=' }, undefined);
39
+ expect(mockExecute).toHaveBeenCalledWith(expect.anything(), 'mobile: pushFile', expect.objectContaining({
40
+ path: '/data/local/tmp/a.txt',
41
+ data: 'SGVsbG8=',
42
+ }));
43
+ });
44
+ test('push: iOS uses remotePath and payload', async () => {
45
+ const { pushFile } = await import('../../../tools/session/file-transfer.js');
46
+ mockGetDriver.mockReturnValue({});
47
+ mockGetPlatformName.mockReturnValue(PLATFORM.ios);
48
+ mockExecute.mockResolvedValue(undefined);
49
+ pushFile(mockServer);
50
+ const tool = mockServer.addTool.mock.calls.at(-1)?.[0];
51
+ await tool.execute({
52
+ remotePath: '@com.example.app:documents/x.txt',
53
+ payloadBase64: 'QQ==',
54
+ }, undefined);
55
+ expect(mockExecute).toHaveBeenCalledWith(expect.anything(), 'mobile: pushFile', expect.objectContaining({
56
+ remotePath: '@com.example.app:documents/x.txt',
57
+ payload: 'QQ==',
58
+ }));
59
+ });
60
+ test('pull: Android uses path', async () => {
61
+ const { pullFile } = await import('../../../tools/session/file-transfer.js');
62
+ mockGetDriver.mockReturnValue({});
63
+ mockGetPlatformName.mockReturnValue(PLATFORM.android);
64
+ mockExecute.mockResolvedValue('YmJiYg==');
65
+ pullFile(mockServer);
66
+ const tool = mockServer.addTool.mock.calls.at(-1)?.[0];
67
+ const result = await tool.execute({ remotePath: '/sdcard/Download/out.bin' }, undefined);
68
+ expect(mockExecute).toHaveBeenCalledWith(expect.anything(), 'mobile: pullFile', expect.objectContaining({ path: '/sdcard/Download/out.bin' }));
69
+ const parsed = JSON.parse(result.content[0].text);
70
+ expect(parsed.contentBase64).toBe('YmJiYg==');
71
+ expect(parsed.platform).toBe('Android');
72
+ });
73
+ test('pull: iOS uses remotePath', async () => {
74
+ const { pullFile } = await import('../../../tools/session/file-transfer.js');
75
+ mockGetDriver.mockReturnValue({});
76
+ mockGetPlatformName.mockReturnValue(PLATFORM.ios);
77
+ mockExecute.mockResolvedValue('eHh4');
78
+ pullFile(mockServer);
79
+ const tool = mockServer.addTool.mock.calls.at(-1)?.[0];
80
+ const result = await tool.execute({ remotePath: '@com.app:documents/f.txt' }, undefined);
81
+ expect(mockExecute).toHaveBeenCalledWith(expect.anything(), 'mobile: pullFile', expect.objectContaining({ remotePath: '@com.app:documents/f.txt' }));
82
+ const parsed = JSON.parse(result.content[0].text);
83
+ expect(parsed.contentBase64).toBe('eHh4');
84
+ expect(parsed.platform).toBe('iOS');
85
+ });
86
+ });
87
+ //# sourceMappingURL=file-transfer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-transfer.test.js","sourceRoot":"","sources":["../../../../src/tests/tools/session/file-transfer.test.ts"],"names":[],"mappings":"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"}
@@ -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;AAoDlC,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAyI3D"}
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;AAqDlC,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CA2I3D"}
@@ -12,6 +12,7 @@ import { lockDevice, unlockDevice } from './session/lock.js';
12
12
  import { setGeolocation, getGeolocation, resetGeolocation, } from './session/geolocation.js';
13
13
  import deviceInfo from './session/device-info.js';
14
14
  import batteryInfo from './session/battery-info.js';
15
+ import { pushFile, pullFile } from './session/file-transfer.js';
15
16
  import bootSimulator from './ios/boot-simulator.js';
16
17
  import setupWDA from './ios/setup-wda.js';
17
18
  import installWDA from './ios/install-wda.js';
@@ -121,6 +122,8 @@ export default function registerTools(server) {
121
122
  resetGeolocation(server);
122
123
  deviceInfo(server);
123
124
  batteryInfo(server);
125
+ pushFile(server);
126
+ pullFile(server);
124
127
  // iOS Setup
125
128
  bootSimulator(server);
126
129
  setupWDA(server);
@@ -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,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,cAAc,MAAM,8BAA8B,CAAC;AAC1D,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,iBAAiB,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EACL,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,WAAW,MAAM,2BAA2B,CAAC;AACpD,OAAO,aAAa,MAAM,yBAAyB,CAAC;AACpD,OAAO,QAAQ,MAAM,oBAAoB,CAAC;AAC1C,OAAO,UAAU,MAAM,sBAAsB,CAAC;AAC9C,OAAO,YAAY,MAAM,qCAAqC,CAAC;AAC/D,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAC7C,OAAO,eAAe,MAAM,oCAAoC,CAAC;AACjE,OAAO,KAAK,MAAM,wBAAwB,CAAC;AAC3C,OAAO,WAAW,MAAM,wBAAwB,CAAC;AACjD,OAAO,GAAG,MAAM,uBAAuB,CAAC;AACxC,OAAO,YAAY,MAAM,yBAAyB,CAAC;AACnD,OAAO,SAAS,MAAM,8BAA8B,CAAC;AACrD,OAAO,SAAS,MAAM,8BAA8B,CAAC;AACrD,OAAO,WAAW,MAAM,iCAAiC,CAAC;AAC1D,OAAO,KAAK,MAAM,yBAAyB,CAAC;AAC5C,OAAO,QAAQ,MAAM,6BAA6B,CAAC;AACnD,OAAO,QAAQ,MAAM,6BAA6B,CAAC;AACnD,OAAO,OAAO,MAAM,4BAA4B,CAAC;AACjD,OAAO,gBAAgB,MAAM,kCAAkC,CAAC;AAChE,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,SAAS,MAAM,6BAA6B,CAAC;AACpD,OAAO,WAAW,MAAM,gCAAgC,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAC3D,OAAO,UAAU,MAAM,iCAAiC,CAAC;AACzD,OAAO,YAAY,MAAM,mCAAmC,CAAC;AAC7D,OAAO,YAAY,MAAM,mCAAmC,CAAC;AAC7D,OAAO,QAAQ,MAAM,+BAA+B,CAAC;AACrD,OAAO,cAAc,MAAM,sCAAsC,CAAC;AAClE,OAAO,QAAQ,MAAM,+BAA+B,CAAC;AACrD,OAAO,WAAW,MAAM,2BAA2B,CAAC;AACpD,OAAO,aAAa,MAAM,6BAA6B,CAAC;AAExD,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,MAAe;IACnD,uDAAuD;IACvD,MAAM,eAAe,GAAI,MAAc,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5D,MAAc,CAAC,OAAO,GAAG,CAAC,OAAY,EAAE,EAAE;QACzC,MAAM,QAAQ,GAAG,OAAO,EAAE,IAAI,IAAI,cAAc,CAAC;QACjD,MAAM,eAAe,GAAG,OAAO,EAAE,OAAO,CAAC;QACzC,IAAI,OAAO,eAAe,KAAK,UAAU,EAAE,CAAC;YAC1C,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,MAAM,cAAc,GAAG;YACrB,UAAU;YACV,OAAO;YACP,aAAa;YACb,eAAe;YACf,QAAQ;YACR,QAAQ;YACR,QAAQ;YACR,cAAc;SACf,CAAC;QACF,MAAM,UAAU,GAAG,CAAC,GAAQ,EAAE,EAAE;YAC9B,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CACf,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;oBACjC,IACE,GAAG;wBACH,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EACzD,CAAC;wBACD,OAAO,YAAY,CAAC;oBACtB,CAAC;oBACD,gDAAgD;oBAChD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;wBAC9D,OAAO,WAAW,KAAK,CAAC,MAAM,GAAG,CAAC;oBACpC,CAAC;oBACD,IACE,KAAK;wBACL,OAAO,MAAM,KAAK,WAAW;wBAC7B,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EACtB,CAAC;wBACD,OAAO,WAAY,KAAgB,CAAC,MAAM,GAAG,CAAC;oBAChD,CAAC;oBACD,OAAO,KAAK,CAAC;gBACf,CAAC,CAAC,CACH,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,uBAAuB,CAAC;YACjC,CAAC;QACH,CAAC,CAAC;QACF,OAAO,eAAe,CAAC;YACrB,GAAG,OAAO;YACV,OAAO,EAAE,KAAK,EAAE,IAAS,EAAE,OAAY,EAAE,EAAE;gBACzC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACzB,GAAG,CAAC,IAAI,CAAC,gBAAgB,QAAQ,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;gBACvD,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;oBACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;oBACpC,GAAG,CAAC,IAAI,CAAC,cAAc,QAAQ,KAAK,QAAQ,KAAK,CAAC,CAAC;oBACnD,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAAC,OAAO,GAAQ,EAAE,CAAC;oBAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;oBACpC,MAAM,GAAG,GAAG,GAAG,EAAE,KAAK,IAAI,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;oBACtD,GAAG,CAAC,KAAK,CAAC,gBAAgB,QAAQ,KAAK,QAAQ,QAAQ,GAAG,EAAE,CAAC,CAAC;oBAC9D,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,qBAAqB;IACrB,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,UAAU,CAAC,MAAM,CAAC,CAAC;IACnB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,UAAU,CAAC,MAAM,CAAC,CAAC;IACnB,WAAW,CAAC,MAAM,CAAC,CAAC;IAEpB,YAAY;IACZ,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjB,UAAU,CAAC,MAAM,CAAC,CAAC;IAEnB,aAAa;IACb,MAAM,CAAC,MAAM,CAAC,CAAC;IACf,eAAe,CAAC,MAAM,CAAC,CAAC;IACxB,KAAK,CAAC,MAAM,CAAC,CAAC;IAEd,uBAAuB;IACvB,qCAAqC;IACrC,8EAA8E;IAC9E,sEAAsE;IACtE,mFAAmF;IACnF,GAAG,CAAC,MAAM,CAAC,CAAC;IACZ,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,KAAK,CAAC,MAAM,CAAC,CAAC;IACd,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjB,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjB,OAAO,CAAC,MAAM,CAAC,CAAC;IAChB,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,UAAU,CAAC,MAAM,CAAC,CAAC;IACnB,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAE1B,iBAAiB;IACjB,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,UAAU,CAAC,MAAM,CAAC,CAAC;IACnB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjB,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEjB,qBAAqB;IACrB,WAAW,CAAC,MAAM,CAAC,CAAC;IACpB,aAAa,CAAC,MAAM,CAAC,CAAC;IAEtB,kBAAkB;IAClB,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,YAAY,CAAC,MAAM,CAAC,CAAC;IAErB,gBAAgB;IAChB,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { FastMCP } from 'fastmcp';
2
+ /**
3
+ * Push a file from the host (MCP client) to the device via `mobile: pushFile`.
4
+ *
5
+ * - Android: `{ path, data }` per UiAutomator2 / legacy JSONWP push_file.
6
+ * - iOS: `{ remotePath, payload }` per XCUITest execute-methods reference.
7
+ */
8
+ export declare function pushFile(server: FastMCP): void;
9
+ /**
10
+ * Pull a file from the device via `mobile: pullFile`. Returns Base64-encoded content in the response text.
11
+ */
12
+ export declare function pullFile(server: FastMCP): void;
13
+ //# sourceMappingURL=file-transfer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-transfer.d.ts","sourceRoot":"","sources":["../../../src/tools/session/file-transfer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AAiCtD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAkE9C;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAqE9C"}
@@ -0,0 +1,159 @@
1
+ import { z } from 'zod';
2
+ import { getDriver, getPlatformName, PLATFORM } from '../../session-store.js';
3
+ import { execute } from '../../command.js';
4
+ /**
5
+ * Normalize the return value of mobile: pullFile (driver may return a string
6
+ * or a wrapped value depending on client/driver).
7
+ */
8
+ function normalizePullResult(result) {
9
+ if (typeof result === 'string') {
10
+ return result;
11
+ }
12
+ if (result &&
13
+ typeof result === 'object' &&
14
+ 'value' in result &&
15
+ typeof result.value === 'string') {
16
+ return result.value;
17
+ }
18
+ return String(result ?? '');
19
+ }
20
+ const remotePathDescription = 'Path to the file on the device. ' +
21
+ 'Android (UiAutomator2): use an absolute path (e.g. /data/local/tmp/foo.txt or /sdcard/Download/foo.txt). ' +
22
+ 'iOS (XCUITest): use the formats described in the Appium XCUITest file transfer guide ' +
23
+ '(e.g. @com.example.app:documents/file.txt or simulator-relative paths).';
24
+ const payloadDescription = 'File contents encoded as Base64 (raw base64 only; do not include a data: URL prefix).';
25
+ /**
26
+ * Push a file from the host (MCP client) to the device via `mobile: pushFile`.
27
+ *
28
+ * - Android: `{ path, data }` per UiAutomator2 / legacy JSONWP push_file.
29
+ * - iOS: `{ remotePath, payload }` per XCUITest execute-methods reference.
30
+ */
31
+ export function pushFile(server) {
32
+ const schema = z.object({
33
+ remotePath: z.string().min(1).describe(remotePathDescription),
34
+ payloadBase64: z.string().min(1).describe(payloadDescription),
35
+ });
36
+ server.addTool({
37
+ name: 'appium_mobile_push_file',
38
+ description: 'Push a file to the device using the Appium `mobile: pushFile` extension. ' +
39
+ 'Android uses `path` + `data` (base64). iOS uses `remotePath` + `payload` (base64). ' +
40
+ 'Path semantics on iOS follow the XCUITest file transfer guide (app containers, documents, simulator paths). ' +
41
+ 'Large payloads produce large requests; avoid pushing huge files through MCP.',
42
+ parameters: schema,
43
+ annotations: {
44
+ readOnlyHint: false,
45
+ openWorldHint: false,
46
+ },
47
+ execute: async (args, _context) => {
48
+ const driver = getDriver();
49
+ if (!driver) {
50
+ throw new Error('No driver found');
51
+ }
52
+ try {
53
+ const platform = getPlatformName(driver);
54
+ if (platform === PLATFORM.android) {
55
+ await execute(driver, 'mobile: pushFile', {
56
+ path: args.remotePath,
57
+ data: args.payloadBase64,
58
+ });
59
+ }
60
+ else if (platform === PLATFORM.ios) {
61
+ await execute(driver, 'mobile: pushFile', {
62
+ remotePath: args.remotePath,
63
+ payload: args.payloadBase64,
64
+ });
65
+ }
66
+ else {
67
+ throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
68
+ }
69
+ return {
70
+ content: [
71
+ {
72
+ type: 'text',
73
+ text: `Successfully pushed file to device path: ${args.remotePath}`,
74
+ },
75
+ ],
76
+ };
77
+ }
78
+ catch (err) {
79
+ const message = err instanceof Error ? err.message : String(err);
80
+ return {
81
+ content: [
82
+ {
83
+ type: 'text',
84
+ text: `Failed to push file. err: ${message}`,
85
+ },
86
+ ],
87
+ };
88
+ }
89
+ },
90
+ });
91
+ }
92
+ /**
93
+ * Pull a file from the device via `mobile: pullFile`. Returns Base64-encoded content in the response text.
94
+ */
95
+ export function pullFile(server) {
96
+ const schema = z.object({
97
+ remotePath: z.string().min(1).describe(remotePathDescription),
98
+ });
99
+ server.addTool({
100
+ name: 'appium_mobile_pull_file',
101
+ description: 'Pull a file from the device using the Appium `mobile: pullFile` extension. ' +
102
+ 'Returns Base64-encoded file content in the response text. ' +
103
+ 'Android uses parameter `path`; iOS uses `remotePath` with the same path formats as push. ' +
104
+ 'Very large files may produce very large responses; prefer downloading or streaming outside MCP for big binaries.',
105
+ parameters: schema,
106
+ annotations: {
107
+ readOnlyHint: true,
108
+ openWorldHint: false,
109
+ },
110
+ execute: async (args, _context) => {
111
+ const driver = getDriver();
112
+ if (!driver) {
113
+ throw new Error('No driver found');
114
+ }
115
+ try {
116
+ const platform = getPlatformName(driver);
117
+ let raw;
118
+ if (platform === PLATFORM.android) {
119
+ raw = await execute(driver, 'mobile: pullFile', {
120
+ path: args.remotePath,
121
+ });
122
+ }
123
+ else if (platform === PLATFORM.ios) {
124
+ raw = await execute(driver, 'mobile: pullFile', {
125
+ remotePath: args.remotePath,
126
+ });
127
+ }
128
+ else {
129
+ throw new Error(`Unsupported platform: ${platform}. Only Android and iOS are supported.`);
130
+ }
131
+ const base64 = normalizePullResult(raw);
132
+ return {
133
+ content: [
134
+ {
135
+ type: 'text',
136
+ text: JSON.stringify({
137
+ remotePath: args.remotePath,
138
+ platform,
139
+ contentBase64: base64,
140
+ }),
141
+ },
142
+ ],
143
+ };
144
+ }
145
+ catch (err) {
146
+ const message = err instanceof Error ? err.message : String(err);
147
+ return {
148
+ content: [
149
+ {
150
+ type: 'text',
151
+ text: `Failed to pull file. err: ${message}`,
152
+ },
153
+ ],
154
+ };
155
+ }
156
+ },
157
+ });
158
+ }
159
+ //# sourceMappingURL=file-transfer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-transfer.js","sourceRoot":"","sources":["../../../src/tools/session/file-transfer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C;;;GAGG;AACH,SAAS,mBAAmB,CAAC,MAAe;IAC1C,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IACE,MAAM;QACN,OAAO,MAAM,KAAK,QAAQ;QAC1B,OAAO,IAAI,MAAM;QACjB,OAAQ,MAA6B,CAAC,KAAK,KAAK,QAAQ,EACxD,CAAC;QACD,OAAQ,MAA4B,CAAC,KAAK,CAAC;IAC7C,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,qBAAqB,GACzB,kCAAkC;IAClC,2GAA2G;IAC3G,uFAAuF;IACvF,yEAAyE,CAAC;AAE5E,MAAM,kBAAkB,GACtB,uFAAuF,CAAC;AAE1F;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAe;IACtC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QAC7D,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC;KAC9D,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EACT,2EAA2E;YAC3E,qFAAqF;YACrF,8GAA8G;YAC9G,8EAA8E;QAChF,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,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBACzC,IAAI,QAAQ,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;oBAClC,MAAM,OAAO,CAAC,MAAM,EAAE,kBAAkB,EAAE;wBACxC,IAAI,EAAE,IAAI,CAAC,UAAU;wBACrB,IAAI,EAAE,IAAI,CAAC,aAAa;qBACzB,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,QAAQ,KAAK,QAAQ,CAAC,GAAG,EAAE,CAAC;oBACrC,MAAM,OAAO,CAAC,MAAM,EAAE,kBAAkB,EAAE;wBACxC,UAAU,EAAE,IAAI,CAAC,UAAU;wBAC3B,OAAO,EAAE,IAAI,CAAC,aAAa;qBAC5B,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,KAAK,CACb,yBAAyB,QAAQ,uCAAuC,CACzE,CAAC;gBACJ,CAAC;gBAED,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,4CAA4C,IAAI,CAAC,UAAU,EAAE;yBACpE;qBACF;iBACF,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,6BAA6B,OAAO,EAAE;yBAC7C;qBACF;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAe;IACtC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACtB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,qBAAqB,CAAC;KAC9D,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EACT,6EAA6E;YAC7E,4DAA4D;YAC5D,2FAA2F;YAC3F,kHAAkH;QACpH,UAAU,EAAE,MAAM;QAClB,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,aAAa,EAAE,KAAK;SACrB;QACD,OAAO,EAAE,KAAK,EACZ,IAA4B,EAC5B,QAA6C,EACrB,EAAE;YAC1B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBACzC,IAAI,GAAY,CAAC;gBACjB,IAAI,QAAQ,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;oBAClC,GAAG,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,kBAAkB,EAAE;wBAC9C,IAAI,EAAE,IAAI,CAAC,UAAU;qBACtB,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,QAAQ,KAAK,QAAQ,CAAC,GAAG,EAAE,CAAC;oBACrC,GAAG,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,kBAAkB,EAAE;wBAC9C,UAAU,EAAE,IAAI,CAAC,UAAU;qBAC5B,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,KAAK,CACb,yBAAyB,QAAQ,uCAAuC,CACzE,CAAC;gBACJ,CAAC;gBAED,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;gBACxC,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,UAAU,EAAE,IAAI,CAAC,UAAU;gCAC3B,QAAQ;gCACR,aAAa,EAAE,MAAM;6BACtB,CAAC;yBACH;qBACF;iBACF,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,6BAA6B,OAAO,EAAE;yBAC7C;qBACF;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appium-mcp",
3
3
  "mcpName": "io.github.appium/appium-mcp",
4
- "version": "1.32.0",
4
+ "version": "1.33.0",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
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.32.0",
6
+ "version": "1.33.0",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "appium-mcp",
11
- "version": "1.32.0",
11
+ "version": "1.33.0",
12
12
  "transport": {
13
13
  "type": "stdio"
14
14
  }
@@ -0,0 +1,158 @@
1
+ import { describe, test, expect, jest, beforeEach } from '@jest/globals';
2
+
3
+ jest.unstable_mockModule('../../../session-store', () => ({
4
+ getDriver: jest.fn(),
5
+ getPlatformName: jest.fn(),
6
+ PLATFORM: { ios: 'iOS', android: 'Android' },
7
+ }));
8
+
9
+ jest.unstable_mockModule('../../../command', () => ({
10
+ execute: jest.fn(),
11
+ }));
12
+
13
+ jest.unstable_mockModule('../../../logger', () => ({
14
+ default: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
15
+ }));
16
+
17
+ const { getDriver, getPlatformName, PLATFORM } =
18
+ await import('../../../session-store.js');
19
+ const { execute } = await import('../../../command.js');
20
+
21
+ const mockGetDriver = getDriver as jest.MockedFunction<typeof getDriver>;
22
+ const mockGetPlatformName = getPlatformName as jest.MockedFunction<
23
+ typeof getPlatformName
24
+ >;
25
+ const mockExecute = execute as jest.MockedFunction<typeof execute>;
26
+
27
+ describe('appium_mobile_push_file / appium_mobile_pull_file', () => {
28
+ const mockServer = { addTool: jest.fn() } as any;
29
+
30
+ beforeEach(() => {
31
+ (mockServer.addTool as jest.MockedFunction<any>).mockClear();
32
+ mockExecute.mockReset();
33
+ });
34
+
35
+ test('push: throws when no driver is active', async () => {
36
+ const { pushFile } =
37
+ await import('../../../tools/session/file-transfer.js');
38
+ mockGetDriver.mockReturnValue(null as any);
39
+ pushFile(mockServer);
40
+
41
+ const tool = (mockServer.addTool as jest.MockedFunction<any>).mock.calls.at(
42
+ -1
43
+ )?.[0];
44
+ await expect(
45
+ tool.execute(
46
+ { remotePath: '/sdcard/x.txt', payloadBase64: 'YQ==' },
47
+ undefined
48
+ )
49
+ ).rejects.toThrow('No driver found');
50
+ });
51
+
52
+ test('push: Android uses path and data', async () => {
53
+ const { pushFile } =
54
+ await import('../../../tools/session/file-transfer.js');
55
+ mockGetDriver.mockReturnValue({} as any);
56
+ mockGetPlatformName.mockReturnValue(PLATFORM.android);
57
+ mockExecute.mockResolvedValue(undefined);
58
+
59
+ pushFile(mockServer);
60
+ const tool = (mockServer.addTool as jest.MockedFunction<any>).mock.calls.at(
61
+ -1
62
+ )?.[0];
63
+ await tool.execute(
64
+ { remotePath: '/data/local/tmp/a.txt', payloadBase64: 'SGVsbG8=' },
65
+ undefined
66
+ );
67
+
68
+ expect(mockExecute).toHaveBeenCalledWith(
69
+ expect.anything(),
70
+ 'mobile: pushFile',
71
+ expect.objectContaining({
72
+ path: '/data/local/tmp/a.txt',
73
+ data: 'SGVsbG8=',
74
+ })
75
+ );
76
+ });
77
+
78
+ test('push: iOS uses remotePath and payload', async () => {
79
+ const { pushFile } =
80
+ await import('../../../tools/session/file-transfer.js');
81
+ mockGetDriver.mockReturnValue({} as any);
82
+ mockGetPlatformName.mockReturnValue(PLATFORM.ios);
83
+ mockExecute.mockResolvedValue(undefined);
84
+
85
+ pushFile(mockServer);
86
+ const tool = (mockServer.addTool as jest.MockedFunction<any>).mock.calls.at(
87
+ -1
88
+ )?.[0];
89
+ await tool.execute(
90
+ {
91
+ remotePath: '@com.example.app:documents/x.txt',
92
+ payloadBase64: 'QQ==',
93
+ },
94
+ undefined
95
+ );
96
+
97
+ expect(mockExecute).toHaveBeenCalledWith(
98
+ expect.anything(),
99
+ 'mobile: pushFile',
100
+ expect.objectContaining({
101
+ remotePath: '@com.example.app:documents/x.txt',
102
+ payload: 'QQ==',
103
+ })
104
+ );
105
+ });
106
+
107
+ test('pull: Android uses path', async () => {
108
+ const { pullFile } =
109
+ await import('../../../tools/session/file-transfer.js');
110
+ mockGetDriver.mockReturnValue({} as any);
111
+ mockGetPlatformName.mockReturnValue(PLATFORM.android);
112
+ mockExecute.mockResolvedValue('YmJiYg==');
113
+
114
+ pullFile(mockServer);
115
+ const tool = (mockServer.addTool as jest.MockedFunction<any>).mock.calls.at(
116
+ -1
117
+ )?.[0];
118
+ const result = await tool.execute(
119
+ { remotePath: '/sdcard/Download/out.bin' },
120
+ undefined
121
+ );
122
+
123
+ expect(mockExecute).toHaveBeenCalledWith(
124
+ expect.anything(),
125
+ 'mobile: pullFile',
126
+ expect.objectContaining({ path: '/sdcard/Download/out.bin' })
127
+ );
128
+ const parsed = JSON.parse(result.content[0].text);
129
+ expect(parsed.contentBase64).toBe('YmJiYg==');
130
+ expect(parsed.platform).toBe('Android');
131
+ });
132
+
133
+ test('pull: iOS uses remotePath', async () => {
134
+ const { pullFile } =
135
+ await import('../../../tools/session/file-transfer.js');
136
+ mockGetDriver.mockReturnValue({} as any);
137
+ mockGetPlatformName.mockReturnValue(PLATFORM.ios);
138
+ mockExecute.mockResolvedValue('eHh4');
139
+
140
+ pullFile(mockServer);
141
+ const tool = (mockServer.addTool as jest.MockedFunction<any>).mock.calls.at(
142
+ -1
143
+ )?.[0];
144
+ const result = await tool.execute(
145
+ { remotePath: '@com.app:documents/f.txt' },
146
+ undefined
147
+ );
148
+
149
+ expect(mockExecute).toHaveBeenCalledWith(
150
+ expect.anything(),
151
+ 'mobile: pullFile',
152
+ expect.objectContaining({ remotePath: '@com.app:documents/f.txt' })
153
+ );
154
+ const parsed = JSON.parse(result.content[0].text);
155
+ expect(parsed.contentBase64).toBe('eHh4');
156
+ expect(parsed.platform).toBe('iOS');
157
+ });
158
+ });
@@ -13,6 +13,7 @@ This directory contains all MCP tools available in MCP Appium.
13
13
  - `lock.ts` - Unlock device (`appium_mobile_unlock`)
14
14
  - `select-platform.ts` - Choose Android or iOS
15
15
  - `select-device.ts` - Choose specific device
16
+ - `file-transfer.ts` - Push/pull files on device (`appium_mobile_push_file`, `appium_mobile_pull_file`)
16
17
 
17
18
  ### iOS Setup (`ios/`)
18
19
 
@@ -31,6 +31,7 @@ import {
31
31
  } from './session/geolocation.js';
32
32
  import deviceInfo from './session/device-info.js';
33
33
  import batteryInfo from './session/battery-info.js';
34
+ import { pushFile, pullFile } from './session/file-transfer.js';
34
35
  import bootSimulator from './ios/boot-simulator.js';
35
36
  import setupWDA from './ios/setup-wda.js';
36
37
  import installWDA from './ios/install-wda.js';
@@ -146,6 +147,8 @@ export default function registerTools(server: FastMCP): void {
146
147
  resetGeolocation(server);
147
148
  deviceInfo(server);
148
149
  batteryInfo(server);
150
+ pushFile(server);
151
+ pullFile(server);
149
152
 
150
153
  // iOS Setup
151
154
  bootSimulator(server);
@@ -0,0 +1,180 @@
1
+ import type { ContentResult, FastMCP } from 'fastmcp';
2
+ import { z } from 'zod';
3
+ import { getDriver, getPlatformName, PLATFORM } from '../../session-store.js';
4
+ import { execute } from '../../command.js';
5
+
6
+ /**
7
+ * Normalize the return value of mobile: pullFile (driver may return a string
8
+ * or a wrapped value depending on client/driver).
9
+ */
10
+ function normalizePullResult(result: unknown): string {
11
+ if (typeof result === 'string') {
12
+ return result;
13
+ }
14
+ if (
15
+ result &&
16
+ typeof result === 'object' &&
17
+ 'value' in result &&
18
+ typeof (result as { value: unknown }).value === 'string'
19
+ ) {
20
+ return (result as { value: string }).value;
21
+ }
22
+ return String(result ?? '');
23
+ }
24
+
25
+ const remotePathDescription =
26
+ 'Path to the file on the device. ' +
27
+ 'Android (UiAutomator2): use an absolute path (e.g. /data/local/tmp/foo.txt or /sdcard/Download/foo.txt). ' +
28
+ 'iOS (XCUITest): use the formats described in the Appium XCUITest file transfer guide ' +
29
+ '(e.g. @com.example.app:documents/file.txt or simulator-relative paths).';
30
+
31
+ const payloadDescription =
32
+ 'File contents encoded as Base64 (raw base64 only; do not include a data: URL prefix).';
33
+
34
+ /**
35
+ * Push a file from the host (MCP client) to the device via `mobile: pushFile`.
36
+ *
37
+ * - Android: `{ path, data }` per UiAutomator2 / legacy JSONWP push_file.
38
+ * - iOS: `{ remotePath, payload }` per XCUITest execute-methods reference.
39
+ */
40
+ export function pushFile(server: FastMCP): void {
41
+ const schema = z.object({
42
+ remotePath: z.string().min(1).describe(remotePathDescription),
43
+ payloadBase64: z.string().min(1).describe(payloadDescription),
44
+ });
45
+
46
+ server.addTool({
47
+ name: 'appium_mobile_push_file',
48
+ description:
49
+ 'Push a file to the device using the Appium `mobile: pushFile` extension. ' +
50
+ 'Android uses `path` + `data` (base64). iOS uses `remotePath` + `payload` (base64). ' +
51
+ 'Path semantics on iOS follow the XCUITest file transfer guide (app containers, documents, simulator paths). ' +
52
+ 'Large payloads produce large requests; avoid pushing huge files through MCP.',
53
+ parameters: schema,
54
+ annotations: {
55
+ readOnlyHint: false,
56
+ openWorldHint: false,
57
+ },
58
+ execute: async (
59
+ args: z.infer<typeof schema>,
60
+ _context: Record<string, unknown> | undefined
61
+ ): Promise<ContentResult> => {
62
+ const driver = getDriver();
63
+ if (!driver) {
64
+ throw new Error('No driver found');
65
+ }
66
+
67
+ try {
68
+ const platform = getPlatformName(driver);
69
+ if (platform === PLATFORM.android) {
70
+ await execute(driver, 'mobile: pushFile', {
71
+ path: args.remotePath,
72
+ data: args.payloadBase64,
73
+ });
74
+ } else if (platform === PLATFORM.ios) {
75
+ await execute(driver, 'mobile: pushFile', {
76
+ remotePath: args.remotePath,
77
+ payload: args.payloadBase64,
78
+ });
79
+ } else {
80
+ throw new Error(
81
+ `Unsupported platform: ${platform}. Only Android and iOS are supported.`
82
+ );
83
+ }
84
+
85
+ return {
86
+ content: [
87
+ {
88
+ type: 'text',
89
+ text: `Successfully pushed file to device path: ${args.remotePath}`,
90
+ },
91
+ ],
92
+ };
93
+ } catch (err: unknown) {
94
+ const message = err instanceof Error ? err.message : String(err);
95
+ return {
96
+ content: [
97
+ {
98
+ type: 'text',
99
+ text: `Failed to push file. err: ${message}`,
100
+ },
101
+ ],
102
+ };
103
+ }
104
+ },
105
+ });
106
+ }
107
+
108
+ /**
109
+ * Pull a file from the device via `mobile: pullFile`. Returns Base64-encoded content in the response text.
110
+ */
111
+ export function pullFile(server: FastMCP): void {
112
+ const schema = z.object({
113
+ remotePath: z.string().min(1).describe(remotePathDescription),
114
+ });
115
+
116
+ server.addTool({
117
+ name: 'appium_mobile_pull_file',
118
+ description:
119
+ 'Pull a file from the device using the Appium `mobile: pullFile` extension. ' +
120
+ 'Returns Base64-encoded file content in the response text. ' +
121
+ 'Android uses parameter `path`; iOS uses `remotePath` with the same path formats as push. ' +
122
+ 'Very large files may produce very large responses; prefer downloading or streaming outside MCP for big binaries.',
123
+ parameters: schema,
124
+ annotations: {
125
+ readOnlyHint: true,
126
+ openWorldHint: false,
127
+ },
128
+ execute: async (
129
+ args: z.infer<typeof schema>,
130
+ _context: Record<string, unknown> | undefined
131
+ ): Promise<ContentResult> => {
132
+ const driver = getDriver();
133
+ if (!driver) {
134
+ throw new Error('No driver found');
135
+ }
136
+
137
+ try {
138
+ const platform = getPlatformName(driver);
139
+ let raw: unknown;
140
+ if (platform === PLATFORM.android) {
141
+ raw = await execute(driver, 'mobile: pullFile', {
142
+ path: args.remotePath,
143
+ });
144
+ } else if (platform === PLATFORM.ios) {
145
+ raw = await execute(driver, 'mobile: pullFile', {
146
+ remotePath: args.remotePath,
147
+ });
148
+ } else {
149
+ throw new Error(
150
+ `Unsupported platform: ${platform}. Only Android and iOS are supported.`
151
+ );
152
+ }
153
+
154
+ const base64 = normalizePullResult(raw);
155
+ return {
156
+ content: [
157
+ {
158
+ type: 'text',
159
+ text: JSON.stringify({
160
+ remotePath: args.remotePath,
161
+ platform,
162
+ contentBase64: base64,
163
+ }),
164
+ },
165
+ ],
166
+ };
167
+ } catch (err: unknown) {
168
+ const message = err instanceof Error ? err.message : String(err);
169
+ return {
170
+ content: [
171
+ {
172
+ type: 'text',
173
+ text: `Failed to pull file. err: ${message}`,
174
+ },
175
+ ],
176
+ };
177
+ }
178
+ },
179
+ });
180
+ }