mobridge-mcp 0.3.0 → 0.4.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.
@@ -24,6 +24,10 @@
24
24
  "mobile.device.connect": {
25
25
  "level": "FULL"
26
26
  },
27
+ "mobile.device.list": {
28
+ "level": "FULL",
29
+ "note": "Host-side enumeration, platform-neutral. It was omitted here while staying registered and callable, so an iOS session's supportedTools hid a tool that worked — a contract lie in the direction nobody checks for."
30
+ },
27
31
  "mobile.app.open": {
28
32
  "level": "FULL",
29
33
  "allowedOperations": [
@@ -51,7 +51,17 @@
51
51
  "action": { "$ref": "#/$defs/actionVerb" },
52
52
  "target": {
53
53
  "$ref": "msm:node-ref.schema.json",
54
- "description": "Required for node actions."
54
+ "description": "The NodeRef to act on, copied verbatim from a find/inspect result. Mutually exclusive with `find`."
55
+ },
56
+ "find": {
57
+ "type": "object",
58
+ "additionalProperties": false,
59
+ "required": ["query"],
60
+ "description": "Locate the target and act in one call, instead of passing `target`. The query is resolved immediately before the action and re-resolved ONCE if the screen moved underneath, which closes the race the separate find-then-act pair leaves open. Matching several nodes is AMBIGUOUS_MATCH with candidates — it never picks one.",
61
+ "properties": {
62
+ "query": { "type": "object", "description": "SemanticQuery, same shape as mobile.screen.find." },
63
+ "actionable": { "type": "boolean", "default": true, "description": "Resolve to the nearest ACTIONABLE ancestor when the matched node cannot be acted on. Never climbs past an ancestor covering more than one match." }
64
+ }
55
65
  },
56
66
  "value": {
57
67
  "type": ["string", "null"],
@@ -129,10 +139,22 @@
129
139
  },
130
140
  "target": {
131
141
  "type": "object",
132
- "description": "Human-readable identity of the acted node.",
142
+ "description": "Human-readable identity of the acted node. For app.open, `label` is the package OBSERVED in front afterwards and `requested` is what the call asked for — they differ whenever the launch did not land where it was aimed.",
133
143
  "properties": {
134
144
  "role": { "$ref": "msm:common.schema.json#/$defs/role" },
135
- "label": { "type": ["string", "null"] }
145
+ "label": { "type": ["string", "null"] },
146
+ "requested": { "type": ["string", "null"], "description": "app.open only: the package or deep link the caller asked for." }
147
+ }
148
+ },
149
+ "app": {
150
+ "type": "object",
151
+ "additionalProperties": true,
152
+ "description": "The foreground app OBSERVED after the action. Present on app.open and system.action so a caller learns where it actually ended up without a second round trip.",
153
+ "properties": {
154
+ "platform": { "type": ["string", "null"] },
155
+ "appId": { "type": ["string", "null"], "description": "Package name on Android, bundle id on iOS." },
156
+ "title": { "type": ["string", "null"] },
157
+ "generation": { "type": ["integer", "null"] }
136
158
  }
137
159
  },
138
160
  "appInstallCheck": {
@@ -0,0 +1,65 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "msm:scenario.schema.json",
4
+ "title": "ScenarioRecord",
5
+ "description": "A saved, replayable test workflow. Stores the QUERY that located each target, never the NodeRef it produced: refs are generation-scoped, so every replay re-resolves from scratch.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schemaVersion", "scenarioVersion", "id", "intent", "platform", "status", "steps", "warnings", "recordedAt"],
9
+ "properties": {
10
+ "schemaVersion": { "type": "integer", "const": 1, "description": "File format. Independent of scenarioVersion." },
11
+ "scenarioVersion": { "type": "integer", "minimum": 1, "description": "The definition's revision. Owned by the store: 1 on create, previous+1 on update." },
12
+ "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{1,63}$", "description": "Names the user-visible goal (settings-network), never the steps (tap-button-1)." },
13
+ "intent": { "type": "string", "minLength": 1, "description": "One sentence. This is how a later agent recognises the scenario as a match for a task." },
14
+ "platform": { "enum": ["android", "ios"], "description": "Execution constraint: a mismatch is rejected before the device is touched." },
15
+ "status": {
16
+ "enum": ["RECORDED", "VERIFIED"],
17
+ "description": "RECORDED on save. VERIFIED only when explicitly saved after a successful run — a run never rewrites the artifact."
18
+ },
19
+ "arrange": {
20
+ "type": "object",
21
+ "additionalProperties": false,
22
+ "description": "Precondition, deliberately limited to bringing one app to the front.",
23
+ "properties": { "packageName": { "type": "string" } }
24
+ },
25
+ "steps": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/step" } },
26
+ "warnings": { "type": "array", "items": { "type": "string" }, "description": "Why a step is not replayable as recorded: unverified action, unresolved target, redacted input." },
27
+ "recordedAt": { "type": "string" },
28
+ "recordedOn": {
29
+ "type": "object",
30
+ "additionalProperties": true,
31
+ "properties": { "deviceId": { "type": "string" }, "osVersion": { "type": "string" } }
32
+ }
33
+ },
34
+ "$defs": {
35
+ "step": {
36
+ "type": "object",
37
+ "additionalProperties": false,
38
+ "required": ["id", "action"],
39
+ "description": "The workflow step shape, plus inputRequired. One vocabulary, so replay is a delegation rather than a second executor.",
40
+ "properties": {
41
+ "id": { "type": "string", "minLength": 1 },
42
+ "find": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "required": ["query"],
46
+ "properties": {
47
+ "query": { "type": "object", "description": "Re-resolved on every run. A target that moved is still found; a target that no longer exists fails rather than falling back to coordinates." },
48
+ "actionable": { "type": "boolean", "default": true }
49
+ }
50
+ },
51
+ "action": { "type": "string" },
52
+ "value": { "type": ["string", "null"] },
53
+ "inputRequired": {
54
+ "type": "boolean",
55
+ "description": "The value was redacted. RUN yields INPUT_REQUIRED and the caller supplies it for that run only; it is never written back."
56
+ },
57
+ "verify": { "type": "object", "description": "Recorded only when a verification actually occurred. Never inferred from the verb." },
58
+ "unresolved": { "type": "boolean", "description": "No query could be rebuilt for this step; kept for the reader, refused by RUN." },
59
+ "onError": { "enum": ["ABORT", "RETRY", "CONTINUE"], "default": "ABORT" },
60
+ "maxRetries": { "type": "integer", "minimum": 0, "default": 1 },
61
+ "pause": { "type": "boolean", "default": false }
62
+ }
63
+ }
64
+ }
65
+ }
@@ -59,6 +59,10 @@
59
59
  "packageName": { "$ref": "msm:common.schema.json#/$defs/packageName" },
60
60
  "bundleId": { "$ref": "msm:common.schema.json#/$defs/bundleId" },
61
61
  "deepLink": { "type": "string" },
62
+ "expectPackage": {
63
+ "$ref": "msm:common.schema.json#/$defs/packageName",
64
+ "description": "The app you expect to be in front afterwards. Needed to VERIFY a deep link: without a packageName or this, the runtime has nothing to check the outcome against and returns CONDITION_UNKNOWN rather than guessing that any foreground change meant success."
65
+ },
62
66
  "verify": { "$ref": "msm:wait.schema.json" },
63
67
  "diffDetail": { "enum": ["SUMMARY", "FULL"], "default": "SUMMARY", "description": "How much of the semantic delta observation.diff carries. SUMMARY caps each bucket at 8 entries and adds counts + truncated; FULL returns every node delta. Use observation.diffRef with mobile.screen.diff when the full delta is wanted." }
64
68
  }
package/integrity.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "buildId": "56de3b6-20260806163529",
3
- "bundleSha256": "1a31c3e41873903c2e56233e86fcb5c216c666950bd64f62b7cd603fcb9f85f0"
2
+ "buildId": "fa820e4-20260811154759",
3
+ "bundleSha256": "eee1c1832e3b057e5ec10683658c87759866d7a85e1c24ba4982b39808894bdd"
4
4
  }
package/loader.cjs CHANGED
@@ -14,7 +14,7 @@ const os = require("node:os");
14
14
  const path = require("node:path");
15
15
  const crypto = require("node:crypto");
16
16
 
17
- const BUILD_ID = "56de3b6-20260806163529"; // pack-npx stamps the real build id
17
+ const BUILD_ID = "fa820e4-20260811154759"; // pack-npx stamps the real build id
18
18
  const ROOT = __dirname; // package root (loader.cjs sits here)
19
19
  const DIST = path.join(ROOT, "dist");
20
20
  const BUNDLE = path.join(DIST, "main.obf.cjs");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobridge-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "MoBridge MCP — a stdio MCP server exposing on-device Android/iOS UIs as fast semantic read / observe / find / act / verify tools plus screenshots. Ships an installer that wires it into Claude Code, Claude Desktop, Cursor and Codex.",
5
5
  "keywords": [
6
6
  "mcp",