@mobilenext/mobile-mcp 0.0.43 → 0.0.45

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/lib/android.js CHANGED
@@ -42,6 +42,7 @@ const node_child_process_1 = require("node:child_process");
42
42
  const node_fs_1 = require("node:fs");
43
43
  const xml = __importStar(require("fast-xml-parser"));
44
44
  const robot_1 = require("./robot");
45
+ const utils_1 = require("./utils");
45
46
  const getAdbPath = () => {
46
47
  const exeName = process.env.platform === "win32" ? "adb.exe" : "adb";
47
48
  if (process.env.ANDROID_HOME) {
@@ -137,6 +138,7 @@ class AndroidRobot {
137
138
  .map(line => line.substring("package:".length));
138
139
  }
139
140
  async launchApp(packageName) {
141
+ (0, utils_1.validatePackageName)(packageName);
140
142
  try {
141
143
  this.silentAdb("shell", "monkey", "-p", packageName, "-c", "android.intent.category.LAUNCHER", "1");
142
144
  }
@@ -326,6 +328,7 @@ class AndroidRobot {
326
328
  return elements;
327
329
  }
328
330
  async terminateApp(packageName) {
331
+ (0, utils_1.validatePackageName)(packageName);
329
332
  this.adb("shell", "am", "force-stop", packageName);
330
333
  }
331
334
  async installApp(path) {
@@ -351,7 +354,7 @@ class AndroidRobot {
351
354
  }
352
355
  }
353
356
  async openUrl(url) {
354
- this.adb("shell", "am", "start", "-a", "android.intent.action.VIEW", "-d", url);
357
+ this.adb("shell", "am", "start", "-a", "android.intent.action.VIEW", "-d", this.escapeShellText(url));
355
358
  }
356
359
  isAscii(text) {
357
360
  return /^[\x00-\x7F]*$/.test(text);
package/lib/ios.js CHANGED
@@ -5,6 +5,7 @@ const node_net_1 = require("node:net");
5
5
  const node_child_process_1 = require("node:child_process");
6
6
  const webdriver_agent_1 = require("./webdriver-agent");
7
7
  const robot_1 = require("./robot");
8
+ const utils_1 = require("./utils");
8
9
  const WDA_PORT = 8100;
9
10
  const IOS_TUNNEL_PORT = 60105;
10
11
  const getGoIosPath = () => {
@@ -94,10 +95,12 @@ class IosRobot {
94
95
  });
95
96
  }
96
97
  async launchApp(packageName) {
98
+ (0, utils_1.validatePackageName)(packageName);
97
99
  await this.assertTunnelRunning();
98
100
  await this.ios("launch", packageName);
99
101
  }
100
102
  async terminateApp(packageName) {
103
+ (0, utils_1.validatePackageName)(packageName);
101
104
  await this.assertTunnelRunning();
102
105
  await this.ios("kill", packageName);
103
106
  }
@@ -8,6 +8,7 @@ const node_path_1 = require("node:path");
8
8
  const logger_1 = require("./logger");
9
9
  const webdriver_agent_1 = require("./webdriver-agent");
10
10
  const robot_1 = require("./robot");
11
+ const utils_1 = require("./utils");
11
12
  const TIMEOUT = 30000;
12
13
  const WDA_PORT = 8100;
13
14
  const MAX_BUFFER_SIZE = 1024 * 1024 * 8;
@@ -71,9 +72,11 @@ class Simctl {
71
72
  // alternative: this.simctl("openurl", this.simulatorUuid, url);
72
73
  }
73
74
  async launchApp(packageName) {
75
+ (0, utils_1.validatePackageName)(packageName);
74
76
  this.simctl("launch", this.simulatorUuid, packageName);
75
77
  }
76
78
  async terminateApp(packageName) {
79
+ (0, utils_1.validatePackageName)(packageName);
77
80
  this.simctl("terminate", this.simulatorUuid, packageName);
78
81
  }
79
82
  findAppBundle(dir) {
package/lib/mobilecli.js CHANGED
@@ -71,6 +71,15 @@ class Mobilecli {
71
71
  return "failed " + error.message;
72
72
  }
73
73
  }
74
+ fleetListDevices() {
75
+ return this.executeCommand(["fleet", "list-devices"]);
76
+ }
77
+ fleetAllocate(platform) {
78
+ return this.executeCommand(["fleet", "allocate", "--platform", platform]);
79
+ }
80
+ fleetRelease(deviceId) {
81
+ return this.executeCommand(["fleet", "release", "--device", deviceId]);
82
+ }
74
83
  getDevices(options) {
75
84
  const args = ["devices"];
76
85
  if (options) {
package/lib/server.js CHANGED
@@ -27,8 +27,6 @@ const createMcpServer = () => {
27
27
  name: "mobile-mcp",
28
28
  version: (0, exports.getAgentVersion)(),
29
29
  });
30
- // an empty object to satisfy windsurf
31
- const noParams = zod_1.z.object({});
32
30
  const getClientName = () => {
33
31
  try {
34
32
  const clientInfo = server.server.getClientVersion();
@@ -156,9 +154,7 @@ const createMcpServer = () => {
156
154
  }
157
155
  throw new robot_1.ActionableError(`Device "${deviceId}" not found. Use the mobile_list_available_devices tool to see available devices.`);
158
156
  };
159
- tool("mobile_list_available_devices", "List Devices", "List all available devices. This includes both physical devices and simulators. If there is more than one device returned, you need to let the user select one of them.", {
160
- noParams
161
- }, { readOnlyHint: true }, async ({}) => {
157
+ tool("mobile_list_available_devices", "List Devices", "List all available devices. This includes both physical devices and simulators. If there is more than one device returned, you need to let the user select one of them.", {}, { readOnlyHint: true }, async ({}) => {
162
158
  // from today onward, we must have mobilecli working
163
159
  ensureMobilecliAvailable();
164
160
  const iosManager = new ios_1.IosManager();
@@ -214,6 +210,27 @@ const createMcpServer = () => {
214
210
  const out = { devices };
215
211
  return JSON.stringify(out);
216
212
  });
213
+ if (process.env.MOBILEFLEET_ENABLE === "1") {
214
+ tool("mobile_list_fleet_devices", "List Fleet Devices", "List devices available in the remote fleet", {}, { readOnlyHint: true }, async ({}) => {
215
+ ensureMobilecliAvailable();
216
+ const result = mobilecli.fleetListDevices();
217
+ return result;
218
+ });
219
+ tool("mobile_allocate_fleet_device", "Allocate Fleet Device", "Reserve a device from the remote fleet", {
220
+ platform: zod_1.z.enum(["ios", "android"]).describe("The platform to allocate a device for"),
221
+ }, { destructiveHint: true }, async ({ platform }) => {
222
+ ensureMobilecliAvailable();
223
+ const result = mobilecli.fleetAllocate(platform);
224
+ return result;
225
+ });
226
+ tool("mobile_release_fleet_device", "Release Fleet Device", "Release a device back to the remote fleet", {
227
+ device: zod_1.z.string().describe("The device identifier to release back to the fleet"),
228
+ }, { destructiveHint: true }, async ({ device }) => {
229
+ ensureMobilecliAvailable();
230
+ const result = mobilecli.fleetRelease(device);
231
+ return result;
232
+ });
233
+ }
217
234
  tool("mobile_list_apps", "List Apps", "List all the installed apps on the device", {
218
235
  device: zod_1.z.string().describe("The device identifier to use. Use mobile_list_available_devices to find which devices are available to you.")
219
236
  }, { readOnlyHint: true }, async ({ device }) => {
package/lib/utils.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validatePackageName = validatePackageName;
4
+ const robot_1 = require("./robot");
5
+ function validatePackageName(packageName) {
6
+ if (!/^[a-zA-Z0-9._]+$/.test(packageName)) {
7
+ throw new robot_1.ActionableError(`Invalid package name: "${packageName}"`);
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mobilenext/mobile-mcp",
3
3
  "mcpName": "io.github.mobile-next/mobile-mcp",
4
- "version": "0.0.43",
4
+ "version": "0.0.45",
5
5
  "description": "Mobile MCP",
6
6
  "repository": {
7
7
  "type": "git",
@@ -28,13 +28,13 @@
28
28
  "ajv": "^8.18.0",
29
29
  "commander": "14.0.0",
30
30
  "express": "5.1.0",
31
- "fast-xml-parser": "5.3.7",
31
+ "fast-xml-parser": "5.3.8",
32
32
  "qs": "^6.15.0",
33
33
  "zod": "^4.1.13",
34
34
  "zod-to-json-schema": "3.25.0"
35
35
  },
36
36
  "optionalDependencies": {
37
- "@mobilenext/mobilecli": "0.0.54"
37
+ "@mobilenext/mobilecli": "0.1.59"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@eslint/eslintrc": "^3.2.0",