mobile-debug-mcp 0.34.0 → 0.35.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.
Files changed (432) hide show
  1. package/.agents/skills/mcp-builder/SKILL.md +44 -64
  2. package/.agents/skills/mcp-builder/references/build-flags.md +44 -43
  3. package/.agents/skills/mcp-builder/references/diagnostics-schema.md +4 -1
  4. package/.agents/skills/mcp-builder/references/toolchain-details.md +49 -62
  5. package/.agents/skills/spec-review/references/spec-review-checklist.md +7 -0
  6. package/.agents/skills/test-authoring/SKILL.md +7 -6
  7. package/.agents/skills/test-authoring/references/repo-test-layout.md +3 -1
  8. package/.agents/skills/test-authoring/references/test-authoring-workflow.md +16 -7
  9. package/.agents/skills/test-authoring/references/test-quality-checklist.md +2 -1
  10. package/.github/copilot-instructions.md +14 -36
  11. package/.github/workflows/ci.yml +4 -4
  12. package/AGENTS.md +12 -3
  13. package/CONTRIBUTING.md +79 -0
  14. package/README.md +156 -129
  15. package/agent-plugin/.claude-plugin/plugin.json +2 -2
  16. package/agent-plugin/.codex-plugin/plugin.json +1 -1
  17. package/agent-plugin/.cursor-plugin/plugin.json +2 -2
  18. package/agent-plugin/.mcp.json +1 -1
  19. package/agent-plugin/README.md +16 -6
  20. package/agent-plugin/mcp.json +1 -1
  21. package/agent-plugin/plugin.json +1 -1
  22. package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +27 -2
  23. package/dist/contracts/execution-controls.js +24 -0
  24. package/dist/contracts/selector.js +8 -0
  25. package/dist/contracts/target.js +15 -0
  26. package/dist/contracts/ui.js +1 -0
  27. package/dist/debugger/android.js +2 -1
  28. package/dist/debugger/diagnostics.js +54 -0
  29. package/dist/debugger/protocol.js +33 -0
  30. package/dist/debugger/sessions.js +104 -62
  31. package/dist/debugger/worker.js +53 -42
  32. package/dist/interact/adjustment/adjustment-context.js +41 -0
  33. package/dist/interact/adjustment/adjustment-gestures.js +55 -0
  34. package/dist/interact/adjustment/adjustment-plan.js +18 -0
  35. package/dist/interact/adjustment/adjustment-resolution.js +127 -0
  36. package/dist/interact/adjustment/adjustment-result.js +19 -0
  37. package/dist/interact/adjustment/adjustment-semantic.js +64 -0
  38. package/dist/interact/adjustment/adjustment-verification.js +35 -0
  39. package/dist/interact/adjustment/adjustment.js +52 -0
  40. package/dist/interact/adjustment/control-geometry.js +87 -0
  41. package/dist/interact/adjustment/control-values.js +136 -0
  42. package/dist/interact/adjustment/slider-labels.js +84 -0
  43. package/dist/interact/assertions/screen-assertion.js +37 -0
  44. package/dist/interact/assertions/state-assertion-context.js +35 -0
  45. package/dist/interact/assertions/state-assertion.js +76 -0
  46. package/dist/interact/assertions/state-comparison.js +141 -0
  47. package/dist/interact/assertions/visibility-assertion.js +34 -0
  48. package/dist/interact/conditional-action.js +12 -7
  49. package/dist/interact/index.js +65 -3090
  50. package/dist/interact/input-service.js +1 -0
  51. package/dist/interact/interaction-trace.js +21 -0
  52. package/dist/interact/model.js +1 -0
  53. package/dist/interact/observation.js +1 -0
  54. package/dist/interact/targeting/candidate-ancestor.js +58 -0
  55. package/dist/interact/targeting/candidate-result.js +23 -0
  56. package/dist/interact/targeting/candidate-scoring.js +50 -0
  57. package/dist/interact/targeting/element-helpers.js +216 -0
  58. package/dist/interact/targeting/find-element.js +65 -0
  59. package/dist/interact/targeting/nearby-control.js +56 -0
  60. package/dist/interact/targeting/resolved-elements.js +21 -0
  61. package/dist/interact/targeting/tap-target.js +17 -0
  62. package/dist/interact/targeting/target-scope.js +104 -0
  63. package/dist/interact/waits/legacy-log-wait.js +38 -0
  64. package/dist/interact/waits/legacy-wait-observations.js +103 -0
  65. package/dist/interact/waits/legacy-wait-state.js +34 -0
  66. package/dist/interact/waits/legacy-wait.js +48 -0
  67. package/dist/interact/waits/screen-wait.js +49 -0
  68. package/dist/interact/waits/ui-change-helpers.js +157 -0
  69. package/dist/interact/waits/ui-change-state.js +18 -0
  70. package/dist/interact/waits/ui-change-wait.js +77 -0
  71. package/dist/interact/waits/ui-wait-condition.js +56 -0
  72. package/dist/interact/waits/ui-wait-state.js +38 -0
  73. package/dist/interact/waits/ui-wait.js +106 -0
  74. package/dist/manage/android.js +55 -51
  75. package/dist/manage/index.js +38 -75
  76. package/dist/manage/ios/build-types.js +1 -0
  77. package/dist/manage/ios/build.js +210 -0
  78. package/dist/manage/ios/project-discovery.js +29 -0
  79. package/dist/manage/ios/scheme.js +18 -0
  80. package/dist/manage/ios.js +42 -256
  81. package/dist/observe/android/context.js +1 -0
  82. package/dist/observe/android/current-screen.js +55 -0
  83. package/dist/observe/android/fingerprint.js +13 -0
  84. package/dist/observe/android/log-stream.js +132 -0
  85. package/dist/observe/android/logs.js +107 -0
  86. package/dist/observe/android/screenshot.js +70 -0
  87. package/dist/observe/android/ui-tree.js +107 -0
  88. package/dist/observe/android.js +16 -520
  89. package/dist/observe/evidence.js +114 -75
  90. package/dist/observe/index.js +16 -263
  91. package/dist/observe/ios/context.js +16 -0
  92. package/dist/observe/ios/fingerprint.js +12 -0
  93. package/dist/observe/ios/log-stream.js +105 -0
  94. package/dist/observe/ios/logs.js +168 -0
  95. package/dist/observe/ios/screenshot.js +22 -0
  96. package/dist/observe/ios/ui-elements.js +241 -0
  97. package/dist/observe/ios/ui-tree.js +137 -0
  98. package/dist/observe/ios.js +17 -715
  99. package/dist/observe/logs/model.js +1 -0
  100. package/dist/{network → observe/network}/index.js +2 -2
  101. package/dist/observe/readiness-service.js +4 -2
  102. package/dist/observe/readiness.js +29 -10
  103. package/dist/observe/screenshot-encoding.js +41 -0
  104. package/dist/observe/snapshot-collection.js +79 -0
  105. package/dist/observe/snapshot-logs.js +30 -0
  106. package/dist/observe/snapshot-metadata.js +43 -31
  107. package/dist/observe/snapshot-semantic.js +115 -0
  108. package/dist/observe/system-trace.js +22 -13
  109. package/dist/runtime/action-results.js +270 -0
  110. package/dist/runtime/clock.js +4 -0
  111. package/dist/server/common.js +1 -256
  112. package/dist/server/definitions/assert.js +162 -0
  113. package/dist/server/definitions/diagnostic.js +118 -0
  114. package/dist/server/definitions/input.js +274 -0
  115. package/dist/server/definitions/manage.js +194 -0
  116. package/dist/server/definitions/observe.js +149 -0
  117. package/dist/server/definitions/target.js +138 -0
  118. package/dist/server/definitions/wait.js +105 -0
  119. package/dist/server/handlers/action-execution.js +163 -0
  120. package/dist/server/handlers/correlation.js +23 -0
  121. package/dist/server/{debugger-handlers.js → handlers/debugger-handlers.js} +22 -12
  122. package/dist/server/handlers/debugger-response.js +9 -0
  123. package/dist/server/handlers/diagnostic.js +69 -0
  124. package/dist/server/handlers/handler-types.js +1 -0
  125. package/dist/server/handlers/input.js +154 -0
  126. package/dist/server/handlers/interact.js +113 -0
  127. package/dist/server/handlers/journey-assertions.js +35 -0
  128. package/dist/server/handlers/journey.js +147 -0
  129. package/dist/server/handlers/manage.js +119 -0
  130. package/dist/server/handlers/observe.js +95 -0
  131. package/dist/server/handlers/payload.js +4 -0
  132. package/dist/server/handlers/tool-handlers.js +160 -0
  133. package/dist/server/schemas/condition-schema.js +2 -0
  134. package/dist/server/{debugger-schema.js → schemas/debugger-schema.js} +11 -6
  135. package/dist/server/{journey-schema.js → schemas/journey-schema.js} +7 -22
  136. package/dist/server/tool-definitions.js +58 -1135
  137. package/dist/server-core.js +2 -2
  138. package/dist/system/android-status.js +101 -0
  139. package/dist/system/android.js +37 -27
  140. package/dist/system/index.js +13 -439
  141. package/dist/system/ios-status.js +132 -0
  142. package/dist/system/ios.js +11 -8
  143. package/dist/system/model.js +1 -0
  144. package/dist/system/probes.js +105 -0
  145. package/dist/system/status-summary.js +43 -0
  146. package/dist/system/unsupported-status.js +52 -0
  147. package/dist/utils/android/commands.js +89 -0
  148. package/dist/utils/android/gradle.js +139 -0
  149. package/dist/utils/android/logs.js +103 -0
  150. package/dist/utils/android/ui-tree.js +233 -0
  151. package/dist/utils/android/utils.js +6 -564
  152. package/dist/utils/cli/idb/idb-helper.js +4 -6
  153. package/dist/utils/cli/idb/install-idb.js +13 -9
  154. package/dist/utils/cli/ios/preflight-ios.js +42 -38
  155. package/dist/utils/ios/utils.js +45 -46
  156. package/dist/utils/java.js +65 -49
  157. package/dist/utils/resolve-device.js +4 -2
  158. package/dist/utils/ui/index.js +26 -26
  159. package/docs/CHANGELOG.md +44 -0
  160. package/docs/README.md +38 -0
  161. package/docs/benchmarks/README.md +18 -0
  162. package/docs/benchmarks/interaction-latency/README.md +14 -0
  163. package/docs/benchmarks/interaction-latency/final.md +4 -1
  164. package/docs/benchmarks/interaction-latency/ios-validation.md +2 -0
  165. package/docs/benchmarks/ui-tree-performance/README.md +24 -16
  166. package/docs/benchmarks/ui-tree-performance/comparison.md +11 -10
  167. package/docs/code-structure.md +118 -0
  168. package/docs/specs/001-state-verification/spec.md +16 -14
  169. package/docs/specs/002-richer-element-identity/spec.md +12 -10
  170. package/docs/specs/003-wait-and-synchronization-reliability/spec.md +12 -10
  171. package/docs/specs/004-action-verification-routing/spec.md +3 -3
  172. package/docs/specs/005-unified-action-execution-and-verification-model/spec.md +2 -0
  173. package/docs/specs/006-runtime-action-instrumentation-and-binding-layer/spec.md +2 -0
  174. package/docs/specs/007-actionability-resolution-and-executable-target-selection/spec.md +3 -1
  175. package/docs/specs/008-adjustable-control-support-and-semantic-value-manipulation/spec.md +3 -4
  176. package/docs/specs/009-semantic-control-modeling-for-custom-and-composite-controls/spec.md +2 -0
  177. package/docs/specs/010-verification-stabilization-and-temporal-convergence/spec.md +2 -2
  178. package/docs/specs/011-recovery-and-replanning-for-failed-or-ambiguous-interaction-flows/spec.md +2 -2
  179. package/docs/specs/011.1-recovery-contract-types-and-runtime-wiring-spec/spec.md +2 -0
  180. package/docs/specs/012-action-trace-and-xecution-observability/spec.md +2 -3
  181. package/docs/specs/013-wait-and-synchronization-reliability/spec.md +19 -19
  182. package/docs/specs/014-actionability-resolution/spec.md +20 -18
  183. package/docs/specs/015-adjustable-control-support/spec.md +17 -15
  184. package/docs/specs/016-environment-tools/spec.md +20 -20
  185. package/docs/specs/018-interaction-latency-and-batched-journeys/spec.md +2 -0
  186. package/docs/specs/019-ui-tree-observation-performance/spec.md +3 -1
  187. package/docs/specs/020-conditional-actions/intent.md +5 -3
  188. package/docs/specs/020-conditional-actions/plan.md +8 -9
  189. package/docs/specs/020-conditional-actions/spec.md +4 -2
  190. package/docs/specs/021-app-readiness-signals/intent.md +4 -2
  191. package/docs/specs/021-app-readiness-signals/plan.md +7 -6
  192. package/docs/specs/021-app-readiness-signals/spec.md +3 -1
  193. package/docs/specs/022-correlated-evidence/intent.md +4 -4
  194. package/docs/specs/022-correlated-evidence/plan.md +10 -11
  195. package/docs/specs/022-correlated-evidence/spec.md +3 -1
  196. package/docs/specs/023-android-system-traces/intent.md +3 -2
  197. package/docs/specs/023-android-system-traces/plan.md +5 -7
  198. package/docs/specs/023-android-system-traces/spec.md +3 -1
  199. package/docs/specs/024-android-heap-dump-capture/intent.md +3 -1
  200. package/docs/specs/025-android-runtime-debugger/intent.md +4 -3
  201. package/docs/specs/025-android-runtime-debugger/plan.md +3 -1
  202. package/docs/specs/025-android-runtime-debugger/spec.md +4 -3
  203. package/docs/specs/026-code-structure-and-quality-gates/health-plan.md +88 -0
  204. package/docs/specs/026-code-structure-and-quality-gates/intent.md +47 -0
  205. package/docs/specs/026-code-structure-and-quality-gates/plan.md +191 -0
  206. package/docs/specs/026-code-structure-and-quality-gates/spec.md +138 -0
  207. package/docs/specs/026-code-structure-and-quality-gates/verification-final.json +200 -0
  208. package/docs/specs/026-code-structure-and-quality-gates/verification-thin-facade.json +154 -0
  209. package/docs/specs/026-code-structure-and-quality-gates/verification.json +136 -0
  210. package/docs/specs/027-debugger-failure-diagnostics/intent.md +43 -0
  211. package/docs/specs/027-debugger-failure-diagnostics/plan.md +86 -0
  212. package/docs/specs/027-debugger-failure-diagnostics/spec.md +133 -0
  213. package/docs/specs/028-compact-debugger-responses/intent.md +41 -0
  214. package/docs/specs/028-compact-debugger-responses/plan.md +112 -0
  215. package/docs/specs/028-compact-debugger-responses/spec.md +108 -0
  216. package/docs/specs/029-kotlin-locals-validation/report.md +119 -0
  217. package/docs/specs/030-android-coroutine-validation/evidence.json +475 -0
  218. package/docs/specs/030-android-coroutine-validation/intent.md +47 -0
  219. package/docs/specs/030-android-coroutine-validation/plan.md +147 -0
  220. package/docs/specs/030-android-coroutine-validation/report.md +240 -0
  221. package/docs/specs/030-android-coroutine-validation/spec.md +107 -0
  222. package/docs/tools/TOOLS.md +31 -18
  223. package/docs/tools/{evidence.md → correlated-evidence.md} +9 -1
  224. package/docs/tools/debugger.md +76 -0
  225. package/docs/tools/interact.md +42 -20
  226. package/docs/tools/manage.md +25 -44
  227. package/docs/tools/observe.md +55 -23
  228. package/docs/tools/system-traces.md +9 -1
  229. package/docs/tools/system.md +12 -5
  230. package/package.json +11 -8
  231. package/scripts/architecture-graph.mjs +69 -0
  232. package/scripts/check-architecture.mjs +47 -0
  233. package/scripts/code-quality-baseline.json +1 -0
  234. package/scripts/code-quality.mjs +161 -0
  235. package/src/contracts/execution-controls.ts +26 -0
  236. package/src/contracts/selector.ts +17 -0
  237. package/src/contracts/target.ts +19 -0
  238. package/src/contracts/ui.ts +94 -0
  239. package/src/debugger/android.ts +2 -1
  240. package/src/debugger/diagnostics.ts +43 -0
  241. package/src/debugger/guard.ts +6 -1
  242. package/{worker → src/debugger/java}/DebuggerWorker.java +9 -3
  243. package/src/debugger/protocol.ts +37 -0
  244. package/src/debugger/sessions.ts +314 -155
  245. package/src/debugger/worker.ts +51 -38
  246. package/src/interact/adjustment/adjustment-context.ts +83 -0
  247. package/src/interact/adjustment/adjustment-gestures.ts +48 -0
  248. package/src/interact/adjustment/adjustment-plan.ts +23 -0
  249. package/src/interact/adjustment/adjustment-resolution.ts +104 -0
  250. package/src/interact/adjustment/adjustment-result.ts +34 -0
  251. package/src/interact/adjustment/adjustment-semantic.ts +53 -0
  252. package/src/interact/adjustment/adjustment-verification.ts +60 -0
  253. package/src/interact/adjustment/adjustment.ts +49 -0
  254. package/src/interact/adjustment/control-geometry.ts +120 -0
  255. package/src/interact/adjustment/control-values.ts +141 -0
  256. package/src/interact/adjustment/slider-labels.ts +84 -0
  257. package/src/interact/assertions/screen-assertion.ts +39 -0
  258. package/src/interact/assertions/state-assertion-context.ts +48 -0
  259. package/src/interact/assertions/state-assertion.ts +64 -0
  260. package/src/interact/assertions/state-comparison.ts +168 -0
  261. package/src/interact/assertions/visibility-assertion.ts +41 -0
  262. package/src/interact/conditional-action.ts +18 -12
  263. package/src/interact/index.ts +76 -3492
  264. package/src/interact/input-service.ts +11 -0
  265. package/src/interact/interaction-trace.ts +36 -0
  266. package/src/interact/model.ts +109 -0
  267. package/src/interact/observation.ts +7 -0
  268. package/src/interact/targeting/candidate-ancestor.ts +49 -0
  269. package/src/interact/targeting/candidate-result.ts +24 -0
  270. package/src/interact/targeting/candidate-scoring.ts +42 -0
  271. package/src/interact/targeting/element-helpers.ts +243 -0
  272. package/src/interact/targeting/find-element.ts +47 -0
  273. package/src/interact/targeting/nearby-control.ts +46 -0
  274. package/src/interact/targeting/resolved-elements.ts +20 -0
  275. package/src/interact/targeting/tap-target.ts +22 -0
  276. package/src/interact/targeting/target-scope.ts +121 -0
  277. package/src/interact/waits/legacy-log-wait.ts +30 -0
  278. package/src/interact/waits/legacy-wait-observations.ts +55 -0
  279. package/src/interact/waits/legacy-wait-state.ts +48 -0
  280. package/src/interact/waits/legacy-wait.ts +34 -0
  281. package/src/interact/waits/screen-wait.ts +37 -0
  282. package/src/interact/waits/ui-change-helpers.ts +184 -0
  283. package/src/interact/waits/ui-change-state.ts +26 -0
  284. package/src/interact/waits/ui-change-wait.ts +59 -0
  285. package/src/interact/waits/ui-wait-condition.ts +52 -0
  286. package/src/interact/waits/ui-wait-state.ts +55 -0
  287. package/src/interact/waits/ui-wait.ts +84 -0
  288. package/src/manage/android.ts +56 -50
  289. package/src/manage/index.ts +63 -93
  290. package/src/manage/ios/build-types.ts +14 -0
  291. package/src/manage/ios/build.ts +230 -0
  292. package/src/manage/ios/project-discovery.ts +26 -0
  293. package/src/manage/ios/scheme.ts +16 -0
  294. package/src/manage/ios.ts +65 -298
  295. package/src/observe/android/context.ts +1 -0
  296. package/src/observe/android/current-screen.ts +63 -0
  297. package/src/observe/android/fingerprint.ts +13 -0
  298. package/src/observe/android/log-stream.ts +127 -0
  299. package/src/observe/android/logs.ts +110 -0
  300. package/src/observe/android/screenshot.ts +79 -0
  301. package/src/observe/android/ui-tree.ts +113 -0
  302. package/src/observe/android.ts +17 -531
  303. package/src/observe/evidence.ts +122 -70
  304. package/src/observe/index.ts +24 -286
  305. package/src/observe/ios/context.ts +22 -0
  306. package/src/observe/ios/fingerprint.ts +12 -0
  307. package/src/observe/ios/log-stream.ts +103 -0
  308. package/src/observe/ios/logs.ts +179 -0
  309. package/src/observe/ios/screenshot.ts +26 -0
  310. package/src/observe/ios/ui-elements.ts +266 -0
  311. package/src/observe/ios/ui-tree.ts +143 -0
  312. package/src/observe/ios.ts +18 -749
  313. package/src/observe/logs/model.ts +11 -0
  314. package/src/{network → observe/network}/index.ts +2 -2
  315. package/src/observe/readiness-service.ts +5 -3
  316. package/src/observe/readiness.ts +39 -18
  317. package/src/observe/screenshot-encoding.ts +32 -0
  318. package/src/observe/snapshot-collection.ts +69 -0
  319. package/src/observe/snapshot-logs.ts +26 -0
  320. package/src/observe/snapshot-metadata.ts +51 -31
  321. package/src/observe/snapshot-semantic.ts +153 -0
  322. package/src/observe/system-trace.ts +24 -15
  323. package/src/runtime/action-results.ts +376 -0
  324. package/src/runtime/clock.ts +9 -0
  325. package/src/server/common.ts +9 -359
  326. package/src/server/definitions/assert.ts +164 -0
  327. package/src/server/definitions/diagnostic.ts +120 -0
  328. package/src/server/definitions/input.ts +276 -0
  329. package/src/server/definitions/manage.ts +196 -0
  330. package/src/server/definitions/observe.ts +151 -0
  331. package/src/server/definitions/target.ts +140 -0
  332. package/src/server/definitions/wait.ts +107 -0
  333. package/src/server/handlers/action-execution.ts +164 -0
  334. package/src/server/handlers/correlation.ts +20 -0
  335. package/src/server/handlers/debugger-handlers.ts +39 -0
  336. package/src/server/handlers/debugger-response.ts +7 -0
  337. package/src/server/handlers/diagnostic.ts +79 -0
  338. package/src/server/handlers/handler-types.ts +7 -0
  339. package/src/server/handlers/input.ts +173 -0
  340. package/src/server/handlers/interact.ts +135 -0
  341. package/src/server/handlers/journey-assertions.ts +39 -0
  342. package/src/server/handlers/journey.ts +132 -0
  343. package/src/server/handlers/manage.ts +141 -0
  344. package/src/server/handlers/observe.ts +116 -0
  345. package/src/server/handlers/payload.ts +36 -0
  346. package/src/server/handlers/tool-handlers.ts +159 -0
  347. package/src/server/schemas/condition-schema.ts +2 -0
  348. package/src/server/{debugger-schema.ts → schemas/debugger-schema.ts} +13 -7
  349. package/src/server/{journey-schema.ts → schemas/journey-schema.ts} +7 -22
  350. package/src/server/{system-trace-schema.ts → schemas/system-trace-schema.ts} +3 -3
  351. package/src/server/tool-definitions.ts +67 -1143
  352. package/src/server-core.ts +2 -2
  353. package/src/server.ts +1 -1
  354. package/src/system/android-status.ts +124 -0
  355. package/src/system/android.ts +33 -21
  356. package/src/system/index.ts +21 -525
  357. package/src/system/ios-status.ts +147 -0
  358. package/src/system/ios.ts +14 -10
  359. package/src/system/model.ts +41 -0
  360. package/src/system/probes.ts +114 -0
  361. package/src/system/status-summary.ts +47 -0
  362. package/src/system/unsupported-status.ts +55 -0
  363. package/src/types.ts +16 -106
  364. package/src/utils/android/commands.ts +81 -0
  365. package/src/utils/android/gradle.ts +132 -0
  366. package/src/utils/android/logs.ts +103 -0
  367. package/src/utils/android/ui-tree.ts +239 -0
  368. package/src/utils/android/utils.ts +16 -556
  369. package/src/utils/cli/idb/check-idb.ts +4 -4
  370. package/src/utils/cli/idb/idb-helper.ts +6 -8
  371. package/src/utils/cli/idb/install-idb.ts +15 -9
  372. package/src/utils/cli/ios/preflight-ios.ts +39 -31
  373. package/src/utils/cli/ios/run-ios-ui-tree-tap.ts +4 -4
  374. package/src/utils/ios/utils.ts +57 -61
  375. package/src/utils/java.ts +62 -43
  376. package/src/utils/resolve-device.ts +9 -6
  377. package/src/utils/ui/index.ts +28 -28
  378. package/test/device/automated/debugger/coroutine-lifecycle.ts +27 -0
  379. package/test/device/automated/debugger/coroutine-support.ts +140 -0
  380. package/test/device/automated/debugger/coroutine.android.test.ts +169 -0
  381. package/test/device/automated/debugger/kotlin.android.test.ts +127 -0
  382. package/test/device/automated/debugger/runtime.android.test.ts +19 -3
  383. package/test/device/automated/interact/interaction.android.test.ts +56 -0
  384. package/test/device/automated/observe/system-trace.smoke.ts +1 -1
  385. package/test/device/manual/interact/modul8_selector_tap.manual.ts +1 -1
  386. package/test/fixtures/contracts/tool-definitions.json +3536 -0
  387. package/test/fixtures/coroutine-app/MainActivity.kt +100 -0
  388. package/test/fixtures/coroutine-app/README.md +48 -0
  389. package/test/fixtures/coroutine-app/build.ts +35 -0
  390. package/test/fixtures/debugger-app/MainActivity.java +21 -0
  391. package/test/fixtures/debugger-app/MainActivity.kt +32 -0
  392. package/test/fixtures/debugger-app/README.md +52 -0
  393. package/test/fixtures/debugger-app/build.ts +25 -15
  394. package/test/fixtures/latency-journey-app/README.md +2 -0
  395. package/test/fixtures/readiness-app/README.md +2 -0
  396. package/test/unit/debugger/coroutine-lifecycle.test.ts +43 -0
  397. package/test/unit/debugger/diagnostic-sessions.test.ts +67 -0
  398. package/test/unit/debugger/diagnostics.test.ts +80 -0
  399. package/test/unit/debugger/guards.test.ts +4 -0
  400. package/test/unit/debugger/preparation.test.ts +32 -0
  401. package/test/unit/debugger/protocol.test.ts +15 -0
  402. package/test/unit/debugger/sessions.test.ts +1 -1
  403. package/test/unit/interact/adjustment_service.test.ts +42 -0
  404. package/test/unit/interact/adjustment_verification.test.ts +35 -0
  405. package/test/unit/interact/clock_dependencies.test.ts +63 -0
  406. package/test/unit/interact/element_helpers.test.ts +19 -0
  407. package/test/unit/interact/legacy_wait_service.test.ts +17 -0
  408. package/test/unit/interact/state_comparison.test.ts +20 -0
  409. package/test/unit/interact/target_scope.test.ts +35 -0
  410. package/test/unit/network/get_network_activity.test.ts +1 -1
  411. package/test/unit/observe/screenshot-encoding.test.ts +30 -0
  412. package/test/unit/observe/snapshot-logs.test.ts +14 -0
  413. package/test/unit/quality/architecture.test.ts +60 -0
  414. package/test/unit/quality/code-quality.test.ts +58 -0
  415. package/test/unit/quality/interaction_boundaries.test.ts +24 -0
  416. package/test/unit/server/conditional_actions.test.ts +1 -1
  417. package/test/unit/server/conditional_schema.test.ts +2 -2
  418. package/test/unit/server/debugger-response.test.ts +63 -0
  419. package/test/unit/server/tool_definitions_parity.test.ts +8 -0
  420. package/dist/server/tool-handlers.js +0 -1000
  421. package/docs/ROADMAP.md +0 -646
  422. package/docs/benchmarks/ui-tree-performance/bridge-probe.json +0 -29
  423. package/docs/benchmarks/ui-tree-performance/initial-baseline.json +0 -59560
  424. package/docs/specs/baseline-spec-v0.md +0 -312
  425. package/docs/specs/mcp-tooling-spec-v1.md +0 -446
  426. package/src/server/debugger-handlers.ts +0 -29
  427. package/src/server/tool-handlers.ts +0 -1008
  428. /package/dist/{server/condition-schema.js → contracts/condition.js} +0 -0
  429. /package/dist/server/{readiness-schema.js → schemas/readiness-schema.js} +0 -0
  430. /package/dist/server/{system-trace-schema.js → schemas/system-trace-schema.js} +0 -0
  431. /package/src/{server/condition-schema.ts → contracts/condition.ts} +0 -0
  432. /package/src/server/{readiness-schema.ts → schemas/readiness-schema.ts} +0 -0
@@ -1,27 +1,9 @@
1
- import { currentEvidenceActionId } from '../observe/evidence.js'
2
1
  import { recordTiming } from '../utils/timing.js'
3
- import type {
4
- ActionTrace,
5
- ActionExecutionResult,
6
- ActionFailureCode,
7
- ActionTargetResolved,
8
- FailureClass,
9
- RecoveryState,
10
- TraceResult,
11
- TraceStage,
12
- TraceStep
13
- } from '../types.js'
14
- import { ToolsObserve } from '../observe/index.js'
15
- import { isConditionBlocked } from '../interact/conditional-action.js'
16
-
17
- export const DEFAULT_MAX_RECOVERY_ATTEMPTS = 3
18
- export const DEFAULT_MAX_RETRY_DEPTH = 3
19
-
20
2
  export function wrapResponse<T>(data: T, includeSerializationTiming = true) {
21
3
  const start = performance.now()
22
4
  let text = JSON.stringify(data, null, 2)
23
5
  const elapsed = performance.now() - start
24
- const value = data as any
6
+ const value = data as { timing?: { serialization_ms?: number } } | null
25
7
  if (includeSerializationTiming && value && typeof value === 'object' && value.timing && typeof value.timing === 'object') {
26
8
  value.timing.serialization_ms = (value.timing.serialization_ms ?? 0) + elapsed
27
9
  recordTiming('serialization_ms', elapsed)
@@ -86,351 +68,17 @@ export function getArrayArg<T>(args: ToolCallArgs, key: string): T[] | undefined
86
68
  return Array.isArray(value) ? value as T[] : undefined
87
69
  }
88
70
 
89
- let actionSequence = 0
90
-
91
- export function nextActionId(actionType: string, timestamp: number) {
92
- const current = currentEvidenceActionId()
93
- if (current) return current
94
- actionSequence += 1
95
- return `${actionType}_${timestamp}_${actionSequence}`
96
- }
97
-
98
- export async function captureActionFingerprint(platform?: 'android' | 'ios', deviceId?: string): Promise<string | null> {
99
- if (isConditionBlocked()) return null
100
- if (!platform) return null
101
- try {
102
- const result = await ToolsObserve.getScreenFingerprintHandler({ platform, deviceId }) as any
103
- return result?.fingerprint ?? null
104
- } catch {
105
- return null
106
- }
107
- }
108
-
109
- export function createTraceStep({
110
- stage,
111
- timestamp,
112
- result,
113
- attemptIndex,
114
- cycleId,
115
- metadata
116
- }: {
117
- stage: TraceStage
118
- timestamp: number
119
- result: TraceResult
120
- attemptIndex: number
121
- cycleId?: number
122
- metadata?: Record<string, unknown>
123
- }): TraceStep {
124
- return {
125
- stage,
126
- timestamp,
127
- result,
128
- attempt_index: attemptIndex,
129
- ...(cycleId !== undefined ? { cycle_id: cycleId } : {}),
130
- ...(metadata ? { metadata } : {})
131
- }
132
- }
133
-
134
- export function buildActionTrace({
135
- actionId,
136
- actionType,
137
- sourceModule,
138
- selector,
139
- resolved,
140
- success,
141
- failure,
142
- details,
143
- recovery,
144
- attempts = 1,
145
- steps
146
- }: {
147
- actionId: string
148
- actionType: string
149
- sourceModule: 'server' | 'interact'
150
- selector: Record<string, unknown> | null
151
- resolved?: Partial<ActionTargetResolved> | null
152
- success: boolean
153
- failure?: { failureCode: ActionFailureCode; retryable: boolean }
154
- details?: Record<string, unknown>
155
- recovery?: RecoveryState
156
- attempts?: number
157
- steps?: TraceStep[]
158
- }): ActionTrace {
159
- if (steps && steps.length > 0) {
160
- return {
161
- action_id: actionId,
162
- steps,
163
- final_outcome: success ? 'success' : 'failure',
164
- attempts: Math.max(1, Math.floor(attempts || steps.length))
165
- }
166
- }
167
-
168
- const start = Date.now()
169
- const builtSteps: TraceStep[] = []
170
- let attemptIndex = 0
171
- const totalAttempts = Math.max(1, Math.floor(attempts || 1))
172
-
173
- if (selector || resolved) {
174
- const stageResult: TraceResult = resolved ? 'success' : 'failure'
175
- builtSteps.push(createTraceStep({
176
- stage: 'resolve',
177
- timestamp: start,
178
- result: stageResult,
179
- attemptIndex: attemptIndex++,
180
- metadata: {
181
- action_type: actionType,
182
- source_module: sourceModule,
183
- selector: selector ?? null,
184
- resolved: resolved ? normalizeResolvedTarget(resolved) : null
185
- }
186
- }))
187
- }
188
-
189
- builtSteps.push(createTraceStep({
190
- stage: 'execute',
191
- timestamp: Date.now(),
192
- result: success ? 'success' : 'failure',
193
- attemptIndex: attemptIndex++,
194
- metadata: {
195
- action_type: actionType,
196
- source_module: sourceModule,
197
- ...(failure ? { failure_code: failure.failureCode, retryable: failure.retryable } : {})
198
- }
199
- }))
200
-
201
- const hasStabilizeDetails = Boolean(details && (
202
- Object.prototype.hasOwnProperty.call(details, 'stabilization_attempts') ||
203
- Object.prototype.hasOwnProperty.call(details, 'stable_observation_count') ||
204
- Object.prototype.hasOwnProperty.call(details, 'snapshot_freshness_ms')
205
- ))
206
- const hasVerifyDetails = Boolean(details && (
207
- Object.prototype.hasOwnProperty.call(details, 'within_tolerance') ||
208
- Object.prototype.hasOwnProperty.call(details, 'converged') ||
209
- Object.prototype.hasOwnProperty.call(details, 'observed_state')
210
- ))
211
-
212
- if (hasStabilizeDetails) {
213
- builtSteps.push(createTraceStep({
214
- stage: 'stabilize',
215
- timestamp: Date.now(),
216
- result: success ? 'success' : 'failure',
217
- attemptIndex: attemptIndex++,
218
- metadata: {
219
- stabilization_attempts: (details?.stabilization_attempts as number | undefined) ?? null,
220
- stable_observation_count: (details?.stable_observation_count as number | undefined) ?? null,
221
- snapshot_freshness_ms: (details?.snapshot_freshness_ms as number | undefined) ?? null
222
- }
223
- }))
224
- }
225
-
226
- if (hasVerifyDetails) {
227
- builtSteps.push(createTraceStep({
228
- stage: 'verify',
229
- timestamp: Date.now(),
230
- result: success ? 'success' : 'failure',
231
- attemptIndex: attemptIndex++,
232
- metadata: {
233
- within_tolerance: details?.within_tolerance ?? null,
234
- converged: details?.converged ?? null,
235
- actual_state: details?.actual_state ?? null,
236
- reason: details?.reason ?? null
237
- }
238
- }))
239
- }
240
-
241
- if (failure) {
242
- builtSteps.push(createTraceStep({
243
- stage: 'recover',
244
- timestamp: Date.now(),
245
- result: failure.retryable ? 'retry' : 'failure',
246
- attemptIndex: attemptIndex++,
247
- metadata: {
248
- failure_class: recovery?.failure_class ?? mapFailureCodeToFailureClass(failure.failureCode),
249
- runtime_code: failure.failureCode,
250
- retry_allowed: failure.retryable,
251
- recovery_attempts: recovery?.recovery_attempts ?? 0,
252
- retry_depth: recovery?.retry_depth ?? 0
253
- }
254
- }))
255
- }
256
-
257
- if (!builtSteps.length) {
258
- builtSteps.push(createTraceStep({
259
- stage: 'execute',
260
- timestamp: start,
261
- result: success ? 'success' : 'failure',
262
- attemptIndex: 0,
263
- metadata: { action_type: actionType, source_module: sourceModule }
264
- }))
265
- }
266
-
267
- return {
268
- action_id: actionId,
269
- steps: builtSteps,
270
- final_outcome: success ? 'success' : 'failure',
271
- attempts: totalAttempts
272
- }
273
- }
274
-
275
- export function normalizeResolvedTarget(value: Partial<ActionTargetResolved> | null = null): ActionTargetResolved | null {
276
- if (!value) return null
277
- return {
278
- elementId: value.elementId ?? null,
279
- text: value.text ?? null,
280
- resource_id: value.resource_id ?? null,
281
- accessibility_id: value.accessibility_id ?? null,
282
- class: value.class ?? null,
283
- bounds: value.bounds ?? null,
284
- index: value.index ?? null,
285
- state: value.state ?? null
286
- }
287
- }
288
-
289
- export function inferGenericFailure(message: string | undefined): { failureCode: ActionFailureCode; retryable: boolean } {
290
- if (message && /timeout/i.test(message)) return { failureCode: 'TIMEOUT', retryable: true }
291
- if (message && /semantic mismatch/i.test(message)) return { failureCode: 'SEMANTIC_MISMATCH', retryable: false }
292
- return { failureCode: 'UNKNOWN', retryable: false }
293
- }
294
-
295
- export function inferScrollFailure(message: string | undefined): { failureCode: ActionFailureCode; retryable: boolean } {
296
- if (message && /unchanged|no change|end of list/i.test(message)) return { failureCode: 'NAVIGATION_NO_CHANGE', retryable: true }
297
- if (message && /timeout/i.test(message)) return { failureCode: 'TIMEOUT', retryable: true }
298
- return { failureCode: 'UNKNOWN', retryable: false }
299
- }
300
-
301
- const ACTION_LIFECYCLE_STATE_BY_OUTCOME = {
302
- success: 'pending_verification',
303
- failure: 'failed'
304
- } as const
305
-
306
- export function determineActionLifecycleState({
307
- success,
308
- failure
309
- }: {
310
- success: boolean
311
- failure?: { failureCode: ActionFailureCode; retryable: boolean }
312
- }): NonNullable<ActionExecutionResult['lifecycle_state']> {
313
- if (failure) return ACTION_LIFECYCLE_STATE_BY_OUTCOME.failure
314
- if (success) return ACTION_LIFECYCLE_STATE_BY_OUTCOME.success
315
- return ACTION_LIFECYCLE_STATE_BY_OUTCOME.success
316
- }
317
-
318
- function mapFailureCodeToFailureClass(code: ActionFailureCode): FailureClass {
319
- switch (code) {
320
- case 'ELEMENT_NOT_FOUND':
321
- case 'AMBIGUOUS_TARGET':
322
- case 'STALE_REFERENCE':
323
- return 'TargetResolutionFailure'
324
- case 'ELEMENT_NOT_INTERACTABLE':
325
- case 'PRECONDITION_FAILED':
326
- case 'PRECONDITION_UNAVAILABLE':
327
- case 'ACTION_TIMEOUT':
328
- return 'ExecutionFailure'
329
- case 'TIMEOUT':
330
- case 'ACTION_REJECTED':
331
- case 'NAVIGATION_NO_CHANGE':
332
- case 'UNKNOWN':
333
- return 'ExecutionFailure'
334
- case 'VERIFICATION_FAILED':
335
- case 'EXPECT_STATE_MISMATCH':
336
- case 'ASSERTION_TIMEOUT':
337
- return 'VerificationFailure'
338
- case 'CONTROL_CONVERGENCE_FAILED':
339
- return 'ControlConvergenceFailure'
340
- case 'SEMANTIC_MISMATCH':
341
- return 'SemanticMismatchFailure'
342
- }
343
- }
344
-
345
- function buildRecoveryState(failureCode: ActionFailureCode, retryable: boolean): RecoveryState {
346
- return {
347
- failure_class: mapFailureCodeToFailureClass(failureCode),
348
- runtime_code: failureCode,
349
- recovery_attempts: 0,
350
- max_recovery_attempts: DEFAULT_MAX_RECOVERY_ATTEMPTS,
351
- retry_depth: 0,
352
- max_retry_depth: DEFAULT_MAX_RETRY_DEPTH,
353
- is_terminal: false,
354
- retry_allowed: retryable
355
- }
356
- }
357
-
358
- export function buildActionExecutionResult({
359
- actionType,
360
- device,
361
- selector,
362
- resolved,
363
- success,
364
- uiFingerprintBefore,
365
- uiFingerprintAfter,
366
- failure,
367
- details,
368
- sourceModule,
369
- traceSteps
370
- }: {
371
- actionType: string
372
- device?: ActionExecutionResult['device']
373
- selector: Record<string, unknown> | null
374
- resolved?: Partial<ActionTargetResolved> | null
375
- success: boolean
376
- uiFingerprintBefore: string | null
377
- uiFingerprintAfter: string | null
378
- failure?: { failureCode: ActionFailureCode; retryable: boolean }
379
- details?: Record<string, unknown>
380
- sourceModule: 'server' | 'interact'
381
- traceSteps?: TraceStep[]
382
- }): ActionExecutionResult {
383
- const timestampMs = Date.now()
384
- const timestamp = new Date(timestampMs).toISOString()
385
- const actionId = nextActionId(actionType, timestampMs)
386
- const recoveryState = failure ? buildRecoveryState(failure.failureCode, failure.retryable) : undefined
387
- const attempts = typeof details?.attempts === 'number' && Number.isFinite(details.attempts)
388
- ? Math.max(1, Math.floor(details.attempts))
389
- : 1
390
- return {
391
- action_id: actionId,
392
- timestamp,
393
- action_type: actionType,
394
- lifecycle_state: determineActionLifecycleState({ success, failure }),
395
- source_module: sourceModule,
396
- ...(device ? { device } : {}),
397
- target: {
398
- selector,
399
- resolved: normalizeResolvedTarget(resolved)
400
- },
401
- success,
402
- ...(failure ? { failure_code: failure.failureCode, retryable: failure.retryable } : {}),
403
- ...(recoveryState ? { recovery: recoveryState } : {}),
404
- trace: buildActionTrace({
405
- actionId,
406
- actionType,
407
- sourceModule,
408
- selector,
409
- resolved,
410
- success,
411
- failure,
412
- details,
413
- recovery: recoveryState,
414
- attempts,
415
- steps: traceSteps
416
- }),
417
- ui_fingerprint_before: uiFingerprintBefore,
418
- ui_fingerprint_after: uiFingerprintAfter,
419
- ...(details ? { details } : {})
420
- }
421
- }
422
-
423
71
  export function wrapToolError(name: string, error: unknown) {
424
72
  const message = error instanceof Error
425
73
  ? error.message
426
74
  : typeof error === 'object' && error !== null
427
75
  ? (() => {
428
- try {
429
- return JSON.stringify(error, null, 2)
430
- } catch {
431
- return '[unserializable error object]'
432
- }
433
- })()
76
+ try {
77
+ return JSON.stringify(error, null, 2)
78
+ } catch {
79
+ return '[unserializable error object]'
80
+ }
81
+ })()
434
82
  : String(error)
435
83
  return {
436
84
  content: [{
@@ -444,3 +92,5 @@ export function wrapToolError(name: string, error: unknown) {
444
92
  }]
445
93
  }
446
94
  }
95
+
96
+ export { DEFAULT_MAX_RECOVERY_ATTEMPTS, DEFAULT_MAX_RETRY_DEPTH, nextActionId, captureActionFingerprint, createTraceStep, buildActionTrace, normalizeResolvedTarget, inferGenericFailure, inferScrollFailure, determineActionLifecycleState, buildActionExecutionResult } from '../runtime/action-results.js'
@@ -0,0 +1,164 @@
1
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js'
2
+
3
+ export const assertTools: Tool[] = [
4
+ {
5
+ name: 'expect_screen',
6
+ description: `Purpose:
7
+ Deterministically verify that the intended navigation outcome of an action has occurred.
8
+
9
+ Inputs:
10
+ - fingerprint: preferred exact-match screen fingerprint
11
+ - screen: exact semantic screen identifier when a fingerprint is not available
12
+
13
+ Output Structure:
14
+ - success: true when the expected screen matches the observed screen
15
+ - observed_screen: current fingerprint and screen identifier
16
+ - expected_screen: the expected fingerprint and/or screen identifier
17
+ - confidence: 1 for an exact match, otherwise 0
18
+
19
+ Recommended Usage:
20
+ 1. Define the expected screen before executing the action
21
+ 2. Resolve the target element or screen state
22
+ 3. Call an action tool such as tap_element
23
+ 4. If needed, wait for transition using wait_for_*
24
+ 5. Call expect_screen as the final verification step
25
+ 6. If success=false, treat the outcome as unverified and follow the action tool retry guidance
26
+
27
+ Verification Guidance:
28
+ - Primary and authoritative verification tool for navigation outcomes
29
+ - Prefer fingerprint whenever you have one
30
+ - Use screen only as a fallback exact match against known identifiers
31
+ - Works best when the expected screen identifier is known ahead of time
32
+ - If the expected screen is not already known, capture or define it before executing the action
33
+
34
+ Constraints:
35
+ - Returns structured binary success/failure only
36
+ - Must not rely on natural-language interpretation or reasoning
37
+
38
+ Failure Handling:
39
+ - success=false means the expected screen was not reached; retry or recover using the action tool's failure strategy`,
40
+ inputSchema: {
41
+ type: 'object',
42
+ properties: {
43
+ platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override (android|ios)' },
44
+ fingerprint: { type: 'string', description: 'Expected screen fingerprint. Preferred verification mechanism.' },
45
+ screen: { type: 'string', description: 'Expected exact screen identifier when no fingerprint is available.' },
46
+ deviceId: { type: 'string', description: 'Optional device id/udid to target' }
47
+ }
48
+ }
49
+ },
50
+ {
51
+ name: 'expect_element_visible',
52
+ description: `Purpose:
53
+ Deterministically verify that the intended UI outcome of an action has occurred by confirming a target element is visible.
54
+
55
+ Inputs:
56
+ - selector: required selector used to resolve the target element
57
+ - element_id: optional previously resolved element identifier used only as context
58
+
59
+ Output Structure:
60
+ - success: true when the element is visible
61
+ - selector: selector used for verification
62
+ - element_id: resolved element identifier when available
63
+ - element: minimal resolved element info when visible
64
+ - failure_code: TIMEOUT or UNKNOWN when verification fails
65
+ - retryable: true when failure_code=TIMEOUT
66
+
67
+ Recommended Usage:
68
+ 1. Define the expected element state before executing the action
69
+ 2. Resolve the target element or triggering control
70
+ 3. Call an action tool such as tap_element
71
+ 4. If needed, wait for UI availability using wait_for_*
72
+ 5. Call expect_element_visible as the final verification step
73
+ 6. If success=false, follow the action tool retry guidance
74
+
75
+ Verification Guidance:
76
+ - Primary and authoritative verification tool for expected element appearance or visibility
77
+ - Use this when the screen should stay the same but the UI should reveal or update a specific element
78
+ - selector is the primary input; element_id is an optional optimization only
79
+ - The tool resolves the selector internally when needed
80
+
81
+ Constraints:
82
+ - Returns structured binary success/failure only
83
+ - Must not rely on natural-language interpretation or reasoning
84
+
85
+ Failure Handling:
86
+ - TIMEOUT → retry verification once or retry the action after re-resolving
87
+ - UNKNOWN → capture a snapshot and stop`,
88
+ inputSchema: {
89
+ type: 'object',
90
+ properties: {
91
+ selector: {
92
+ type: 'object',
93
+ properties: {
94
+ text: { type: 'string' },
95
+ resource_id: { type: 'string' },
96
+ accessibility_id: { type: 'string' },
97
+ contains: { type: 'boolean', default: false }
98
+ }
99
+ },
100
+ element_id: { type: 'string', description: 'Optional previously resolved element identifier.' },
101
+ timeout_ms: { type: 'number', default: 5000 },
102
+ poll_interval_ms: { type: 'number', default: 300 },
103
+ platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override' },
104
+ deviceId: { type: 'string', description: 'Optional device serial/udid' }
105
+ },
106
+ required: ['selector']
107
+ }
108
+ },
109
+ {
110
+ name: 'expect_state',
111
+ description: `Purpose:
112
+ Verify a readable UI state property on the currently visible element.
113
+
114
+ Inputs:
115
+ - selector or element_id
116
+ - property
117
+ - expected
118
+ - platform/deviceId (optional)
119
+
120
+ Supported properties:
121
+ - checked, selected, focused, expanded, enabled, text_value, value, raw_value
122
+
123
+ Verification Guidance:
124
+ - Use this when the UI element is visible but its state must also be confirmed
125
+ - Prefer the canonical property names above
126
+ - The tool compares the normalized readable state and returns the observed value when available
127
+
128
+ Constraints:
129
+ - Returns structured success/failure only
130
+ - Does not infer a state when the property is unavailable
131
+
132
+ Failure Handling:
133
+ - ELEMENT_NOT_FOUND → re-resolve the element or wait for UI stabilization
134
+ - UNKNOWN → capture a snapshot and stop`,
135
+ inputSchema: {
136
+ type: 'object',
137
+ properties: {
138
+ selector: {
139
+ type: 'object',
140
+ properties: {
141
+ text: { type: 'string' },
142
+ resource_id: { type: 'string' },
143
+ accessibility_id: { type: 'string' },
144
+ contains: { type: 'boolean', default: false }
145
+ }
146
+ },
147
+ element_id: { type: 'string', description: 'Optional previously resolved element identifier.' },
148
+ property: { type: 'string', description: 'Readable state property to verify.' },
149
+ expected: {
150
+ description: 'Expected normalized state value.',
151
+ oneOf: [
152
+ { type: 'boolean' },
153
+ { type: 'number' },
154
+ { type: 'string' },
155
+ { type: 'object' }
156
+ ]
157
+ },
158
+ platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override' },
159
+ deviceId: { type: 'string', description: 'Optional device serial/udid' }
160
+ },
161
+ required: ['property', 'expected']
162
+ }
163
+ }
164
+ ]
@@ -0,0 +1,120 @@
1
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js'
2
+
3
+ export const diagnosticTools: Tool[] = [
4
+ {
5
+ name: 'get_system_status',
6
+ description: 'Quick healthcheck of local mobile debugging environment (adb, devices, logs, env, iOS).',
7
+ inputSchema: { type: 'object', properties: {} }
8
+ },
9
+ {
10
+ name: 'classify_action_outcome',
11
+ description: `Classify the outcome of the most recent action into exactly one of: success, no_op, backend_failure, ui_failure, unknown.
12
+
13
+ Use the runtime action result's \`action_type\` as \`actionType\` so the classifier can distinguish local-state actions from side-effect actions.
14
+ Use this when the intended outcome is not already fully verified by the UI signal alone.
15
+ For backend/API activity, compare get_screen_fingerprint before and after the action and call get_network_activity immediately after the action if the outcome is still ambiguous.
16
+
17
+ HOW TO GATHER INPUTS before calling:
18
+ 1. Call wait_for_screen_change or compare get_screen_fingerprint before/after — set uiChanged accordingly.
19
+ 2. If you checked for a specific element with wait_for_ui, set expectedElementVisible.
20
+ 3. Pass actionType from the action response when available.
21
+ 4. Only provide networkRequests if you already collected them or want to classify a side-effect action with backend evidence.
22
+
23
+ RULES (applied in order — stop at first match):
24
+ 1. If uiChanged=true OR expectedElementVisible=true → outcome=success
25
+ 2. If actionType is missing → outcome=unknown
26
+ 3. If any request has status=failure or retryable → outcome=backend_failure
27
+ 4. If actionType maps to a local-state action → outcome=no_op; prefer state-based verification and avoid default network fallback
28
+ 5. If actionType maps to a side-effect action and no networkRequests were supplied → outcome=unknown
29
+ 6. If no requests returned → outcome=no_op
30
+ 7. If all requests succeeded → outcome=ui_failure
31
+ 8. Otherwise → outcome=unknown
32
+
33
+ BEHAVIOUR after outcome:
34
+ - success → continue
35
+ - no_op → retry with richer state verification or re-resolve the element
36
+ - backend_failure → stop and report the failing endpoint
37
+ - ui_failure → stop and report failure
38
+ - unknown → take one recovery step (e.g. capture_debug_snapshot), then stop`,
39
+ inputSchema: {
40
+ type: 'object',
41
+ properties: {
42
+ uiChanged: {
43
+ type: 'boolean',
44
+ description: 'true if the screen fingerprint or activity changed after the action. Use wait_for_screen_change or compare get_screen_fingerprint before and after.'
45
+ },
46
+ expectedElementVisible: {
47
+ type: 'boolean',
48
+ description: 'true if the element you expected to appear is now visible (from wait_for_ui). Omit if you did not check for a specific element.'
49
+ },
50
+ actionType: {
51
+ type: 'string',
52
+ description: 'The runtime action_type from the action response (for example tap, tap_element, swipe, type_text, press_back, start_app).'
53
+ },
54
+ networkRequests: {
55
+ type: 'array',
56
+ description: 'Optional network evidence collected after the action. Use it when the expected outcome is backend/API activity or when the UI signal is ambiguous.',
57
+ items: {
58
+ type: 'object',
59
+ properties: {
60
+ endpoint: { type: 'string', description: 'Request endpoint or full URL' },
61
+ status: { type: 'string', enum: ['success', 'failure', 'retryable'], description: 'Outcome of the request' }
62
+ },
63
+ required: ['endpoint', 'status']
64
+ }
65
+ },
66
+ hasLogErrors: {
67
+ type: 'boolean',
68
+ description: 'true if structured log errors were observed (e.g. from read_log_stream). Optional — include if you have already read logs.'
69
+ }
70
+ },
71
+ required: ['uiChanged']
72
+ }
73
+ },
74
+ {
75
+ name: 'run_journey',
76
+ description: 'Run an ordered, bounded sequence of start_app, tap, wait, and deterministic assertion steps against one device. Stops after the first failed step.',
77
+ inputSchema: {
78
+ type: 'object',
79
+ properties: {
80
+ platform: { type: 'string', enum: ['android', 'ios'] },
81
+ deviceId: { type: 'string' },
82
+ responseMode: { type: 'string', enum: ['compact', 'debug'], default: 'compact' },
83
+ steps: { type: 'array', minItems: 1, maxItems: 50, items: { type: 'object', properties: { id: { type: 'string' }, type: { type: 'string', enum: ['start_app', 'tap', 'wait', 'assert'] } }, required: ['id', 'type'] } }
84
+ },
85
+ required: ['platform', 'steps']
86
+ }
87
+ },
88
+ {
89
+ name: 'get_network_activity',
90
+ description: `Returns structured network events captured from platform logs since the last action.
91
+
92
+ Call this immediately after an action when you want backend evidence for a side-effect flow, only if the result is still ambiguous.
93
+ Do not call more than once per action.
94
+
95
+ Events are filtered to significant (non-background) requests only.
96
+ Each event includes endpoint, method, statusCode, networkError, status, and durationMs.
97
+
98
+ status values:
99
+ - success: HTTP 2xx or request detected with no error signal
100
+ - failure: HTTP 4xx
101
+ - retryable: HTTP 5xx, network error (timeout, dns_error, tls_error, etc.)
102
+
103
+ Returns { requests: [], count: 0 } when no credible network signals are found.`,
104
+ inputSchema: {
105
+ type: 'object',
106
+ properties: {
107
+ platform: {
108
+ type: 'string',
109
+ enum: ['android', 'ios'],
110
+ description: 'Platform to read network logs from'
111
+ },
112
+ deviceId: {
113
+ type: 'string',
114
+ description: 'Device Serial (Android) or UDID (iOS). Defaults to connected/booted device.'
115
+ }
116
+ },
117
+ required: ['platform']
118
+ }
119
+ }
120
+ ]