@mobilenext/mobile-mcp 0.0.28 → 0.0.29

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/README.md CHANGED
@@ -25,8 +25,11 @@ This server allows Agents and LLMs to interact with native iOS/Android applicati
25
25
  </h4>
26
26
 
27
27
  <h4 align="center">
28
+ <a href="https://github.com/mobile-next/mobile-mcp/wiki">
29
+ <img src="https://img.shields.io/badge/documentation-wiki-blue" alt="wiki" />
30
+ </a>
28
31
  <a href="http://mobilenexthq.com/join-slack">
29
- <img src="https://img.shields.io/badge/join-Slack-blueviolet?logo=slack&style=flat" alt="Slack community channel" />
32
+ <img src="https://img.shields.io/badge/join-Slack-blueviolet?logo=slack&style=flat" alt="join on Slack" />
30
33
  </a>
31
34
  </h4>
32
35
 
@@ -102,6 +105,12 @@ Setup our MCP with Cline, Cursor, Claude, VS Code, Github Copilot:
102
105
  claude mcp add mobile -- npx -y @mobilenext/mobile-mcp@latest
103
106
  ```
104
107
 
108
+ [Gemini CLI:](https://cloud.google.com/gemini/docs/codeassist/gemini-cli)
109
+
110
+ ```
111
+ gemini mcp add mobile npx -y @mobilenext/mobile-mcp@latest
112
+ ```
113
+
105
114
  [Read more in our wiki](https://github.com/mobile-next/mobile-mcp/wiki)! 🚀
106
115
 
107
116
 
package/lib/android.js CHANGED
@@ -336,122 +336,6 @@ class AndroidRobot {
336
336
  const rotation = this.adb("shell", "settings", "get", "system", "user_rotation").toString().trim();
337
337
  return rotation === "0" ? "portrait" : "landscape";
338
338
  }
339
- async getDeviceLogs(options) {
340
- const timeWindow = options?.timeWindow || "1m";
341
- const filter = options?.filter;
342
- const processFilter = options?.process;
343
- let packageFilter = null;
344
- let searchQuery = null;
345
- let effectiveFilter = filter;
346
- // For Android: if both process and filter are provided, combine them as "package:<process> <filter>"
347
- if (processFilter && filter && !filter.includes("package:")) {
348
- effectiveFilter = `package:${processFilter} ${filter}`;
349
- }
350
- else if (processFilter && !filter) {
351
- effectiveFilter = `package:${processFilter}`;
352
- }
353
- // Handle Android package filtering syntax
354
- if (effectiveFilter) {
355
- if (effectiveFilter.startsWith("package:mine")) {
356
- // Filter to user apps only
357
- const query = effectiveFilter.replace("package:mine", "").trim();
358
- searchQuery = query || null;
359
- // Will filter user packages in post-processing
360
- }
361
- else if (effectiveFilter.includes("package:")) {
362
- // Handle specific package filters like package:com.example.app search_term
363
- const packageMatch = effectiveFilter.match(/package:([^\s]+)(?:\s+(.+))?/);
364
- if (packageMatch) {
365
- packageFilter = packageMatch[1];
366
- searchQuery = packageMatch[2] || null;
367
- }
368
- }
369
- else {
370
- // Regular search filter
371
- searchQuery = effectiveFilter;
372
- }
373
- }
374
- const args = ["shell", "logcat"];
375
- if (timeWindow) {
376
- // Calculate timestamp for time-based filtering using -T
377
- const timeInSeconds = this.parseTimeWindow(timeWindow);
378
- const startTime = new Date(Date.now() - (timeInSeconds * 1000));
379
- // Format as MM-dd HH:mm:ss.mmm
380
- const month = String(startTime.getMonth() + 1).padStart(2, "0");
381
- const day = String(startTime.getDate()).padStart(2, "0");
382
- const hours = String(startTime.getHours()).padStart(2, "0");
383
- const minutes = String(startTime.getMinutes()).padStart(2, "0");
384
- const seconds = String(startTime.getSeconds()).padStart(2, "0");
385
- const milliseconds = String(startTime.getMilliseconds()).padStart(3, "0");
386
- const timeFormat = `${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
387
- args.push("-T", timeFormat);
388
- }
389
- else {
390
- args.push("-d");
391
- }
392
- // Add package filtering directly to logcat if we have a specific package
393
- if (packageFilter && packageFilter !== "mine") {
394
- // Use logcat's native package filtering with --pid
395
- try {
396
- // First get the PID(s) for this package
397
- const pidOutput = this.adb("shell", "pidof", packageFilter).toString().trim();
398
- if (pidOutput) {
399
- const pids = pidOutput.split(/\s+/);
400
- for (const pid of pids) {
401
- args.push("--pid", pid);
402
- }
403
- }
404
- }
405
- catch (error) {
406
- // If pidof fails, fall back to post-processing
407
- }
408
- }
409
- const output = this.adb(...args).toString();
410
- // Post-process filtering
411
- const lines = output.split("\n").filter(line => line.trim());
412
- let filteredLines = lines;
413
- // Filter by specific package if provided (fallback if --pid didn't work)
414
- if (packageFilter && packageFilter !== "mine") {
415
- filteredLines = filteredLines.filter(line => {
416
- return line.includes(packageFilter);
417
- });
418
- }
419
- // Filter for user packages if package:mine
420
- if (filter && filter.startsWith("package:mine")) {
421
- filteredLines = filteredLines.filter(line => {
422
- // Look for user app indicators - avoid system/Android logs
423
- return !line.includes("com.android.") &&
424
- !line.includes("android.") &&
425
- !line.includes("system_") &&
426
- (line.includes("com.") || line.includes("io.") || line.includes("net.") || line.includes("app."));
427
- });
428
- }
429
- // Apply text search if provided
430
- if (searchQuery) {
431
- filteredLines = filteredLines.filter(line => {
432
- return line.toLowerCase().includes(searchQuery.toLowerCase());
433
- });
434
- }
435
- return filteredLines.join("\n");
436
- }
437
- parseTimeWindow(timeWindow) {
438
- const match = timeWindow.match(/^(\d+)([smh])$/);
439
- if (!match) {
440
- return 60;
441
- }
442
- const value = parseInt(match[1], 10);
443
- const unit = match[2];
444
- switch (unit) {
445
- case "s":
446
- return value;
447
- case "m":
448
- return value * 60;
449
- case "h":
450
- return value * 3600;
451
- default:
452
- return 60;
453
- }
454
- }
455
339
  async getUiAutomatorDump() {
456
340
  for (let tries = 0; tries < 10; tries++) {
457
341
  const dump = this.adb("exec-out", "uiautomator", "dump", "/dev/tty").toString();
package/lib/ios.js CHANGED
@@ -145,42 +145,6 @@ class IosRobot {
145
145
  const wda = await this.wda();
146
146
  return await wda.getOrientation();
147
147
  }
148
- async getDeviceLogs(options) {
149
- await this.assertTunnelRunning();
150
- const timeWindow = options?.timeWindow || "1m";
151
- const filter = options?.filter;
152
- const args = ["syslog"];
153
- if (timeWindow) {
154
- const timeInSeconds = this.parseTimeWindow(timeWindow);
155
- args.push("--since");
156
- args.push(`${timeInSeconds}s`);
157
- }
158
- let output = await this.ios(...args);
159
- if (filter) {
160
- const lines = output.split("\n");
161
- const filteredLines = lines.filter(line => line.toLowerCase().includes(filter.toLowerCase()));
162
- output = filteredLines.join("\n");
163
- }
164
- return output;
165
- }
166
- parseTimeWindow(timeWindow) {
167
- const match = timeWindow.match(/^(\d+)([smh])$/);
168
- if (!match) {
169
- return 60;
170
- }
171
- const value = parseInt(match[1], 10);
172
- const unit = match[2];
173
- switch (unit) {
174
- case "s":
175
- return value;
176
- case "m":
177
- return value * 60;
178
- case "h":
179
- return value * 3600;
180
- default:
181
- return 60;
182
- }
183
- }
184
148
  }
185
149
  exports.IosRobot = IosRobot;
186
150
  class IosManager {
@@ -124,64 +124,6 @@ class Simctl {
124
124
  const wda = await this.wda();
125
125
  return wda.getOrientation();
126
126
  }
127
- async getDeviceLogs(options) {
128
- const timeWindow = options?.timeWindow || "1m";
129
- const filter = options?.filter;
130
- const processFilter = options?.process;
131
- const deviceUuid = this.simulatorUuid;
132
- let predicate = "";
133
- let currentApp = null;
134
- // If a specific process is provided, use that
135
- if (processFilter) {
136
- currentApp = processFilter;
137
- predicate = `subsystem == "${processFilter}"`;
138
- }
139
- else {
140
- // Try to detect currently running user apps from installed apps
141
- try {
142
- const runningApps = await this.listApps();
143
- // Filter to non-Apple user apps
144
- const userApps = runningApps
145
- .map((app) => app.packageName)
146
- .filter((appId) => !appId.startsWith("com.apple.") && appId.includes("."));
147
- if (userApps.length > 0) {
148
- // For now, just use the first user app found
149
- // In the future, we could try to detect which is actually running
150
- currentApp = userApps[0];
151
- predicate = `subsystem == "${currentApp}"`;
152
- }
153
- }
154
- catch (error) {
155
- // Failed to get apps, continue with fallback
156
- }
157
- // If no user app detected, use broader filter for non-Apple apps
158
- if (!predicate) {
159
- predicate = "subsystem CONTAINS \"com.\" AND NOT subsystem BEGINSWITH \"com.apple.\"";
160
- }
161
- }
162
- if (filter) {
163
- predicate += ` AND composedMessage CONTAINS[c] "${filter}"`;
164
- }
165
- const args = [
166
- "spawn", deviceUuid, "log", "show",
167
- "--last", timeWindow,
168
- "--predicate", predicate,
169
- "--info",
170
- "--debug"
171
- ];
172
- try {
173
- const logs = this.simctl(...args).toString();
174
- const appInfo = currentApp ? ` (focused on: ${currentApp})` : " (all non-Apple apps)";
175
- const debugInfo = `DEBUG: Using predicate: ${predicate}${appInfo}\n\n`;
176
- return `${debugInfo}${logs}`;
177
- }
178
- catch (error) {
179
- if (error instanceof Error && error.message.includes("No logging subsystem")) {
180
- return "No logs found for the current running applications.";
181
- }
182
- throw error;
183
- }
184
- }
185
127
  }
186
128
  exports.Simctl = Simctl;
187
129
  class SimctlManager {
package/lib/server.js CHANGED
@@ -253,7 +253,7 @@ const createMcpServer = () => {
253
253
  await robot.openUrl(url);
254
254
  return `Opened URL: ${url}`;
255
255
  });
256
- tool("swipe_on_screen", "Swipe on the screen", {
256
+ tool("mobile_swipe_on_screen", "Swipe on the screen", {
257
257
  device: zod_1.z.string().describe("The device identifier to use. Use mobile_list_available_devices to find which devices are available to you."),
258
258
  direction: zod_1.z.enum(["up", "down", "left", "right"]).describe("The direction to swipe"),
259
259
  x: zod_1.z.number().optional().describe("The x coordinate to start the swipe from, in pixels. If not provided, uses center of screen"),
@@ -348,25 +348,6 @@ const createMcpServer = () => {
348
348
  const orientation = await robot.getOrientation();
349
349
  return `Current device orientation is ${orientation}`;
350
350
  });
351
- /*
352
- tool(
353
- "mobile_get_logs",
354
- "Get device logs",
355
- {
356
- timeWindow: z.string().optional().describe("Time window to look back (e.g., '5m' for 5 minutes, '1h' for 1 hour). Defaults to '1m'"),
357
- filter: z.string().optional().describe("Filter logs containing this query (case-insensitive). For Android: supports 'package:mine <query>' (user apps only), 'package:com.app.bundle <query>' (specific app), or '<query>' (text search). For iOS: simple text search only."),
358
- process: z.string().optional().describe("Filter logs to a specific process/app bundle ID")
359
- },
360
- async ({ timeWindow, filter, process }) => {
361
- requireRobot();
362
- const logs = await robot!.getDeviceLogs({ timeWindow, filter, process });
363
- const filterText = filter ? ` (filtered by: ${filter})` : "";
364
- const processText = process ? ` (process: ${process})` : "";
365
- const timeText = timeWindow ? ` from last ${timeWindow}` : "";
366
- return `Device logs${timeText}${filterText}${processText}:\n${logs}`;
367
- }
368
- );
369
- */
370
351
  // async check for latest agent version
371
352
  checkForLatestAgentVersion().then();
372
353
  return server;
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.28",
4
+ "version": "0.0.29",
5
5
  "description": "Mobile MCP",
6
6
  "repository": {
7
7
  "type": "git",
@@ -24,11 +24,11 @@
24
24
  "lib"
25
25
  ],
26
26
  "dependencies": {
27
- "@modelcontextprotocol/sdk": "^1.6.1",
28
- "commander": "^14.0.0",
29
- "express": "^5.1.0",
30
- "fast-xml-parser": "^5.0.9",
31
- "zod-to-json-schema": "^3.24.4"
27
+ "@modelcontextprotocol/sdk": "1.13.2",
28
+ "commander": "14.0.0",
29
+ "express": "5.1.0",
30
+ "fast-xml-parser": "5.2.5",
31
+ "zod-to-json-schema": "3.24.6"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@eslint/eslintrc": "^3.2.0",