mobile-debug-mcp 0.24.6 → 0.24.8
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/.github/workflows/ci.yml +1 -3
- package/README.md +12 -5
- package/dist/interact/index.js +89 -3
- package/dist/manage/android.js +9 -5
- package/dist/manage/index.js +37 -23
- package/dist/manage/ios.js +12 -15
- package/dist/server/common.js +46 -0
- package/dist/server/tool-handlers.js +120 -33
- package/dist/server-core.js +1 -1
- package/dist/utils/android/utils.js +17 -5
- package/dist/utils/cli/idb/check-idb.js +1 -1
- package/docs/CHANGELOG.md +18 -10
- package/eslint.config.js +2 -47
- package/package.json +7 -6
- package/src/interact/index.ts +112 -4
- package/src/manage/android.ts +22 -11
- package/src/manage/index.ts +37 -16
- package/src/manage/ios.ts +28 -15
- package/src/server/common.ts +50 -0
- package/src/server/tool-handlers.ts +136 -32
- package/src/server-core.ts +1 -1
- package/src/utils/android/utils.ts +18 -7
- package/src/utils/cli/idb/check-idb.ts +1 -1
- package/test/device/automated/observe/capture_screenshot.android.smoke.ts +1 -1
- package/test/device/automated/observe/capture_screenshot.ios.smoke.ts +1 -1
- package/test/device/automated/observe/get_logs.android.smoke.ts +1 -1
- package/test/device/automated/observe/get_logs.ios.smoke.ts +1 -1
- package/test/device/automated/observe/get_ui_tree.android.smoke.ts +1 -1
- package/test/device/automated/observe/get_ui_tree.ios.smoke.ts +1 -1
- package/test/device/manual/interact/app_lifecycle.manual.ts +3 -3
- package/test/device/manual/observe/capture_screenshot.manual.ts +2 -2
- package/test/device/manual/observe/get_logs.manual.ts +2 -2
- package/test/device/manual/observe/get_ui_tree.manual.ts +2 -2
- package/test/device/manual/observe/logstream.manual.ts +1 -1
- package/test/device/manual/observe/screen_fingerprint.manual.ts +2 -2
- package/test/unit/manage/scoped_env.test.ts +137 -0
- package/test/unit/observe/find_element.test.ts +64 -5
- package/test/unit/server/capture_screenshot.test.ts +17 -0
- package/test/unit/server/common.test.ts +18 -0
- package/test/unit/server/contract.test.ts +3 -0
- package/test/unit/server/get_logs.test.ts +17 -0
- package/test/unit/server/get_network_activity.test.ts +17 -0
- package/test/unit/server/get_ui_tree.test.ts +17 -0
- package/test/unit/server/response_shapes.test.ts +18 -0
- package/test/unit/server/start_log_stream.test.ts +37 -0
- package/.eslintignore +0 -5
- package/.eslintrc.cjs +0 -18
- package/eslint.config.cjs +0 -36
|
@@ -4,9 +4,11 @@ import { ToolsObserve } from '../observe/index.js';
|
|
|
4
4
|
import { classifyActionOutcome } from '../interact/classify.js';
|
|
5
5
|
import { ToolsNetwork } from '../network/index.js';
|
|
6
6
|
import { getSystemStatus } from '../system/index.js';
|
|
7
|
-
import { buildActionExecutionResult, captureActionFingerprint, inferGenericFailure, inferScrollFailure, wrapResponse, wrapToolError } from './common.js';
|
|
7
|
+
import { buildActionExecutionResult, captureActionFingerprint, getArrayArg, getBooleanArg, getNumberArg, getObjectArg, getStringArg, inferGenericFailure, inferScrollFailure, requireBooleanArg, requireNumberArg, requireObjectArg, requireStringArg, wrapResponse, wrapToolError } from './common.js';
|
|
8
8
|
async function handleStartApp(args) {
|
|
9
|
-
const
|
|
9
|
+
const platform = requireStringArg(args, 'platform');
|
|
10
|
+
const appId = requireStringArg(args, 'appId');
|
|
11
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
10
12
|
const uiFingerprintBefore = await captureActionFingerprint(platform, deviceId);
|
|
11
13
|
ToolsNetwork.notifyActionStart();
|
|
12
14
|
const res = await (platform === 'android' ? new AndroidManage().startApp(appId, deviceId) : new iOSManage().startApp(appId, deviceId));
|
|
@@ -29,13 +31,17 @@ async function handleStartApp(args) {
|
|
|
29
31
|
}));
|
|
30
32
|
}
|
|
31
33
|
async function handleTerminateApp(args) {
|
|
32
|
-
const
|
|
34
|
+
const platform = requireStringArg(args, 'platform');
|
|
35
|
+
const appId = requireStringArg(args, 'appId');
|
|
36
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
33
37
|
const res = await (platform === 'android' ? new AndroidManage().terminateApp(appId, deviceId) : new iOSManage().terminateApp(appId, deviceId));
|
|
34
38
|
const response = { device: res.device, appTerminated: res.appTerminated };
|
|
35
39
|
return wrapResponse(response);
|
|
36
40
|
}
|
|
37
41
|
async function handleRestartApp(args) {
|
|
38
|
-
const
|
|
42
|
+
const platform = requireStringArg(args, 'platform');
|
|
43
|
+
const appId = requireStringArg(args, 'appId');
|
|
44
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
39
45
|
const uiFingerprintBefore = await captureActionFingerprint(platform, deviceId);
|
|
40
46
|
ToolsNetwork.notifyActionStart();
|
|
41
47
|
const res = await (platform === 'android' ? new AndroidManage().restartApp(appId, deviceId) : new iOSManage().restartApp(appId, deviceId));
|
|
@@ -59,13 +65,18 @@ async function handleRestartApp(args) {
|
|
|
59
65
|
}));
|
|
60
66
|
}
|
|
61
67
|
async function handleResetAppData(args) {
|
|
62
|
-
const
|
|
68
|
+
const platform = requireStringArg(args, 'platform');
|
|
69
|
+
const appId = requireStringArg(args, 'appId');
|
|
70
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
63
71
|
const res = await (platform === 'android' ? new AndroidManage().resetAppData(appId, deviceId) : new iOSManage().resetAppData(appId, deviceId));
|
|
64
72
|
const response = { device: res.device, dataCleared: res.dataCleared };
|
|
65
73
|
return wrapResponse(response);
|
|
66
74
|
}
|
|
67
75
|
async function handleInstallApp(args) {
|
|
68
|
-
const
|
|
76
|
+
const platform = requireStringArg(args, 'platform');
|
|
77
|
+
const projectType = requireStringArg(args, 'projectType');
|
|
78
|
+
const appPath = requireStringArg(args, 'appPath');
|
|
79
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
69
80
|
const res = await ToolsManage.installAppHandler({ platform, appPath, deviceId, projectType });
|
|
70
81
|
const response = {
|
|
71
82
|
device: res.device,
|
|
@@ -76,12 +87,19 @@ async function handleInstallApp(args) {
|
|
|
76
87
|
return wrapResponse(response);
|
|
77
88
|
}
|
|
78
89
|
async function handleBuildApp(args) {
|
|
79
|
-
const
|
|
90
|
+
const platform = requireStringArg(args, 'platform');
|
|
91
|
+
const projectType = requireStringArg(args, 'projectType');
|
|
92
|
+
const projectPath = requireStringArg(args, 'projectPath');
|
|
93
|
+
const variant = getStringArg(args, 'variant');
|
|
80
94
|
const res = await ToolsManage.buildAppHandler({ platform, projectPath, variant, projectType });
|
|
81
95
|
return wrapResponse(res);
|
|
82
96
|
}
|
|
83
97
|
async function handleBuildAndInstall(args) {
|
|
84
|
-
const
|
|
98
|
+
const platform = requireStringArg(args, 'platform');
|
|
99
|
+
const projectType = requireStringArg(args, 'projectType');
|
|
100
|
+
const projectPath = requireStringArg(args, 'projectPath');
|
|
101
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
102
|
+
const timeout = getNumberArg(args, 'timeout');
|
|
85
103
|
const res = await ToolsManage.buildAndInstallHandler({ platform, projectPath, deviceId, timeout, projectType });
|
|
86
104
|
return {
|
|
87
105
|
content: [
|
|
@@ -91,7 +109,16 @@ async function handleBuildAndInstall(args) {
|
|
|
91
109
|
};
|
|
92
110
|
}
|
|
93
111
|
async function handleGetLogs(args) {
|
|
94
|
-
const
|
|
112
|
+
const platform = requireStringArg(args, 'platform');
|
|
113
|
+
const appId = getStringArg(args, 'appId');
|
|
114
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
115
|
+
const pid = getNumberArg(args, 'pid');
|
|
116
|
+
const tag = getStringArg(args, 'tag');
|
|
117
|
+
const level = getStringArg(args, 'level');
|
|
118
|
+
const contains = getStringArg(args, 'contains');
|
|
119
|
+
const since_seconds = getNumberArg(args, 'since_seconds');
|
|
120
|
+
const limit = getNumberArg(args, 'limit');
|
|
121
|
+
const lines = getNumberArg(args, 'lines');
|
|
95
122
|
const res = await ToolsObserve.getLogsHandler({ platform, appId, deviceId, pid, tag, level, contains, since_seconds, limit, lines });
|
|
96
123
|
const filtered = !!(pid || tag || level || contains || since_seconds || appId);
|
|
97
124
|
return {
|
|
@@ -102,7 +129,8 @@ async function handleGetLogs(args) {
|
|
|
102
129
|
};
|
|
103
130
|
}
|
|
104
131
|
async function handleListDevices(args) {
|
|
105
|
-
const
|
|
132
|
+
const platform = getStringArg(args, 'platform');
|
|
133
|
+
const appId = getStringArg(args, 'appId');
|
|
106
134
|
const res = await ToolsManage.listDevicesHandler({ platform, appId });
|
|
107
135
|
return wrapResponse(res);
|
|
108
136
|
}
|
|
@@ -111,7 +139,8 @@ async function handleGetSystemStatus() {
|
|
|
111
139
|
return wrapResponse(result);
|
|
112
140
|
}
|
|
113
141
|
async function handleCaptureScreenshot(args) {
|
|
114
|
-
const
|
|
142
|
+
const platform = requireStringArg(args, 'platform');
|
|
143
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
115
144
|
const res = await ToolsObserve.captureScreenshotHandler({ platform, deviceId });
|
|
116
145
|
const mime = res.screenshot_mime || 'image/png';
|
|
117
146
|
const content = [
|
|
@@ -125,52 +154,86 @@ async function handleCaptureScreenshot(args) {
|
|
|
125
154
|
return { content };
|
|
126
155
|
}
|
|
127
156
|
async function handleCaptureDebugSnapshot(args) {
|
|
128
|
-
const
|
|
157
|
+
const reason = getStringArg(args, 'reason');
|
|
158
|
+
const includeLogs = getBooleanArg(args, 'includeLogs');
|
|
159
|
+
const logLines = getNumberArg(args, 'logLines');
|
|
160
|
+
const platform = getStringArg(args, 'platform');
|
|
161
|
+
const appId = getStringArg(args, 'appId');
|
|
162
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
163
|
+
const sessionId = getStringArg(args, 'sessionId');
|
|
129
164
|
const res = await ToolsObserve.captureDebugSnapshotHandler({ reason, includeLogs, logLines, platform, appId, deviceId, sessionId });
|
|
130
165
|
return wrapResponse(res);
|
|
131
166
|
}
|
|
132
167
|
async function handleGetUITree(args) {
|
|
133
|
-
const
|
|
168
|
+
const platform = requireStringArg(args, 'platform');
|
|
169
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
134
170
|
const res = await ToolsObserve.getUITreeHandler({ platform, deviceId });
|
|
135
171
|
return wrapResponse(res);
|
|
136
172
|
}
|
|
137
173
|
async function handleGetCurrentScreen(args) {
|
|
138
|
-
const
|
|
174
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
139
175
|
const res = await ToolsObserve.getCurrentScreenHandler({ deviceId });
|
|
140
176
|
return wrapResponse(res);
|
|
141
177
|
}
|
|
142
178
|
async function handleGetScreenFingerprint(args) {
|
|
143
|
-
const
|
|
179
|
+
const platform = getStringArg(args, 'platform');
|
|
180
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
144
181
|
const res = await ToolsObserve.getScreenFingerprintHandler({ platform, deviceId });
|
|
145
182
|
return wrapResponse(res);
|
|
146
183
|
}
|
|
147
184
|
async function handleWaitForScreenChange(args) {
|
|
148
|
-
const
|
|
185
|
+
const platform = getStringArg(args, 'platform');
|
|
186
|
+
const previousFingerprint = requireStringArg(args, 'previousFingerprint');
|
|
187
|
+
const timeoutMs = getNumberArg(args, 'timeoutMs');
|
|
188
|
+
const pollIntervalMs = getNumberArg(args, 'pollIntervalMs');
|
|
189
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
149
190
|
const res = await ToolsInteract.waitForScreenChangeHandler({ platform, previousFingerprint, timeoutMs, pollIntervalMs, deviceId });
|
|
150
191
|
return wrapResponse(res);
|
|
151
192
|
}
|
|
152
193
|
async function handleExpectScreen(args) {
|
|
153
|
-
const
|
|
194
|
+
const platform = getStringArg(args, 'platform');
|
|
195
|
+
const fingerprint = getStringArg(args, 'fingerprint');
|
|
196
|
+
const screen = getStringArg(args, 'screen');
|
|
197
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
154
198
|
const res = await ToolsInteract.expectScreenHandler({ platform, fingerprint, screen, deviceId });
|
|
155
199
|
return wrapResponse(res);
|
|
156
200
|
}
|
|
157
201
|
async function handleExpectElementVisible(args) {
|
|
158
|
-
const
|
|
202
|
+
const selector = requireObjectArg(args, 'selector');
|
|
203
|
+
const element_id = getStringArg(args, 'element_id');
|
|
204
|
+
const timeout_ms = getNumberArg(args, 'timeout_ms');
|
|
205
|
+
const poll_interval_ms = getNumberArg(args, 'poll_interval_ms');
|
|
206
|
+
const platform = getStringArg(args, 'platform');
|
|
207
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
159
208
|
const res = await ToolsInteract.expectElementVisibleHandler({ selector, element_id, timeout_ms, poll_interval_ms, platform, deviceId });
|
|
160
209
|
return wrapResponse(res);
|
|
161
210
|
}
|
|
162
211
|
async function handleWaitForUI(args) {
|
|
163
|
-
const
|
|
212
|
+
const selector = getObjectArg(args, 'selector');
|
|
213
|
+
const condition = getStringArg(args, 'condition') ?? 'exists';
|
|
214
|
+
const timeout_ms = getNumberArg(args, 'timeout_ms') ?? 60000;
|
|
215
|
+
const poll_interval_ms = getNumberArg(args, 'poll_interval_ms') ?? 300;
|
|
216
|
+
const match = getObjectArg(args, 'match');
|
|
217
|
+
const retry = getObjectArg(args, 'retry');
|
|
218
|
+
const platform = getStringArg(args, 'platform');
|
|
219
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
164
220
|
const res = await ToolsInteract.waitForUIHandler({ selector, condition, timeout_ms, poll_interval_ms, match, retry, platform, deviceId });
|
|
165
221
|
return wrapResponse(res);
|
|
166
222
|
}
|
|
167
223
|
async function handleFindElement(args) {
|
|
168
|
-
const
|
|
224
|
+
const query = requireStringArg(args, 'query');
|
|
225
|
+
const exact = getBooleanArg(args, 'exact') ?? false;
|
|
226
|
+
const timeoutMs = getNumberArg(args, 'timeoutMs') ?? 3000;
|
|
227
|
+
const platform = getStringArg(args, 'platform');
|
|
228
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
169
229
|
const res = await ToolsInteract.findElementHandler({ query, exact, timeoutMs, platform, deviceId });
|
|
170
230
|
return wrapResponse(res);
|
|
171
231
|
}
|
|
172
232
|
async function handleTap(args) {
|
|
173
|
-
const
|
|
233
|
+
const platform = getStringArg(args, 'platform');
|
|
234
|
+
const x = requireNumberArg(args, 'x');
|
|
235
|
+
const y = requireNumberArg(args, 'y');
|
|
236
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
174
237
|
const uiFingerprintBefore = await captureActionFingerprint(platform, deviceId);
|
|
175
238
|
ToolsNetwork.notifyActionStart();
|
|
176
239
|
const res = await ToolsInteract.tapHandler({ platform, x, y, deviceId });
|
|
@@ -185,13 +248,19 @@ async function handleTap(args) {
|
|
|
185
248
|
}));
|
|
186
249
|
}
|
|
187
250
|
async function handleTapElement(args) {
|
|
188
|
-
const
|
|
251
|
+
const elementId = requireStringArg(args, 'elementId');
|
|
189
252
|
ToolsNetwork.notifyActionStart();
|
|
190
253
|
const res = await ToolsInteract.tapElementHandler({ elementId });
|
|
191
254
|
return wrapResponse(res);
|
|
192
255
|
}
|
|
193
256
|
async function handleSwipe(args) {
|
|
194
|
-
const
|
|
257
|
+
const platform = getStringArg(args, 'platform') ?? 'android';
|
|
258
|
+
const x1 = requireNumberArg(args, 'x1');
|
|
259
|
+
const y1 = requireNumberArg(args, 'y1');
|
|
260
|
+
const x2 = requireNumberArg(args, 'x2');
|
|
261
|
+
const y2 = requireNumberArg(args, 'y2');
|
|
262
|
+
const duration = requireNumberArg(args, 'duration');
|
|
263
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
195
264
|
const uiFingerprintBefore = await captureActionFingerprint(platform, deviceId);
|
|
196
265
|
ToolsNetwork.notifyActionStart();
|
|
197
266
|
const res = await ToolsInteract.swipeHandler({ platform, x1, y1, x2, y2, duration, deviceId });
|
|
@@ -206,14 +275,19 @@ async function handleSwipe(args) {
|
|
|
206
275
|
}));
|
|
207
276
|
}
|
|
208
277
|
async function handleScrollToElement(args) {
|
|
209
|
-
const
|
|
278
|
+
const platform = requireStringArg(args, 'platform');
|
|
279
|
+
const selector = requireObjectArg(args, 'selector');
|
|
280
|
+
const direction = getStringArg(args, 'direction');
|
|
281
|
+
const maxScrolls = getNumberArg(args, 'maxScrolls');
|
|
282
|
+
const scrollAmount = getNumberArg(args, 'scrollAmount');
|
|
283
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
210
284
|
const uiFingerprintBefore = await captureActionFingerprint(platform, deviceId);
|
|
211
285
|
ToolsNetwork.notifyActionStart();
|
|
212
286
|
const res = await ToolsInteract.scrollToElementHandler({ platform, selector, direction, maxScrolls, scrollAmount, deviceId });
|
|
213
287
|
const uiFingerprintAfter = await captureActionFingerprint(platform, deviceId);
|
|
214
288
|
return wrapResponse(buildActionExecutionResult({
|
|
215
289
|
actionType: 'scroll_to_element',
|
|
216
|
-
selector,
|
|
290
|
+
selector: selector ?? null,
|
|
217
291
|
resolved: res?.success && res?.element ? {
|
|
218
292
|
elementId: null,
|
|
219
293
|
text: res.element.text ?? null,
|
|
@@ -230,7 +304,8 @@ async function handleScrollToElement(args) {
|
|
|
230
304
|
}));
|
|
231
305
|
}
|
|
232
306
|
async function handleTypeText(args) {
|
|
233
|
-
const
|
|
307
|
+
const text = requireStringArg(args, 'text');
|
|
308
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
234
309
|
const uiFingerprintBefore = await captureActionFingerprint('android', deviceId);
|
|
235
310
|
ToolsNetwork.notifyActionStart();
|
|
236
311
|
const res = await ToolsInteract.typeTextHandler({ text, deviceId });
|
|
@@ -245,7 +320,7 @@ async function handleTypeText(args) {
|
|
|
245
320
|
}));
|
|
246
321
|
}
|
|
247
322
|
async function handlePressBack(args) {
|
|
248
|
-
const
|
|
323
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
249
324
|
const uiFingerprintBefore = await captureActionFingerprint('android', deviceId);
|
|
250
325
|
ToolsNetwork.notifyActionStart();
|
|
251
326
|
const res = await ToolsInteract.pressBackHandler({ deviceId });
|
|
@@ -260,24 +335,35 @@ async function handlePressBack(args) {
|
|
|
260
335
|
}));
|
|
261
336
|
}
|
|
262
337
|
async function handleStartLogStream(args) {
|
|
263
|
-
const
|
|
338
|
+
const platform = getStringArg(args, 'platform') ?? 'android';
|
|
339
|
+
const packageName = requireStringArg(args, 'packageName');
|
|
340
|
+
const level = getStringArg(args, 'level') ?? 'error';
|
|
341
|
+
const sessionId = getStringArg(args, 'sessionId');
|
|
342
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
264
343
|
const res = await ToolsObserve.startLogStreamHandler({ platform, packageName, level, sessionId, deviceId });
|
|
265
344
|
return wrapResponse(res);
|
|
266
345
|
}
|
|
267
346
|
async function handleReadLogStream(args) {
|
|
268
|
-
const
|
|
347
|
+
const platform = getStringArg(args, 'platform');
|
|
348
|
+
const sessionId = getStringArg(args, 'sessionId');
|
|
349
|
+
const limit = getNumberArg(args, 'limit');
|
|
350
|
+
const since = getStringArg(args, 'since');
|
|
269
351
|
const res = await ToolsObserve.readLogStreamHandler({ platform, sessionId, limit, since });
|
|
270
352
|
return wrapResponse(res);
|
|
271
353
|
}
|
|
272
354
|
async function handleStopLogStream(args) {
|
|
273
|
-
const
|
|
355
|
+
const platform = getStringArg(args, 'platform');
|
|
356
|
+
const sessionId = getStringArg(args, 'sessionId');
|
|
274
357
|
const res = await ToolsObserve.stopLogStreamHandler({ platform, sessionId });
|
|
275
358
|
return wrapResponse(res);
|
|
276
359
|
}
|
|
277
360
|
function handleClassifyActionOutcome(args) {
|
|
278
|
-
const
|
|
361
|
+
const uiChanged = requireBooleanArg(args, 'uiChanged');
|
|
362
|
+
const expectedElementVisible = getBooleanArg(args, 'expectedElementVisible');
|
|
363
|
+
const networkRequests = getArrayArg(args, 'networkRequests');
|
|
364
|
+
const hasLogErrors = getBooleanArg(args, 'hasLogErrors');
|
|
279
365
|
const result = classifyActionOutcome({
|
|
280
|
-
uiChanged
|
|
366
|
+
uiChanged,
|
|
281
367
|
expectedElementVisible: expectedElementVisible ?? null,
|
|
282
368
|
networkRequests: networkRequests ?? null,
|
|
283
369
|
hasLogErrors: hasLogErrors ?? null
|
|
@@ -285,7 +371,8 @@ function handleClassifyActionOutcome(args) {
|
|
|
285
371
|
return Promise.resolve(wrapResponse(result));
|
|
286
372
|
}
|
|
287
373
|
async function handleGetNetworkActivity(args) {
|
|
288
|
-
const
|
|
374
|
+
const platform = requireStringArg(args, 'platform');
|
|
375
|
+
const deviceId = getStringArg(args, 'deviceId');
|
|
289
376
|
const result = await ToolsNetwork.getNetworkActivity({ platform, deviceId });
|
|
290
377
|
return wrapResponse(result);
|
|
291
378
|
}
|
package/dist/server-core.js
CHANGED
|
@@ -6,7 +6,7 @@ import { handleToolCall } from './server/tool-handlers.js';
|
|
|
6
6
|
export { wrapResponse, toolDefinitions, handleToolCall };
|
|
7
7
|
export const serverInfo = {
|
|
8
8
|
name: 'mobile-debug-mcp',
|
|
9
|
-
version: '0.
|
|
9
|
+
version: '0.24.8'
|
|
10
10
|
};
|
|
11
11
|
export function createServer() {
|
|
12
12
|
const server = new Server(serverInfo, {
|
|
@@ -57,23 +57,36 @@ export function ensureAdbAvailable() {
|
|
|
57
57
|
return { adbCmd: adb, ok: false, error: String(err) };
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
+
function mergeEnv(overrides) {
|
|
61
|
+
const env = {};
|
|
62
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
63
|
+
if (typeof value === 'string')
|
|
64
|
+
env[key] = value;
|
|
65
|
+
}
|
|
66
|
+
for (const [key, value] of Object.entries(overrides || {})) {
|
|
67
|
+
if (typeof value === 'string')
|
|
68
|
+
env[key] = value;
|
|
69
|
+
}
|
|
70
|
+
return env;
|
|
71
|
+
}
|
|
60
72
|
/**
|
|
61
73
|
* Prepare Gradle execution options for building an Android project.
|
|
62
74
|
* Returns execCmd (wrapper or gradle), base gradleArgs array, and spawn options including env.
|
|
63
75
|
*/
|
|
64
|
-
export async function prepareGradle(projectPath) {
|
|
76
|
+
export async function prepareGradle(projectPath, envOverrides = {}) {
|
|
77
|
+
const env = mergeEnv(envOverrides);
|
|
65
78
|
const gradlewPath = path.join(projectPath, 'gradlew');
|
|
66
79
|
const gradleCmd = existsSync(gradlewPath) ? './gradlew' : 'gradle';
|
|
67
80
|
const execCmd = existsSync(gradlewPath) ? gradlewPath : gradleCmd;
|
|
68
81
|
// Start with a default task; callers may append/override via env flags
|
|
69
|
-
const gradleArgs = [
|
|
82
|
+
const gradleArgs = [env.MCP_GRADLE_TASK || 'assembleDebug'];
|
|
70
83
|
// Respect generic MCP_BUILD_JOBS and Android-specific MCP_GRADLE_WORKERS
|
|
71
|
-
const workers =
|
|
84
|
+
const workers = env.MCP_GRADLE_WORKERS || env.MCP_BUILD_JOBS;
|
|
72
85
|
if (workers) {
|
|
73
86
|
gradleArgs.push(`--max-workers=${workers}`);
|
|
74
87
|
}
|
|
75
88
|
// Respect gradle cache env: default enabled; set MCP_GRADLE_CACHE=0 to disable
|
|
76
|
-
if (
|
|
89
|
+
if (env.MCP_GRADLE_CACHE === '0') {
|
|
77
90
|
gradleArgs.push('-Dorg.gradle.caching=false');
|
|
78
91
|
}
|
|
79
92
|
const detectedJavaHome = await detectJavaHome().catch(() => undefined);
|
|
@@ -85,7 +98,6 @@ export async function prepareGradle(projectPath) {
|
|
|
85
98
|
catch {
|
|
86
99
|
gradleCheck = { gradleJavaHome: undefined, gradleValid: false, filesChecked: [], issues: [] };
|
|
87
100
|
}
|
|
88
|
-
const env = Object.assign({}, process.env);
|
|
89
101
|
// Ensure child processes can find Android platform-tools (adb, etc.) by
|
|
90
102
|
// prepending the platform-tools directory to PATH for spawned processes.
|
|
91
103
|
const adbPath = resolveAdbCmd();
|
|
@@ -24,7 +24,7 @@ async function runInstaller() {
|
|
|
24
24
|
// prefer invoking the TS script via npx/tsx to ensure environment
|
|
25
25
|
const runner = which('npx') ? 'npx' : which('tsx') ? 'tsx' : null;
|
|
26
26
|
if (runner) {
|
|
27
|
-
const args = runner === 'npx' ? ['tsx', './src/cli/idb/install-idb.ts'] : ['./src/cli/idb/install-idb.ts'];
|
|
27
|
+
const args = runner === 'npx' ? ['tsx', './src/utils/cli/idb/install-idb.ts'] : ['./src/utils/cli/idb/install-idb.ts'];
|
|
28
28
|
const res = spawnSync(runner, args, { stdio: 'inherit' });
|
|
29
29
|
return typeof res.status === 'number' ? res.status === 0 : false;
|
|
30
30
|
}
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the **Mobile Debug MCP** project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [0.24.
|
|
6
|
-
-
|
|
5
|
+
## [0.24.8]
|
|
6
|
+
- Improved slider interaction
|
|
7
|
+
|
|
8
|
+
## [0.24.7]
|
|
9
|
+
- Aligned runtime metadata with the published package version.
|
|
10
|
+
- Fixed stale CLI helper paths in npm scripts and the `idb` healthcheck helper.
|
|
11
|
+
- Simplified ESLint configuration by keeping the flat config and removing legacy config files.
|
|
12
|
+
- Updated CI to use the current automated device test runner.
|
|
13
|
+
- Tightened server handler argument parsing and added contract coverage for version and required-argument error responses.
|
|
14
|
+
- Scoped temporary build environment overrides to the duration of each build helper call and added regression coverage for env restoration.
|
|
7
15
|
|
|
8
16
|
## [0.24.5]
|
|
9
17
|
- Improved snapshots
|
|
@@ -27,7 +35,7 @@ All notable changes to the **Mobile Debug MCP** project will be documented in th
|
|
|
27
35
|
|
|
28
36
|
## [0.23.0]
|
|
29
37
|
- Added network monitoring
|
|
30
|
-
- Added
|
|
38
|
+
- Added action-outcome classification tooling for backend-driven flows without visible UI changes.
|
|
31
39
|
|
|
32
40
|
## [0.22.0]
|
|
33
41
|
- Added a portable `test-authoring` skill package and documented the repository's vendor-neutral skill format
|
|
@@ -38,14 +46,14 @@ All notable changes to the **Mobile Debug MCP** project will be documented in th
|
|
|
38
46
|
- Fixed incorrect timeout
|
|
39
47
|
|
|
40
48
|
## [0.21.4]
|
|
41
|
-
-
|
|
42
|
-
-
|
|
49
|
+
- Updated `wait_for_ui` with better contract and observability
|
|
50
|
+
- Updated `get_logs` to return more useful structured output
|
|
43
51
|
|
|
44
52
|
## [0.21.3]
|
|
45
53
|
- Added structured logs
|
|
46
54
|
|
|
47
55
|
## [0.21.2]
|
|
48
|
-
- Fixed screenshots not working
|
|
56
|
+
- Fixed screenshots not working and improved the tool output
|
|
49
57
|
|
|
50
58
|
## [0.21.1]
|
|
51
59
|
- Removed wait_for_element and renamed observe_until to wait_for_ui (obsolete references removed)
|
|
@@ -54,7 +62,7 @@ All notable changes to the **Mobile Debug MCP** project will be documented in th
|
|
|
54
62
|
- Added `wait_for_ui` as a tool for agents to wait for things like API requests
|
|
55
63
|
|
|
56
64
|
## [0.20.1]
|
|
57
|
-
-
|
|
65
|
+
- Fixed Gradle home handling for Android
|
|
58
66
|
|
|
59
67
|
## [0.20.0]
|
|
60
68
|
- Added `get_system_status` tool and refactored system health checks into `src/system`.
|
|
@@ -63,8 +71,8 @@ All notable changes to the **Mobile Debug MCP** project will be documented in th
|
|
|
63
71
|
|
|
64
72
|
|
|
65
73
|
## [0.19.2]
|
|
66
|
-
- Added healthcheck
|
|
67
|
-
- Added skills
|
|
74
|
+
- Added healthcheck improvements
|
|
75
|
+
- Added reusable agent skills
|
|
68
76
|
|
|
69
77
|
## [0.19.1]
|
|
70
78
|
|
|
@@ -113,7 +121,7 @@ All notable changes to the **Mobile Debug MCP** project will be documented in th
|
|
|
113
121
|
## [0.12.1]
|
|
114
122
|
- Improve iOS build/install reliability: project auto-scan, explicit simulator destination, configurable watchdog timeout (MCP_XCODEBUILD_TIMEOUT) and retries (MCP_XCODEBUILD_RETRIES), and DerivedData fallback for locating .app artifacts.
|
|
115
123
|
- Make install_app capable of building iOS projects before installing so agents can autonomously fix, build, install and validate apps.
|
|
116
|
-
- Migrate CLI scripts into typed
|
|
124
|
+
- Migrate CLI scripts into typed source modules and update npm scripts; fix ESM import paths and lint issues.
|
|
117
125
|
- Add preflight checks and idb resolution helpers (getIdbCmd, isIDBInstalled) and add idb_companion health checks.
|
|
118
126
|
- Capture build stdout/stderr into build-results/ for easier diagnostics and surfaced suggestions when KMP frameworks are missing.
|
|
119
127
|
- Add device test runner under test/device and gate device-dependent tests behind RUN_DEVICE_TESTS.
|
package/eslint.config.js
CHANGED
|
@@ -3,7 +3,6 @@ import tsPlugin from '@typescript-eslint/eslint-plugin'
|
|
|
3
3
|
import unusedImports from 'eslint-plugin-unused-imports'
|
|
4
4
|
|
|
5
5
|
export default [
|
|
6
|
-
// Files/directories to ignore
|
|
7
6
|
{
|
|
8
7
|
ignores: [
|
|
9
8
|
'dist/',
|
|
@@ -11,55 +10,11 @@ export default [
|
|
|
11
10
|
'.git/',
|
|
12
11
|
'.vscode/',
|
|
13
12
|
'coverage/',
|
|
14
|
-
'.env'
|
|
13
|
+
'.env'
|
|
15
14
|
]
|
|
16
15
|
},
|
|
17
|
-
// Apply rules to JS/TS source
|
|
18
16
|
{
|
|
19
|
-
files: ['src/**/*.ts', 'src/**/*.js'],
|
|
20
|
-
languageOptions: {
|
|
21
|
-
parser: tsParser,
|
|
22
|
-
parserOptions: {
|
|
23
|
-
ecmaVersion: 2020,
|
|
24
|
-
sourceType: 'module',
|
|
25
|
-
project: './tsconfig.json'
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
plugins: {
|
|
29
|
-
'@typescript-eslint': tsPlugin,
|
|
30
|
-
'unused-imports': unusedImports
|
|
31
|
-
},
|
|
32
|
-
rules: {
|
|
33
|
-
// Use plugin to error on unused imports and provide autofix where possible
|
|
34
|
-
'unused-imports/no-unused-imports': 'error',
|
|
35
|
-
'unused-imports/no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }],
|
|
36
|
-
// Disable the default TS rule to avoid duplicate warnings
|
|
37
|
-
'@typescript-eslint/no-unused-vars': 'off'
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
// Apply lighter rules to test files (no project reference to avoid TS project parsing)
|
|
41
|
-
{
|
|
42
|
-
files: ['test/**/*.ts', 'test/**/*.js'],
|
|
43
|
-
languageOptions: {
|
|
44
|
-
parser: tsParser,
|
|
45
|
-
parserOptions: {
|
|
46
|
-
ecmaVersion: 2020,
|
|
47
|
-
sourceType: 'module'
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
plugins: {
|
|
51
|
-
'@typescript-eslint': tsPlugin,
|
|
52
|
-
'unused-imports': unusedImports
|
|
53
|
-
},
|
|
54
|
-
rules: {
|
|
55
|
-
'unused-imports/no-unused-imports': 'error',
|
|
56
|
-
'unused-imports/no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }],
|
|
57
|
-
'@typescript-eslint/no-unused-vars': 'off'
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
// Apply rules to CLI tooling
|
|
61
|
-
{
|
|
62
|
-
files: ['src/cli/**/*.ts', 'src/cli/**/*.js'],
|
|
17
|
+
files: ['src/**/*.ts', 'src/**/*.js', 'test/**/*.ts', 'test/**/*.js'],
|
|
63
18
|
languageOptions: {
|
|
64
19
|
parser: tsParser,
|
|
65
20
|
parserOptions: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobile-debug-mcp",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.8",
|
|
4
4
|
"description": "MCP server for mobile app debugging (Android + iOS), with focus on security and reliability",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,15 +10,16 @@
|
|
|
10
10
|
"build": "tsc",
|
|
11
11
|
"start": "node ./dist/server.js",
|
|
12
12
|
"prepare": "npm run build",
|
|
13
|
-
"healthcheck": "tsx ./src/cli/idb/check-idb.ts",
|
|
14
|
-
"install-idb": "tsx ./src/cli/idb/install-idb.ts",
|
|
15
|
-
"preflight-ios": "tsx ./src/cli/ios/preflight-ios.ts",
|
|
13
|
+
"healthcheck": "tsx ./src/utils/cli/idb/check-idb.ts",
|
|
14
|
+
"install-idb": "tsx ./src/utils/cli/idb/install-idb.ts",
|
|
15
|
+
"preflight-ios": "tsx ./src/utils/cli/ios/preflight-ios.ts",
|
|
16
16
|
"test:unit": "tsx test/unit/index.ts",
|
|
17
17
|
"test:integration": "npm run test:device",
|
|
18
18
|
"test:device": "npm run build && tsx test/device/index.ts",
|
|
19
19
|
"test": "npm run test:unit",
|
|
20
|
-
"lint": "eslint
|
|
21
|
-
"lint:fix": "eslint
|
|
20
|
+
"lint": "eslint src test --quiet",
|
|
21
|
+
"lint:fix": "eslint src test --fix",
|
|
22
|
+
"verify": "npm run lint && npm run build && npm run test:unit"
|
|
22
23
|
},
|
|
23
24
|
|
|
24
25
|
"engines": {
|