mobridge-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.
- package/dist/main.obf.cjs +1 -1
- package/dist/protocol/platform-capabilities.json +28 -18
- package/dist/protocol/schemas/action.schema.json +17 -2
- package/dist/protocol/schemas/error.schema.json +1 -1
- package/dist/protocol/schemas/tools.schema.json +2 -1
- package/integrity.json +2 -2
- package/loader.cjs +1 -1
- package/package.json +1 -1
- package/resources/android/app-developer-release.apk +0 -0
- package/resources/ios/MobileSemantic/UITests/BridgeServer.swift +1 -1
- package/resources/ios/MobileSemantic/UITests/XcuiSnapshot.swift +3 -1
|
@@ -57,13 +57,24 @@
|
|
|
57
57
|
"note": "iOS has no global BACK/RECENTS/NOTIFICATION_SHADE; BACK is a semantic UI action (find nav button + act)."
|
|
58
58
|
},
|
|
59
59
|
"mobile.capability.invoke": {
|
|
60
|
-
"level": "
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
"
|
|
60
|
+
"level": "NONE",
|
|
61
|
+
"reasonCode": "NO_BRIDGE_METHOD",
|
|
62
|
+
"note": "The Runner serves no capability.invoke and nothing composes it host-side; every call answered ACTION_UNSUPPORTED. Advertised PARTIAL[APP,CLIPBOARD] until a live tool sweep called it."
|
|
63
|
+
},
|
|
64
|
+
"mobile.screen.watch": {
|
|
65
|
+
"level": "NONE",
|
|
66
|
+
"reasonCode": "NO_EVENT_STREAM_ON_IOS",
|
|
67
|
+
"note": "watch blocks on the Android companion's accessibility event stream; XCUITest exposes no equivalent. Use screen.wait (polling) on iOS."
|
|
68
|
+
},
|
|
69
|
+
"mobile.screen.analyze": {
|
|
70
|
+
"level": "NONE",
|
|
71
|
+
"reasonCode": "DEPENDS_ON_SCREEN_OBSERVE",
|
|
72
|
+
"note": "analyze is host-side but reads screen.observe, which the Runner does not serve."
|
|
73
|
+
},
|
|
74
|
+
"mobile.screen.record": {
|
|
75
|
+
"level": "NONE",
|
|
76
|
+
"reasonCode": "ANDROID_ONLY",
|
|
77
|
+
"note": "Host-side adb screenrecord. iOS recording would be a Runner/simctl path that does not exist."
|
|
67
78
|
},
|
|
68
79
|
"mobile.batch": {
|
|
69
80
|
"level": "NONE",
|
|
@@ -105,13 +116,9 @@
|
|
|
105
116
|
"replacesAndroidTool": "mobile.diag.batterystats"
|
|
106
117
|
},
|
|
107
118
|
"runtime.setup": {
|
|
108
|
-
"level": "
|
|
109
|
-
"reasonCode": "
|
|
110
|
-
"
|
|
111
|
-
"STATUS",
|
|
112
|
-
"OPEN",
|
|
113
|
-
"VERIFY"
|
|
114
|
-
]
|
|
119
|
+
"level": "NONE",
|
|
120
|
+
"reasonCode": "ANDROID_COMPANION_ONLY",
|
|
121
|
+
"note": "setup.status/open/verify are the Android companion's own permission centre. The Runner serves none of them; iOS setup is device.connect's provisioning path."
|
|
115
122
|
},
|
|
116
123
|
"mobile.app.control": {
|
|
117
124
|
"level": "FULL"
|
|
@@ -177,10 +184,11 @@
|
|
|
177
184
|
},
|
|
178
185
|
"mobile.screen.inspect": {
|
|
179
186
|
"level": "PARTIAL",
|
|
180
|
-
"reasonCode": "
|
|
187
|
+
"reasonCode": "NO_VISIBLE_OR_CHILDREN_ON_IOS",
|
|
181
188
|
"allowedOperations": [
|
|
182
189
|
"DELTA",
|
|
183
|
-
"TARGET"
|
|
190
|
+
"TARGET",
|
|
191
|
+
"APP"
|
|
184
192
|
]
|
|
185
193
|
}
|
|
186
194
|
},
|
|
@@ -244,9 +252,11 @@
|
|
|
244
252
|
},
|
|
245
253
|
"mobile.screen.inspect": {
|
|
246
254
|
"level": "PARTIAL",
|
|
247
|
-
"reasonCode": "
|
|
255
|
+
"reasonCode": "NO_VISIBLE_OR_CHILDREN_ON_IOS",
|
|
248
256
|
"allowedOperations": [
|
|
249
|
-
"DELTA"
|
|
257
|
+
"DELTA",
|
|
258
|
+
"TARGET",
|
|
259
|
+
"APP"
|
|
250
260
|
]
|
|
251
261
|
}
|
|
252
262
|
}
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"description": "Whether the after-snapshot was captured on a settled screen."
|
|
40
40
|
},
|
|
41
41
|
"actionMethod": {
|
|
42
|
-
"enum": ["ACCESSIBILITY_ACTION", "ACCESSIBILITY_ACTION_ANCESTOR", "GESTURE_FALLBACK"]
|
|
42
|
+
"enum": ["ACCESSIBILITY_ACTION", "ACCESSIBILITY_ACTION_ANCESTOR", "GESTURE_FALLBACK", "GLOBAL_ACTION"],
|
|
43
|
+
"description": "GLOBAL_ACTION = a device-level navigation key (Android performGlobalAction, iOS XCUIDevice press) — no node is targeted."
|
|
43
44
|
},
|
|
44
45
|
|
|
45
46
|
"actionRequest": {
|
|
@@ -87,6 +88,11 @@
|
|
|
87
88
|
"generation": {
|
|
88
89
|
"type": ["integer", "null"],
|
|
89
90
|
"description": "Capture generation the target ref was built against (from screen.find/target). When set, the runtime hard-checks it and returns STALE_NODE_REF if the screen advanced — it never re-resolves a stale ref to a similar node."
|
|
91
|
+
},
|
|
92
|
+
"diffDetail": {
|
|
93
|
+
"enum": ["SUMMARY", "FULL"],
|
|
94
|
+
"default": "SUMMARY",
|
|
95
|
+
"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. A screen transition diffs hundreds of nodes and the caller rarely needs them all — observation.diffRef re-fetches the exact pair via mobile.screen.diff."
|
|
90
96
|
}
|
|
91
97
|
},
|
|
92
98
|
"allOf": [
|
|
@@ -129,7 +135,16 @@
|
|
|
129
135
|
"label": { "type": ["string", "null"] }
|
|
130
136
|
}
|
|
131
137
|
},
|
|
132
|
-
"
|
|
138
|
+
"appInstallCheck": {
|
|
139
|
+
"enum": ["INSTALLED", "NOT_INSTALLED", "UNKNOWN_TIMEOUT", null],
|
|
140
|
+
"description": "app.open only (iOS): whether the target bundle was confirmed installed before launching. UNKNOWN_TIMEOUT means the inventory did not answer in time — the launch was attempted without asserting installation."
|
|
141
|
+
},
|
|
142
|
+
"warnings": {
|
|
143
|
+
"type": "array",
|
|
144
|
+
"items": { "type": "string" },
|
|
145
|
+
"description": "Non-fatal notices about how this result was produced (e.g. an unverified install state). Never a substitute for an error."
|
|
146
|
+
},
|
|
147
|
+
"mutationClass": {
|
|
133
148
|
"enum": ["READ_ONLY", "REVERSIBLE", "SIDE_EFFECT", "EXTERNAL_SIDE_EFFECT", "DESTRUCTIVE"],
|
|
134
149
|
"description": "Advisory, payload-derived classification of how mutating this specific action was. Metadata for the host, not a gate."
|
|
135
150
|
},
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"$defs": {
|
|
24
24
|
"code": {
|
|
25
25
|
"enum": [
|
|
26
|
-
"DEVICE_OFFLINE", "SESSION_EXPIRED", "PERMISSION_REQUIRED", "WINDOW_UNAVAILABLE",
|
|
26
|
+
"DEVICE_OFFLINE", "SESSION_EXPIRED", "PERMISSION_REQUIRED", "WINDOW_UNAVAILABLE", "DEVICE_LOCKED",
|
|
27
27
|
"SECURE_WINDOW", "NODE_NOT_FOUND", "AMBIGUOUS_MATCH", "STALE_NODE_REF",
|
|
28
28
|
"WEAK_REFERENCE_UNRESOLVABLE",
|
|
29
29
|
"ACTION_UNSUPPORTED", "ACTION_REJECTED", "NO_EFFECT", "VERIFY_TIMEOUT",
|
|
@@ -59,7 +59,8 @@
|
|
|
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
|
-
"verify": { "$ref": "msm:wait.schema.json" }
|
|
62
|
+
"verify": { "$ref": "msm:wait.schema.json" },
|
|
63
|
+
"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." }
|
|
63
64
|
}
|
|
64
65
|
},
|
|
65
66
|
"output": {
|
package/integrity.json
CHANGED
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 = "
|
|
17
|
+
const BUILD_ID = "56de3b6-20260806163529"; // 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
|
+
"version": "0.3.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",
|
|
Binary file
|
|
@@ -194,7 +194,7 @@ final class BridgeServer: XCTestCase {
|
|
|
194
194
|
if let snap = try? app.snapshot() {
|
|
195
195
|
return Xcui.toScreenSnapshot(snap, bundleId: bundle, deviceId: "ios-sim")
|
|
196
196
|
}
|
|
197
|
-
return ["schemaVersion": 1, "platform": "IOS", "nodes": [], "treeHash": "0", "packageName": bundle, "appId": bundle, "snapshotId": "s_empty", "deviceId": "ios-sim", "windowId": 0, "orientation": "portrait", "screenStateId": "ios:0", "compact": true, "timestamp":
|
|
197
|
+
return ["schemaVersion": 1, "platform": "IOS", "nodes": [], "treeHash": "0", "packageName": bundle, "appId": bundle, "snapshotId": "s_empty", "deviceId": "ios-sim", "windowId": 0, "orientation": "portrait", "screenStateId": "ios:0", "compact": true, "timestamp": Int(Date().timeIntervalSince1970 * 1000)]
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
private func appOpen(_ p: [String: Any], ok: ([String: Any]) -> ([String: Any], Bool), err: (String, String, [String]) -> ([String: Any], Bool)) -> ([String: Any], Bool) {
|
|
@@ -79,7 +79,9 @@ enum Xcui {
|
|
|
79
79
|
return [
|
|
80
80
|
"schemaVersion": 1, "platform": "IOS", "deviceId": deviceId,
|
|
81
81
|
"snapshotId": "s_\(UUID().uuidString.prefix(8))",
|
|
82
|
-
|
|
82
|
+
// Epoch MILLISECONDS, per common.schema.json#/$defs/timestampMs. An ISO8601 string here
|
|
83
|
+
// made every iOS screen.get fail a strict client's output-schema check.
|
|
84
|
+
"timestamp": Int(Date().timeIntervalSince1970 * 1000),
|
|
83
85
|
"packageName": bundleId, "appId": bundleId, "windowId": 0, "orientation": "portrait",
|
|
84
86
|
"screenStateId": "ios:\(th.prefix(4))", "compact": true, "nodes": nodes, "treeHash": th,
|
|
85
87
|
"truncated": truncated,
|