mobile-debug-mcp 0.30.2 → 0.31.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.
@@ -11,14 +11,22 @@ export async function checkIOS() {
11
11
  iosAvailable = true;
12
12
  try {
13
13
  const simOut = execSync(`${xcrun} simctl list devices booted --json`, { encoding: 'utf8', timeout: 1500, stdio: ['ignore', 'pipe', 'ignore'] });
14
- const data = JSON.parse(simOut);
15
- let count = 0;
16
- for (const k in data.devices) {
17
- const arr = data.devices[k];
18
- if (Array.isArray(arr))
19
- count += arr.filter((d) => (d.state || '').toLowerCase() === 'booted').length;
14
+ try {
15
+ const data = JSON.parse(simOut);
16
+ let count = 0;
17
+ for (const k in data.devices) {
18
+ const arr = data.devices[k];
19
+ if (Array.isArray(arr))
20
+ count += arr.filter((d) => (d.state || '').toLowerCase() === 'booted').length;
21
+ }
22
+ iosDevices = count;
23
+ }
24
+ catch {
25
+ // Some xcrun builds include extra text around the JSON response.
26
+ // Fall back to counting explicit booted state markers so readiness remains deterministic.
27
+ const bootedMatches = (simOut.match(/"state"\s*:\s*"Booted"/gi) || []).length;
28
+ iosDevices = bootedMatches;
20
29
  }
21
- iosDevices = count;
22
30
  if (iosDevices === 0)
23
31
  issues.push('No iOS simulators/devices booted');
24
32
  }
@@ -25,9 +25,12 @@ function findInPath(cmd) {
25
25
  return null;
26
26
  }
27
27
  export function resolveAdbCmd() {
28
- // Priority: explicit env ADB_PATH -> ANDROID_SDK_ROOT/platform-tools/adb -> ANDROID_HOME/platform-tools/adb -> ~/Library/Android/sdk/platform-tools/adb -> PATH discovery -> 'adb'
28
+ // Priority: explicit env ADB_PATH -> PATH -> known SDK locations -> fallback to 'adb'
29
29
  if (process.env.ADB_PATH && process.env.ADB_PATH.trim())
30
30
  return process.env.ADB_PATH;
31
+ const found = findInPath('adb');
32
+ if (found)
33
+ return found;
31
34
  const sdkRoot = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
32
35
  if (sdkRoot) {
33
36
  const candidate = path.join(sdkRoot, 'platform-tools', process.platform === 'win32' ? 'adb.exe' : 'adb');
@@ -38,9 +41,6 @@ export function resolveAdbCmd() {
38
41
  const homeSdk = path.join(process.env.HOME || '', 'Library', 'Android', 'sdk', 'platform-tools', process.platform === 'win32' ? 'adb.exe' : 'adb');
39
42
  if (existsSync(homeSdk))
40
43
  return homeSdk;
41
- const found = findInPath('adb');
42
- if (found)
43
- return found;
44
44
  return 'adb';
45
45
  }
46
46
  export function getAdbCmd() { return resolveAdbCmd(); }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { execSync, spawnSync } from 'child_process';
3
3
  import { main as installMain } from './install-idb.js';
4
- import { getIdbCmd, isIDBInstalled } from './idb-helper.js';
4
+ import { getIdbCmd, getIdbPackageVersion, isIDBInstalled } from './idb-helper.js';
5
5
  function which(cmd) {
6
6
  try {
7
7
  const r = spawnSync('command', ['-v', cmd], { stdio: ['ignore', 'pipe', 'ignore'] });
@@ -48,11 +48,18 @@ try {
48
48
  const idb = process.env.IDB_PATH || getIdbCmd();
49
49
  print('idb:', idb);
50
50
  if (idb && isIDBInstalled()) {
51
+ const version = getIdbPackageVersion();
52
+ if (version)
53
+ print('fb-idb version:', version);
51
54
  try {
52
- print('idb --version:', execSync(`${idb} --version`, { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim());
55
+ const output = execSync(`${idb} list-targets --json`, { stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000 }).toString().trim();
56
+ print('idb list-targets --json: ok');
57
+ if (output)
58
+ print(output);
53
59
  }
54
60
  catch (e) {
55
- print('idb --version: (failed)', e instanceof Error ? e.message : String(e));
61
+ print('idb list-targets --json: (failed)', e instanceof Error ? e.message : String(e));
62
+ process.exit(2);
56
63
  }
57
64
  const companion = which('idb_companion');
58
65
  print('which idb_companion:', companion);
@@ -71,21 +71,55 @@ export function getIdbCmd() {
71
71
  }
72
72
  return null;
73
73
  }
74
- export function isIDBInstalled() {
75
- const cmd = getIdbCmd();
74
+ export function getIdbPackageVersion() {
75
+ const runners = ['python3', 'python'];
76
+ for (const runner of runners) {
77
+ try {
78
+ const result = spawnSync(runner, ['-m', 'pip', 'show', 'fb-idb'], {
79
+ encoding: 'utf8',
80
+ stdio: ['ignore', 'pipe', 'pipe']
81
+ });
82
+ if (result.status !== 0)
83
+ continue;
84
+ const output = `${result.stdout || ''}\n${result.stderr || ''}`;
85
+ const match = output.match(/^Version:\s*(.+)$/mi);
86
+ if (match?.[1])
87
+ return match[1].trim();
88
+ }
89
+ catch {
90
+ continue;
91
+ }
92
+ }
93
+ return null;
94
+ }
95
+ export function probeIdb(cmd = getIdbCmd()) {
76
96
  if (!cmd)
77
- return false;
97
+ return { ok: false, raw: null, parsedVersion: null };
78
98
  try {
79
- // command -v <cmd>
80
- const r = spawnSync('command', ['-v', cmd], { stdio: ['ignore', 'pipe', 'ignore'] });
81
- if (r && r.status === 0)
82
- return true;
99
+ const result = spawnSync(cmd, ['list-targets', '--json'], {
100
+ encoding: 'utf8',
101
+ stdio: ['ignore', 'pipe', 'pipe'],
102
+ timeout: 5000
103
+ });
104
+ if (result.status !== 0) {
105
+ return {
106
+ ok: false,
107
+ raw: `${result.stdout || ''}${result.stderr || ''}`.trim() || null,
108
+ parsedVersion: null
109
+ };
110
+ }
111
+ const version = getIdbPackageVersion();
112
+ const raw = version ? `fb-idb ${version}` : `${result.stdout || ''}${result.stderr || ''}`.trim() || null;
113
+ return {
114
+ ok: true,
115
+ raw,
116
+ parsedVersion: version
117
+ };
83
118
  }
84
- catch { }
85
- try {
86
- execSync(`${cmd} list-targets --json`, { stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000 });
87
- return true;
119
+ catch {
120
+ return { ok: false, raw: null, parsedVersion: null };
88
121
  }
89
- catch { }
90
- return false;
122
+ }
123
+ export function isIDBInstalled() {
124
+ return probeIdb().ok;
91
125
  }
@@ -71,19 +71,15 @@ export function getIdbCmd() {
71
71
  }
72
72
  export async function isIDBInstalled() {
73
73
  const cmd = getIdbCmd();
74
+ if (!cmd)
75
+ return false;
74
76
  try {
75
- execSync(`command -v ${cmd}`, { stdio: ['ignore', 'pipe', 'ignore'] });
77
+ execSync(`${cmd} list-targets --json`, { stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000 });
76
78
  return true;
77
79
  }
78
80
  catch (e) {
79
- try {
80
- execSync(`${cmd} list-targets --json`, { stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000 });
81
- return true;
82
- }
83
- catch (e2) {
84
- console.debug(`[isIDBInstalled] idb presence check failed for '${cmd}': ${e instanceof Error ? e.message : String(e2)}`);
85
- return false;
86
- }
81
+ console.debug(`[isIDBInstalled] idb probe failed for '${cmd}': ${e instanceof Error ? e.message : String(e)}`);
82
+ return false;
87
83
  }
88
84
  }
89
85
  // Validate bundle ID to prevent any potential injection or invalid characters
package/docs/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  All notable changes to the **Mobile Debug MCP** project will be documented in this file.
4
4
 
5
+ ## [0.31.0]
6
+ - Added tool discovery/automation
7
+
5
8
  ## [0.30.2]
6
9
  - improved slider interaction
7
10
 
package/docs/ROADMAP.md CHANGED
@@ -9,12 +9,12 @@
9
9
  - Action Trace and Execution Observability
10
10
  - Wait and Synchronization Reliability
11
11
  - Actionability Resolution
12
+ - Environment Auto-Configuration and Toolchain Discovery
13
+ - Adjustable Control Support
12
14
 
13
15
  ## Todo
14
16
 
15
- - Adjustable Control Support
16
17
  - Adjustable Control Precision Hardening
17
- - Environment Auto-Configuration and Toolchain Discovery
18
18
  - Signal-Oriented Diagnostic Filtering
19
19
  - Long Press Gesture
20
20
  - Runtime Debugger Introspection
@@ -205,7 +205,7 @@ Strengthens:
205
205
  ## Rationale
206
206
  Reduces failures caused by interacting with discoverable but non-actionable UI nodes.
207
207
 
208
- **Status:** Planned
208
+ **Status:** Completed
209
209
 
210
210
  Addresses cases where:
211
211
  - visible text is not the true click target
@@ -251,7 +251,7 @@ Blocks or strengthens:
251
251
  ## Rationale
252
252
  High leverage improvement for sliders and parameterized controls.
253
253
 
254
- **Status:** Planned
254
+ **Status:** Completed
255
255
 
256
256
  Addresses friction around:
257
257
  - coordinate-calibrated slider interaction
@@ -81,7 +81,7 @@ That flow is applied to `start_app`, `restart_app`, `tap`, `swipe`, `scroll_to_e
81
81
  | --- | --- | --- | --- | --- |
82
82
  | `classify_action_outcome` | Deterministic rule-based classifier over supplied signals. | `{ uiChanged: boolean, expectedElementVisible?: boolean, networkRequests?: { url?: string, status: 'success'\|'failure'\|'retryable' }[], hasLogErrors?: boolean }` | `{ outcome: 'success'\|'no_op'\|'backend_failure'\|'ui_failure'\|'unknown', reasoning: string, nextAction?: 'call_get_network_activity' }` | Pure computation. |
83
83
  | `get_network_activity` | Return normalized request events since last action window. | `{}` | `{ requests: NetworkRequestSummary[], count: number }` | Reads logs, advances internal `lastConsumedTimestamp`. |
84
- | `get_system_status` | Aggregate Android/iOS/Gradle readiness. | `{}` | `{ success, status: 'ready'\|'degraded'\|'blocked', adbAvailable, adbVersion, devices, deviceStates, logsAvailable, envValid, issues, appInstalled, iosAvailable, iosDevices, gradleJavaHome, gradleValid, gradleFilesChecked, gradleSuggestedFixes, summary }` | Reads toolchain/device state. |
84
+ | `get_system_status` | Aggregate Android/iOS/Gradle readiness. | `{}` | Additive response: legacy flat fields remain available, with nested `host`, `android`, and `ios` objects added alongside them. | Reads toolchain/device state. |
85
85
 
86
86
  ## 3. Action Tools (Mutation Tools)
87
87