@metamask/device-mcp 0.1.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 (315) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +266 -0
  3. package/dist/backends/adb-backend.cjs +442 -0
  4. package/dist/backends/adb-backend.cjs.map +1 -0
  5. package/dist/backends/adb-backend.d.cts +35 -0
  6. package/dist/backends/adb-backend.d.cts.map +1 -0
  7. package/dist/backends/adb-backend.d.mts +35 -0
  8. package/dist/backends/adb-backend.d.mts.map +1 -0
  9. package/dist/backends/adb-backend.mjs +436 -0
  10. package/dist/backends/adb-backend.mjs.map +1 -0
  11. package/dist/backends/appium-backend.cjs +518 -0
  12. package/dist/backends/appium-backend.cjs.map +1 -0
  13. package/dist/backends/appium-backend.d.cts +36 -0
  14. package/dist/backends/appium-backend.d.cts.map +1 -0
  15. package/dist/backends/appium-backend.d.mts +36 -0
  16. package/dist/backends/appium-backend.d.mts.map +1 -0
  17. package/dist/backends/appium-backend.mjs +512 -0
  18. package/dist/backends/appium-backend.mjs.map +1 -0
  19. package/dist/backends/idb-backend.cjs +442 -0
  20. package/dist/backends/idb-backend.cjs.map +1 -0
  21. package/dist/backends/idb-backend.d.cts +35 -0
  22. package/dist/backends/idb-backend.d.cts.map +1 -0
  23. package/dist/backends/idb-backend.d.mts +35 -0
  24. package/dist/backends/idb-backend.d.mts.map +1 -0
  25. package/dist/backends/idb-backend.mjs +436 -0
  26. package/dist/backends/idb-backend.mjs.map +1 -0
  27. package/dist/backends/index.cjs +134 -0
  28. package/dist/backends/index.cjs.map +1 -0
  29. package/dist/backends/index.d.cts +16 -0
  30. package/dist/backends/index.d.cts.map +1 -0
  31. package/dist/backends/index.d.mts +16 -0
  32. package/dist/backends/index.d.mts.map +1 -0
  33. package/dist/backends/index.mjs +126 -0
  34. package/dist/backends/index.mjs.map +1 -0
  35. package/dist/backends/session-file.cjs +62 -0
  36. package/dist/backends/session-file.cjs.map +1 -0
  37. package/dist/backends/session-file.d.cts +24 -0
  38. package/dist/backends/session-file.d.cts.map +1 -0
  39. package/dist/backends/session-file.d.mts +24 -0
  40. package/dist/backends/session-file.d.mts.map +1 -0
  41. package/dist/backends/session-file.mjs +59 -0
  42. package/dist/backends/session-file.mjs.map +1 -0
  43. package/dist/backends/types.cjs +3 -0
  44. package/dist/backends/types.cjs.map +1 -0
  45. package/dist/backends/types.d.cts +93 -0
  46. package/dist/backends/types.d.cts.map +1 -0
  47. package/dist/backends/types.d.mts +93 -0
  48. package/dist/backends/types.d.mts.map +1 -0
  49. package/dist/backends/types.mjs +2 -0
  50. package/dist/backends/types.mjs.map +1 -0
  51. package/dist/backends/webdriver-client.cjs +167 -0
  52. package/dist/backends/webdriver-client.cjs.map +1 -0
  53. package/dist/backends/webdriver-client.d.cts +55 -0
  54. package/dist/backends/webdriver-client.d.cts.map +1 -0
  55. package/dist/backends/webdriver-client.d.mts +55 -0
  56. package/dist/backends/webdriver-client.d.mts.map +1 -0
  57. package/dist/backends/webdriver-client.mjs +162 -0
  58. package/dist/backends/webdriver-client.mjs.map +1 -0
  59. package/dist/cli/device-mcp.cjs +20 -0
  60. package/dist/cli/device-mcp.cjs.map +1 -0
  61. package/dist/cli/device-mcp.d.cts +3 -0
  62. package/dist/cli/device-mcp.d.cts.map +1 -0
  63. package/dist/cli/device-mcp.d.mts +3 -0
  64. package/dist/cli/device-mcp.d.mts.map +1 -0
  65. package/dist/cli/device-mcp.mjs +18 -0
  66. package/dist/cli/device-mcp.mjs.map +1 -0
  67. package/dist/index.cjs +9 -0
  68. package/dist/index.cjs.map +1 -0
  69. package/dist/index.d.cts +5 -0
  70. package/dist/index.d.cts.map +1 -0
  71. package/dist/index.d.mts +5 -0
  72. package/dist/index.d.mts.map +1 -0
  73. package/dist/index.mjs +3 -0
  74. package/dist/index.mjs.map +1 -0
  75. package/dist/server.cjs +43 -0
  76. package/dist/server.cjs.map +1 -0
  77. package/dist/server.d.cts +4 -0
  78. package/dist/server.d.cts.map +1 -0
  79. package/dist/server.d.mts +4 -0
  80. package/dist/server.d.mts.map +1 -0
  81. package/dist/server.mjs +40 -0
  82. package/dist/server.mjs.map +1 -0
  83. package/dist/tools/alert-text.cjs +25 -0
  84. package/dist/tools/alert-text.cjs.map +1 -0
  85. package/dist/tools/alert-text.d.cts +4 -0
  86. package/dist/tools/alert-text.d.cts.map +1 -0
  87. package/dist/tools/alert-text.d.mts +4 -0
  88. package/dist/tools/alert-text.d.mts.map +1 -0
  89. package/dist/tools/alert-text.mjs +22 -0
  90. package/dist/tools/alert-text.mjs.map +1 -0
  91. package/dist/tools/app-state.cjs +40 -0
  92. package/dist/tools/app-state.cjs.map +1 -0
  93. package/dist/tools/app-state.d.cts +4 -0
  94. package/dist/tools/app-state.d.cts.map +1 -0
  95. package/dist/tools/app-state.d.mts +4 -0
  96. package/dist/tools/app-state.d.mts.map +1 -0
  97. package/dist/tools/app-state.mjs +37 -0
  98. package/dist/tools/app-state.mjs.map +1 -0
  99. package/dist/tools/clipboard.cjs +42 -0
  100. package/dist/tools/clipboard.cjs.map +1 -0
  101. package/dist/tools/clipboard.d.cts +4 -0
  102. package/dist/tools/clipboard.d.cts.map +1 -0
  103. package/dist/tools/clipboard.d.mts +4 -0
  104. package/dist/tools/clipboard.d.mts.map +1 -0
  105. package/dist/tools/clipboard.mjs +39 -0
  106. package/dist/tools/clipboard.mjs.map +1 -0
  107. package/dist/tools/close-app.cjs +25 -0
  108. package/dist/tools/close-app.cjs.map +1 -0
  109. package/dist/tools/close-app.d.cts +4 -0
  110. package/dist/tools/close-app.d.cts.map +1 -0
  111. package/dist/tools/close-app.d.mts +4 -0
  112. package/dist/tools/close-app.d.mts.map +1 -0
  113. package/dist/tools/close-app.mjs +22 -0
  114. package/dist/tools/close-app.mjs.map +1 -0
  115. package/dist/tools/context.cjs +52 -0
  116. package/dist/tools/context.cjs.map +1 -0
  117. package/dist/tools/context.d.cts +4 -0
  118. package/dist/tools/context.d.cts.map +1 -0
  119. package/dist/tools/context.d.mts +4 -0
  120. package/dist/tools/context.d.mts.map +1 -0
  121. package/dist/tools/context.mjs +49 -0
  122. package/dist/tools/context.mjs.map +1 -0
  123. package/dist/tools/device-info.cjs +34 -0
  124. package/dist/tools/device-info.cjs.map +1 -0
  125. package/dist/tools/device-info.d.cts +4 -0
  126. package/dist/tools/device-info.d.cts.map +1 -0
  127. package/dist/tools/device-info.d.mts +4 -0
  128. package/dist/tools/device-info.d.mts.map +1 -0
  129. package/dist/tools/device-info.mjs +31 -0
  130. package/dist/tools/device-info.mjs.map +1 -0
  131. package/dist/tools/dismiss-alert.cjs +34 -0
  132. package/dist/tools/dismiss-alert.cjs.map +1 -0
  133. package/dist/tools/dismiss-alert.d.cts +4 -0
  134. package/dist/tools/dismiss-alert.d.cts.map +1 -0
  135. package/dist/tools/dismiss-alert.d.mts +4 -0
  136. package/dist/tools/dismiss-alert.d.mts.map +1 -0
  137. package/dist/tools/dismiss-alert.mjs +31 -0
  138. package/dist/tools/dismiss-alert.mjs.map +1 -0
  139. package/dist/tools/dismiss-keyboard.cjs +23 -0
  140. package/dist/tools/dismiss-keyboard.cjs.map +1 -0
  141. package/dist/tools/dismiss-keyboard.d.cts +4 -0
  142. package/dist/tools/dismiss-keyboard.d.cts.map +1 -0
  143. package/dist/tools/dismiss-keyboard.d.mts +4 -0
  144. package/dist/tools/dismiss-keyboard.d.mts.map +1 -0
  145. package/dist/tools/dismiss-keyboard.mjs +20 -0
  146. package/dist/tools/dismiss-keyboard.mjs.map +1 -0
  147. package/dist/tools/generate-locators.cjs +33 -0
  148. package/dist/tools/generate-locators.cjs.map +1 -0
  149. package/dist/tools/generate-locators.d.cts +4 -0
  150. package/dist/tools/generate-locators.d.cts.map +1 -0
  151. package/dist/tools/generate-locators.d.mts +4 -0
  152. package/dist/tools/generate-locators.d.mts.map +1 -0
  153. package/dist/tools/generate-locators.mjs +30 -0
  154. package/dist/tools/generate-locators.mjs.map +1 -0
  155. package/dist/tools/index.cjs +50 -0
  156. package/dist/tools/index.cjs.map +1 -0
  157. package/dist/tools/index.d.cts +24 -0
  158. package/dist/tools/index.d.cts.map +1 -0
  159. package/dist/tools/index.d.mts +24 -0
  160. package/dist/tools/index.d.mts.map +1 -0
  161. package/dist/tools/index.mjs +24 -0
  162. package/dist/tools/index.mjs.map +1 -0
  163. package/dist/tools/logs.cjs +48 -0
  164. package/dist/tools/logs.cjs.map +1 -0
  165. package/dist/tools/logs.d.cts +4 -0
  166. package/dist/tools/logs.d.cts.map +1 -0
  167. package/dist/tools/logs.d.mts +4 -0
  168. package/dist/tools/logs.d.mts.map +1 -0
  169. package/dist/tools/logs.mjs +45 -0
  170. package/dist/tools/logs.mjs.map +1 -0
  171. package/dist/tools/long-press.cjs +38 -0
  172. package/dist/tools/long-press.cjs.map +1 -0
  173. package/dist/tools/long-press.d.cts +4 -0
  174. package/dist/tools/long-press.d.cts.map +1 -0
  175. package/dist/tools/long-press.d.mts +4 -0
  176. package/dist/tools/long-press.d.mts.map +1 -0
  177. package/dist/tools/long-press.mjs +35 -0
  178. package/dist/tools/long-press.mjs.map +1 -0
  179. package/dist/tools/open-app.cjs +26 -0
  180. package/dist/tools/open-app.cjs.map +1 -0
  181. package/dist/tools/open-app.d.cts +4 -0
  182. package/dist/tools/open-app.d.cts.map +1 -0
  183. package/dist/tools/open-app.d.mts +4 -0
  184. package/dist/tools/open-app.d.mts.map +1 -0
  185. package/dist/tools/open-app.mjs +23 -0
  186. package/dist/tools/open-app.mjs.map +1 -0
  187. package/dist/tools/press-button.cjs +30 -0
  188. package/dist/tools/press-button.cjs.map +1 -0
  189. package/dist/tools/press-button.d.cts +4 -0
  190. package/dist/tools/press-button.d.cts.map +1 -0
  191. package/dist/tools/press-button.d.mts +4 -0
  192. package/dist/tools/press-button.d.mts.map +1 -0
  193. package/dist/tools/press-button.mjs +27 -0
  194. package/dist/tools/press-button.mjs.map +1 -0
  195. package/dist/tools/screen-recording.cjs +46 -0
  196. package/dist/tools/screen-recording.cjs.map +1 -0
  197. package/dist/tools/screen-recording.d.cts +4 -0
  198. package/dist/tools/screen-recording.d.cts.map +1 -0
  199. package/dist/tools/screen-recording.d.mts +4 -0
  200. package/dist/tools/screen-recording.d.mts.map +1 -0
  201. package/dist/tools/screen-recording.mjs +43 -0
  202. package/dist/tools/screen-recording.mjs.map +1 -0
  203. package/dist/tools/screenshot.cjs +42 -0
  204. package/dist/tools/screenshot.cjs.map +1 -0
  205. package/dist/tools/screenshot.d.cts +4 -0
  206. package/dist/tools/screenshot.d.cts.map +1 -0
  207. package/dist/tools/screenshot.d.mts +4 -0
  208. package/dist/tools/screenshot.d.mts.map +1 -0
  209. package/dist/tools/screenshot.mjs +39 -0
  210. package/dist/tools/screenshot.mjs.map +1 -0
  211. package/dist/tools/scroll-to-element.cjs +56 -0
  212. package/dist/tools/scroll-to-element.cjs.map +1 -0
  213. package/dist/tools/scroll-to-element.d.cts +4 -0
  214. package/dist/tools/scroll-to-element.d.cts.map +1 -0
  215. package/dist/tools/scroll-to-element.d.mts +4 -0
  216. package/dist/tools/scroll-to-element.d.mts.map +1 -0
  217. package/dist/tools/scroll-to-element.mjs +53 -0
  218. package/dist/tools/scroll-to-element.mjs.map +1 -0
  219. package/dist/tools/shared.cjs +11 -0
  220. package/dist/tools/shared.cjs.map +1 -0
  221. package/dist/tools/shared.d.cts +8 -0
  222. package/dist/tools/shared.d.cts.map +1 -0
  223. package/dist/tools/shared.d.mts +8 -0
  224. package/dist/tools/shared.d.mts.map +1 -0
  225. package/dist/tools/shared.mjs +8 -0
  226. package/dist/tools/shared.mjs.map +1 -0
  227. package/dist/tools/snapshot.cjs +37 -0
  228. package/dist/tools/snapshot.cjs.map +1 -0
  229. package/dist/tools/snapshot.d.cts +4 -0
  230. package/dist/tools/snapshot.d.cts.map +1 -0
  231. package/dist/tools/snapshot.d.mts +4 -0
  232. package/dist/tools/snapshot.d.mts.map +1 -0
  233. package/dist/tools/snapshot.mjs +34 -0
  234. package/dist/tools/snapshot.mjs.map +1 -0
  235. package/dist/tools/swipe.cjs +39 -0
  236. package/dist/tools/swipe.cjs.map +1 -0
  237. package/dist/tools/swipe.d.cts +4 -0
  238. package/dist/tools/swipe.d.cts.map +1 -0
  239. package/dist/tools/swipe.d.mts +4 -0
  240. package/dist/tools/swipe.d.mts.map +1 -0
  241. package/dist/tools/swipe.mjs +36 -0
  242. package/dist/tools/swipe.mjs.map +1 -0
  243. package/dist/tools/tap-coordinates.cjs +27 -0
  244. package/dist/tools/tap-coordinates.cjs.map +1 -0
  245. package/dist/tools/tap-coordinates.d.cts +4 -0
  246. package/dist/tools/tap-coordinates.d.cts.map +1 -0
  247. package/dist/tools/tap-coordinates.d.mts +4 -0
  248. package/dist/tools/tap-coordinates.d.mts.map +1 -0
  249. package/dist/tools/tap-coordinates.mjs +24 -0
  250. package/dist/tools/tap-coordinates.mjs.map +1 -0
  251. package/dist/tools/tap-element.cjs +46 -0
  252. package/dist/tools/tap-element.cjs.map +1 -0
  253. package/dist/tools/tap-element.d.cts +4 -0
  254. package/dist/tools/tap-element.d.cts.map +1 -0
  255. package/dist/tools/tap-element.d.mts +4 -0
  256. package/dist/tools/tap-element.d.mts.map +1 -0
  257. package/dist/tools/tap-element.mjs +43 -0
  258. package/dist/tools/tap-element.mjs.map +1 -0
  259. package/dist/tools/type-text.cjs +32 -0
  260. package/dist/tools/type-text.cjs.map +1 -0
  261. package/dist/tools/type-text.d.cts +4 -0
  262. package/dist/tools/type-text.d.cts.map +1 -0
  263. package/dist/tools/type-text.d.mts +4 -0
  264. package/dist/tools/type-text.d.mts.map +1 -0
  265. package/dist/tools/type-text.mjs +29 -0
  266. package/dist/tools/type-text.mjs.map +1 -0
  267. package/dist/tools/wait-for.cjs +51 -0
  268. package/dist/tools/wait-for.cjs.map +1 -0
  269. package/dist/tools/wait-for.d.cts +4 -0
  270. package/dist/tools/wait-for.d.cts.map +1 -0
  271. package/dist/tools/wait-for.d.mts +4 -0
  272. package/dist/tools/wait-for.d.mts.map +1 -0
  273. package/dist/tools/wait-for.mjs +48 -0
  274. package/dist/tools/wait-for.mjs.map +1 -0
  275. package/dist/tools/window-size.cjs +29 -0
  276. package/dist/tools/window-size.cjs.map +1 -0
  277. package/dist/tools/window-size.d.cts +4 -0
  278. package/dist/tools/window-size.d.cts.map +1 -0
  279. package/dist/tools/window-size.d.mts +4 -0
  280. package/dist/tools/window-size.d.mts.map +1 -0
  281. package/dist/tools/window-size.mjs +26 -0
  282. package/dist/tools/window-size.mjs.map +1 -0
  283. package/dist/utils/alert-labels.cjs +26 -0
  284. package/dist/utils/alert-labels.cjs.map +1 -0
  285. package/dist/utils/alert-labels.d.cts +3 -0
  286. package/dist/utils/alert-labels.d.cts.map +1 -0
  287. package/dist/utils/alert-labels.d.mts +3 -0
  288. package/dist/utils/alert-labels.d.mts.map +1 -0
  289. package/dist/utils/alert-labels.mjs +23 -0
  290. package/dist/utils/alert-labels.mjs.map +1 -0
  291. package/dist/utils/element.cjs +212 -0
  292. package/dist/utils/element.cjs.map +1 -0
  293. package/dist/utils/element.d.cts +24 -0
  294. package/dist/utils/element.d.cts.map +1 -0
  295. package/dist/utils/element.d.mts +24 -0
  296. package/dist/utils/element.d.mts.map +1 -0
  297. package/dist/utils/element.mjs +203 -0
  298. package/dist/utils/element.mjs.map +1 -0
  299. package/dist/utils/exec.cjs +57 -0
  300. package/dist/utils/exec.cjs.map +1 -0
  301. package/dist/utils/exec.d.cts +23 -0
  302. package/dist/utils/exec.d.cts.map +1 -0
  303. package/dist/utils/exec.d.mts +23 -0
  304. package/dist/utils/exec.d.mts.map +1 -0
  305. package/dist/utils/exec.mjs +52 -0
  306. package/dist/utils/exec.mjs.map +1 -0
  307. package/dist/utils/platform.cjs +93 -0
  308. package/dist/utils/platform.cjs.map +1 -0
  309. package/dist/utils/platform.d.cts +8 -0
  310. package/dist/utils/platform.d.cts.map +1 -0
  311. package/dist/utils/platform.d.mts +8 -0
  312. package/dist/utils/platform.d.mts.map +1 -0
  313. package/dist/utils/platform.mjs +90 -0
  314. package/dist/utils/platform.mjs.map +1 -0
  315. package/package.json +106 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0]
11
+
12
+ ### Added
13
+
14
+ - Initial release of `@metamask/device-mcp`
15
+ - MCP server with stdio transport and lazy backend initialization
16
+ - Three backends: iOS (IDB), Android (ADB), Appium/BrowserStack (W3C WebDriver)
17
+ - `.device-session` file for attaching to existing Appium sessions or creating new ones
18
+ - 16 MCP tools: `device_snapshot`, `device_screenshot`, `device_info`, `device_tap_element`, `device_tap_coordinates`, `device_type`, `device_swipe`, `device_long_press`, `device_wait_for`, `device_app_state`, `device_open_app`, `device_close_app`, `device_press_button`, `device_dismiss_keyboard`, `device_dismiss_alert`, `device_logs`
19
+ - Fuzzy element matching (case-insensitive, partial text)
20
+ - Android tree-structured UI hierarchy parser (nested, not flat)
21
+ - iOS `snapshotMaxDepth` and `mobile: source` fallback for deep hierarchies
22
+ - Auto-detection of platform from `.device-session`, `DEVICE_ID`, or booted simulator/emulator
23
+ - Runtime health check with actionable error messages for missing IDB/ADB
24
+ - SKILL.md agent reference with core loop, common patterns, and platform differences
25
+ - MetaMask module template compliance (ts-bridge, dual CJS/ESM, yarn constraints, CI workflows)
26
+
27
+ ### Fixed
28
+
29
+ - fix: GH workflow release creation
30
+ - chore: add additional tools
31
+ - chore: add MetaMask Security Code Scanner workflow
32
+
33
+ [Unreleased]: https://github.com/MetaMask/device-mcp/compare/v0.1.0...HEAD
34
+ [0.1.0]: https://github.com/MetaMask/device-mcp/releases/tag/v0.1.0
package/README.md ADDED
@@ -0,0 +1,266 @@
1
+ # @metamask/device-mcp
2
+
3
+ MCP server for mobile device interaction — iOS (IDB), Android (ADB), and remote devices (Appium/BrowserStack).
4
+
5
+ Provides device interaction tools for LLM agents to inspect UI state, interact with elements, capture evidence, and control app lifecycle. Works standalone for debugging or as part of the [self-healing test infrastructure](https://github.com/MetaMask/metamask-mobile) for MetaMask Mobile.
6
+
7
+ ## Use Cases
8
+
9
+ - **Debugging locally** — attach to a running simulator/emulator from your AI coding agent, inspect what's on screen, tap elements, check logs
10
+ - **Debugging Appium tests** — attach to a live Appium session while a test is running to see what the test sees
11
+ - **Self-healing tests** — the healer agent uses these tools to recover from test failures by finding alternative UI paths
12
+ - **Exploratory testing** — let an agent navigate the app, exercise flows, and collect evidence
13
+ - **Building E2E tests** — discover element identifiers, labels, and layout to write test assertions
14
+
15
+ ## Requirements
16
+
17
+ - **Node.js** `^20 || ^22 || >=24`
18
+ - **iOS local**: [IDB](https://fbidb.io/) (`brew tap facebook/fb && brew install idb-companion && pip3 install fb-idb`)
19
+ - **Android local**: ADB (Android SDK platform-tools)
20
+ - **Remote/BrowserStack**: No local tools needed — connects via Appium W3C WebDriver HTTP
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ yarn add @metamask/device-mcp
26
+ ```
27
+
28
+ Or run directly:
29
+
30
+ ```bash
31
+ npx @metamask/device-mcp
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ The server communicates over stdio using the [Model Context Protocol](https://modelcontextprotocol.io/). It starts immediately and defers device connection to the first tool call — so the MCP handshake completes even when no device is available yet.
37
+
38
+ ```bash
39
+ # Auto-detect connected device
40
+ device-mcp
41
+
42
+ # Target a specific device
43
+ DEVICE_ID=<udid-or-serial> device-mcp
44
+ ```
45
+
46
+ ### Backend Selection
47
+
48
+ The server selects a backend in this order:
49
+
50
+ 1. **`.device-session` file** — if present in the working directory, connects via Appium (local or BrowserStack)
51
+ 2. **`DEVICE_ID` env var** — format determines platform (UUID = iOS, serial = Android)
52
+ 3. **Auto-detect** — checks for booted iOS simulator (IDB), then connected Android device (ADB)
53
+
54
+ ### BrowserStack / Appium
55
+
56
+ For remote devices or cloud testing, create a `.device-session` file in the working directory.
57
+
58
+ **Attach to an existing Appium session (local):**
59
+
60
+ ```json
61
+ {
62
+ "appiumUrl": "http://localhost:4723",
63
+ "sessionId": "abc123-def456",
64
+ "platform": "ios"
65
+ }
66
+ ```
67
+
68
+ **Attach to a BrowserStack session:**
69
+
70
+ ```json
71
+ {
72
+ "appiumUrl": "https://hub-cloud.browserstack.com/wd/hub",
73
+ "sessionId": "abc123-def456",
74
+ "platform": "android",
75
+ "auth": {
76
+ "user": "YOUR_USERNAME",
77
+ "key": "YOUR_ACCESS_KEY"
78
+ }
79
+ }
80
+ ```
81
+
82
+ **Create a new BrowserStack session:**
83
+
84
+ ```json
85
+ {
86
+ "appiumUrl": "https://hub-cloud.browserstack.com/wd/hub",
87
+ "platform": "ios",
88
+ "capabilities": {
89
+ "platformName": "iOS",
90
+ "appium:deviceName": "iPhone 15",
91
+ "appium:app": "bs://app-hash",
92
+ "bstack:options": { "userName": "...", "accessKey": "..." }
93
+ },
94
+ "auth": {
95
+ "user": "YOUR_USERNAME",
96
+ "key": "YOUR_ACCESS_KEY"
97
+ }
98
+ }
99
+ ```
100
+
101
+ The `.device-session` file is typically written by the test runner when it creates an Appium session, and read by the MCP server when healing or agent interaction is needed.
102
+
103
+ ## Tools
104
+
105
+ ### Inspection
106
+
107
+ | Tool | Description |
108
+ | ------------------- | ---------------------------------------------------------------- |
109
+ | `device_snapshot` | Capture the UI accessibility hierarchy. Call before interacting. |
110
+ | `device_screenshot` | Capture a screenshot as base64 PNG. Optionally save to file. |
111
+ | `device_info` | Get device platform, name, OS version, and device ID. |
112
+ | `device_app_state` | Check if an app is running, installed, or absent. |
113
+ | `device_logs` | Capture recent device logs (syslog/logcat) with optional filter. |
114
+
115
+ ### Interaction
116
+
117
+ | Tool | Description |
118
+ | ------------------------ | ------------------------------------------------------------------ |
119
+ | `device_tap_element` | Find an element by label/identifier/text/type and tap its center. |
120
+ | `device_tap_coordinates` | Tap at exact screen coordinates. Last resort when queries fail. |
121
+ | `device_type` | Type text into the currently focused input field. |
122
+ | `device_swipe` | Swipe in a direction with optional start coordinates and distance. |
123
+ | `device_long_press` | Long press an element for context menus or drag initiation. |
124
+ | `device_wait_for` | Poll until an element matching a query appears. |
125
+ | `device_press_button` | Press a device button (home/back/enter/lock). |
126
+
127
+ ### App & Device Control
128
+
129
+ | Tool | Description |
130
+ | ------------------------- | ------------------------------------------------------ |
131
+ | `device_open_app` | Launch or foreground an app by bundle ID. |
132
+ | `device_close_app` | Force-stop an app by bundle ID. |
133
+ | `device_dismiss_keyboard` | Hide the on-screen keyboard after typing. |
134
+ | `device_dismiss_alert` | Accept or dismiss a system alert or permission dialog. |
135
+
136
+ ### Element Identification
137
+
138
+ Elements are identified by accessibility attributes — not internal refs. Matching is **fuzzy**: partial text and case-insensitive matches work. For example, querying `{ label: "Confirm" }` matches an element with label `"Confirm Transaction"`.
139
+
140
+ - **iOS**: accessibility label, accessibility identifier
141
+ - **Android**: content-description, resource-id, text
142
+
143
+ ### Backend Implementation
144
+
145
+ | Tool | iOS (IDB) | Android (ADB) | Appium (W3C WebDriver) |
146
+ | ------------------------- | ----------------------- | -------------------- | ----------------------------- |
147
+ | `device_snapshot` | `idb ui describe-all` | `uiautomator dump` | `mobile: source` |
148
+ | `device_screenshot` | `idb screenshot` | `screencap` + `pull` | `mobile: getScreenshot` |
149
+ | `device_info` | `idb describe` | `getprop` | session capabilities |
150
+ | `device_tap_element` | find + `idb ui tap` | find + `input tap` | find + W3C Actions |
151
+ | `device_tap_coordinates` | `idb ui tap x y` | `input tap x y` | W3C Actions |
152
+ | `device_type` | `idb ui text` | `input text` | `findElement` + `sendKeys` |
153
+ | `device_swipe` | `idb ui swipe` | `input swipe` | W3C Actions |
154
+ | `device_long_press` | `idb ui tap --duration` | `input swipe` (hold) | W3C Actions (pause) |
155
+ | `device_wait_for` | poll snapshot | poll snapshot | poll snapshot |
156
+ | `device_app_state` | `idb list-apps` | `dumpsys activity` | `mobile: queryAppState` |
157
+ | `device_open_app` | `idb launch` | `monkey -p` | `mobile: activateApp` |
158
+ | `device_close_app` | `idb terminate` | `am force-stop` | `mobile: terminateApp` |
159
+ | `device_press_button` | `idb ui key` | `input keyevent` | `mobile: pressButton/Key` |
160
+ | `device_dismiss_keyboard` | `idb ui key RETURN` | `input keyevent 111` | `mobile: hideKeyboard` |
161
+ | `device_dismiss_alert` | find button + tap | find button + tap | `mobile: accept/dismissAlert` |
162
+ | `device_logs` | `idb log` | `logcat` | `mobile: getLog` |
163
+
164
+ ## MCP Client Configuration
165
+
166
+ ### opencode
167
+
168
+ Add to `~/.config/opencode/opencode.json`:
169
+
170
+ ```json
171
+ {
172
+ "mcp": {
173
+ "device": {
174
+ "type": "local",
175
+ "command": ["node", "/path/to/metamask-device-mcp/dist/index.js"],
176
+ "environment": {
177
+ "PATH": "/path/to/idb/bin:/usr/local/bin:/usr/bin:/bin"
178
+ }
179
+ }
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### Cursor
185
+
186
+ Add to `.cursor/mcp.json` in your project root:
187
+
188
+ ```json
189
+ {
190
+ "mcpServers": {
191
+ "device": {
192
+ "command": "npx",
193
+ "args": ["-y", "@metamask/device-mcp"]
194
+ }
195
+ }
196
+ }
197
+ ```
198
+
199
+ ### Claude Code
200
+
201
+ Add to `.claude/settings.json` in your project root:
202
+
203
+ ```json
204
+ {
205
+ "mcpServers": {
206
+ "device": {
207
+ "command": "npx",
208
+ "args": ["-y", "@metamask/device-mcp"]
209
+ }
210
+ }
211
+ }
212
+ ```
213
+
214
+ ### Claude Desktop
215
+
216
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
217
+
218
+ ```json
219
+ {
220
+ "mcpServers": {
221
+ "device": {
222
+ "command": "npx",
223
+ "args": ["-y", "@metamask/device-mcp"],
224
+ "env": {
225
+ "DEVICE_ID": "<optional-device-id>"
226
+ }
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ ## Architecture
233
+
234
+ ```
235
+ @metamask/device-mcp
236
+ ├── src/
237
+ │ ├── index.ts # Entry point — lazy backend, stdio MCP server
238
+ │ ├── server.ts # MCP server — registers 16 tools
239
+ │ ├── backends/
240
+ │ │ ├── types.ts # DeviceBackend interface (16 operations)
241
+ │ │ ├── idb-backend.ts # iOS local — IDB commands + JSON parser
242
+ │ │ ├── adb-backend.ts # Android local — ADB commands + XML parser
243
+ │ │ ├── appium-backend.ts # Remote — Appium/BrowserStack via W3C WebDriver
244
+ │ │ ├── webdriver-client.ts # Minimal W3C WebDriver HTTP client (fetch)
245
+ │ │ ├── session-file.ts # .device-session file reader
246
+ │ │ └── index.ts # createBackend() + createLazyBackend() factory
247
+ │ ├── tools/ # One file per MCP tool (16 tools)
248
+ │ └── utils/
249
+ │ ├── exec.ts # Shell execution wrapper
250
+ │ ├── platform.ts # Platform auto-detection
251
+ │ └── element.ts # Element search, matching, formatting
252
+ ```
253
+
254
+ ## Development
255
+
256
+ ```bash
257
+ yarn build # Compile TypeScript
258
+ yarn test # Run tests (69 tests)
259
+ yarn lint # Lint everything (ESLint + Prettier + changelog)
260
+ yarn lint:fix # Auto-fix lint issues
261
+ yarn dev # Watch mode compilation
262
+ ```
263
+
264
+ ## License
265
+
266
+ (MIT OR Apache-2.0)