mobile-debug-mcp 0.32.2 → 0.33.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/.agents/plugins/marketplace.json +20 -0
- package/{skills → .agents/skills}/mcp-builder/SKILL.md +1 -2
- package/{skills → .agents/skills}/spec-review/SKILL.md +1 -1
- package/{skills → .agents/skills}/test-authoring/SKILL.md +1 -1
- package/.claude-plugin/marketplace.json +14 -0
- package/.cursor-plugin/marketplace.json +16 -0
- package/.github/copilot-instructions.md +7 -0
- package/AGENTS.md +21 -10
- package/README.md +67 -5
- package/agent-plugin/.claude-plugin/plugin.json +20 -0
- package/agent-plugin/.codex-plugin/plugin.json +34 -0
- package/agent-plugin/.cursor-plugin/plugin.json +23 -0
- package/agent-plugin/.mcp.json +14 -0
- package/agent-plugin/LICENSE +201 -0
- package/agent-plugin/README.md +29 -0
- package/agent-plugin/assets/mobile-debug-tools.svg +28 -0
- package/agent-plugin/mcp.json +11 -0
- package/agent-plugin/plugin.json +29 -0
- package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +124 -0
- package/dist/interact/android.js +10 -4
- package/dist/interact/conditional-action.js +68 -0
- package/dist/interact/index.js +54 -14
- package/dist/interact/ios.js +14 -8
- package/dist/manage/android.js +4 -3
- package/dist/manage/ios.js +3 -2
- package/dist/observe/android.js +27 -22
- package/dist/observe/evidence.js +273 -0
- package/dist/observe/index.js +33 -6
- package/dist/observe/ios.js +25 -6
- package/dist/observe/readiness-service.js +115 -0
- package/dist/observe/readiness.js +81 -0
- package/dist/observe/recording.js +165 -0
- package/dist/observe/snapshot-metadata.js +10 -2
- package/dist/server/common.js +9 -0
- package/dist/server/condition-schema.js +32 -0
- package/dist/server/journey-schema.js +3 -2
- package/dist/server/readiness-schema.js +12 -0
- package/dist/server/tool-definitions.js +46 -0
- package/dist/server/tool-handlers.js +99 -17
- package/dist/server-core.js +1 -1
- package/dist/utils/ios/utils.js +4 -4
- package/dist/utils/operation-budget.js +24 -2
- package/dist/utils/resolve-device.js +7 -0
- package/docs/CHANGELOG.md +16 -2
- package/docs/benchmarks/ui-tree-performance/README.md +4 -3
- package/docs/specs/020-conditional-actions/intent.md +62 -0
- package/docs/specs/020-conditional-actions/plan.md +214 -0
- package/docs/specs/020-conditional-actions/spec.md +301 -0
- package/docs/specs/021-app-readiness-signals/intent.md +40 -0
- package/docs/specs/021-app-readiness-signals/plan.md +102 -0
- package/docs/specs/021-app-readiness-signals/spec.md +144 -0
- package/docs/specs/022-correlated-evidence/device-evidence.json +60 -0
- package/docs/specs/022-correlated-evidence/intent.md +37 -0
- package/docs/specs/022-correlated-evidence/modul8-evidence.json +1062 -0
- package/docs/specs/022-correlated-evidence/modul8-selector-fix-evidence.json +336 -0
- package/docs/specs/022-correlated-evidence/plan.md +171 -0
- package/docs/specs/022-correlated-evidence/spec.md +123 -0
- package/docs/specs/process.md +213 -0
- package/docs/tools/TOOLS.md +3 -0
- package/docs/tools/evidence.md +79 -0
- package/docs/tools/interact.md +125 -2
- package/docs/tools/observe.md +64 -2
- package/package.json +4 -2
- package/scripts/agent-plugin.mjs +98 -0
- package/src/interact/android.ts +10 -4
- package/src/interact/conditional-action.ts +75 -0
- package/src/interact/index.ts +42 -14
- package/src/interact/ios.ts +14 -8
- package/src/manage/android.ts +4 -3
- package/src/manage/ios.ts +3 -2
- package/src/observe/android.ts +35 -21
- package/src/observe/evidence.ts +255 -0
- package/src/observe/index.ts +32 -8
- package/src/observe/ios.ts +32 -8
- package/src/observe/readiness-service.ts +89 -0
- package/src/observe/readiness.ts +78 -0
- package/src/observe/snapshot-metadata.ts +12 -1
- package/src/server/common.ts +7 -0
- package/src/server/condition-schema.ts +32 -0
- package/src/server/journey-schema.ts +3 -2
- package/src/server/readiness-schema.ts +12 -0
- package/src/server/tool-definitions.ts +44 -0
- package/src/server/tool-handlers.ts +93 -17
- package/src/server-core.ts +1 -1
- package/src/types.ts +30 -1
- package/src/utils/ios/utils.ts +4 -4
- package/src/utils/operation-budget.ts +22 -2
- package/src/utils/resolve-device.ts +8 -0
- package/test/device/automated/observe/correlated-evidence.smoke.ts +70 -0
- package/test/device/manual/interact/conditional_actions.manual.ts +95 -0
- package/test/device/manual/interact/modul8_selector_tap.manual.ts +63 -0
- package/test/device/manual/interact/readiness.manual.ts +99 -0
- package/test/fixtures/readiness-app/MainActivity.java +23 -0
- package/test/fixtures/readiness-app/README.md +12 -0
- package/test/fixtures/readiness-app/build.ts +37 -0
- package/test/fixtures/readiness-app/main.m +27 -0
- package/test/unit/observe/android_tree_acquisition.test.ts +6 -0
- package/test/unit/observe/capture_debug_snapshot.test.ts +42 -32
- package/test/unit/observe/compact_tree_error.test.ts +24 -0
- package/test/unit/observe/conditional_snapshots.test.ts +33 -0
- package/test/unit/observe/correlated-evidence.test.ts +128 -0
- package/test/unit/observe/ios-getlogs.test.ts +3 -3
- package/test/unit/observe/ios_tree_acquisition.test.ts +2 -0
- package/test/unit/observe/readiness.test.ts +39 -0
- package/test/unit/observe/readiness_native.test.ts +68 -0
- package/test/unit/server/conditional_actions.test.ts +299 -0
- package/test/unit/server/conditional_schema.test.ts +59 -0
- package/test/unit/server/correlated-evidence.test.ts +194 -0
- package/test/unit/server/readiness.test.ts +170 -0
- package/test/unit/server/selector_tap_target.test.ts +79 -0
- package/test/unit/server/tap_selector.test.ts +2 -0
- package/skills/mobile-debug-tools-usage/SKILL.md +0 -74
- package/src/observe/android-tree-bridge.ts +0 -178
- package/test/device/automated/ui-tree-performance/bridge-probe.ts +0 -197
- package/test/fixtures/ui-tree-bridge/AndroidManifest.xml +0 -6
- package/test/fixtures/ui-tree-bridge/build.ts +0 -34
- package/test/fixtures/ui-tree-bridge/src/TreeInstrumentation.java +0 -126
- package/test/unit/observe/android_tree_bridge_cleanup.test.ts +0 -95
- /package/{skills → .agents/skills}/README.md +0 -0
- /package/{skills → .agents/skills}/mcp-builder/references/build-flags.md +0 -0
- /package/{skills → .agents/skills}/mcp-builder/references/diagnostics-schema.md +0 -0
- /package/{skills → .agents/skills}/mcp-builder/references/toolchain-details.md +0 -0
- /package/{skills → .agents/skills}/spec-review/references/spec-review-checklist.md +0 -0
- /package/{skills → .agents/skills}/spec-review/references/spec-review-template.md +0 -0
- /package/{skills → .agents/skills}/test-authoring/references/repo-test-layout.md +0 -0
- /package/{skills → .agents/skills}/test-authoring/references/test-authoring-workflow.md +0 -0
- /package/{skills → .agents/skills}/test-authoring/references/test-quality-checklist.md +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Development process
|
|
2
|
+
|
|
3
|
+
This is the required development workflow for all agents contributing to
|
|
4
|
+
`mobile-debug-mcp`. Read it before planning, editing, or reviewing changes,
|
|
5
|
+
including work delegated to another agent. [AGENTS.md](../../AGENTS.md) is the
|
|
6
|
+
shared entrypoint; tool-specific instructions must point here rather than
|
|
7
|
+
maintain a competing workflow. Explicit user instructions take precedence.
|
|
8
|
+
|
|
9
|
+
The agent owns investigation, implementation, and verification. The user or
|
|
10
|
+
explicitly designated maintainer owns product decisions and approval. Existing
|
|
11
|
+
explicit approval in the current conversation counts for unchanged scope; do
|
|
12
|
+
not ask for it again. Never invent approval or treat silence as approval.
|
|
13
|
+
|
|
14
|
+
## Choose the path
|
|
15
|
+
|
|
16
|
+
Use the full process for new MCP capabilities, material tool behaviour or
|
|
17
|
+
contract changes, and significant architecture changes. Use the lightweight
|
|
18
|
+
path for fixes restoring agreed behaviour, refactors that preserve contracts,
|
|
19
|
+
small maintenance changes, and documentation-only work. Move to the full path
|
|
20
|
+
if investigation exposes new behaviour or unresolved product decisions.
|
|
21
|
+
|
|
22
|
+
For the lightweight path:
|
|
23
|
+
|
|
24
|
+
1. State the intended outcome and scope in the conversation.
|
|
25
|
+
2. Inspect the relevant code, contracts, tests, and instructions.
|
|
26
|
+
3. Make focused changes and run applicable verification below.
|
|
27
|
+
4. Report the changes, evidence, and remaining limitations.
|
|
28
|
+
|
|
29
|
+
Separate intent, spec, and plan files and approval stages are not required for
|
|
30
|
+
the lightweight path. Documentation-only changes require a diff, link/path,
|
|
31
|
+
and consistency check; application builds and device tests are unnecessary.
|
|
32
|
+
|
|
33
|
+
For the full path, keep `intent.md`, `spec.md`, and `plan.md` together under
|
|
34
|
+
`docs/specs/<next-number>-<feature>/`, following existing directory naming.
|
|
35
|
+
Reuse the relevant feature directory where appropriate. Do not rewrite old
|
|
36
|
+
specs retrospectively; add only what is needed for the current scope.
|
|
37
|
+
|
|
38
|
+
## Keep artifacts concise
|
|
39
|
+
|
|
40
|
+
Use these writing defaults for the full path:
|
|
41
|
+
|
|
42
|
+
- `intent.md`: about 200 words covering the problem, outcome, scope, and open
|
|
43
|
+
questions, with the required source and status information.
|
|
44
|
+
- `spec.md`: about 800 words focused on behaviour, contracts, failure handling,
|
|
45
|
+
acceptance criteria, and decisions needing approval.
|
|
46
|
+
- `plan.md`: a short implementation checklist and verification table. Append
|
|
47
|
+
actual results as work finishes.
|
|
48
|
+
|
|
49
|
+
These are defaults, not limits. Add detail when complexity or risk requires it;
|
|
50
|
+
retain all applicable stage requirements and approval gates below. Record each
|
|
51
|
+
decision once and reference it from later documents instead of repeating it.
|
|
52
|
+
Use examples only where a contract would otherwise be ambiguous. Link detailed
|
|
53
|
+
logs and evidence rather than copying them, while keeping enough result detail
|
|
54
|
+
in the plan to assess coverage and any remaining failures or blockers.
|
|
55
|
+
|
|
56
|
+
## 1. Capture intent
|
|
57
|
+
|
|
58
|
+
Write `intent.md` with:
|
|
59
|
+
|
|
60
|
+
- The original problem and its source, preserving the originator's meaning.
|
|
61
|
+
- Desired outcome, affected MCP clients, platforms, and observable success criteria.
|
|
62
|
+
- Constraints, exclusions, assumptions, and open questions.
|
|
63
|
+
- Originator, date, and status; do not invent identities.
|
|
64
|
+
|
|
65
|
+
Show the captured intent and obtain explicit agreement before drafting the
|
|
66
|
+
spec. A clarification about one detail does not approve the complete intent.
|
|
67
|
+
Record the approving response and its scope. While approval is pending,
|
|
68
|
+
continue only investigation or independent work that does not assume it.
|
|
69
|
+
|
|
70
|
+
**Exit:** the user has accepted the problem and scope, and questions blocking
|
|
71
|
+
specification are answered.
|
|
72
|
+
|
|
73
|
+
## 2. Investigate and specify
|
|
74
|
+
|
|
75
|
+
Inspect the current checkout and preserve unrelated changes. Ground the design
|
|
76
|
+
in existing code, tests, and [tool contracts](../tools/TOOLS.md). Reuse helpers
|
|
77
|
+
and conventions rather than introducing new frameworks without a concrete need.
|
|
78
|
+
|
|
79
|
+
Load applicable repository skills from [.agents/skills/](../../.agents/skills/README.md):
|
|
80
|
+
|
|
81
|
+
- [Test authoring](../../.agents/skills/test-authoring/SKILL.md) before creating or updating tests.
|
|
82
|
+
- [MCP builder](../../.agents/skills/mcp-builder/SKILL.md) for build, installation, or native toolchain work.
|
|
83
|
+
- [Spec review](../../.agents/skills/spec-review/SKILL.md) when reviewing an RFC or spec draft.
|
|
84
|
+
|
|
85
|
+
For device-dependent behaviour, inspect the current tool flow on an available
|
|
86
|
+
device or emulator when practical. Record the platform, device, server build,
|
|
87
|
+
observed behaviour, and inaccessible states. If unavailable, record the concrete
|
|
88
|
+
blocker and available code/test evidence; plan a runnable device check.
|
|
89
|
+
|
|
90
|
+
Write `spec.md` defining:
|
|
91
|
+
|
|
92
|
+
- Required behaviour and plain-language acceptance criteria linked to the intent.
|
|
93
|
+
- Scope, exclusions, failure, recovery, cancellation, and timeout behaviour.
|
|
94
|
+
- MCP tool names, input schemas, response/error shapes, and compatibility impact.
|
|
95
|
+
- Android/iOS differences and unsupported environments where applicable.
|
|
96
|
+
- Device/process targeting, resource ownership and cleanup, concurrency, and
|
|
97
|
+
subprocess handling where relevant.
|
|
98
|
+
- Security and privacy implications, including arguments, logs, screenshots,
|
|
99
|
+
UI trees, credentials, and potentially destructive device actions.
|
|
100
|
+
- Affected modules, risks, assumptions, and unresolved decisions.
|
|
101
|
+
- Approval status and a reference to the actual approving decision.
|
|
102
|
+
|
|
103
|
+
Show the complete spec and obtain explicit acceptance before planning
|
|
104
|
+
implementation. An instruction to implement an already accepted spec authorises
|
|
105
|
+
that scope.
|
|
106
|
+
|
|
107
|
+
**Exit:** the spec is accepted and decisions blocking correctness or scope are resolved.
|
|
108
|
+
|
|
109
|
+
## 3. Plan implementation and verification
|
|
110
|
+
|
|
111
|
+
Save `plan.md` with:
|
|
112
|
+
|
|
113
|
+
- References to intent, spec, and approval decisions.
|
|
114
|
+
- Affected files/modules and an ordered implementation checklist.
|
|
115
|
+
- Dependencies, risks, compatibility, rollout, and cleanup steps as applicable.
|
|
116
|
+
- Each acceptance criterion mapped to a meaningful automated or device check.
|
|
117
|
+
- Exact verification commands and required runtime/toolchain versions.
|
|
118
|
+
- For device checks: platform/device, starting state, actions, expected result,
|
|
119
|
+
evidence to capture, fixture requirements, and cleanup.
|
|
120
|
+
- Unavailable prerequisites, their owner where known, and how to obtain them or
|
|
121
|
+
perform an equivalent check.
|
|
122
|
+
|
|
123
|
+
Use the existing lightweight self-running `tsx` test pattern. No separate
|
|
124
|
+
scenario language, scenario-approval stage, or new test framework is required.
|
|
125
|
+
Routine technical planning choices do not need separate approval.
|
|
126
|
+
|
|
127
|
+
Check scarce prerequisites early. Treat missing access as a planning blocker
|
|
128
|
+
only when an essential acceptance criterion has no credible verification route.
|
|
129
|
+
Do not describe readiness checks as verification of unimplemented behaviour.
|
|
130
|
+
|
|
131
|
+
**Exit:** implementation is authorised within the accepted spec, the plan has
|
|
132
|
+
no unresolved blocking dependency, and essential criteria have credible checks.
|
|
133
|
+
|
|
134
|
+
## 4. Implement and handle discoveries
|
|
135
|
+
|
|
136
|
+
Implement the agreed scope, add meaningful regression coverage where needed,
|
|
137
|
+
and update the plan as work completes. Place tests according to the test-authoring
|
|
138
|
+
skill: automated unit tests in `test/unit/`, automated device checks in
|
|
139
|
+
`test/device/automated/`, and manual/debug helpers in `test/device/manual/`.
|
|
140
|
+
|
|
141
|
+
Review changed code, tests, and relevant callers for contract consistency,
|
|
142
|
+
platform differences, error handling, subprocess safety, resource cleanup,
|
|
143
|
+
state ownership, and reliable assertions. Review depth should match the risk.
|
|
144
|
+
|
|
145
|
+
When new evidence changes the work:
|
|
146
|
+
|
|
147
|
+
- Update the plan for routine implementation choices.
|
|
148
|
+
- Update the spec for design or contract changes. Obtain agreement before
|
|
149
|
+
implementing material changes to behaviour, scope, permissions, data handling,
|
|
150
|
+
compatibility, or acceptance criteria.
|
|
151
|
+
- Update intent only if the problem or intended outcome changes; preserve the
|
|
152
|
+
original statement and record the accepted revision.
|
|
153
|
+
- Record blockers and continue independent work. Do not silently weaken requirements.
|
|
154
|
+
|
|
155
|
+
Keep [tool documentation](../tools/TOOLS.md), [README](../../README.md), and
|
|
156
|
+
[changelog](../CHANGELOG.md) aligned when usage or behaviour changes.
|
|
157
|
+
|
|
158
|
+
**Exit:** agreed implementation and relevant documentation are ready for verification.
|
|
159
|
+
|
|
160
|
+
## 5. Verify and report
|
|
161
|
+
|
|
162
|
+
For code or test changes, run focused checks during iteration and complete the
|
|
163
|
+
maintainer verification command from the repository root:
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
npm run verify
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This currently runs lint, TypeScript build, all automated unit tests, and the
|
|
170
|
+
agent-plugin consistency check. Confirm scripts in [package.json](../../package.json)
|
|
171
|
+
and applicable [CI workflows](../../.github/workflows) in the current checkout;
|
|
172
|
+
report any difference between local validation and CI coverage.
|
|
173
|
+
|
|
174
|
+
For changes affecting native device interaction, platform tooling, or runtime
|
|
175
|
+
behaviour, also run applicable automated device checks:
|
|
176
|
+
|
|
177
|
+
```sh
|
|
178
|
+
npm run test:device
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Confirm which platforms and behaviours the suite actually covers. Supplement
|
|
182
|
+
it with focused end-to-end tool calls on the updated server where required by
|
|
183
|
+
the acceptance criteria. A passing Android check does not establish iOS support;
|
|
184
|
+
a dispatched action or mocked subprocess does not prove a real device outcome.
|
|
185
|
+
|
|
186
|
+
For each relevant device journey, record the platform/device and server build,
|
|
187
|
+
starting state, actions/tool calls, expected and observed result, evidence, and
|
|
188
|
+
**pass**, **fail**, **blocked**, or **unrun** status. Include failure, recovery,
|
|
189
|
+
interruption, and cleanup paths where relevant. Restore device settings and
|
|
190
|
+
remove temporary test data after verification. If a platform or prerequisite is
|
|
191
|
+
unavailable, state the concrete blocker and next step rather than claiming full
|
|
192
|
+
verification.
|
|
193
|
+
|
|
194
|
+
For schema changes, check registration, dispatch, runtime validation, response
|
|
195
|
+
serialization, documentation, and callers/fixtures together. Distinguish local
|
|
196
|
+
compatibility checks from testing an installed or published package.
|
|
197
|
+
|
|
198
|
+
Investigate failures against the agreed contract. Do not weaken assertions,
|
|
199
|
+
skip tests, add suppressions, or lower validation rules merely to get a pass.
|
|
200
|
+
Distinguish pre-existing failures from regressions with evidence; neither is a
|
|
201
|
+
pass. Fix issues introduced by the change and report unrelated findings separately.
|
|
202
|
+
|
|
203
|
+
For documentation-only work, inspect the diff, validate changed relative links
|
|
204
|
+
and referenced paths/commands, and check consistency across agent entrypoints.
|
|
205
|
+
Required pull-request CI checks still apply.
|
|
206
|
+
|
|
207
|
+
**Exit:** all agreed work and applicable checks have passed. Otherwise, report
|
|
208
|
+
partial completion and identify failed, blocked, or unrun checks explicitly.
|
|
209
|
+
|
|
210
|
+
The final report states what changed, verification commands and outcomes,
|
|
211
|
+
relevant device evidence, focused review findings, and remaining limitations.
|
|
212
|
+
Summarise successful output rather than pasting full logs. Commit, push, publish,
|
|
213
|
+
and release only within the user's authorised workflow.
|
package/docs/tools/TOOLS.md
CHANGED
|
@@ -7,6 +7,9 @@ This repository groups the MCP tools into four areas aligned with the codebase:
|
|
|
7
7
|
- [interact](interact.md) — UI resolution, actions, waits, and deterministic verification
|
|
8
8
|
- [system](system.md) — environment and health checks
|
|
9
9
|
|
|
10
|
+
Correlation metadata and explicit action references are documented in
|
|
11
|
+
[correlated evidence](evidence.md).
|
|
12
|
+
|
|
10
13
|
## Agent guidance
|
|
11
14
|
|
|
12
15
|
For interactive flows, the intended deterministic pattern is:
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Correlated evidence
|
|
2
|
+
|
|
3
|
+
Covered actions (`start_app`, `restart_app`, `tap`, `tap_element`, `swipe`,
|
|
4
|
+
`scroll_to_element`, `type_text`, `press_back`, `adjust_control`) return an
|
|
5
|
+
additive `evidence` object in their first JSON content block. Journey steps have
|
|
6
|
+
individual action IDs and share the journey correlation ID.
|
|
7
|
+
|
|
8
|
+
To associate a later observation, copy the action's `evidence.session_id` and
|
|
9
|
+
`evidence.action_id` into `actionRef`:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"platform": "android",
|
|
14
|
+
"deviceId": "emulator-5554",
|
|
15
|
+
"actionRef": { "sessionId": "<evidence.session_id>", "actionId": "<evidence.action_id>" }
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Supported observation tools: `capture_screenshot`, `get_ui_tree`,
|
|
20
|
+
`get_screen_fingerprint`, `capture_debug_snapshot`, `get_logs`, `read_log_stream`.
|
|
21
|
+
The reference object accepts only those two keys, each a string of 1–128
|
|
22
|
+
characters. Malformed references fail before collection. References never select
|
|
23
|
+
or redirect devices. A missing reference creates independent evidence; there is
|
|
24
|
+
no automatic association with the latest action.
|
|
25
|
+
|
|
26
|
+
Completed actions remain referenceable for 10 minutes, with at most 1,000 records
|
|
27
|
+
per server process. Restart changes the session. Unknown, expired, foreign-session,
|
|
28
|
+
or different-device references still collect independently: `association` is
|
|
29
|
+
`unavailable`, `association_reason` explains why, and `action_id` is null.
|
|
30
|
+
|
|
31
|
+
## Reading metadata
|
|
32
|
+
|
|
33
|
+
`session_id`, `correlation_id`, `action_id`, `device`, and `association` identify
|
|
34
|
+
the operation. Association is `self`, `explicit`, `internal`, `none`, or
|
|
35
|
+
`unavailable`. Target scope is the resolved device; UI evidence is device-wide.
|
|
36
|
+
`requested_action_ref` preserves an explicit reference, including unavailable ones.
|
|
37
|
+
|
|
38
|
+
Phase fields are server UTC epoch milliseconds:
|
|
39
|
+
|
|
40
|
+
- `operation_started_at_ms` / `operation_completed_at_ms`: this call.
|
|
41
|
+
- `dispatch_started_at_ms` / `dispatch_completed_at_ms`: actual action dispatch,
|
|
42
|
+
first start through last completion for multi-input actions.
|
|
43
|
+
- `verification_completed_at_ms`: verification attempt completion, including
|
|
44
|
+
failure or timeout; null when skipped. Read the existing verification status
|
|
45
|
+
to determine its outcome.
|
|
46
|
+
|
|
47
|
+
Unreached/uninstrumented phases are null. Host monotonic time governs ordering
|
|
48
|
+
and retention. A detected clock reversal yields unknown freshness and null age.
|
|
49
|
+
|
|
50
|
+
`observations` contains separate screenshot, UI-tree, fingerprint, activity, and
|
|
51
|
+
log records. Each has `evidence_id`, device, component status (`captured`, `failed`,
|
|
52
|
+
`omitted`), acquisition start/completion, source capture time, age, freshness and
|
|
53
|
+
reasons. Tree records retain snapshot session/revision references. Failed records
|
|
54
|
+
preserve available errors; disabled captures are omitted. Snapshot components
|
|
55
|
+
are collected independently, including fallback log reads; they are not atomic.
|
|
56
|
+
|
|
57
|
+
Visual freshness is evaluated when the response is assembled:
|
|
58
|
+
|
|
59
|
+
| Status | Meaning |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `fresh` | Newly acquired with no known intervening covered dispatch; app state may still change. |
|
|
62
|
+
| `stale` | Before the associated action or superseded by a later covered dispatch. |
|
|
63
|
+
| `unknown` | Overlap, reuse, failure, unknown scope/timing, or clock discontinuity. |
|
|
64
|
+
| `not_applicable` | Omitted logs. |
|
|
65
|
+
|
|
66
|
+
Cached source times are preserved. `age_ms` is nullable; clients choose their own
|
|
67
|
+
age limits. Freshness does not replace conditional-action safeguards.
|
|
68
|
+
|
|
69
|
+
Log timestamps remain on their original device/unknown clock. Log records expose
|
|
70
|
+
`clock: "device_or_unknown"`, `event_time_comparable: false`, and nullable earliest/
|
|
71
|
+
latest parseable entry times. Collection after an action does not prove emission
|
|
72
|
+
after it, or causality. No implicit action-window filtering is applied.
|
|
73
|
+
|
|
74
|
+
Existing payloads, image blocks, and log blocks remain available. Snapshot evidence
|
|
75
|
+
sits alongside `raw` and `semantic`. Android ADB and supported iOS simulators share
|
|
76
|
+
this contract; existing provider limitations still apply. `trace_context` reports
|
|
77
|
+
`{ "supported": false, "trace_id": null, "markers": [] }` for future trace linkage.
|
|
78
|
+
Correlation history stores identifiers, scope, timing, and ordering only, without
|
|
79
|
+
captured content, arguments, persistence, or telemetry.
|
package/docs/tools/interact.md
CHANGED
|
@@ -19,6 +19,90 @@ Important:
|
|
|
19
19
|
- action tools report execution success, not outcome correctness.
|
|
20
20
|
- `classify_action_outcome` should receive the runtime `action_type` when you want routing to distinguish local-state and side-effect actions.
|
|
21
21
|
|
|
22
|
+
## Conditional actions
|
|
23
|
+
|
|
24
|
+
`tap`, `tap_element`, and `swipe` (Android/iOS), plus Android `type_text` and
|
|
25
|
+
`press_back`, accept an optional expected UI snapshot. Journey `tap` steps accept
|
|
26
|
+
it per step. Other tools and step types reject `expectedState` rather than ignore
|
|
27
|
+
it; it is not supported in journey defaults or on the journey itself.
|
|
28
|
+
|
|
29
|
+
First call `get_ui_tree` on the chosen device. From a successful response, use
|
|
30
|
+
`snapshot_session_id` and `snapshot_revision`:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"platform": "android",
|
|
35
|
+
"deviceId": "emulator-5554",
|
|
36
|
+
"selector": { "accessibility_id": "save" },
|
|
37
|
+
"expectedState": {
|
|
38
|
+
"snapshotSessionId": "<snapshot_session_id from get_ui_tree>",
|
|
39
|
+
"snapshotRevision": 12,
|
|
40
|
+
"timeoutMs": 15000
|
|
41
|
+
},
|
|
42
|
+
"verificationMode": "none",
|
|
43
|
+
"responseMode": "compact"
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This is a `tap_element` example. Guarded standalone calls require explicit
|
|
48
|
+
`platform` and concrete `deviceId` from the observed tree; `booted` and `default`
|
|
49
|
+
are not valid guarded targets. Journey taps inherit the journey's resolved device.
|
|
50
|
+
The expected-state object is strict: nonempty session ID, positive safe-integer
|
|
51
|
+
revision, and optional integer timeout of 100–60000 ms (default 15000). Invalid or
|
|
52
|
+
unsupported conditions return an `INVALID_ARGUMENT` error before device work.
|
|
53
|
+
Calls without a condition preserve existing behaviour.
|
|
54
|
+
|
|
55
|
+
After target and adapter preparation, the server reads a new hierarchy just
|
|
56
|
+
before native input. It checks the same device, server session, and retained
|
|
57
|
+
revision, even with post-action verification disabled. It never waits for a
|
|
58
|
+
mismatch to become a match or retries/rebases the input automatically.
|
|
59
|
+
|
|
60
|
+
| Result | Meaning |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| `condition.status: matched` | Condition passed; inspect the ordinary action outcome for delivery and verify its effect separately. |
|
|
63
|
+
| `PRECONDITION_FAILED`, `condition.status: mismatch` | State changed; input was not dispatched. The condition includes the exact fresh tree that blocked it. |
|
|
64
|
+
| `PRECONDITION_UNAVAILABLE`, `condition.status: unavailable` | Condition could not be established; input was not dispatched. Inspect `condition.reason` and observe again. |
|
|
65
|
+
| `condition.status: not_checked` | Target/capability preparation failed before reaching the guard; inspect the original action failure. |
|
|
66
|
+
|
|
67
|
+
Blocked input reports `success: false`, `dispatch_started: false`,
|
|
68
|
+
`delivery_status: not_delivered`, `retryable: false`, `lifecycle_state: failed`,
|
|
69
|
+
and `verification.status: not_requested`. No network action window is reset and
|
|
70
|
+
no post-input verification runs. A failed conditional journey tap stops the
|
|
71
|
+
journey; subsequent steps are `not_run` and earlier steps are not rolled back.
|
|
72
|
+
|
|
73
|
+
Condition evidence has `status`, `expected: { snapshot_session_id,
|
|
74
|
+
snapshot_revision }`, `observed`, `elapsed_ms`, and an optional `reason`.
|
|
75
|
+
Reasons are `BASELINE_UNAVAILABLE`, `SESSION_MISMATCH`, `DEVICE_MISMATCH`,
|
|
76
|
+
`OBSERVATION_FAILED`, `OBSERVATION_INVALID`, `CONDITION_TIMEOUT`, or
|
|
77
|
+
`ACTION_NOT_REACHED` for a check that was not reached. `observed` is null when
|
|
78
|
+
there is no valid fresh observation. Error trees cannot authorise input merely
|
|
79
|
+
because they contain an old revision.
|
|
80
|
+
|
|
81
|
+
Debug responses retain the full condition tree. Compact responses retain full
|
|
82
|
+
fresh evidence on mismatch/unavailable results; this is an exception to their
|
|
83
|
+
usual omission of device identifiers and tree contents. Successful compact
|
|
84
|
+
conditions contain only observed session/revision and capture time. UI trees may
|
|
85
|
+
contain private text; avoid copying them into logs unnecessarily.
|
|
86
|
+
|
|
87
|
+
The baseline must still be in this process's same-device history: up to eight
|
|
88
|
+
revisions, retained for ten minutes since a successful observation of that
|
|
89
|
+
revision. Server restart or eviction requires a new observation. A fresh read
|
|
90
|
+
cannot evict the baseline out from under its own check.
|
|
91
|
+
|
|
92
|
+
The match covers the existing normalized accessibility snapshot: element text,
|
|
93
|
+
identity, state, bounds, actionability, screen, and resolution. String identity
|
|
94
|
+
fields are trimmed/lowercased; native state values retain their representation.
|
|
95
|
+
All elements contribute, even when delta history is truncated. Pixel-only,
|
|
96
|
+
backend, inaccessible, and normalization-hidden changes are not detected; the
|
|
97
|
+
current tree does not establish foreground package/activity. Any observed
|
|
98
|
+
intervening change invalidates the old revision, even if the UI later returns.
|
|
99
|
+
|
|
100
|
+
A condition read can add seconds. Its timeout budget is separate from the action
|
|
101
|
+
budget; enclosing operation deadlines still apply. This check is not atomic with
|
|
102
|
+
OS input delivery. Concurrent clients, external input, or app updates can change
|
|
103
|
+
the UI after the observation. Sequence device actions and verify outcomes with
|
|
104
|
+
`expect_*` as usual.
|
|
105
|
+
|
|
22
106
|
## tap / swipe / type_text / press_back
|
|
23
107
|
|
|
24
108
|
These tools return a shared action execution envelope.
|
|
@@ -633,7 +717,13 @@ has unknown delivery and must be observed before retrying.
|
|
|
633
717
|
|
|
634
718
|
`tap_element` accepts exactly one `elementId` or `selector`. A selector supports
|
|
635
719
|
`text`, `resource_id`, `accessibility_id`, and `contains`. Optional `waitFor` waits
|
|
636
|
-
for the target before tapping; without it, resolution uses one observation
|
|
720
|
+
for the target before tapping; without it, resolution uses one observation with
|
|
721
|
+
the action budget (default 5,000 ms), rather than a separate shorter cutoff.
|
|
722
|
+
An explicit `waitFor.timeoutMs` remains bounded by the overall action budget.
|
|
723
|
+
Selector taps resolve matching label text to its actionable ancestor using the
|
|
724
|
+
existing clickable-wait policy, including `waitFor.condition: exists` or `visible`.
|
|
725
|
+
Hidden/disabled labels and non-actionable containers cannot be bypassed.
|
|
726
|
+
Observation timeouts report `ACTION_TIMEOUT`, rather than claiming a missing target.
|
|
637
727
|
Ambiguous targets require an explicit `waitFor.match.index`.
|
|
638
728
|
|
|
639
729
|
`run_journey` accepts 1–50 steps with unique IDs, on one platform/device. Supported
|
|
@@ -644,5 +734,38 @@ the journey; remaining steps report `not_run`. Journey responses default to comp
|
|
|
644
734
|
`defaults` supplies execution controls; step verification overrides the default.
|
|
645
735
|
`captureOnFailure: true` includes a screenshot-free failure snapshot in debug mode.
|
|
646
736
|
|
|
647
|
-
See [RFC 018](../specs
|
|
737
|
+
See [RFC 018](../specs/018-interaction-latency-and-batched-journeys/spec.md) for
|
|
648
738
|
contracts and the device acceptance criteria.
|
|
739
|
+
|
|
740
|
+
## Readiness-aware waits
|
|
741
|
+
|
|
742
|
+
`wait_for_ui` and `wait_for_ui_change` accept optional `requireReady: true`.
|
|
743
|
+
They require both their existing predicate and settled full-screen UI readiness
|
|
744
|
+
on the same current samples. Readiness uses a 500 ms stability window, shares the
|
|
745
|
+
wait's existing deadline (including retries), and adds no separate hierarchy reads.
|
|
746
|
+
|
|
747
|
+
```json
|
|
748
|
+
{
|
|
749
|
+
"platform": "android",
|
|
750
|
+
"deviceId": "emulator-5554",
|
|
751
|
+
"selector": { "text": "Ready" },
|
|
752
|
+
"condition": "visible",
|
|
753
|
+
"requireReady": true,
|
|
754
|
+
"timeout_ms": 30000
|
|
755
|
+
}
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
Results retain the existing success/timeout envelope and include `readiness`
|
|
759
|
+
evidence and limitations. Opt-in shared-deadline expiry uses a `TIMEOUT` error
|
|
760
|
+
code and preserves whether the underlying predicate was observed; a readiness
|
|
761
|
+
timeout does not mean that a visible target was absent. A visible target while a loader is active cannot pass
|
|
762
|
+
this opt-in check. Observation failure resets readiness continuity; an exhausted
|
|
763
|
+
budget is not proof of an unresponsive app. Default `requireReady: false` retains
|
|
764
|
+
existing predicate semantics. There is no new journey assertion or change to
|
|
765
|
+
conditional-action preconditions. See [app readiness](observe.md#app-readiness)
|
|
766
|
+
for supported states and limits.
|
|
767
|
+
|
|
768
|
+
## Correlated evidence
|
|
769
|
+
|
|
770
|
+
Actions and journeys expose identifiers and phase timestamps for linking later
|
|
771
|
+
observations. See [correlated evidence](evidence.md) for scope and limitations.
|
package/docs/tools/observe.md
CHANGED
|
@@ -10,6 +10,9 @@ These tools are primarily for:
|
|
|
10
10
|
|
|
11
11
|
They are **not** the primary success signal when an applicable `expect_*` tool exists.
|
|
12
12
|
|
|
13
|
+
Covered observations support optional `actionRef` and return acquisition/freshness
|
|
14
|
+
metadata; see [correlated evidence](evidence.md).
|
|
15
|
+
|
|
13
16
|
## get_logs
|
|
14
17
|
|
|
15
18
|
Fetch recent logs as structured entries optimized for AI agents.
|
|
@@ -91,9 +94,13 @@ Notes:
|
|
|
91
94
|
- Elements may include a normalized `state` object when the platform exposes readable state such as checked, selected, focused, expanded, text input, or slider values.
|
|
92
95
|
- Elements may also include platform-native identity hints such as `stable_id`, `role`, `test_tag`, `selector`, and `semantic`.
|
|
93
96
|
- The tree response may include `snapshot_revision`, `captured_at_ms`, and `loading_state` when a reliable signal is available.
|
|
97
|
+
- Successful tree observations include `snapshot_session_id`, a random identifier
|
|
98
|
+
for this server process. Use it with `snapshot_revision` for
|
|
99
|
+
[conditional actions](interact.md#conditional-actions) on the same device.
|
|
100
|
+
Compact tree responses preserve the session ID and revision. Never derive an
|
|
101
|
+
expectation from an error tree, even when it carries an older revision.
|
|
94
102
|
- Prefer `wait_for_ui` for deterministic element resolution in interactive flows.
|
|
95
|
-
- Android
|
|
96
|
-
- If the bridge fails, the server stops it before falling back to `uiautomator dump`. While running, the bridge holds Android's UiAutomation connection, so other `uiautomator dump` clients may fail. Leave the variable unset for the default reader. To reproduce the device probe, set `LATENCY_DEVICE_ID` and run `npx tsx test/device/automated/ui-tree-performance/bridge-probe.ts`.
|
|
103
|
+
- Android reads the accessibility hierarchy with `uiautomator dump` through ADB. No helper APK is installed or required.
|
|
97
104
|
|
|
98
105
|
---
|
|
99
106
|
|
|
@@ -211,3 +218,58 @@ read_log_stream response example:
|
|
|
211
218
|
```json
|
|
212
219
|
{ "entries": [ { "timestamp": "2026-03-20T...Z", "level": "ERROR", "tag": "AppTag", "pid": 1234, "message": "FATAL EXCEPTION" } ], "crash_summary": { "crash_detected": true } }
|
|
213
220
|
```
|
|
221
|
+
|
|
222
|
+
## App readiness
|
|
223
|
+
|
|
224
|
+
`get_app_readiness` observes at most two fresh hierarchies; `wait_for_app_ready`
|
|
225
|
+
waits for settled evidence within one budget. Require `platform`; optionally
|
|
226
|
+
select `deviceId`. Each call pins its device and keeps its sampling history local.
|
|
227
|
+
|
|
228
|
+
| Control | Default | Accepted integer range |
|
|
229
|
+
| --- | --- | --- |
|
|
230
|
+
| `timeoutMs` | get: 15000; wait: 30000 | 100–60000 |
|
|
231
|
+
| `stabilityMs` | 500 | 100–10000 |
|
|
232
|
+
| `pollIntervalMs` (wait only) | 500 | 100–1000 |
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{ "platform": "android", "deviceId": "emulator-5554", "timeoutMs": 30000 }
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Both return `success`, resolved `device`, `readiness`, `elapsed_ms`, and a
|
|
239
|
+
`failure_code` on failure. Get succeeds when it establishes loading, transitioning,
|
|
240
|
+
or settled; wait succeeds only for settled. Codes are `READINESS_UNAVAILABLE`,
|
|
241
|
+
`READINESS_TIMEOUT`, and `DEVICE_UNAVAILABLE`. Invalid/unknown inputs are rejected.
|
|
242
|
+
|
|
243
|
+
`readiness.state` is loading, transitioning, settled, or unknown. Visible native
|
|
244
|
+
progress indicators establish loading; exact Loading / Please wait labels
|
|
245
|
+
(ignoring case, whitespace and trailing dots/ellipsis) are heuristic loading.
|
|
246
|
+
Loading takes priority over unchanged trees. Two different valid signatures show
|
|
247
|
+
transitioning; consecutive identical signatures spanning the stability window
|
|
248
|
+
show settled. Failed, empty, stale-session, wrong-device, or invalid observations
|
|
249
|
+
cannot establish stability and break sample continuity. Broad legacy loading
|
|
250
|
+
keywords such as a button named Progress are not sufficient loading evidence.
|
|
251
|
+
|
|
252
|
+
The summary includes `basis` (observed/heuristic/insufficient), `scope: device_ui`,
|
|
253
|
+
latest successful capture time and snapshot session/revision, sample
|
|
254
|
+
count, `stable_for_ms`, up to eight signal-code evidence records, limitations,
|
|
255
|
+
and capability flags. Evidence excludes UI text and full trees. Unresponsive and
|
|
256
|
+
debugger-paused detection are explicitly unsupported on both platforms; neither
|
|
257
|
+
slow hierarchy retrieval nor a deadline expiry proves either state.
|
|
258
|
+
|
|
259
|
+
Tree responses (including compact and nested trees) add a single-sample readiness
|
|
260
|
+
summary without extra reads. Such a summary reports loading or unknown; it does
|
|
261
|
+
not inherit stability from an earlier request. Existing loading/delta fields and
|
|
262
|
+
compact error evidence are preserved.
|
|
263
|
+
|
|
264
|
+
Readiness describes normalized foreground UI during the capture window. It does
|
|
265
|
+
not verify foreground app identity, process health, business outcome, or backend
|
|
266
|
+
completion, and does not guarantee the UI will stay unchanged. A screenshot alone
|
|
267
|
+
never establishes readiness. Network observation windows and device input are
|
|
268
|
+
untouched. Timeouts include `DEADLINE_EXCEEDED` and retain the last valid
|
|
269
|
+
readiness evidence; an aborted acquisition does not erase that observation.
|
|
270
|
+
A non-timeout observation failure still makes current state unknown. Sampling and subprocess cleanup respect outer budgets.
|
|
271
|
+
Transport cancellation does not currently abort platform reads immediately;
|
|
272
|
+
reads remain bounded by their operation deadlines.
|
|
273
|
+
|
|
274
|
+
See [readiness-aware waits](interact.md#readiness-aware-waits) to combine this
|
|
275
|
+
signal with a specific UI predicate.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobile-debug-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "MCP server for mobile app debugging (Android + iOS), with focus on security and reliability",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"test": "npm run test:unit",
|
|
20
20
|
"lint": "eslint src test --quiet",
|
|
21
21
|
"lint:fix": "eslint src test --fix",
|
|
22
|
-
"
|
|
22
|
+
"sync:agent-plugin": "node scripts/agent-plugin.mjs --sync",
|
|
23
|
+
"check:agent-plugin": "node scripts/agent-plugin.mjs --check",
|
|
24
|
+
"verify": "npm run lint && npm run build && npm run test:unit && npm run check:agent-plugin"
|
|
23
25
|
},
|
|
24
26
|
|
|
25
27
|
"engines": {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
const repositoryRoot = fileURLToPath(new URL('../', import.meta.url))
|
|
7
|
+
const pluginRoot = path.join(repositoryRoot, 'agent-plugin')
|
|
8
|
+
const packagedSkill = path.join(pluginRoot, 'skills/mobile-debug-mcp/SKILL.md')
|
|
9
|
+
const packagedLicense = path.join(pluginRoot, 'LICENSE')
|
|
10
|
+
const sourceLicense = path.join(repositoryRoot, 'LICENSE')
|
|
11
|
+
const packageInfo = JSON.parse(readFileSync(path.join(repositoryRoot, 'package.json'), 'utf8'))
|
|
12
|
+
const plugin = JSON.parse(readFileSync(path.join(pluginRoot, 'plugin.json'), 'utf8'))
|
|
13
|
+
const mcp = JSON.parse(readFileSync(path.join(pluginRoot, 'mcp.json'), 'utf8'))
|
|
14
|
+
const { $schema: _pluginSchema, extensions, ...pluginMetadata } = plugin
|
|
15
|
+
const { $schema: _mcpSchema, ...codexMcp } = mcp
|
|
16
|
+
const codexManifest = {
|
|
17
|
+
...pluginMetadata,
|
|
18
|
+
skills: './skills/',
|
|
19
|
+
mcpServers: './.mcp.json',
|
|
20
|
+
interface: extensions['com.openai'].interface,
|
|
21
|
+
}
|
|
22
|
+
const sharedMetadata = Object.fromEntries(
|
|
23
|
+
['name', 'version', 'description', 'author', 'repository', 'license'].map(key => [key, plugin[key]]),
|
|
24
|
+
)
|
|
25
|
+
const claudeManifest = {
|
|
26
|
+
...sharedMetadata,
|
|
27
|
+
mcpServers: {
|
|
28
|
+
[packageInfo.name]: {
|
|
29
|
+
command: 'npx',
|
|
30
|
+
args: ['--yes', `${packageInfo.name}@${packageInfo.version}`, 'server'],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
const cursorManifest = {
|
|
35
|
+
...sharedMetadata,
|
|
36
|
+
skills: './skills/',
|
|
37
|
+
mcpServers: {
|
|
38
|
+
[packageInfo.name]: {
|
|
39
|
+
type: 'stdio',
|
|
40
|
+
command: 'npx',
|
|
41
|
+
args: ['--yes', `${packageInfo.name}@${packageInfo.version}`, 'server'],
|
|
42
|
+
cwd: '${CURSOR_PLUGIN_ROOT}',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
const manifests = [
|
|
47
|
+
['.codex-plugin/plugin.json', codexManifest],
|
|
48
|
+
['.claude-plugin/plugin.json', claudeManifest],
|
|
49
|
+
['.cursor-plugin/plugin.json', cursorManifest],
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
if (process.argv[2] === '--sync') {
|
|
53
|
+
copyFileSync(sourceLicense, packagedLicense)
|
|
54
|
+
writeFileSync(path.join(pluginRoot, '.mcp.json'), `${JSON.stringify(codexMcp, null, 2)}\n`)
|
|
55
|
+
for (const [relativePath, manifest] of manifests) {
|
|
56
|
+
const manifestPath = path.join(pluginRoot, relativePath)
|
|
57
|
+
mkdirSync(path.dirname(manifestPath), { recursive: true })
|
|
58
|
+
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (process.argv[2] !== '--check') throw new Error('Use --sync or --check')
|
|
62
|
+
|
|
63
|
+
assert.equal(plugin.$schema, 'https://agent-plugins.org/schemas/1.0.0/plugin.schema.json')
|
|
64
|
+
assert.equal(plugin.name, 'mobile-debug-tools')
|
|
65
|
+
assert.match(plugin.version, /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/, 'plugin releases use an independent semantic version')
|
|
66
|
+
assert.equal(mcp.$schema, 'https://agent-plugins.org/schemas/1.0.0/mcp.schema.json')
|
|
67
|
+
const servers = Object.entries(mcp.mcpServers)
|
|
68
|
+
assert.equal(servers.length, 1, 'the plugin should expose only this MCP server')
|
|
69
|
+
const [serverName, server] = servers[0]
|
|
70
|
+
assert.equal(serverName, packageInfo.name)
|
|
71
|
+
assert.equal(server.type, 'stdio')
|
|
72
|
+
assert.equal(server.command, 'npx')
|
|
73
|
+
assert.deepEqual(server.args, ['--yes', `${packageInfo.name}@${packageInfo.version}`, 'server'])
|
|
74
|
+
assert.equal(server.cwd, '${PLUGIN_DATA}', 'npx must run outside the same-named source checkout')
|
|
75
|
+
assert.deepEqual(readFileSync(packagedLicense), readFileSync(sourceLicense), 'packaged license must match its source')
|
|
76
|
+
assert.deepEqual(JSON.parse(readFileSync(path.join(pluginRoot, '.mcp.json'), 'utf8')), codexMcp, 'Codex MCP configuration must match the portable plugin')
|
|
77
|
+
for (const [relativePath, expectedManifest] of manifests) {
|
|
78
|
+
assert.deepEqual(
|
|
79
|
+
JSON.parse(readFileSync(path.join(pluginRoot, relativePath), 'utf8')),
|
|
80
|
+
expectedManifest,
|
|
81
|
+
`${relativePath} must match the portable plugin`,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
for (const marketplacePath of [
|
|
85
|
+
'.agents/plugins/marketplace.json',
|
|
86
|
+
'.claude-plugin/marketplace.json',
|
|
87
|
+
'.cursor-plugin/marketplace.json',
|
|
88
|
+
]) {
|
|
89
|
+
const marketplace = JSON.parse(readFileSync(path.join(repositoryRoot, marketplacePath), 'utf8'))
|
|
90
|
+
assert.equal(marketplace.name, plugin.name, `${marketplacePath} name must match the plugin`)
|
|
91
|
+
assert.equal(marketplace.plugins.length, 1, `${marketplacePath} must expose one plugin`)
|
|
92
|
+
assert.equal(marketplace.plugins[0].name, plugin.name, `${marketplacePath} plugin name must match`)
|
|
93
|
+
assert.equal(marketplace.plugins[0].source?.path ?? marketplace.plugins[0].source, './agent-plugin', `${marketplacePath} source must point to the plugin`)
|
|
94
|
+
}
|
|
95
|
+
const skillName = readFileSync(packagedSkill, 'utf8').match(/^---\r?\nname: ([a-z0-9-]+)\r?\n/)?.[1]
|
|
96
|
+
assert.equal(skillName, path.basename(path.dirname(packagedSkill)), 'packaged skill directory must match its frontmatter name')
|
|
97
|
+
|
|
98
|
+
console.log('Agent plugin package is in sync')
|