mobile-debug-mcp 0.30.1 → 0.31.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/README.md +50 -5
- package/dist/interact/index.js +546 -133
- package/dist/manage/ios.js +1 -1
- package/dist/server/tool-definitions.js +13 -9
- package/dist/server/tool-handlers.js +4 -4
- package/dist/server-core.js +1 -1
- package/dist/system/index.js +485 -48
- package/dist/system/ios.js +15 -7
- package/dist/utils/android/utils.js +4 -4
- package/dist/utils/cli/idb/check-idb.js +10 -3
- package/dist/utils/cli/idb/idb-helper.js +47 -13
- package/dist/utils/ios/utils.js +5 -9
- package/docs/CHANGELOG.md +6 -0
- package/docs/ROADMAP.md +88 -212
- package/docs/specs/baseline-spec-v0.md +1 -1
- package/docs/specs-deltas/015-adjustable-control-support.md +541 -0
- package/docs/specs-deltas/016-environment-tools.md +533 -0
- package/docs/tools/TOOLS.md +2 -0
- package/docs/tools/manage.md +4 -2
- package/docs/tools/system.md +9 -2
- package/package.json +1 -1
- package/skills/spec-review/SKILL.md +52 -0
- package/skills/spec-review/references/spec-review-checklist.md +13 -0
- package/skills/spec-review/references/spec-review-template.md +28 -0
- package/src/interact/index.ts +581 -138
- package/src/manage/ios.ts +1 -1
- package/src/server/tool-definitions.ts +13 -9
- package/src/server/tool-handlers.ts +4 -4
- package/src/server-core.ts +1 -1
- package/src/system/index.ts +572 -50
- package/src/system/ios.ts +14 -7
- package/src/types.ts +2 -0
- package/src/utils/android/utils.ts +3 -3
- package/src/utils/cli/idb/check-idb.ts +11 -2
- package/src/utils/cli/idb/idb-helper.ts +49 -12
- package/src/utils/ios/utils.ts +4 -8
- package/test/unit/interact/adjust_control.test.ts +586 -33
- package/test/unit/interact/tap_element.test.ts +1 -1
- package/test/unit/server/contract.test.ts +4 -0
- package/test/unit/system/get_system_status.test.ts +3 -0
- package/test/unit/system/system_status.test.ts +40 -0
- package/test/unit/utils/idb_health.test.ts +74 -0
- package/skills/rfc-review/SKILL.md +0 -52
- package/skills/rfc-review/references/rfc-review-checklist.md +0 -12
- package/skills/rfc-review/references/rfc-review-template.md +0 -28
- /package/docs/{rfcs → specs-deltas}/001-state-verification.md +0 -0
- /package/docs/{rfcs → specs-deltas}/002-richer-element-identity +0 -0
- /package/docs/{rfcs → specs-deltas}/003-wait-and-synchronization-reliability.md +0 -0
- /package/docs/{rfcs → specs-deltas}/004-action-verification-routing.md +0 -0
- /package/docs/{rfcs → specs-deltas}/005-unified-action-execution-and-verification-model.md +0 -0
- /package/docs/{rfcs → specs-deltas}/006-runtime-action-instrumentation-and-binding-layer.md +0 -0
- /package/docs/{rfcs → specs-deltas}/007-actionability-resolution-and-executable-target-selection.md +0 -0
- /package/docs/{rfcs → specs-deltas}/008-adjustable-control-support-and-semantic-value-manipulation.md +0 -0
- /package/docs/{rfcs → specs-deltas}/009-semantic-control-modeling-for-custom-and-composite-controls.md +0 -0
- /package/docs/{rfcs → specs-deltas}/010-verification-stabilization-and-temporal-convergence.md +0 -0
- /package/docs/{rfcs → specs-deltas}/011-recovery-and-replanning-for-failed-or-ambiguous-interaction-flows.md +0 -0
- /package/docs/{rfcs → specs-deltas}/011.1-recovery-contract-types-and-runtime-wiring-spec.md +0 -0
- /package/docs/{rfcs → specs-deltas}/012-action-trace-and-xecution-observability.md +0 -0
- /package/docs/{rfcs → specs-deltas}/013-wait-and-synchronization-reliability.md +0 -0
- /package/docs/{rfcs → specs-deltas}/014-actionability-resolution.md +0 -0
|
@@ -70,6 +70,10 @@ async function run() {
|
|
|
70
70
|
const adjustControl = toolDefinitions.find((tool) => tool.name === 'adjust_control')
|
|
71
71
|
assert(adjustControl, 'adjust_control should be registered')
|
|
72
72
|
assert.deepStrictEqual((adjustControl as any).inputSchema.required, ['targetValue'])
|
|
73
|
+
assert.deepStrictEqual((adjustControl as any).inputSchema.oneOf, [
|
|
74
|
+
{ required: ['selector'] },
|
|
75
|
+
{ required: ['element_id'] }
|
|
76
|
+
])
|
|
73
77
|
assert.strictEqual((adjustControl as any).inputSchema.properties.targetValue.type, 'number')
|
|
74
78
|
assert.match((adjustControl as any).description, /numeric control value/i)
|
|
75
79
|
assert.match((adjustControl as any).description, /expect_state/i)
|
|
@@ -9,6 +9,9 @@ async function run() {
|
|
|
9
9
|
assert(typeof (payload as any).status === 'string')
|
|
10
10
|
assert(Array.isArray(payload.issues))
|
|
11
11
|
assert((payload as any).summary && typeof (payload as any).summary.overall === 'string')
|
|
12
|
+
assert((payload as any).host && typeof (payload as any).host.os === 'string')
|
|
13
|
+
assert((payload as any).android && typeof (payload as any).android.status === 'string')
|
|
14
|
+
assert((payload as any).ios && typeof (payload as any).ios.status === 'string')
|
|
12
15
|
|
|
13
16
|
const adb = ensureAdbAvailable()
|
|
14
17
|
assert(adb && typeof adb.ok === 'boolean')
|
|
@@ -74,6 +74,7 @@ async function run() {
|
|
|
74
74
|
XCRUN_VERSION_OUTPUT: process.env.XCRUN_VERSION_OUTPUT,
|
|
75
75
|
XCRUN_VERSION_STATUS: process.env.XCRUN_VERSION_STATUS,
|
|
76
76
|
SIMCTL_LIST_OUTPUT: process.env.SIMCTL_LIST_OUTPUT,
|
|
77
|
+
MOBILE_DEBUG_MCP_HOST_OS: process.env.MOBILE_DEBUG_MCP_HOST_OS,
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
try {
|
|
@@ -82,6 +83,7 @@ async function run() {
|
|
|
82
83
|
process.env.XCRUN_PATH = xcrunPath
|
|
83
84
|
|
|
84
85
|
setScenario({
|
|
86
|
+
MOBILE_DEBUG_MCP_HOST_OS: 'darwin',
|
|
85
87
|
ADB_VERSION_STATUS: '0',
|
|
86
88
|
ADB_VERSION_OUTPUT: '8.1.0\n',
|
|
87
89
|
ADB_DEVICES_OUTPUT: 'List of devices attached\nemulator-5554\tdevice product:sdk\n',
|
|
@@ -94,12 +96,16 @@ async function run() {
|
|
|
94
96
|
const healthy = await systemStatus.getSystemStatus()
|
|
95
97
|
assert.strictEqual(healthy.success, true)
|
|
96
98
|
assert.strictEqual((healthy as any).status, 'ready')
|
|
99
|
+
assert.strictEqual((healthy as any).host.supported, true)
|
|
97
100
|
assert.strictEqual(healthy.adbAvailable, true)
|
|
98
101
|
assert.strictEqual(typeof healthy.adbVersion, 'string')
|
|
102
|
+
assert.strictEqual((healthy as any).android.status, 'ready')
|
|
103
|
+
assert.strictEqual((healthy as any).ios.status, 'ready')
|
|
99
104
|
assert.strictEqual((healthy as any).summary.android.ready, true)
|
|
100
105
|
assert.strictEqual(typeof (healthy as any).summary.ios.summary, 'string')
|
|
101
106
|
|
|
102
107
|
setScenario({
|
|
108
|
+
MOBILE_DEBUG_MCP_HOST_OS: 'darwin',
|
|
103
109
|
ADB_VERSION_STATUS: '1',
|
|
104
110
|
ADB_VERSION_OUTPUT: 'not found',
|
|
105
111
|
ADB_DEVICES_OUTPUT: 'List of devices attached\n',
|
|
@@ -112,6 +118,7 @@ async function run() {
|
|
|
112
118
|
assert(missingAdb.issues.some((issue: string) => issue.includes('ADB')))
|
|
113
119
|
|
|
114
120
|
setScenario({
|
|
121
|
+
MOBILE_DEBUG_MCP_HOST_OS: 'darwin',
|
|
115
122
|
ADB_VERSION_STATUS: '0',
|
|
116
123
|
ADB_VERSION_OUTPUT: '8.1.0\n',
|
|
117
124
|
ADB_DEVICES_OUTPUT: 'List of devices attached\nserial1\tunauthorized\nserial2\toffline\n',
|
|
@@ -125,6 +132,7 @@ async function run() {
|
|
|
125
132
|
assert(unauthorized.issues.some((issue: string) => issue.includes('offline')))
|
|
126
133
|
|
|
127
134
|
setScenario({
|
|
135
|
+
MOBILE_DEBUG_MCP_HOST_OS: 'darwin',
|
|
128
136
|
ADB_VERSION_STATUS: '0',
|
|
129
137
|
ADB_VERSION_OUTPUT: '8.1.0\n',
|
|
130
138
|
ADB_DEVICES_OUTPUT: 'List of devices attached\nemulator-5554\tdevice product:sdk\n',
|
|
@@ -136,6 +144,38 @@ async function run() {
|
|
|
136
144
|
assert.strictEqual(missingXcrun.adbAvailable, true)
|
|
137
145
|
assert.strictEqual(Array.isArray(missingXcrun.issues), true)
|
|
138
146
|
assert.strictEqual((missingXcrun as any).summary.ios.ready, false)
|
|
147
|
+
assert.strictEqual((missingXcrun as any).ios.status, 'unavailable')
|
|
148
|
+
|
|
149
|
+
setScenario({
|
|
150
|
+
MOBILE_DEBUG_MCP_HOST_OS: 'win32',
|
|
151
|
+
ADB_VERSION_STATUS: '0',
|
|
152
|
+
ADB_VERSION_OUTPUT: '8.1.0\n',
|
|
153
|
+
ADB_DEVICES_OUTPUT: 'List of devices attached\nemulator-5554\tdevice product:sdk\n',
|
|
154
|
+
XCRUN_VERSION_STATUS: '1',
|
|
155
|
+
XCRUN_VERSION_OUTPUT: 'not found',
|
|
156
|
+
})
|
|
157
|
+
const windowsHost = await systemStatus.getSystemStatus()
|
|
158
|
+
assert.strictEqual((windowsHost as any).host.os, 'windows')
|
|
159
|
+
assert.strictEqual((windowsHost as any).host.supported, true)
|
|
160
|
+
assert.strictEqual((windowsHost as any).ios.status, 'unsupported')
|
|
161
|
+
assert.deepStrictEqual((windowsHost as any).ios.tools, {})
|
|
162
|
+
assert.deepStrictEqual((windowsHost as any).ios.providers, {})
|
|
163
|
+
assert.deepStrictEqual((windowsHost as any).ios.capabilities, {})
|
|
164
|
+
assert.deepStrictEqual((windowsHost as any).ios.devices, [])
|
|
165
|
+
assert.strictEqual((windowsHost as any).ios.failures[0].code, 'PLATFORM_UNAVAILABLE')
|
|
166
|
+
assert.strictEqual((windowsHost as any).summary.ios.ready, false)
|
|
167
|
+
|
|
168
|
+
setScenario({ MOBILE_DEBUG_MCP_HOST_OS: 'freebsd' })
|
|
169
|
+
const unsupported = await systemStatus.getSystemStatus()
|
|
170
|
+
assert.strictEqual((unsupported as any).host.os, 'freebsd')
|
|
171
|
+
assert.strictEqual((unsupported as any).host.supported, false)
|
|
172
|
+
assert.strictEqual((unsupported as any).android.status, 'unsupported')
|
|
173
|
+
assert.strictEqual((unsupported as any).ios.status, 'unsupported')
|
|
174
|
+
assert.strictEqual((unsupported as any).android.failures[0].code, 'HOST_OS_UNSUPPORTED')
|
|
175
|
+
assert.strictEqual((unsupported as any).ios.failures[0].code, 'HOST_OS_UNSUPPORTED')
|
|
176
|
+
assert.strictEqual(unsupported.adbAvailable, false)
|
|
177
|
+
assert.strictEqual(unsupported.iosAvailable, false)
|
|
178
|
+
assert.strictEqual((unsupported as any).summary.overall, 'blocked')
|
|
139
179
|
|
|
140
180
|
console.log('system_status checks passed')
|
|
141
181
|
} finally {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import fs from 'fs/promises'
|
|
3
|
+
import os from 'os'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
|
|
6
|
+
import { isIDBInstalled, probeIdb } from '../../../src/utils/cli/idb/idb-helper.js'
|
|
7
|
+
|
|
8
|
+
async function run() {
|
|
9
|
+
const binDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mcp-idb-health-'))
|
|
10
|
+
const idbPath = path.join(binDir, 'idb')
|
|
11
|
+
const python3Path = path.join(binDir, 'python3')
|
|
12
|
+
const pythonPath = path.join(binDir, 'python')
|
|
13
|
+
const pythonLog = path.join(binDir, 'python.log')
|
|
14
|
+
const originalIdbPath = process.env.MCP_IDB_PATH
|
|
15
|
+
const originalPath = process.env.PATH
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
await fs.writeFile(idbPath, `#!/bin/sh
|
|
19
|
+
if [ "$1" = "list-targets" ] && [ "$2" = "--json" ]; then
|
|
20
|
+
printf '{"targets":[]}\n'
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
if [ "$1" = "--version" ]; then
|
|
24
|
+
printf 'idb: error: unrecognized arguments: --version\n' >&2
|
|
25
|
+
exit 2
|
|
26
|
+
fi
|
|
27
|
+
exit 0
|
|
28
|
+
`, { mode: 0o755 })
|
|
29
|
+
|
|
30
|
+
await fs.writeFile(python3Path, `#!/bin/sh
|
|
31
|
+
printf 'python3 called\\n' >> "${pythonLog}"
|
|
32
|
+
exit 0
|
|
33
|
+
`, { mode: 0o755 })
|
|
34
|
+
|
|
35
|
+
await fs.writeFile(pythonPath, `#!/bin/sh
|
|
36
|
+
printf 'python called\\n' >> "${pythonLog}"
|
|
37
|
+
exit 0
|
|
38
|
+
`, { mode: 0o755 })
|
|
39
|
+
|
|
40
|
+
process.env.MCP_IDB_PATH = idbPath
|
|
41
|
+
process.env.PATH = `${binDir}${path.delimiter}${originalPath || ''}`
|
|
42
|
+
|
|
43
|
+
const probe = probeIdb(idbPath)
|
|
44
|
+
assert.strictEqual(probe.ok, true)
|
|
45
|
+
assert.strictEqual(isIDBInstalled(), true)
|
|
46
|
+
assert(probe.raw === null || typeof probe.raw === 'string')
|
|
47
|
+
|
|
48
|
+
await fs.rm(pythonLog, { force: true }).catch(() => {})
|
|
49
|
+
const failedIdbPath = path.join(binDir, 'idb-failing')
|
|
50
|
+
await fs.writeFile(failedIdbPath, `#!/bin/sh
|
|
51
|
+
if [ "$1" = "list-targets" ] && [ "$2" = "--json" ]; then
|
|
52
|
+
printf 'idb unavailable\\n' >&2
|
|
53
|
+
exit 2
|
|
54
|
+
fi
|
|
55
|
+
exit 2
|
|
56
|
+
`, { mode: 0o755 })
|
|
57
|
+
|
|
58
|
+
const failureProbe = probeIdb(failedIdbPath)
|
|
59
|
+
assert.strictEqual(failureProbe.ok, false)
|
|
60
|
+
assert.strictEqual(await fs.readFile(pythonLog, 'utf8').catch(() => ''), '')
|
|
61
|
+
|
|
62
|
+
console.log('idb health probe test passed')
|
|
63
|
+
} finally {
|
|
64
|
+
if (originalIdbPath === undefined) delete process.env.MCP_IDB_PATH
|
|
65
|
+
else process.env.MCP_IDB_PATH = originalIdbPath
|
|
66
|
+
|
|
67
|
+
if (originalPath === undefined) delete process.env.PATH
|
|
68
|
+
else process.env.PATH = originalPath
|
|
69
|
+
|
|
70
|
+
await fs.rm(binDir, { recursive: true, force: true }).catch(() => {})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
run().catch((error) => { console.error(error); process.exit(1) })
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# RFC Review skill
|
|
2
|
-
|
|
3
|
-
name: rfc-review
|
|
4
|
-
version: 0.1.1
|
|
5
|
-
summary: Reusable workflow for reviewing RFCs/specs in this repository with a consistent readiness rubric and output template.
|
|
6
|
-
|
|
7
|
-
# Purpose
|
|
8
|
-
Help an agent review an RFC for clarity, implementation readiness, and alignment with the current codebase. Use a common template so reviews stay consistent across documents and reviewers.
|
|
9
|
-
|
|
10
|
-
# Activation conditions
|
|
11
|
-
Activate when an agent needs to:
|
|
12
|
-
- review a new or revised RFC
|
|
13
|
-
- assess whether an RFC is implementation-ready
|
|
14
|
-
- identify whether feedback is an RFC issue or an implementation issue
|
|
15
|
-
- compare a spec against the current `src/` contract surface and docs
|
|
16
|
-
|
|
17
|
-
# Surface area (actions)
|
|
18
|
-
- locate-rfc
|
|
19
|
-
- compare-against-code
|
|
20
|
-
- assess-contract-completeness
|
|
21
|
-
- classify-gaps
|
|
22
|
-
- produce-review
|
|
23
|
-
|
|
24
|
-
# Core guidance
|
|
25
|
-
1. Read the RFC first, then compare it against the relevant code, docs, and tests.
|
|
26
|
-
2. Separate **spec gaps** from **implementation gaps**.
|
|
27
|
-
3. Check for: problem clarity, scope boundaries, explicit contracts, acceptance criteria, non-goals, and consistency with existing behavior.
|
|
28
|
-
4. Prefer precise feedback that names the missing contract, unclear rule, or inconsistent behavior.
|
|
29
|
-
5. Use the shared review template in `references/rfc-review-template.md` for the final output.
|
|
30
|
-
6. If the RFC is not ready, say exactly what must be clarified before implementation can start.
|
|
31
|
-
7. Classify each blocker as either a **spec gap** or an **implementation contract gap** and stop at that boundary.
|
|
32
|
-
|
|
33
|
-
# Inputs & outputs
|
|
34
|
-
- review-rfc(input: { rfcPath, relatedPaths?, focusAreas? }) -> { verdict, risks, specGaps, implementationGaps, recommendations }
|
|
35
|
-
- compare-against-code(input: { rfcPath, codePaths[] }) -> { matches, mismatches, notes }
|
|
36
|
-
- produce-review(input: { rfcPath, findings[] }) -> { summary, verdict, checklist, nextStep }
|
|
37
|
-
|
|
38
|
-
# Failure handling
|
|
39
|
-
- If the RFC file is missing, stop and report the missing path explicitly.
|
|
40
|
-
- If the RFC is ambiguous, classify each concern as either "spec" or "implementation" instead of blending them.
|
|
41
|
-
- If the review cannot be grounded in the current repo, state that the RFC is not reviewable yet.
|
|
42
|
-
|
|
43
|
-
# Progressive disclosure
|
|
44
|
-
- Keep this file short.
|
|
45
|
-
- Load the reference template only when writing the final review.
|
|
46
|
-
|
|
47
|
-
# References
|
|
48
|
-
- `references/rfc-review-template.md` — standard review format and verdict rubric
|
|
49
|
-
- `references/rfc-review-checklist.md` — questions to apply while reviewing an RFC
|
|
50
|
-
|
|
51
|
-
# License
|
|
52
|
-
Same as repository (MIT).
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# RFC Review Checklist
|
|
2
|
-
|
|
3
|
-
Ask these questions while reviewing:
|
|
4
|
-
|
|
5
|
-
1. Is the problem statement specific and grounded in current failures?
|
|
6
|
-
2. Are non-goals explicit?
|
|
7
|
-
3. Are contracts concrete enough to implement?
|
|
8
|
-
4. Are acceptance criteria testable?
|
|
9
|
-
5. Does the RFC define the source of truth for new fields or behaviors?
|
|
10
|
-
6. Does it match existing code paths and public tool surfaces?
|
|
11
|
-
7. Can each open concern be classified as a spec issue or an implementation issue?
|
|
12
|
-
8. Is the RFC ready to implement without further interpretation?
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# RFC Review Template
|
|
2
|
-
|
|
3
|
-
Use this structure for every RFC review:
|
|
4
|
-
|
|
5
|
-
## Verdict
|
|
6
|
-
- Ready / Needs clarification / Needs implementation contract / Not ready
|
|
7
|
-
|
|
8
|
-
## Summary
|
|
9
|
-
- One short paragraph on the RFC's current quality.
|
|
10
|
-
|
|
11
|
-
## What is good
|
|
12
|
-
- List the strongest parts of the RFC.
|
|
13
|
-
|
|
14
|
-
## Issues
|
|
15
|
-
For each issue, include:
|
|
16
|
-
- **Type:** spec / implementation / implementation contract / doc
|
|
17
|
-
- **Severity:** low / medium / high
|
|
18
|
-
- **Why it matters:** one sentence
|
|
19
|
-
- **Fix:** exact change needed
|
|
20
|
-
|
|
21
|
-
## Missing contract surfaces
|
|
22
|
-
- List any API shapes, response fields, state transitions, or invariants that are still undefined.
|
|
23
|
-
|
|
24
|
-
## Codebase alignment
|
|
25
|
-
- Note whether the RFC matches current `src/`, docs, and tests.
|
|
26
|
-
|
|
27
|
-
## Next step
|
|
28
|
-
- State the smallest next action needed to move the RFC forward.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/docs/{rfcs → specs-deltas}/007-actionability-resolution-and-executable-target-selection.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/docs/{rfcs → specs-deltas}/010-verification-stabilization-and-temporal-convergence.md
RENAMED
|
File without changes
|
|
File without changes
|
/package/docs/{rfcs → specs-deltas}/011.1-recovery-contract-types-and-runtime-wiring-spec.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|