@metamask/device-mcp 0.2.0 → 0.3.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 (107) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/README.md +55 -2
  3. package/dist/backends/adb-backend.cjs +12 -5
  4. package/dist/backends/adb-backend.cjs.map +1 -1
  5. package/dist/backends/adb-backend.d.cts +8 -2
  6. package/dist/backends/adb-backend.d.cts.map +1 -1
  7. package/dist/backends/adb-backend.d.mts +8 -2
  8. package/dist/backends/adb-backend.d.mts.map +1 -1
  9. package/dist/backends/adb-backend.mjs +12 -5
  10. package/dist/backends/adb-backend.mjs.map +1 -1
  11. package/dist/backends/appium-backend.cjs +16 -7
  12. package/dist/backends/appium-backend.cjs.map +1 -1
  13. package/dist/backends/appium-backend.d.cts +8 -2
  14. package/dist/backends/appium-backend.d.cts.map +1 -1
  15. package/dist/backends/appium-backend.d.mts +8 -2
  16. package/dist/backends/appium-backend.d.mts.map +1 -1
  17. package/dist/backends/appium-backend.mjs +16 -7
  18. package/dist/backends/appium-backend.mjs.map +1 -1
  19. package/dist/backends/idb-backend.cjs +12 -5
  20. package/dist/backends/idb-backend.cjs.map +1 -1
  21. package/dist/backends/idb-backend.d.cts +8 -2
  22. package/dist/backends/idb-backend.d.cts.map +1 -1
  23. package/dist/backends/idb-backend.d.mts +8 -2
  24. package/dist/backends/idb-backend.d.mts.map +1 -1
  25. package/dist/backends/idb-backend.mjs +12 -5
  26. package/dist/backends/idb-backend.mjs.map +1 -1
  27. package/dist/backends/index.cjs +4 -3
  28. package/dist/backends/index.cjs.map +1 -1
  29. package/dist/backends/index.d.cts.map +1 -1
  30. package/dist/backends/index.d.mts.map +1 -1
  31. package/dist/backends/index.mjs +4 -3
  32. package/dist/backends/index.mjs.map +1 -1
  33. package/dist/backends/types.cjs.map +1 -1
  34. package/dist/backends/types.d.cts +19 -1
  35. package/dist/backends/types.d.cts.map +1 -1
  36. package/dist/backends/types.d.mts +19 -1
  37. package/dist/backends/types.d.mts.map +1 -1
  38. package/dist/backends/types.mjs.map +1 -1
  39. package/dist/hermes/hermes-cdp.cjs +674 -0
  40. package/dist/hermes/hermes-cdp.cjs.map +1 -0
  41. package/dist/hermes/hermes-cdp.d.cts +248 -0
  42. package/dist/hermes/hermes-cdp.d.cts.map +1 -0
  43. package/dist/hermes/hermes-cdp.d.mts +248 -0
  44. package/dist/hermes/hermes-cdp.d.mts.map +1 -0
  45. package/dist/hermes/hermes-cdp.mjs +665 -0
  46. package/dist/hermes/hermes-cdp.mjs.map +1 -0
  47. package/dist/hermes/session.cjs +196 -0
  48. package/dist/hermes/session.cjs.map +1 -0
  49. package/dist/hermes/session.d.cts +163 -0
  50. package/dist/hermes/session.d.cts.map +1 -0
  51. package/dist/hermes/session.d.mts +163 -0
  52. package/dist/hermes/session.d.mts.map +1 -0
  53. package/dist/hermes/session.mjs +189 -0
  54. package/dist/hermes/session.mjs.map +1 -0
  55. package/dist/index.cjs +16 -1
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.d.cts +5 -1
  58. package/dist/index.d.cts.map +1 -1
  59. package/dist/index.d.mts +5 -1
  60. package/dist/index.d.mts.map +1 -1
  61. package/dist/index.mjs +2 -0
  62. package/dist/index.mjs.map +1 -1
  63. package/dist/server.cjs +3 -0
  64. package/dist/server.cjs.map +1 -1
  65. package/dist/server.d.cts.map +1 -1
  66. package/dist/server.d.mts.map +1 -1
  67. package/dist/server.mjs +4 -1
  68. package/dist/server.mjs.map +1 -1
  69. package/dist/tools/hermes-cdp.cjs +102 -0
  70. package/dist/tools/hermes-cdp.cjs.map +1 -0
  71. package/dist/tools/hermes-cdp.d.cts +18 -0
  72. package/dist/tools/hermes-cdp.d.cts.map +1 -0
  73. package/dist/tools/hermes-cdp.d.mts +18 -0
  74. package/dist/tools/hermes-cdp.d.mts.map +1 -0
  75. package/dist/tools/hermes-cdp.mjs +99 -0
  76. package/dist/tools/hermes-cdp.mjs.map +1 -0
  77. package/dist/tools/hermes-targets.cjs +127 -0
  78. package/dist/tools/hermes-targets.cjs.map +1 -0
  79. package/dist/tools/hermes-targets.d.cts +20 -0
  80. package/dist/tools/hermes-targets.d.cts.map +1 -0
  81. package/dist/tools/hermes-targets.d.mts +20 -0
  82. package/dist/tools/hermes-targets.d.mts.map +1 -0
  83. package/dist/tools/hermes-targets.mjs +124 -0
  84. package/dist/tools/hermes-targets.mjs.map +1 -0
  85. package/dist/tools/index.cjs +5 -1
  86. package/dist/tools/index.cjs.map +1 -1
  87. package/dist/tools/index.d.cts +2 -0
  88. package/dist/tools/index.d.cts.map +1 -1
  89. package/dist/tools/index.d.mts +2 -0
  90. package/dist/tools/index.d.mts.map +1 -1
  91. package/dist/tools/index.mjs +2 -0
  92. package/dist/tools/index.mjs.map +1 -1
  93. package/dist/tools/screenshot.cjs +8 -3
  94. package/dist/tools/screenshot.cjs.map +1 -1
  95. package/dist/tools/screenshot.d.cts.map +1 -1
  96. package/dist/tools/screenshot.d.mts.map +1 -1
  97. package/dist/tools/screenshot.mjs +8 -3
  98. package/dist/tools/screenshot.mjs.map +1 -1
  99. package/dist/utils/output-path.cjs +128 -0
  100. package/dist/utils/output-path.cjs.map +1 -0
  101. package/dist/utils/output-path.d.cts +41 -0
  102. package/dist/utils/output-path.d.cts.map +1 -0
  103. package/dist/utils/output-path.d.mts +41 -0
  104. package/dist/utils/output-path.d.mts.map +1 -0
  105. package/dist/utils/output-path.mjs +123 -0
  106. package/dist/utils/output-path.mjs.map +1 -0
  107. package/package.json +1 -1
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Standalone state module for Hermes CDP sessions.
3
+ *
4
+ * Holds the resolved Metro inspector proxy port, the expected app bundle ID,
5
+ * and the pinned Hermes `logicalDeviceId`. This module is intentionally
6
+ * INDEPENDENT of `DeviceBackend` — Hermes/Metro access is orthogonal to the
7
+ * idb/adb/Appium UI automation backends, and it stores no live connection
8
+ * (the MVP opens a fresh WebSocket per tool call).
9
+ *
10
+ * Resolution precedence for both port and appId is exactly:
11
+ * per-call param > environment variable > default.
12
+ */
13
+ /**
14
+ * Platform identifier used to pick the default app bundle ID.
15
+ *
16
+ * Kept structurally compatible with the backend's `Platform` type without
17
+ * importing it, so this module stays decoupled from `DeviceBackend`.
18
+ */
19
+ export type HermesPlatform = 'ios' | 'android';
20
+ /** Default MetaMask app bundle ID for iOS (`CFBundleIdentifier`). */
21
+ export declare const DEFAULT_IOS_APP_ID = "io.metamask.MetaMask";
22
+ /** Default MetaMask app bundle ID for Android (`applicationId`). */
23
+ export declare const DEFAULT_ANDROID_APP_ID = "io.metamask";
24
+ /** Default Metro inspector proxy port when none is configured. */
25
+ export declare const DEFAULT_METRO_PORT = 8081;
26
+ /**
27
+ * Resolve the platform default app bundle ID.
28
+ *
29
+ * Defaults to the iOS id when the platform is unknown. Agents running against
30
+ * Android should pass `appId` per call or set `HERMES_APP_ID` if they are not
31
+ * using the default Android bundle ID.
32
+ *
33
+ * @param platform - The active platform, if known.
34
+ * @returns The default app bundle ID for the platform.
35
+ */
36
+ export declare function defaultAppIdForPlatform(platform?: HermesPlatform): string;
37
+ /**
38
+ * Per-call overrides accepted by {@link HermesSession.resolve}.
39
+ *
40
+ * Any field left undefined falls back to the session default (which itself
41
+ * falls back to the relevant environment variable, then the hardcoded default).
42
+ */
43
+ export type HermesSessionResolveInput = {
44
+ /** Override the Metro inspector proxy port for this call. */
45
+ metroPort?: number;
46
+ /** Override the expected app bundle ID for this call. */
47
+ appId?: string;
48
+ /**
49
+ * The active platform, used to compute the default app bundle ID AT CALL
50
+ * TIME when neither a per-call `appId` nor the `HERMES_APP_ID` env var is set.
51
+ * This lets a session created before a device connects (when the platform is
52
+ * not yet known) still resolve the correct Android default once the backend's
53
+ * live platform becomes `'android'`.
54
+ */
55
+ platform?: HermesPlatform;
56
+ };
57
+ /**
58
+ * The effective Hermes session parameters for a single tool call.
59
+ */
60
+ export type HermesSessionResolved = {
61
+ /** The resolved Metro inspector proxy port. */
62
+ metroPort: number;
63
+ /** The resolved expected app bundle ID. */
64
+ appId: string;
65
+ /** The pinned Hermes `logicalDeviceId`, if one has been set. */
66
+ pinnedDeviceId: string | undefined;
67
+ };
68
+ /**
69
+ * Options for constructing a {@link HermesSession}.
70
+ */
71
+ export type HermesSessionOptions = {
72
+ /** The active platform, used only to pick the default app bundle ID. */
73
+ platform?: HermesPlatform;
74
+ /**
75
+ * Environment source. Defaults to `process.env`. Injectable for tests so
76
+ * resolution precedence can be exercised without mutating the real env.
77
+ */
78
+ env?: NodeJS.ProcessEnv;
79
+ };
80
+ /**
81
+ * Holds the Metro port, expected appId, and pinned Hermes deviceId for a
82
+ * process-lifetime Hermes session.
83
+ *
84
+ * The stored port and appId are resolved once at construction time using
85
+ * `param ?? env ?? default` precedence (with `param` being undefined at
86
+ * construction — those are supplied per call to {@link resolve}). The pinned
87
+ * deviceId starts unset and is assigned on the first successful identity probe.
88
+ */
89
+ export declare class HermesSession {
90
+ #private;
91
+ constructor(options?: HermesSessionOptions);
92
+ /**
93
+ * Returns the session-default Metro inspector proxy port.
94
+ *
95
+ * @returns The resolved Metro port (env `HERMES_METRO_PORT`, else 8081).
96
+ */
97
+ getMetroPort(): number;
98
+ /**
99
+ * Returns the session-default expected app bundle ID.
100
+ *
101
+ * @returns The resolved app bundle ID (env `HERMES_APP_ID`, else the
102
+ * platform default).
103
+ */
104
+ getAppId(): string;
105
+ /**
106
+ * Returns the pinned Hermes `logicalDeviceId` for this session, if any.
107
+ *
108
+ * Set on the first successful identity probe via
109
+ * {@link setPinnedHermesDeviceId}. Persists for the lifetime of this
110
+ * instance.
111
+ *
112
+ * @returns The pinned `logicalDeviceId`, or undefined when no pin is set.
113
+ */
114
+ getPinnedHermesDeviceId(): string | undefined;
115
+ /**
116
+ * Pin the Hermes `logicalDeviceId` for this session.
117
+ *
118
+ * Called after a successful identity probe on the first call. Subsequent
119
+ * calls then enforce this pin to prevent target switching mid-session.
120
+ *
121
+ * @param id - The `logicalDeviceId` from Metro's
122
+ * `reactNative.logicalDeviceId` field.
123
+ */
124
+ setPinnedHermesDeviceId(id: string): void;
125
+ /**
126
+ * Resolve the effective parameters for a single tool call by merging
127
+ * per-call overrides over the stored session defaults.
128
+ *
129
+ * This is PURE with respect to stored state: it never mutates the stored
130
+ * port, appId, or pin. Only {@link setPinnedHermesDeviceId} mutates the pin.
131
+ *
132
+ * Port precedence is `input.metroPort ?? this.getMetroPort()` (which itself
133
+ * already encodes `env ?? default`), mirroring the reference driver's
134
+ * `input.metroPort ?? driver.getMetroPort() ?? 8081`.
135
+ *
136
+ * AppId precedence is `input.appId ?? <HERMES_APP_ID env if set> ??
137
+ * defaultAppIdForPlatform(input.platform) ?? <stored default>`. An explicitly
138
+ * set `HERMES_APP_ID` always WINS over the per-call platform default (env is a
139
+ * deliberate override): in that case the stored appId is returned unchanged.
140
+ * Only when the stored appId is the construction-time platform fallback does
141
+ * a per-call `platform` recompute it — without mutating stored state. When no
142
+ * `platform` is passed this preserves the original iOS-default behavior.
143
+ *
144
+ * @param input - Optional per-call overrides for port, appId, and platform.
145
+ * @returns The effective `{ metroPort, appId, pinnedDeviceId }`.
146
+ */
147
+ resolve(input?: HermesSessionResolveInput): HermesSessionResolved;
148
+ }
149
+ /**
150
+ * Returns the process-singleton {@link HermesSession}, creating it on first
151
+ * access.
152
+ *
153
+ * @param options - Options applied only when the singleton is first created.
154
+ * @returns The shared `HermesSession` instance.
155
+ */
156
+ export declare function getHermesSession(options?: HermesSessionOptions): HermesSession;
157
+ /**
158
+ * Resets the process-singleton {@link HermesSession}.
159
+ *
160
+ * Intended for test isolation so each test can start from a clean session.
161
+ */
162
+ export declare function resetHermesSession(): void;
163
+ //# sourceMappingURL=session.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.mts","sourceRoot":"","sources":["../../src/hermes/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,CAAC;AAE/C,qEAAqE;AACrE,eAAO,MAAM,kBAAkB,yBAAyB,CAAC;AAEzD,oEAAoE;AACpE,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD,kEAAkE;AAClE,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAEzE;AAgBD;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,CAAC;AAEF;;;;;;;;GAQG;AACH,qBAAa,aAAa;;gBAiBZ,OAAO,GAAE,oBAAyB;IAY9C;;;;OAIG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;OAKG;IACH,QAAQ,IAAI,MAAM;IAIlB;;;;;;;;OAQG;IACH,uBAAuB,IAAI,MAAM,GAAG,SAAS;IAI7C;;;;;;;;OAQG;IACH,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIzC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,KAAK,GAAE,yBAA8B,GAAG,qBAAqB;CAqBtE;AAOD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,aAAa,CAKf;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Standalone state module for Hermes CDP sessions.
3
+ *
4
+ * Holds the resolved Metro inspector proxy port, the expected app bundle ID,
5
+ * and the pinned Hermes `logicalDeviceId`. This module is intentionally
6
+ * INDEPENDENT of `DeviceBackend` — Hermes/Metro access is orthogonal to the
7
+ * idb/adb/Appium UI automation backends, and it stores no live connection
8
+ * (the MVP opens a fresh WebSocket per tool call).
9
+ *
10
+ * Resolution precedence for both port and appId is exactly:
11
+ * per-call param > environment variable > default.
12
+ */
13
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
14
+ if (kind === "m") throw new TypeError("Private method is not writable");
15
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
16
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
17
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
18
+ };
19
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
20
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
21
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
22
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
23
+ };
24
+ var _HermesSession_instances, _HermesSession_metroPort, _HermesSession_expectedAppId, _HermesSession_appIdFromEnv, _HermesSession_pinnedDeviceId, _HermesSession_resolveAppId;
25
+ /** Default MetaMask app bundle ID for iOS (`CFBundleIdentifier`). */
26
+ export const DEFAULT_IOS_APP_ID = 'io.metamask.MetaMask';
27
+ /** Default MetaMask app bundle ID for Android (`applicationId`). */
28
+ export const DEFAULT_ANDROID_APP_ID = 'io.metamask';
29
+ /** Default Metro inspector proxy port when none is configured. */
30
+ export const DEFAULT_METRO_PORT = 8081;
31
+ /**
32
+ * Resolve the platform default app bundle ID.
33
+ *
34
+ * Defaults to the iOS id when the platform is unknown. Agents running against
35
+ * Android should pass `appId` per call or set `HERMES_APP_ID` if they are not
36
+ * using the default Android bundle ID.
37
+ *
38
+ * @param platform - The active platform, if known.
39
+ * @returns The default app bundle ID for the platform.
40
+ */
41
+ export function defaultAppIdForPlatform(platform) {
42
+ return platform === 'android' ? DEFAULT_ANDROID_APP_ID : DEFAULT_IOS_APP_ID;
43
+ }
44
+ /**
45
+ * Parse a Metro port from an environment variable value.
46
+ *
47
+ * @param value - The raw environment variable value (or undefined).
48
+ * @returns The parsed integer port, or undefined when unset/invalid.
49
+ */
50
+ function parseEnvPort(value) {
51
+ if (value === undefined) {
52
+ return undefined;
53
+ }
54
+ const parsed = Number.parseInt(value, 10);
55
+ return Number.isInteger(parsed) ? parsed : undefined;
56
+ }
57
+ /**
58
+ * Holds the Metro port, expected appId, and pinned Hermes deviceId for a
59
+ * process-lifetime Hermes session.
60
+ *
61
+ * The stored port and appId are resolved once at construction time using
62
+ * `param ?? env ?? default` precedence (with `param` being undefined at
63
+ * construction — those are supplied per call to {@link resolve}). The pinned
64
+ * deviceId starts unset and is assigned on the first successful identity probe.
65
+ */
66
+ export class HermesSession {
67
+ constructor(options = {}) {
68
+ _HermesSession_instances.add(this);
69
+ _HermesSession_metroPort.set(this, void 0);
70
+ _HermesSession_expectedAppId.set(this, void 0);
71
+ /**
72
+ * Whether {@link #expectedAppId} came from an explicit source (the
73
+ * `HERMES_APP_ID` env var) rather than the platform fallback. When true, the
74
+ * stored appId is a deliberate override and {@link resolve} must not let a
75
+ * per-call `platform` recompute it; when false, the stored value is only the
76
+ * construction-time platform default and `resolve` may recompute it from the
77
+ * live platform.
78
+ */
79
+ _HermesSession_appIdFromEnv.set(this, void 0);
80
+ _HermesSession_pinnedDeviceId.set(this, void 0);
81
+ const env = options.env ?? process.env;
82
+ __classPrivateFieldSet(this, _HermesSession_metroPort, parseEnvPort(env.HERMES_METRO_PORT) ?? DEFAULT_METRO_PORT, "f");
83
+ __classPrivateFieldSet(this, _HermesSession_appIdFromEnv, env.HERMES_APP_ID !== undefined, "f");
84
+ __classPrivateFieldSet(this, _HermesSession_expectedAppId, env.HERMES_APP_ID ?? defaultAppIdForPlatform(options.platform), "f");
85
+ __classPrivateFieldSet(this, _HermesSession_pinnedDeviceId, undefined, "f");
86
+ }
87
+ /**
88
+ * Returns the session-default Metro inspector proxy port.
89
+ *
90
+ * @returns The resolved Metro port (env `HERMES_METRO_PORT`, else 8081).
91
+ */
92
+ getMetroPort() {
93
+ return __classPrivateFieldGet(this, _HermesSession_metroPort, "f");
94
+ }
95
+ /**
96
+ * Returns the session-default expected app bundle ID.
97
+ *
98
+ * @returns The resolved app bundle ID (env `HERMES_APP_ID`, else the
99
+ * platform default).
100
+ */
101
+ getAppId() {
102
+ return __classPrivateFieldGet(this, _HermesSession_expectedAppId, "f");
103
+ }
104
+ /**
105
+ * Returns the pinned Hermes `logicalDeviceId` for this session, if any.
106
+ *
107
+ * Set on the first successful identity probe via
108
+ * {@link setPinnedHermesDeviceId}. Persists for the lifetime of this
109
+ * instance.
110
+ *
111
+ * @returns The pinned `logicalDeviceId`, or undefined when no pin is set.
112
+ */
113
+ getPinnedHermesDeviceId() {
114
+ return __classPrivateFieldGet(this, _HermesSession_pinnedDeviceId, "f");
115
+ }
116
+ /**
117
+ * Pin the Hermes `logicalDeviceId` for this session.
118
+ *
119
+ * Called after a successful identity probe on the first call. Subsequent
120
+ * calls then enforce this pin to prevent target switching mid-session.
121
+ *
122
+ * @param id - The `logicalDeviceId` from Metro's
123
+ * `reactNative.logicalDeviceId` field.
124
+ */
125
+ setPinnedHermesDeviceId(id) {
126
+ __classPrivateFieldSet(this, _HermesSession_pinnedDeviceId, id, "f");
127
+ }
128
+ /**
129
+ * Resolve the effective parameters for a single tool call by merging
130
+ * per-call overrides over the stored session defaults.
131
+ *
132
+ * This is PURE with respect to stored state: it never mutates the stored
133
+ * port, appId, or pin. Only {@link setPinnedHermesDeviceId} mutates the pin.
134
+ *
135
+ * Port precedence is `input.metroPort ?? this.getMetroPort()` (which itself
136
+ * already encodes `env ?? default`), mirroring the reference driver's
137
+ * `input.metroPort ?? driver.getMetroPort() ?? 8081`.
138
+ *
139
+ * AppId precedence is `input.appId ?? <HERMES_APP_ID env if set> ??
140
+ * defaultAppIdForPlatform(input.platform) ?? <stored default>`. An explicitly
141
+ * set `HERMES_APP_ID` always WINS over the per-call platform default (env is a
142
+ * deliberate override): in that case the stored appId is returned unchanged.
143
+ * Only when the stored appId is the construction-time platform fallback does
144
+ * a per-call `platform` recompute it — without mutating stored state. When no
145
+ * `platform` is passed this preserves the original iOS-default behavior.
146
+ *
147
+ * @param input - Optional per-call overrides for port, appId, and platform.
148
+ * @returns The effective `{ metroPort, appId, pinnedDeviceId }`.
149
+ */
150
+ resolve(input = {}) {
151
+ return {
152
+ metroPort: input.metroPort ?? __classPrivateFieldGet(this, _HermesSession_metroPort, "f"),
153
+ appId: input.appId ?? __classPrivateFieldGet(this, _HermesSession_instances, "m", _HermesSession_resolveAppId).call(this, input.platform),
154
+ pinnedDeviceId: __classPrivateFieldGet(this, _HermesSession_pinnedDeviceId, "f"),
155
+ };
156
+ }
157
+ }
158
+ _HermesSession_metroPort = new WeakMap(), _HermesSession_expectedAppId = new WeakMap(), _HermesSession_appIdFromEnv = new WeakMap(), _HermesSession_pinnedDeviceId = new WeakMap(), _HermesSession_instances = new WeakSet(), _HermesSession_resolveAppId = function _HermesSession_resolveAppId(platform) {
159
+ if (__classPrivateFieldGet(this, _HermesSession_appIdFromEnv, "f") || platform === undefined) {
160
+ return __classPrivateFieldGet(this, _HermesSession_expectedAppId, "f");
161
+ }
162
+ return defaultAppIdForPlatform(platform);
163
+ };
164
+ /**
165
+ * Module-level lazily-created singleton instance.
166
+ */
167
+ let sessionInstance;
168
+ /**
169
+ * Returns the process-singleton {@link HermesSession}, creating it on first
170
+ * access.
171
+ *
172
+ * @param options - Options applied only when the singleton is first created.
173
+ * @returns The shared `HermesSession` instance.
174
+ */
175
+ export function getHermesSession(options) {
176
+ if (sessionInstance === undefined) {
177
+ sessionInstance = new HermesSession(options);
178
+ }
179
+ return sessionInstance;
180
+ }
181
+ /**
182
+ * Resets the process-singleton {@link HermesSession}.
183
+ *
184
+ * Intended for test isolation so each test can start from a clean session.
185
+ */
186
+ export function resetHermesSession() {
187
+ sessionInstance = undefined;
188
+ }
189
+ //# sourceMappingURL=session.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.mjs","sourceRoot":"","sources":["../../src/hermes/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;AAUH,qEAAqE;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AAEzD,oEAAoE;AACpE,MAAM,CAAC,MAAM,sBAAsB,GAAG,aAAa,CAAC;AAEpD,kEAAkE;AAClE,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEvC;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAyB;IAC/D,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,kBAAkB,CAAC;AAC9E,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,KAAyB;IAC7C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAgDD;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAa;IAiBxB,YAAY,UAAgC,EAAE;;QAhBrC,2CAAmB;QAEnB,+CAAuB;QAEhC;;;;;;;WAOG;QACM,8CAAuB;QAEhC,gDAAoC;QAGlC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;QAEvC,uBAAA,IAAI,4BAAc,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,kBAAkB,MAAA,CAAC;QAE5E,uBAAA,IAAI,+BAAiB,GAAG,CAAC,aAAa,KAAK,SAAS,MAAA,CAAC;QACrD,uBAAA,IAAI,gCACF,GAAG,CAAC,aAAa,IAAI,uBAAuB,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAA,CAAC;QAEjE,uBAAA,IAAI,iCAAmB,SAAS,MAAA,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACH,YAAY;QACV,OAAO,uBAAA,IAAI,gCAAW,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACN,OAAO,uBAAA,IAAI,oCAAe,CAAC;IAC7B,CAAC;IAED;;;;;;;;OAQG;IACH,uBAAuB;QACrB,OAAO,uBAAA,IAAI,qCAAgB,CAAC;IAC9B,CAAC;IAED;;;;;;;;OAQG;IACH,uBAAuB,CAAC,EAAU;QAChC,uBAAA,IAAI,iCAAmB,EAAE,MAAA,CAAC;IAC5B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,QAAmC,EAAE;QAC3C,OAAO;YACL,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,uBAAA,IAAI,gCAAW;YAC7C,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,uBAAA,IAAI,6DAAc,MAAlB,IAAI,EAAe,KAAK,CAAC,QAAQ,CAAC;YACxD,cAAc,EAAE,uBAAA,IAAI,qCAAgB;SACrC,CAAC;IACJ,CAAC;CAeF;iSANe,QAAoC;IAChD,IAAI,uBAAA,IAAI,mCAAc,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QACjD,OAAO,uBAAA,IAAI,oCAAe,CAAC;IAC7B,CAAC;IACD,OAAO,uBAAuB,CAAC,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAGH;;GAEG;AACH,IAAI,eAA0C,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAA8B;IAE9B,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,eAAe,GAAG,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB;IAChC,eAAe,GAAG,SAAS,CAAC;AAC9B,CAAC","sourcesContent":["/**\n * Standalone state module for Hermes CDP sessions.\n *\n * Holds the resolved Metro inspector proxy port, the expected app bundle ID,\n * and the pinned Hermes `logicalDeviceId`. This module is intentionally\n * INDEPENDENT of `DeviceBackend` — Hermes/Metro access is orthogonal to the\n * idb/adb/Appium UI automation backends, and it stores no live connection\n * (the MVP opens a fresh WebSocket per tool call).\n *\n * Resolution precedence for both port and appId is exactly:\n * per-call param > environment variable > default.\n */\n\n/**\n * Platform identifier used to pick the default app bundle ID.\n *\n * Kept structurally compatible with the backend's `Platform` type without\n * importing it, so this module stays decoupled from `DeviceBackend`.\n */\nexport type HermesPlatform = 'ios' | 'android';\n\n/** Default MetaMask app bundle ID for iOS (`CFBundleIdentifier`). */\nexport const DEFAULT_IOS_APP_ID = 'io.metamask.MetaMask';\n\n/** Default MetaMask app bundle ID for Android (`applicationId`). */\nexport const DEFAULT_ANDROID_APP_ID = 'io.metamask';\n\n/** Default Metro inspector proxy port when none is configured. */\nexport const DEFAULT_METRO_PORT = 8081;\n\n/**\n * Resolve the platform default app bundle ID.\n *\n * Defaults to the iOS id when the platform is unknown. Agents running against\n * Android should pass `appId` per call or set `HERMES_APP_ID` if they are not\n * using the default Android bundle ID.\n *\n * @param platform - The active platform, if known.\n * @returns The default app bundle ID for the platform.\n */\nexport function defaultAppIdForPlatform(platform?: HermesPlatform): string {\n return platform === 'android' ? DEFAULT_ANDROID_APP_ID : DEFAULT_IOS_APP_ID;\n}\n\n/**\n * Parse a Metro port from an environment variable value.\n *\n * @param value - The raw environment variable value (or undefined).\n * @returns The parsed integer port, or undefined when unset/invalid.\n */\nfunction parseEnvPort(value: string | undefined): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n const parsed = Number.parseInt(value, 10);\n return Number.isInteger(parsed) ? parsed : undefined;\n}\n\n/**\n * Per-call overrides accepted by {@link HermesSession.resolve}.\n *\n * Any field left undefined falls back to the session default (which itself\n * falls back to the relevant environment variable, then the hardcoded default).\n */\nexport type HermesSessionResolveInput = {\n /** Override the Metro inspector proxy port for this call. */\n metroPort?: number;\n /** Override the expected app bundle ID for this call. */\n appId?: string;\n /**\n * The active platform, used to compute the default app bundle ID AT CALL\n * TIME when neither a per-call `appId` nor the `HERMES_APP_ID` env var is set.\n * This lets a session created before a device connects (when the platform is\n * not yet known) still resolve the correct Android default once the backend's\n * live platform becomes `'android'`.\n */\n platform?: HermesPlatform;\n};\n\n/**\n * The effective Hermes session parameters for a single tool call.\n */\nexport type HermesSessionResolved = {\n /** The resolved Metro inspector proxy port. */\n metroPort: number;\n /** The resolved expected app bundle ID. */\n appId: string;\n /** The pinned Hermes `logicalDeviceId`, if one has been set. */\n pinnedDeviceId: string | undefined;\n};\n\n/**\n * Options for constructing a {@link HermesSession}.\n */\nexport type HermesSessionOptions = {\n /** The active platform, used only to pick the default app bundle ID. */\n platform?: HermesPlatform;\n /**\n * Environment source. Defaults to `process.env`. Injectable for tests so\n * resolution precedence can be exercised without mutating the real env.\n */\n env?: NodeJS.ProcessEnv;\n};\n\n/**\n * Holds the Metro port, expected appId, and pinned Hermes deviceId for a\n * process-lifetime Hermes session.\n *\n * The stored port and appId are resolved once at construction time using\n * `param ?? env ?? default` precedence (with `param` being undefined at\n * construction — those are supplied per call to {@link resolve}). The pinned\n * deviceId starts unset and is assigned on the first successful identity probe.\n */\nexport class HermesSession {\n readonly #metroPort: number;\n\n readonly #expectedAppId: string;\n\n /**\n * Whether {@link #expectedAppId} came from an explicit source (the\n * `HERMES_APP_ID` env var) rather than the platform fallback. When true, the\n * stored appId is a deliberate override and {@link resolve} must not let a\n * per-call `platform` recompute it; when false, the stored value is only the\n * construction-time platform default and `resolve` may recompute it from the\n * live platform.\n */\n readonly #appIdFromEnv: boolean;\n\n #pinnedDeviceId: string | undefined;\n\n constructor(options: HermesSessionOptions = {}) {\n const env = options.env ?? process.env;\n\n this.#metroPort = parseEnvPort(env.HERMES_METRO_PORT) ?? DEFAULT_METRO_PORT;\n\n this.#appIdFromEnv = env.HERMES_APP_ID !== undefined;\n this.#expectedAppId =\n env.HERMES_APP_ID ?? defaultAppIdForPlatform(options.platform);\n\n this.#pinnedDeviceId = undefined;\n }\n\n /**\n * Returns the session-default Metro inspector proxy port.\n *\n * @returns The resolved Metro port (env `HERMES_METRO_PORT`, else 8081).\n */\n getMetroPort(): number {\n return this.#metroPort;\n }\n\n /**\n * Returns the session-default expected app bundle ID.\n *\n * @returns The resolved app bundle ID (env `HERMES_APP_ID`, else the\n * platform default).\n */\n getAppId(): string {\n return this.#expectedAppId;\n }\n\n /**\n * Returns the pinned Hermes `logicalDeviceId` for this session, if any.\n *\n * Set on the first successful identity probe via\n * {@link setPinnedHermesDeviceId}. Persists for the lifetime of this\n * instance.\n *\n * @returns The pinned `logicalDeviceId`, or undefined when no pin is set.\n */\n getPinnedHermesDeviceId(): string | undefined {\n return this.#pinnedDeviceId;\n }\n\n /**\n * Pin the Hermes `logicalDeviceId` for this session.\n *\n * Called after a successful identity probe on the first call. Subsequent\n * calls then enforce this pin to prevent target switching mid-session.\n *\n * @param id - The `logicalDeviceId` from Metro's\n * `reactNative.logicalDeviceId` field.\n */\n setPinnedHermesDeviceId(id: string): void {\n this.#pinnedDeviceId = id;\n }\n\n /**\n * Resolve the effective parameters for a single tool call by merging\n * per-call overrides over the stored session defaults.\n *\n * This is PURE with respect to stored state: it never mutates the stored\n * port, appId, or pin. Only {@link setPinnedHermesDeviceId} mutates the pin.\n *\n * Port precedence is `input.metroPort ?? this.getMetroPort()` (which itself\n * already encodes `env ?? default`), mirroring the reference driver's\n * `input.metroPort ?? driver.getMetroPort() ?? 8081`.\n *\n * AppId precedence is `input.appId ?? <HERMES_APP_ID env if set> ??\n * defaultAppIdForPlatform(input.platform) ?? <stored default>`. An explicitly\n * set `HERMES_APP_ID` always WINS over the per-call platform default (env is a\n * deliberate override): in that case the stored appId is returned unchanged.\n * Only when the stored appId is the construction-time platform fallback does\n * a per-call `platform` recompute it — without mutating stored state. When no\n * `platform` is passed this preserves the original iOS-default behavior.\n *\n * @param input - Optional per-call overrides for port, appId, and platform.\n * @returns The effective `{ metroPort, appId, pinnedDeviceId }`.\n */\n resolve(input: HermesSessionResolveInput = {}): HermesSessionResolved {\n return {\n metroPort: input.metroPort ?? this.#metroPort,\n appId: input.appId ?? this.#resolveAppId(input.platform),\n pinnedDeviceId: this.#pinnedDeviceId,\n };\n }\n\n /**\n * Compute the effective default appId for a call, honouring the\n * env-over-platform-default precedence documented on {@link resolve}.\n *\n * @param platform - The live platform passed for this call, if any.\n * @returns The stored env/explicit appId, or the platform default.\n */\n #resolveAppId(platform: HermesPlatform | undefined): string {\n if (this.#appIdFromEnv || platform === undefined) {\n return this.#expectedAppId;\n }\n return defaultAppIdForPlatform(platform);\n }\n}\n\n/**\n * Module-level lazily-created singleton instance.\n */\nlet sessionInstance: HermesSession | undefined;\n\n/**\n * Returns the process-singleton {@link HermesSession}, creating it on first\n * access.\n *\n * @param options - Options applied only when the singleton is first created.\n * @returns The shared `HermesSession` instance.\n */\nexport function getHermesSession(\n options?: HermesSessionOptions,\n): HermesSession {\n if (sessionInstance === undefined) {\n sessionInstance = new HermesSession(options);\n }\n return sessionInstance;\n}\n\n/**\n * Resets the process-singleton {@link HermesSession}.\n *\n * Intended for test isolation so each test can start from a clean session.\n */\nexport function resetHermesSession(): void {\n sessionInstance = undefined;\n}\n"]}
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MultipleDevicesError = exports.detectAllDevices = exports.createMcpServer = exports.createLazyBackend = exports.createBackend = void 0;
3
+ exports.DEFAULT_METRO_PORT = exports.DEFAULT_ANDROID_APP_ID = exports.DEFAULT_IOS_APP_ID = exports.defaultAppIdForPlatform = exports.resetHermesSession = exports.getHermesSession = exports.HermesSession = exports.LEGACY_SYNTHETIC_TITLE = exports.validateWebSocketUrl = exports.hasAmbiguousTarget = exports.selectHermesTarget = exports.fetchDiscoveryTargets = exports.runHermesCdp = exports.MultipleDevicesError = exports.detectAllDevices = exports.createMcpServer = exports.createLazyBackend = exports.createBackend = void 0;
4
4
  var index_js_1 = require("./backends/index.cjs");
5
5
  Object.defineProperty(exports, "createBackend", { enumerable: true, get: function () { return index_js_1.createBackend; } });
6
6
  Object.defineProperty(exports, "createLazyBackend", { enumerable: true, get: function () { return index_js_1.createLazyBackend; } });
@@ -9,4 +9,19 @@ Object.defineProperty(exports, "createMcpServer", { enumerable: true, get: funct
9
9
  var platform_js_1 = require("./utils/platform.cjs");
10
10
  Object.defineProperty(exports, "detectAllDevices", { enumerable: true, get: function () { return platform_js_1.detectAllDevices; } });
11
11
  Object.defineProperty(exports, "MultipleDevicesError", { enumerable: true, get: function () { return platform_js_1.MultipleDevicesError; } });
12
+ var hermes_cdp_js_1 = require("./hermes/hermes-cdp.cjs");
13
+ Object.defineProperty(exports, "runHermesCdp", { enumerable: true, get: function () { return hermes_cdp_js_1.runHermesCdp; } });
14
+ Object.defineProperty(exports, "fetchDiscoveryTargets", { enumerable: true, get: function () { return hermes_cdp_js_1.fetchDiscoveryTargets; } });
15
+ Object.defineProperty(exports, "selectHermesTarget", { enumerable: true, get: function () { return hermes_cdp_js_1.selectHermesTarget; } });
16
+ Object.defineProperty(exports, "hasAmbiguousTarget", { enumerable: true, get: function () { return hermes_cdp_js_1.hasAmbiguousTarget; } });
17
+ Object.defineProperty(exports, "validateWebSocketUrl", { enumerable: true, get: function () { return hermes_cdp_js_1.validateWebSocketUrl; } });
18
+ Object.defineProperty(exports, "LEGACY_SYNTHETIC_TITLE", { enumerable: true, get: function () { return hermes_cdp_js_1.LEGACY_SYNTHETIC_TITLE; } });
19
+ var session_js_1 = require("./hermes/session.cjs");
20
+ Object.defineProperty(exports, "HermesSession", { enumerable: true, get: function () { return session_js_1.HermesSession; } });
21
+ Object.defineProperty(exports, "getHermesSession", { enumerable: true, get: function () { return session_js_1.getHermesSession; } });
22
+ Object.defineProperty(exports, "resetHermesSession", { enumerable: true, get: function () { return session_js_1.resetHermesSession; } });
23
+ Object.defineProperty(exports, "defaultAppIdForPlatform", { enumerable: true, get: function () { return session_js_1.defaultAppIdForPlatform; } });
24
+ Object.defineProperty(exports, "DEFAULT_IOS_APP_ID", { enumerable: true, get: function () { return session_js_1.DEFAULT_IOS_APP_ID; } });
25
+ Object.defineProperty(exports, "DEFAULT_ANDROID_APP_ID", { enumerable: true, get: function () { return session_js_1.DEFAULT_ANDROID_APP_ID; } });
26
+ Object.defineProperty(exports, "DEFAULT_METRO_PORT", { enumerable: true, get: function () { return session_js_1.DEFAULT_METRO_PORT; } });
12
27
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAAuE;AAA9D,yGAAA,aAAa,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAEzC,0CAA8C;AAArC,4GAAA,eAAe,OAAA;AAExB,oDAA6E;AAApE,+GAAA,gBAAgB,OAAA;AAAE,mHAAA,oBAAoB,OAAA","sourcesContent":["export { createBackend, createLazyBackend } from './backends/index.js';\nexport type { LazyDeviceBackend } from './backends/index.js';\nexport { createMcpServer } from './server.js';\n\nexport { detectAllDevices, MultipleDevicesError } from './utils/platform.js';\nexport type { DetectedDevice } from './utils/platform.js';\n\nexport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n UIElement,\n ElementQuery,\n SnapshotResult,\n ScreenshotResult,\n LogsResult,\n TapResult,\n AppStateResult,\n Platform,\n WindowSize,\n} from './backends/types.js';\n\nexport type { SessionConfig } from './backends/session-file.js';\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAAuE;AAA9D,yGAAA,aAAa,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAEzC,0CAA8C;AAArC,4GAAA,eAAe,OAAA;AAExB,oDAA6E;AAApE,+GAAA,gBAAgB,OAAA;AAAE,mHAAA,oBAAoB,OAAA;AAsB/C,yDAOgC;AAN9B,6GAAA,YAAY,OAAA;AACZ,sHAAA,qBAAqB,OAAA;AACrB,mHAAA,kBAAkB,OAAA;AAClB,mHAAA,kBAAkB,OAAA;AAClB,qHAAA,oBAAoB,OAAA;AACpB,uHAAA,sBAAsB,OAAA;AAQxB,mDAQ6B;AAP3B,2GAAA,aAAa,OAAA;AACb,8GAAA,gBAAgB,OAAA;AAChB,gHAAA,kBAAkB,OAAA;AAClB,qHAAA,uBAAuB,OAAA;AACvB,gHAAA,kBAAkB,OAAA;AAClB,oHAAA,sBAAsB,OAAA;AACtB,gHAAA,kBAAkB,OAAA","sourcesContent":["export { createBackend, createLazyBackend } from './backends/index.js';\nexport type { LazyDeviceBackend } from './backends/index.js';\nexport { createMcpServer } from './server.js';\n\nexport { detectAllDevices, MultipleDevicesError } from './utils/platform.js';\nexport type { DetectedDevice } from './utils/platform.js';\n\nexport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n UIElement,\n ElementQuery,\n SnapshotResult,\n ScreenshotResult,\n ScreenshotFileResult,\n ScreenshotOptions,\n LogsResult,\n TapResult,\n AppStateResult,\n Platform,\n WindowSize,\n} from './backends/types.js';\n\nexport type { SessionConfig } from './backends/session-file.js';\n\nexport {\n runHermesCdp,\n fetchDiscoveryTargets,\n selectHermesTarget,\n hasAmbiguousTarget,\n validateWebSocketUrl,\n LEGACY_SYNTHETIC_TITLE,\n} from './hermes/hermes-cdp.js';\nexport type {\n HermesTarget,\n HermesCdpResult,\n RunHermesCdpInput,\n} from './hermes/hermes-cdp.js';\n\nexport {\n HermesSession,\n getHermesSession,\n resetHermesSession,\n defaultAppIdForPlatform,\n DEFAULT_IOS_APP_ID,\n DEFAULT_ANDROID_APP_ID,\n DEFAULT_METRO_PORT,\n} from './hermes/session.js';\nexport type {\n HermesPlatform,\n HermesSessionOptions,\n HermesSessionResolved,\n HermesSessionResolveInput,\n} from './hermes/session.js';\n"]}
package/dist/index.d.cts CHANGED
@@ -3,6 +3,10 @@ export type { LazyDeviceBackend } from "./backends/index.cjs";
3
3
  export { createMcpServer } from "./server.cjs";
4
4
  export { detectAllDevices, MultipleDevicesError } from "./utils/platform.cjs";
5
5
  export type { DetectedDevice } from "./utils/platform.cjs";
6
- export type { DeviceBackend, DeviceButton, DeviceInfo, UIElement, ElementQuery, SnapshotResult, ScreenshotResult, LogsResult, TapResult, AppStateResult, Platform, WindowSize, } from "./backends/types.cjs";
6
+ export type { DeviceBackend, DeviceButton, DeviceInfo, UIElement, ElementQuery, SnapshotResult, ScreenshotResult, ScreenshotFileResult, ScreenshotOptions, LogsResult, TapResult, AppStateResult, Platform, WindowSize, } from "./backends/types.cjs";
7
7
  export type { SessionConfig } from "./backends/session-file.cjs";
8
+ export { runHermesCdp, fetchDiscoveryTargets, selectHermesTarget, hasAmbiguousTarget, validateWebSocketUrl, LEGACY_SYNTHETIC_TITLE, } from "./hermes/hermes-cdp.cjs";
9
+ export type { HermesTarget, HermesCdpResult, RunHermesCdpInput, } from "./hermes/hermes-cdp.cjs";
10
+ export { HermesSession, getHermesSession, resetHermesSession, defaultAppIdForPlatform, DEFAULT_IOS_APP_ID, DEFAULT_ANDROID_APP_ID, DEFAULT_METRO_PORT, } from "./hermes/session.cjs";
11
+ export type { HermesPlatform, HermesSessionOptions, HermesSessionResolved, HermesSessionResolveInput, } from "./hermes/session.cjs";
8
12
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,6BAA4B;AACvE,YAAY,EAAE,iBAAiB,EAAE,6BAA4B;AAC7D,OAAO,EAAE,eAAe,EAAE,qBAAoB;AAE9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,6BAA4B;AAC7E,YAAY,EAAE,cAAc,EAAE,6BAA4B;AAE1D,YAAY,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,SAAS,EACT,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,UAAU,GACX,6BAA4B;AAE7B,YAAY,EAAE,aAAa,EAAE,oCAAmC"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,6BAA4B;AACvE,YAAY,EAAE,iBAAiB,EAAE,6BAA4B;AAC7D,OAAO,EAAE,eAAe,EAAE,qBAAoB;AAE9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,6BAA4B;AAC7E,YAAY,EAAE,cAAc,EAAE,6BAA4B;AAE1D,YAAY,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,SAAS,EACT,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,UAAU,GACX,6BAA4B;AAE7B,YAAY,EAAE,aAAa,EAAE,oCAAmC;AAEhE,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,GACvB,gCAA+B;AAChC,YAAY,EACV,YAAY,EACZ,eAAe,EACf,iBAAiB,GAClB,gCAA+B;AAEhC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,GACnB,6BAA4B;AAC7B,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,GAC1B,6BAA4B"}
package/dist/index.d.mts CHANGED
@@ -3,6 +3,10 @@ export type { LazyDeviceBackend } from "./backends/index.mjs";
3
3
  export { createMcpServer } from "./server.mjs";
4
4
  export { detectAllDevices, MultipleDevicesError } from "./utils/platform.mjs";
5
5
  export type { DetectedDevice } from "./utils/platform.mjs";
6
- export type { DeviceBackend, DeviceButton, DeviceInfo, UIElement, ElementQuery, SnapshotResult, ScreenshotResult, LogsResult, TapResult, AppStateResult, Platform, WindowSize, } from "./backends/types.mjs";
6
+ export type { DeviceBackend, DeviceButton, DeviceInfo, UIElement, ElementQuery, SnapshotResult, ScreenshotResult, ScreenshotFileResult, ScreenshotOptions, LogsResult, TapResult, AppStateResult, Platform, WindowSize, } from "./backends/types.mjs";
7
7
  export type { SessionConfig } from "./backends/session-file.mjs";
8
+ export { runHermesCdp, fetchDiscoveryTargets, selectHermesTarget, hasAmbiguousTarget, validateWebSocketUrl, LEGACY_SYNTHETIC_TITLE, } from "./hermes/hermes-cdp.mjs";
9
+ export type { HermesTarget, HermesCdpResult, RunHermesCdpInput, } from "./hermes/hermes-cdp.mjs";
10
+ export { HermesSession, getHermesSession, resetHermesSession, defaultAppIdForPlatform, DEFAULT_IOS_APP_ID, DEFAULT_ANDROID_APP_ID, DEFAULT_METRO_PORT, } from "./hermes/session.mjs";
11
+ export type { HermesPlatform, HermesSessionOptions, HermesSessionResolved, HermesSessionResolveInput, } from "./hermes/session.mjs";
8
12
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,6BAA4B;AACvE,YAAY,EAAE,iBAAiB,EAAE,6BAA4B;AAC7D,OAAO,EAAE,eAAe,EAAE,qBAAoB;AAE9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,6BAA4B;AAC7E,YAAY,EAAE,cAAc,EAAE,6BAA4B;AAE1D,YAAY,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,SAAS,EACT,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,UAAU,GACX,6BAA4B;AAE7B,YAAY,EAAE,aAAa,EAAE,oCAAmC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,6BAA4B;AACvE,YAAY,EAAE,iBAAiB,EAAE,6BAA4B;AAC7D,OAAO,EAAE,eAAe,EAAE,qBAAoB;AAE9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,6BAA4B;AAC7E,YAAY,EAAE,cAAc,EAAE,6BAA4B;AAE1D,YAAY,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,SAAS,EACT,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,UAAU,GACX,6BAA4B;AAE7B,YAAY,EAAE,aAAa,EAAE,oCAAmC;AAEhE,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,GACvB,gCAA+B;AAChC,YAAY,EACV,YAAY,EACZ,eAAe,EACf,iBAAiB,GAClB,gCAA+B;AAEhC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,GACnB,6BAA4B;AAC7B,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,GAC1B,6BAA4B"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,6 @@
1
1
  export { createBackend, createLazyBackend } from "./backends/index.mjs";
2
2
  export { createMcpServer } from "./server.mjs";
3
3
  export { detectAllDevices, MultipleDevicesError } from "./utils/platform.mjs";
4
+ export { runHermesCdp, fetchDiscoveryTargets, selectHermesTarget, hasAmbiguousTarget, validateWebSocketUrl, LEGACY_SYNTHETIC_TITLE } from "./hermes/hermes-cdp.mjs";
5
+ export { HermesSession, getHermesSession, resetHermesSession, defaultAppIdForPlatform, DEFAULT_IOS_APP_ID, DEFAULT_ANDROID_APP_ID, DEFAULT_METRO_PORT } from "./hermes/session.mjs";
4
6
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,6BAA4B;AAEvE,OAAO,EAAE,eAAe,EAAE,qBAAoB;AAE9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,6BAA4B","sourcesContent":["export { createBackend, createLazyBackend } from './backends/index.js';\nexport type { LazyDeviceBackend } from './backends/index.js';\nexport { createMcpServer } from './server.js';\n\nexport { detectAllDevices, MultipleDevicesError } from './utils/platform.js';\nexport type { DetectedDevice } from './utils/platform.js';\n\nexport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n UIElement,\n ElementQuery,\n SnapshotResult,\n ScreenshotResult,\n LogsResult,\n TapResult,\n AppStateResult,\n Platform,\n WindowSize,\n} from './backends/types.js';\n\nexport type { SessionConfig } from './backends/session-file.js';\n"]}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,6BAA4B;AAEvE,OAAO,EAAE,eAAe,EAAE,qBAAoB;AAE9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,6BAA4B;AAsB7E,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACvB,gCAA+B;AAOhC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EACnB,6BAA4B","sourcesContent":["export { createBackend, createLazyBackend } from './backends/index.js';\nexport type { LazyDeviceBackend } from './backends/index.js';\nexport { createMcpServer } from './server.js';\n\nexport { detectAllDevices, MultipleDevicesError } from './utils/platform.js';\nexport type { DetectedDevice } from './utils/platform.js';\n\nexport type {\n DeviceBackend,\n DeviceButton,\n DeviceInfo,\n UIElement,\n ElementQuery,\n SnapshotResult,\n ScreenshotResult,\n ScreenshotFileResult,\n ScreenshotOptions,\n LogsResult,\n TapResult,\n AppStateResult,\n Platform,\n WindowSize,\n} from './backends/types.js';\n\nexport type { SessionConfig } from './backends/session-file.js';\n\nexport {\n runHermesCdp,\n fetchDiscoveryTargets,\n selectHermesTarget,\n hasAmbiguousTarget,\n validateWebSocketUrl,\n LEGACY_SYNTHETIC_TITLE,\n} from './hermes/hermes-cdp.js';\nexport type {\n HermesTarget,\n HermesCdpResult,\n RunHermesCdpInput,\n} from './hermes/hermes-cdp.js';\n\nexport {\n HermesSession,\n getHermesSession,\n resetHermesSession,\n defaultAppIdForPlatform,\n DEFAULT_IOS_APP_ID,\n DEFAULT_ANDROID_APP_ID,\n DEFAULT_METRO_PORT,\n} from './hermes/session.js';\nexport type {\n HermesPlatform,\n HermesSessionOptions,\n HermesSessionResolved,\n HermesSessionResolveInput,\n} from './hermes/session.js';\n"]}
package/dist/server.cjs CHANGED
@@ -15,6 +15,7 @@ function createMcpServer(backend) {
15
15
  'Check for system alerts with device_snapshot before interacting with app elements.',
16
16
  'If multiple devices are connected and no device is selected, tools will return the device list.',
17
17
  'Call device_list_devices to see available devices, then device_select_device to choose one.',
18
+ 'The Hermes tools (hermes_cdp, hermes_targets) talk to the React Native Hermes JS runtime via Metro and require a DEBUG build with Metro running (Node 20 needs NODE_OPTIONS="--experimental-websocket").',
18
19
  ].join('\n'),
19
20
  });
20
21
  (0, index_js_1.registerSnapshotTool)(server, backend);
@@ -43,6 +44,8 @@ function createMcpServer(backend) {
43
44
  (0, index_js_1.registerGetElementTextTool)(server, backend);
44
45
  (0, index_js_1.registerListDevicesTool)(server, backend);
45
46
  (0, index_js_1.registerSelectDeviceTool)(server, backend);
47
+ (0, index_js_1.registerHermesCdpTool)(server, backend);
48
+ (0, index_js_1.registerHermesTargetsTool)(server, backend);
46
49
  return server;
47
50
  }
48
51
  //# sourceMappingURL=server.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;AAgCA,0CA8CC;AA9ED,oEAAoE;AAGpE,gDA2B0B;AAE1B,SAAgB,eAAe,CAAC,OAA0B;IACxD,MAAM,MAAM,GAAG,IAAI,kBAAS,CAC1B,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE,EAClD;QACE,YAAY,EAAE;YACZ,kEAAkE;YAClE,kFAAkF;YAClF,6DAA6D;YAC7D,4FAA4F;YAC5F,4EAA4E;YAC5E,yFAAyF;YACzF,oFAAoF;YACpF,iGAAiG;YACjG,6FAA6F;SAC9F,CAAC,IAAI,CAAC,IAAI,CAAC;KACb,CACF,CAAC;IAEF,IAAA,+BAAoB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAA,iCAAsB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,IAAA,iCAAsB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,IAAA,iCAAsB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,IAAA,qCAA0B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,IAAA,2BAAgB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,IAAA,4BAAiB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,IAAA,gCAAqB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,IAAA,8BAAmB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAA,+BAAoB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAA,8BAAmB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAA,+BAAoB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAA,kCAAuB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,IAAA,sCAA2B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAA,mCAAwB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,IAAA,2BAAgB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,IAAA,sCAA2B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAA,gCAAqB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,IAAA,iCAAsB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,IAAA,8BAAmB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAA,gCAAqB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,IAAA,sCAA2B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAA,uCAA4B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAA,qCAA0B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,IAAA,kCAAuB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,IAAA,mCAAwB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE1C,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nimport type { LazyDeviceBackend } from './backends/index.js';\nimport {\n registerSnapshotTool,\n registerScreenshotTool,\n registerTapElementTool,\n registerTapCoordinatesTool,\n registerTypeTool,\n registerSwipeTool,\n registerLongPressTool,\n registerWaitForTool,\n registerAppStateTool,\n registerDeviceInfoTool,\n registerOpenAppTool,\n registerCloseAppTool,\n registerPressButtonTool,\n registerDismissKeyboardTool,\n registerDismissAlertTool,\n registerLogsTool,\n registerScrollToElementTool,\n registerAlertTextTool,\n registerWindowSizeTool,\n registerContextTool,\n registerClipboardTool,\n registerScreenRecordingTool,\n registerGenerateLocatorsTool,\n registerGetElementTextTool,\n registerListDevicesTool,\n registerSelectDeviceTool,\n} from './tools/index.js';\n\nexport function createMcpServer(backend: LazyDeviceBackend): McpServer {\n const server = new McpServer(\n { name: '@metamask/device-mcp', version: '0.0.0' },\n {\n instructions: [\n 'Mobile device interaction tools for iOS (IDB) and Android (ADB).',\n 'ALWAYS call device_snapshot before device_tap_element to see the current screen.',\n 'Never call device_snapshot twice without acting in between.',\n 'Elements are identified by accessibility label, resource-id, content-description, or text.',\n 'Element matching is fuzzy: partial text and case-insensitive matches work.',\n 'After typing, call device_dismiss_keyboard to reveal elements obscured by the keyboard.',\n 'Check for system alerts with device_snapshot before interacting with app elements.',\n 'If multiple devices are connected and no device is selected, tools will return the device list.',\n 'Call device_list_devices to see available devices, then device_select_device to choose one.',\n ].join('\\n'),\n },\n );\n\n registerSnapshotTool(server, backend);\n registerScreenshotTool(server, backend);\n registerDeviceInfoTool(server, backend);\n registerTapElementTool(server, backend);\n registerTapCoordinatesTool(server, backend);\n registerTypeTool(server, backend);\n registerSwipeTool(server, backend);\n registerLongPressTool(server, backend);\n registerWaitForTool(server, backend);\n registerAppStateTool(server, backend);\n registerOpenAppTool(server, backend);\n registerCloseAppTool(server, backend);\n registerPressButtonTool(server, backend);\n registerDismissKeyboardTool(server, backend);\n registerDismissAlertTool(server, backend);\n registerLogsTool(server, backend);\n registerScrollToElementTool(server, backend);\n registerAlertTextTool(server, backend);\n registerWindowSizeTool(server, backend);\n registerContextTool(server, backend);\n registerClipboardTool(server, backend);\n registerScreenRecordingTool(server, backend);\n registerGenerateLocatorsTool(server, backend);\n registerGetElementTextTool(server, backend);\n registerListDevicesTool(server, backend);\n registerSelectDeviceTool(server, backend);\n\n return server;\n}\n"]}
1
+ {"version":3,"file":"server.cjs","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;AAkCA,0CAiDC;AAnFD,oEAAoE;AAGpE,gDA6B0B;AAE1B,SAAgB,eAAe,CAAC,OAA0B;IACxD,MAAM,MAAM,GAAG,IAAI,kBAAS,CAC1B,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE,EAClD;QACE,YAAY,EAAE;YACZ,kEAAkE;YAClE,kFAAkF;YAClF,6DAA6D;YAC7D,4FAA4F;YAC5F,4EAA4E;YAC5E,yFAAyF;YACzF,oFAAoF;YACpF,iGAAiG;YACjG,6FAA6F;YAC7F,0MAA0M;SAC3M,CAAC,IAAI,CAAC,IAAI,CAAC;KACb,CACF,CAAC;IAEF,IAAA,+BAAoB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAA,iCAAsB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,IAAA,iCAAsB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,IAAA,iCAAsB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,IAAA,qCAA0B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,IAAA,2BAAgB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,IAAA,4BAAiB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,IAAA,gCAAqB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,IAAA,8BAAmB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAA,+BAAoB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAA,8BAAmB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAA,+BAAoB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAA,kCAAuB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,IAAA,sCAA2B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAA,mCAAwB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,IAAA,2BAAgB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,IAAA,sCAA2B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAA,gCAAqB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,IAAA,iCAAsB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,IAAA,8BAAmB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAA,gCAAqB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,IAAA,sCAA2B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAA,uCAA4B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAA,qCAA0B,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,IAAA,kCAAuB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,IAAA,mCAAwB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,IAAA,gCAAqB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,IAAA,oCAAyB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE3C,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nimport type { LazyDeviceBackend } from './backends/index.js';\nimport {\n registerSnapshotTool,\n registerScreenshotTool,\n registerTapElementTool,\n registerTapCoordinatesTool,\n registerTypeTool,\n registerSwipeTool,\n registerLongPressTool,\n registerWaitForTool,\n registerAppStateTool,\n registerDeviceInfoTool,\n registerOpenAppTool,\n registerCloseAppTool,\n registerPressButtonTool,\n registerDismissKeyboardTool,\n registerDismissAlertTool,\n registerLogsTool,\n registerScrollToElementTool,\n registerAlertTextTool,\n registerWindowSizeTool,\n registerContextTool,\n registerClipboardTool,\n registerScreenRecordingTool,\n registerGenerateLocatorsTool,\n registerGetElementTextTool,\n registerListDevicesTool,\n registerSelectDeviceTool,\n registerHermesCdpTool,\n registerHermesTargetsTool,\n} from './tools/index.js';\n\nexport function createMcpServer(backend: LazyDeviceBackend): McpServer {\n const server = new McpServer(\n { name: '@metamask/device-mcp', version: '0.0.0' },\n {\n instructions: [\n 'Mobile device interaction tools for iOS (IDB) and Android (ADB).',\n 'ALWAYS call device_snapshot before device_tap_element to see the current screen.',\n 'Never call device_snapshot twice without acting in between.',\n 'Elements are identified by accessibility label, resource-id, content-description, or text.',\n 'Element matching is fuzzy: partial text and case-insensitive matches work.',\n 'After typing, call device_dismiss_keyboard to reveal elements obscured by the keyboard.',\n 'Check for system alerts with device_snapshot before interacting with app elements.',\n 'If multiple devices are connected and no device is selected, tools will return the device list.',\n 'Call device_list_devices to see available devices, then device_select_device to choose one.',\n 'The Hermes tools (hermes_cdp, hermes_targets) talk to the React Native Hermes JS runtime via Metro and require a DEBUG build with Metro running (Node 20 needs NODE_OPTIONS=\"--experimental-websocket\").',\n ].join('\\n'),\n },\n );\n\n registerSnapshotTool(server, backend);\n registerScreenshotTool(server, backend);\n registerDeviceInfoTool(server, backend);\n registerTapElementTool(server, backend);\n registerTapCoordinatesTool(server, backend);\n registerTypeTool(server, backend);\n registerSwipeTool(server, backend);\n registerLongPressTool(server, backend);\n registerWaitForTool(server, backend);\n registerAppStateTool(server, backend);\n registerOpenAppTool(server, backend);\n registerCloseAppTool(server, backend);\n registerPressButtonTool(server, backend);\n registerDismissKeyboardTool(server, backend);\n registerDismissAlertTool(server, backend);\n registerLogsTool(server, backend);\n registerScrollToElementTool(server, backend);\n registerAlertTextTool(server, backend);\n registerWindowSizeTool(server, backend);\n registerContextTool(server, backend);\n registerClipboardTool(server, backend);\n registerScreenRecordingTool(server, backend);\n registerGenerateLocatorsTool(server, backend);\n registerGetElementTextTool(server, backend);\n registerListDevicesTool(server, backend);\n registerSelectDeviceTool(server, backend);\n registerHermesCdpTool(server, backend);\n registerHermesTargetsTool(server, backend);\n\n return server;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.cts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gDAAgD;AAEpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,6BAA4B;AA8B7D,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CA8CrE"}
1
+ {"version":3,"file":"server.d.cts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gDAAgD;AAEpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,6BAA4B;AAgC7D,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAiDrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.mts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gDAAgD;AAEpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,6BAA4B;AA8B7D,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CA8CrE"}
1
+ {"version":3,"file":"server.d.mts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gDAAgD;AAEpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,6BAA4B;AAgC7D,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAiDrE"}
package/dist/server.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { registerSnapshotTool, registerScreenshotTool, registerTapElementTool, registerTapCoordinatesTool, registerTypeTool, registerSwipeTool, registerLongPressTool, registerWaitForTool, registerAppStateTool, registerDeviceInfoTool, registerOpenAppTool, registerCloseAppTool, registerPressButtonTool, registerDismissKeyboardTool, registerDismissAlertTool, registerLogsTool, registerScrollToElementTool, registerAlertTextTool, registerWindowSizeTool, registerContextTool, registerClipboardTool, registerScreenRecordingTool, registerGenerateLocatorsTool, registerGetElementTextTool, registerListDevicesTool, registerSelectDeviceTool } from "./tools/index.mjs";
2
+ import { registerSnapshotTool, registerScreenshotTool, registerTapElementTool, registerTapCoordinatesTool, registerTypeTool, registerSwipeTool, registerLongPressTool, registerWaitForTool, registerAppStateTool, registerDeviceInfoTool, registerOpenAppTool, registerCloseAppTool, registerPressButtonTool, registerDismissKeyboardTool, registerDismissAlertTool, registerLogsTool, registerScrollToElementTool, registerAlertTextTool, registerWindowSizeTool, registerContextTool, registerClipboardTool, registerScreenRecordingTool, registerGenerateLocatorsTool, registerGetElementTextTool, registerListDevicesTool, registerSelectDeviceTool, registerHermesCdpTool, registerHermesTargetsTool } from "./tools/index.mjs";
3
3
  export function createMcpServer(backend) {
4
4
  const server = new McpServer({ name: '@metamask/device-mcp', version: '0.0.0' }, {
5
5
  instructions: [
@@ -12,6 +12,7 @@ export function createMcpServer(backend) {
12
12
  'Check for system alerts with device_snapshot before interacting with app elements.',
13
13
  'If multiple devices are connected and no device is selected, tools will return the device list.',
14
14
  'Call device_list_devices to see available devices, then device_select_device to choose one.',
15
+ 'The Hermes tools (hermes_cdp, hermes_targets) talk to the React Native Hermes JS runtime via Metro and require a DEBUG build with Metro running (Node 20 needs NODE_OPTIONS="--experimental-websocket").',
15
16
  ].join('\n'),
16
17
  });
17
18
  registerSnapshotTool(server, backend);
@@ -40,6 +41,8 @@ export function createMcpServer(backend) {
40
41
  registerGetElementTextTool(server, backend);
41
42
  registerListDevicesTool(server, backend);
42
43
  registerSelectDeviceTool(server, backend);
44
+ registerHermesCdpTool(server, backend);
45
+ registerHermesTargetsTool(server, backend);
43
46
  return server;
44
47
  }
45
48
  //# sourceMappingURL=server.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gDAAgD;AAGpE,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,gBAAgB,EAChB,2BAA2B,EAC3B,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,2BAA2B,EAC3B,4BAA4B,EAC5B,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,EACzB,0BAAyB;AAE1B,MAAM,UAAU,eAAe,CAAC,OAA0B;IACxD,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE,EAClD;QACE,YAAY,EAAE;YACZ,kEAAkE;YAClE,kFAAkF;YAClF,6DAA6D;YAC7D,4FAA4F;YAC5F,4EAA4E;YAC5E,yFAAyF;YACzF,oFAAoF;YACpF,iGAAiG;YACjG,6FAA6F;SAC9F,CAAC,IAAI,CAAC,IAAI,CAAC;KACb,CACF,CAAC;IAEF,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,uBAAuB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,2BAA2B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,2BAA2B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,2BAA2B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,4BAA4B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,uBAAuB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE1C,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nimport type { LazyDeviceBackend } from './backends/index.js';\nimport {\n registerSnapshotTool,\n registerScreenshotTool,\n registerTapElementTool,\n registerTapCoordinatesTool,\n registerTypeTool,\n registerSwipeTool,\n registerLongPressTool,\n registerWaitForTool,\n registerAppStateTool,\n registerDeviceInfoTool,\n registerOpenAppTool,\n registerCloseAppTool,\n registerPressButtonTool,\n registerDismissKeyboardTool,\n registerDismissAlertTool,\n registerLogsTool,\n registerScrollToElementTool,\n registerAlertTextTool,\n registerWindowSizeTool,\n registerContextTool,\n registerClipboardTool,\n registerScreenRecordingTool,\n registerGenerateLocatorsTool,\n registerGetElementTextTool,\n registerListDevicesTool,\n registerSelectDeviceTool,\n} from './tools/index.js';\n\nexport function createMcpServer(backend: LazyDeviceBackend): McpServer {\n const server = new McpServer(\n { name: '@metamask/device-mcp', version: '0.0.0' },\n {\n instructions: [\n 'Mobile device interaction tools for iOS (IDB) and Android (ADB).',\n 'ALWAYS call device_snapshot before device_tap_element to see the current screen.',\n 'Never call device_snapshot twice without acting in between.',\n 'Elements are identified by accessibility label, resource-id, content-description, or text.',\n 'Element matching is fuzzy: partial text and case-insensitive matches work.',\n 'After typing, call device_dismiss_keyboard to reveal elements obscured by the keyboard.',\n 'Check for system alerts with device_snapshot before interacting with app elements.',\n 'If multiple devices are connected and no device is selected, tools will return the device list.',\n 'Call device_list_devices to see available devices, then device_select_device to choose one.',\n ].join('\\n'),\n },\n );\n\n registerSnapshotTool(server, backend);\n registerScreenshotTool(server, backend);\n registerDeviceInfoTool(server, backend);\n registerTapElementTool(server, backend);\n registerTapCoordinatesTool(server, backend);\n registerTypeTool(server, backend);\n registerSwipeTool(server, backend);\n registerLongPressTool(server, backend);\n registerWaitForTool(server, backend);\n registerAppStateTool(server, backend);\n registerOpenAppTool(server, backend);\n registerCloseAppTool(server, backend);\n registerPressButtonTool(server, backend);\n registerDismissKeyboardTool(server, backend);\n registerDismissAlertTool(server, backend);\n registerLogsTool(server, backend);\n registerScrollToElementTool(server, backend);\n registerAlertTextTool(server, backend);\n registerWindowSizeTool(server, backend);\n registerContextTool(server, backend);\n registerClipboardTool(server, backend);\n registerScreenRecordingTool(server, backend);\n registerGenerateLocatorsTool(server, backend);\n registerGetElementTextTool(server, backend);\n registerListDevicesTool(server, backend);\n registerSelectDeviceTool(server, backend);\n\n return server;\n}\n"]}
1
+ {"version":3,"file":"server.mjs","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gDAAgD;AAGpE,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,gBAAgB,EAChB,2BAA2B,EAC3B,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,2BAA2B,EAC3B,4BAA4B,EAC5B,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EAC1B,0BAAyB;AAE1B,MAAM,UAAU,eAAe,CAAC,OAA0B;IACxD,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE,EAClD;QACE,YAAY,EAAE;YACZ,kEAAkE;YAClE,kFAAkF;YAClF,6DAA6D;YAC7D,4FAA4F;YAC5F,4EAA4E;YAC5E,yFAAyF;YACzF,oFAAoF;YACpF,iGAAiG;YACjG,6FAA6F;YAC7F,0MAA0M;SAC3M,CAAC,IAAI,CAAC,IAAI,CAAC;KACb,CACF,CAAC;IAEF,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,uBAAuB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,2BAA2B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,2BAA2B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,2BAA2B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,4BAA4B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,uBAAuB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,yBAAyB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE3C,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nimport type { LazyDeviceBackend } from './backends/index.js';\nimport {\n registerSnapshotTool,\n registerScreenshotTool,\n registerTapElementTool,\n registerTapCoordinatesTool,\n registerTypeTool,\n registerSwipeTool,\n registerLongPressTool,\n registerWaitForTool,\n registerAppStateTool,\n registerDeviceInfoTool,\n registerOpenAppTool,\n registerCloseAppTool,\n registerPressButtonTool,\n registerDismissKeyboardTool,\n registerDismissAlertTool,\n registerLogsTool,\n registerScrollToElementTool,\n registerAlertTextTool,\n registerWindowSizeTool,\n registerContextTool,\n registerClipboardTool,\n registerScreenRecordingTool,\n registerGenerateLocatorsTool,\n registerGetElementTextTool,\n registerListDevicesTool,\n registerSelectDeviceTool,\n registerHermesCdpTool,\n registerHermesTargetsTool,\n} from './tools/index.js';\n\nexport function createMcpServer(backend: LazyDeviceBackend): McpServer {\n const server = new McpServer(\n { name: '@metamask/device-mcp', version: '0.0.0' },\n {\n instructions: [\n 'Mobile device interaction tools for iOS (IDB) and Android (ADB).',\n 'ALWAYS call device_snapshot before device_tap_element to see the current screen.',\n 'Never call device_snapshot twice without acting in between.',\n 'Elements are identified by accessibility label, resource-id, content-description, or text.',\n 'Element matching is fuzzy: partial text and case-insensitive matches work.',\n 'After typing, call device_dismiss_keyboard to reveal elements obscured by the keyboard.',\n 'Check for system alerts with device_snapshot before interacting with app elements.',\n 'If multiple devices are connected and no device is selected, tools will return the device list.',\n 'Call device_list_devices to see available devices, then device_select_device to choose one.',\n 'The Hermes tools (hermes_cdp, hermes_targets) talk to the React Native Hermes JS runtime via Metro and require a DEBUG build with Metro running (Node 20 needs NODE_OPTIONS=\"--experimental-websocket\").',\n ].join('\\n'),\n },\n );\n\n registerSnapshotTool(server, backend);\n registerScreenshotTool(server, backend);\n registerDeviceInfoTool(server, backend);\n registerTapElementTool(server, backend);\n registerTapCoordinatesTool(server, backend);\n registerTypeTool(server, backend);\n registerSwipeTool(server, backend);\n registerLongPressTool(server, backend);\n registerWaitForTool(server, backend);\n registerAppStateTool(server, backend);\n registerOpenAppTool(server, backend);\n registerCloseAppTool(server, backend);\n registerPressButtonTool(server, backend);\n registerDismissKeyboardTool(server, backend);\n registerDismissAlertTool(server, backend);\n registerLogsTool(server, backend);\n registerScrollToElementTool(server, backend);\n registerAlertTextTool(server, backend);\n registerWindowSizeTool(server, backend);\n registerContextTool(server, backend);\n registerClipboardTool(server, backend);\n registerScreenRecordingTool(server, backend);\n registerGenerateLocatorsTool(server, backend);\n registerGetElementTextTool(server, backend);\n registerListDevicesTool(server, backend);\n registerSelectDeviceTool(server, backend);\n registerHermesCdpTool(server, backend);\n registerHermesTargetsTool(server, backend);\n\n return server;\n}\n"]}