@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.
- package/CHANGELOG.md +34 -0
- package/README.md +266 -0
- package/dist/backends/adb-backend.cjs +442 -0
- package/dist/backends/adb-backend.cjs.map +1 -0
- package/dist/backends/adb-backend.d.cts +35 -0
- package/dist/backends/adb-backend.d.cts.map +1 -0
- package/dist/backends/adb-backend.d.mts +35 -0
- package/dist/backends/adb-backend.d.mts.map +1 -0
- package/dist/backends/adb-backend.mjs +436 -0
- package/dist/backends/adb-backend.mjs.map +1 -0
- package/dist/backends/appium-backend.cjs +518 -0
- package/dist/backends/appium-backend.cjs.map +1 -0
- package/dist/backends/appium-backend.d.cts +36 -0
- package/dist/backends/appium-backend.d.cts.map +1 -0
- package/dist/backends/appium-backend.d.mts +36 -0
- package/dist/backends/appium-backend.d.mts.map +1 -0
- package/dist/backends/appium-backend.mjs +512 -0
- package/dist/backends/appium-backend.mjs.map +1 -0
- package/dist/backends/idb-backend.cjs +442 -0
- package/dist/backends/idb-backend.cjs.map +1 -0
- package/dist/backends/idb-backend.d.cts +35 -0
- package/dist/backends/idb-backend.d.cts.map +1 -0
- package/dist/backends/idb-backend.d.mts +35 -0
- package/dist/backends/idb-backend.d.mts.map +1 -0
- package/dist/backends/idb-backend.mjs +436 -0
- package/dist/backends/idb-backend.mjs.map +1 -0
- package/dist/backends/index.cjs +134 -0
- package/dist/backends/index.cjs.map +1 -0
- package/dist/backends/index.d.cts +16 -0
- package/dist/backends/index.d.cts.map +1 -0
- package/dist/backends/index.d.mts +16 -0
- package/dist/backends/index.d.mts.map +1 -0
- package/dist/backends/index.mjs +126 -0
- package/dist/backends/index.mjs.map +1 -0
- package/dist/backends/session-file.cjs +62 -0
- package/dist/backends/session-file.cjs.map +1 -0
- package/dist/backends/session-file.d.cts +24 -0
- package/dist/backends/session-file.d.cts.map +1 -0
- package/dist/backends/session-file.d.mts +24 -0
- package/dist/backends/session-file.d.mts.map +1 -0
- package/dist/backends/session-file.mjs +59 -0
- package/dist/backends/session-file.mjs.map +1 -0
- package/dist/backends/types.cjs +3 -0
- package/dist/backends/types.cjs.map +1 -0
- package/dist/backends/types.d.cts +93 -0
- package/dist/backends/types.d.cts.map +1 -0
- package/dist/backends/types.d.mts +93 -0
- package/dist/backends/types.d.mts.map +1 -0
- package/dist/backends/types.mjs +2 -0
- package/dist/backends/types.mjs.map +1 -0
- package/dist/backends/webdriver-client.cjs +167 -0
- package/dist/backends/webdriver-client.cjs.map +1 -0
- package/dist/backends/webdriver-client.d.cts +55 -0
- package/dist/backends/webdriver-client.d.cts.map +1 -0
- package/dist/backends/webdriver-client.d.mts +55 -0
- package/dist/backends/webdriver-client.d.mts.map +1 -0
- package/dist/backends/webdriver-client.mjs +162 -0
- package/dist/backends/webdriver-client.mjs.map +1 -0
- package/dist/cli/device-mcp.cjs +20 -0
- package/dist/cli/device-mcp.cjs.map +1 -0
- package/dist/cli/device-mcp.d.cts +3 -0
- package/dist/cli/device-mcp.d.cts.map +1 -0
- package/dist/cli/device-mcp.d.mts +3 -0
- package/dist/cli/device-mcp.d.mts.map +1 -0
- package/dist/cli/device-mcp.mjs +18 -0
- package/dist/cli/device-mcp.mjs.map +1 -0
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server.cjs +43 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +4 -0
- package/dist/server.d.cts.map +1 -0
- package/dist/server.d.mts +4 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +40 -0
- package/dist/server.mjs.map +1 -0
- package/dist/tools/alert-text.cjs +25 -0
- package/dist/tools/alert-text.cjs.map +1 -0
- package/dist/tools/alert-text.d.cts +4 -0
- package/dist/tools/alert-text.d.cts.map +1 -0
- package/dist/tools/alert-text.d.mts +4 -0
- package/dist/tools/alert-text.d.mts.map +1 -0
- package/dist/tools/alert-text.mjs +22 -0
- package/dist/tools/alert-text.mjs.map +1 -0
- package/dist/tools/app-state.cjs +40 -0
- package/dist/tools/app-state.cjs.map +1 -0
- package/dist/tools/app-state.d.cts +4 -0
- package/dist/tools/app-state.d.cts.map +1 -0
- package/dist/tools/app-state.d.mts +4 -0
- package/dist/tools/app-state.d.mts.map +1 -0
- package/dist/tools/app-state.mjs +37 -0
- package/dist/tools/app-state.mjs.map +1 -0
- package/dist/tools/clipboard.cjs +42 -0
- package/dist/tools/clipboard.cjs.map +1 -0
- package/dist/tools/clipboard.d.cts +4 -0
- package/dist/tools/clipboard.d.cts.map +1 -0
- package/dist/tools/clipboard.d.mts +4 -0
- package/dist/tools/clipboard.d.mts.map +1 -0
- package/dist/tools/clipboard.mjs +39 -0
- package/dist/tools/clipboard.mjs.map +1 -0
- package/dist/tools/close-app.cjs +25 -0
- package/dist/tools/close-app.cjs.map +1 -0
- package/dist/tools/close-app.d.cts +4 -0
- package/dist/tools/close-app.d.cts.map +1 -0
- package/dist/tools/close-app.d.mts +4 -0
- package/dist/tools/close-app.d.mts.map +1 -0
- package/dist/tools/close-app.mjs +22 -0
- package/dist/tools/close-app.mjs.map +1 -0
- package/dist/tools/context.cjs +52 -0
- package/dist/tools/context.cjs.map +1 -0
- package/dist/tools/context.d.cts +4 -0
- package/dist/tools/context.d.cts.map +1 -0
- package/dist/tools/context.d.mts +4 -0
- package/dist/tools/context.d.mts.map +1 -0
- package/dist/tools/context.mjs +49 -0
- package/dist/tools/context.mjs.map +1 -0
- package/dist/tools/device-info.cjs +34 -0
- package/dist/tools/device-info.cjs.map +1 -0
- package/dist/tools/device-info.d.cts +4 -0
- package/dist/tools/device-info.d.cts.map +1 -0
- package/dist/tools/device-info.d.mts +4 -0
- package/dist/tools/device-info.d.mts.map +1 -0
- package/dist/tools/device-info.mjs +31 -0
- package/dist/tools/device-info.mjs.map +1 -0
- package/dist/tools/dismiss-alert.cjs +34 -0
- package/dist/tools/dismiss-alert.cjs.map +1 -0
- package/dist/tools/dismiss-alert.d.cts +4 -0
- package/dist/tools/dismiss-alert.d.cts.map +1 -0
- package/dist/tools/dismiss-alert.d.mts +4 -0
- package/dist/tools/dismiss-alert.d.mts.map +1 -0
- package/dist/tools/dismiss-alert.mjs +31 -0
- package/dist/tools/dismiss-alert.mjs.map +1 -0
- package/dist/tools/dismiss-keyboard.cjs +23 -0
- package/dist/tools/dismiss-keyboard.cjs.map +1 -0
- package/dist/tools/dismiss-keyboard.d.cts +4 -0
- package/dist/tools/dismiss-keyboard.d.cts.map +1 -0
- package/dist/tools/dismiss-keyboard.d.mts +4 -0
- package/dist/tools/dismiss-keyboard.d.mts.map +1 -0
- package/dist/tools/dismiss-keyboard.mjs +20 -0
- package/dist/tools/dismiss-keyboard.mjs.map +1 -0
- package/dist/tools/generate-locators.cjs +33 -0
- package/dist/tools/generate-locators.cjs.map +1 -0
- package/dist/tools/generate-locators.d.cts +4 -0
- package/dist/tools/generate-locators.d.cts.map +1 -0
- package/dist/tools/generate-locators.d.mts +4 -0
- package/dist/tools/generate-locators.d.mts.map +1 -0
- package/dist/tools/generate-locators.mjs +30 -0
- package/dist/tools/generate-locators.mjs.map +1 -0
- package/dist/tools/index.cjs +50 -0
- package/dist/tools/index.cjs.map +1 -0
- package/dist/tools/index.d.cts +24 -0
- package/dist/tools/index.d.cts.map +1 -0
- package/dist/tools/index.d.mts +24 -0
- package/dist/tools/index.d.mts.map +1 -0
- package/dist/tools/index.mjs +24 -0
- package/dist/tools/index.mjs.map +1 -0
- package/dist/tools/logs.cjs +48 -0
- package/dist/tools/logs.cjs.map +1 -0
- package/dist/tools/logs.d.cts +4 -0
- package/dist/tools/logs.d.cts.map +1 -0
- package/dist/tools/logs.d.mts +4 -0
- package/dist/tools/logs.d.mts.map +1 -0
- package/dist/tools/logs.mjs +45 -0
- package/dist/tools/logs.mjs.map +1 -0
- package/dist/tools/long-press.cjs +38 -0
- package/dist/tools/long-press.cjs.map +1 -0
- package/dist/tools/long-press.d.cts +4 -0
- package/dist/tools/long-press.d.cts.map +1 -0
- package/dist/tools/long-press.d.mts +4 -0
- package/dist/tools/long-press.d.mts.map +1 -0
- package/dist/tools/long-press.mjs +35 -0
- package/dist/tools/long-press.mjs.map +1 -0
- package/dist/tools/open-app.cjs +26 -0
- package/dist/tools/open-app.cjs.map +1 -0
- package/dist/tools/open-app.d.cts +4 -0
- package/dist/tools/open-app.d.cts.map +1 -0
- package/dist/tools/open-app.d.mts +4 -0
- package/dist/tools/open-app.d.mts.map +1 -0
- package/dist/tools/open-app.mjs +23 -0
- package/dist/tools/open-app.mjs.map +1 -0
- package/dist/tools/press-button.cjs +30 -0
- package/dist/tools/press-button.cjs.map +1 -0
- package/dist/tools/press-button.d.cts +4 -0
- package/dist/tools/press-button.d.cts.map +1 -0
- package/dist/tools/press-button.d.mts +4 -0
- package/dist/tools/press-button.d.mts.map +1 -0
- package/dist/tools/press-button.mjs +27 -0
- package/dist/tools/press-button.mjs.map +1 -0
- package/dist/tools/screen-recording.cjs +46 -0
- package/dist/tools/screen-recording.cjs.map +1 -0
- package/dist/tools/screen-recording.d.cts +4 -0
- package/dist/tools/screen-recording.d.cts.map +1 -0
- package/dist/tools/screen-recording.d.mts +4 -0
- package/dist/tools/screen-recording.d.mts.map +1 -0
- package/dist/tools/screen-recording.mjs +43 -0
- package/dist/tools/screen-recording.mjs.map +1 -0
- package/dist/tools/screenshot.cjs +42 -0
- package/dist/tools/screenshot.cjs.map +1 -0
- package/dist/tools/screenshot.d.cts +4 -0
- package/dist/tools/screenshot.d.cts.map +1 -0
- package/dist/tools/screenshot.d.mts +4 -0
- package/dist/tools/screenshot.d.mts.map +1 -0
- package/dist/tools/screenshot.mjs +39 -0
- package/dist/tools/screenshot.mjs.map +1 -0
- package/dist/tools/scroll-to-element.cjs +56 -0
- package/dist/tools/scroll-to-element.cjs.map +1 -0
- package/dist/tools/scroll-to-element.d.cts +4 -0
- package/dist/tools/scroll-to-element.d.cts.map +1 -0
- package/dist/tools/scroll-to-element.d.mts +4 -0
- package/dist/tools/scroll-to-element.d.mts.map +1 -0
- package/dist/tools/scroll-to-element.mjs +53 -0
- package/dist/tools/scroll-to-element.mjs.map +1 -0
- package/dist/tools/shared.cjs +11 -0
- package/dist/tools/shared.cjs.map +1 -0
- package/dist/tools/shared.d.cts +8 -0
- package/dist/tools/shared.d.cts.map +1 -0
- package/dist/tools/shared.d.mts +8 -0
- package/dist/tools/shared.d.mts.map +1 -0
- package/dist/tools/shared.mjs +8 -0
- package/dist/tools/shared.mjs.map +1 -0
- package/dist/tools/snapshot.cjs +37 -0
- package/dist/tools/snapshot.cjs.map +1 -0
- package/dist/tools/snapshot.d.cts +4 -0
- package/dist/tools/snapshot.d.cts.map +1 -0
- package/dist/tools/snapshot.d.mts +4 -0
- package/dist/tools/snapshot.d.mts.map +1 -0
- package/dist/tools/snapshot.mjs +34 -0
- package/dist/tools/snapshot.mjs.map +1 -0
- package/dist/tools/swipe.cjs +39 -0
- package/dist/tools/swipe.cjs.map +1 -0
- package/dist/tools/swipe.d.cts +4 -0
- package/dist/tools/swipe.d.cts.map +1 -0
- package/dist/tools/swipe.d.mts +4 -0
- package/dist/tools/swipe.d.mts.map +1 -0
- package/dist/tools/swipe.mjs +36 -0
- package/dist/tools/swipe.mjs.map +1 -0
- package/dist/tools/tap-coordinates.cjs +27 -0
- package/dist/tools/tap-coordinates.cjs.map +1 -0
- package/dist/tools/tap-coordinates.d.cts +4 -0
- package/dist/tools/tap-coordinates.d.cts.map +1 -0
- package/dist/tools/tap-coordinates.d.mts +4 -0
- package/dist/tools/tap-coordinates.d.mts.map +1 -0
- package/dist/tools/tap-coordinates.mjs +24 -0
- package/dist/tools/tap-coordinates.mjs.map +1 -0
- package/dist/tools/tap-element.cjs +46 -0
- package/dist/tools/tap-element.cjs.map +1 -0
- package/dist/tools/tap-element.d.cts +4 -0
- package/dist/tools/tap-element.d.cts.map +1 -0
- package/dist/tools/tap-element.d.mts +4 -0
- package/dist/tools/tap-element.d.mts.map +1 -0
- package/dist/tools/tap-element.mjs +43 -0
- package/dist/tools/tap-element.mjs.map +1 -0
- package/dist/tools/type-text.cjs +32 -0
- package/dist/tools/type-text.cjs.map +1 -0
- package/dist/tools/type-text.d.cts +4 -0
- package/dist/tools/type-text.d.cts.map +1 -0
- package/dist/tools/type-text.d.mts +4 -0
- package/dist/tools/type-text.d.mts.map +1 -0
- package/dist/tools/type-text.mjs +29 -0
- package/dist/tools/type-text.mjs.map +1 -0
- package/dist/tools/wait-for.cjs +51 -0
- package/dist/tools/wait-for.cjs.map +1 -0
- package/dist/tools/wait-for.d.cts +4 -0
- package/dist/tools/wait-for.d.cts.map +1 -0
- package/dist/tools/wait-for.d.mts +4 -0
- package/dist/tools/wait-for.d.mts.map +1 -0
- package/dist/tools/wait-for.mjs +48 -0
- package/dist/tools/wait-for.mjs.map +1 -0
- package/dist/tools/window-size.cjs +29 -0
- package/dist/tools/window-size.cjs.map +1 -0
- package/dist/tools/window-size.d.cts +4 -0
- package/dist/tools/window-size.d.cts.map +1 -0
- package/dist/tools/window-size.d.mts +4 -0
- package/dist/tools/window-size.d.mts.map +1 -0
- package/dist/tools/window-size.mjs +26 -0
- package/dist/tools/window-size.mjs.map +1 -0
- package/dist/utils/alert-labels.cjs +26 -0
- package/dist/utils/alert-labels.cjs.map +1 -0
- package/dist/utils/alert-labels.d.cts +3 -0
- package/dist/utils/alert-labels.d.cts.map +1 -0
- package/dist/utils/alert-labels.d.mts +3 -0
- package/dist/utils/alert-labels.d.mts.map +1 -0
- package/dist/utils/alert-labels.mjs +23 -0
- package/dist/utils/alert-labels.mjs.map +1 -0
- package/dist/utils/element.cjs +212 -0
- package/dist/utils/element.cjs.map +1 -0
- package/dist/utils/element.d.cts +24 -0
- package/dist/utils/element.d.cts.map +1 -0
- package/dist/utils/element.d.mts +24 -0
- package/dist/utils/element.d.mts.map +1 -0
- package/dist/utils/element.mjs +203 -0
- package/dist/utils/element.mjs.map +1 -0
- package/dist/utils/exec.cjs +57 -0
- package/dist/utils/exec.cjs.map +1 -0
- package/dist/utils/exec.d.cts +23 -0
- package/dist/utils/exec.d.cts.map +1 -0
- package/dist/utils/exec.d.mts +23 -0
- package/dist/utils/exec.d.mts.map +1 -0
- package/dist/utils/exec.mjs +52 -0
- package/dist/utils/exec.mjs.map +1 -0
- package/dist/utils/platform.cjs +93 -0
- package/dist/utils/platform.cjs.map +1 -0
- package/dist/utils/platform.d.cts +8 -0
- package/dist/utils/platform.d.cts.map +1 -0
- package/dist/utils/platform.d.mts +8 -0
- package/dist/utils/platform.d.mts.map +1 -0
- package/dist/utils/platform.mjs +90 -0
- package/dist/utils/platform.mjs.map +1 -0
- 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)
|