ucu-mcp 0.4.3 → 0.5.1

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.
@@ -0,0 +1,146 @@
1
+ # Workflows
2
+
3
+ Common task playbooks. Each shows the preferred tool sequence and the fallback
4
+ path when the primary path is blocked.
5
+
6
+ ---
7
+
8
+ ## 1. Fill a form field
9
+
10
+ **Primary (AX):**
11
+ ```
12
+ focus_app("Safari")
13
+ find_element({ text: "Email", role: "AXTextField" })
14
+ → elementId "Safari/w0/42"
15
+ type_in_element({ elementId: "Safari/w0/42", text: "user@example.com" })
16
+ ```
17
+
18
+ **Fallback (AX value set, for non-text controls):**
19
+ ```
20
+ set_value({ elementId: "...", value: "option" })
21
+ ```
22
+
23
+ **Fallback (coordinates, when AX is opaque):**
24
+ ```
25
+ screenshot({})
26
+ ocr({}) → blocks[].text === "Email" → {x, y, width, height}
27
+ click({ x: block.x + block.width/2, y: block.y + block.height/2 })
28
+ type_text({ text: "user@example.com" })
29
+ ```
30
+
31
+ ---
32
+
33
+ ## 2. Operate a menu-bar / tray app (e.g. cc-switch)
34
+
35
+ Tray apps' status items live in `SystemUIServer`, not the app's own window AX
36
+ tree. `focus_app` alone may return `WINDOW_NOT_FOUND`.
37
+
38
+ ```
39
+ focus_app("cc-switch") # establishes tray target if status item found
40
+ click_menu_bar_extra({ app: "cc-switch", name: "switch" }) # opens the menu
41
+ # menu is now open — find items inside it:
42
+ find_element({ text: "使用统计", app: "cc-switch" })
43
+ → elementId
44
+ click_element({ elementId })
45
+ ```
46
+
47
+ If the menu's AX tree is opaque (some Tauri/Electron menus):
48
+ ```
49
+ click_menu_bar_extra({ app: "cc-switch" })
50
+ screenshot({})
51
+ ocr({}) → locate "使用统计" by text → coordinates
52
+ click({ x, y })
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 3. Electron / WebView opaque UI
58
+
59
+ Electron/Tauri apps often expose only a near-empty `AXGroup`. The runtime `hint`
60
+ on `find_element` and `list_windows` tells you when this is happening.
61
+
62
+ ```
63
+ find_element({ text: "Submit" })
64
+ → 0 results, hint: "app is likely Electron... screenshot → ocr → click(x,y)"
65
+
66
+ screenshot({})
67
+ ocr({ region: { x, y, width, height } }) # or full screen
68
+ → blocks[].text === "Submit" → {x, y, width, height}
69
+ click({ x: block.x + block.width/2, y: block.y + block.height/2 })
70
+ ```
71
+
72
+ For repeated interaction with a known-opaque app, snapshot once with
73
+ `describe_screen` to plan, then drive by coordinates.
74
+
75
+ ---
76
+
77
+ ## 4. Vision-degraded environment (image content not visible)
78
+
79
+ When the model cannot see `screenshot` image blocks (relay/downgrade to URLs),
80
+ switch to text-based screen reading:
81
+
82
+ ```
83
+ describe_screen({ ocr: true, includeAx: true })
84
+ → { screen, foregroundWindow, ocr:{blocks}, ax:{elements}, errors }
85
+
86
+ # or, if you also want the image for clients that DO support it:
87
+ screenshot({ describe: true })
88
+ → [image block, text description block]
89
+ ```
90
+
91
+ `describe_screen` never throws — OCR and AX each try/catch independently, so a
92
+ Vision failure still returns AX state and vice versa. Check `errors[]` to know
93
+ what was skipped/failed.
94
+
95
+ ---
96
+
97
+ ## 5. Recover from TARGET_STALE
98
+
99
+ The active window target can go stale (window closed, app restarted, pid
100
+ changed). AX tools throw `TARGET_STALE`.
101
+
102
+ ```
103
+ # error response includes hint: "Run focus_app again for the target app..."
104
+ focus_app("Safari") # re-establishes target
105
+ find_element({ text: "Save" }) # retry — cache refetches equivalent nodes
106
+ click_element({ elementId })
107
+ ```
108
+
109
+ `type_in_element` automatically refetches an equivalent AX node if the original
110
+ `elementId` is stale, so a single retry often succeeds without `focus_app`.
111
+
112
+ ---
113
+
114
+ ## 6. Verify an action succeeded
115
+
116
+ Always verify after clicks/types — UI may not have updated, or the wrong element
117
+ was hit.
118
+
119
+ ```
120
+ click_element({ elementId, captureAfter: true }) # screenshot in response
121
+ # or explicitly:
122
+ screenshot({})
123
+ # or check AX state:
124
+ get_window_state({}) → focusedElement / tree reflects the change
125
+ # or wait for a specific change:
126
+ wait_for_element({ text: "Saved", until: "appear", timeout: 3000 })
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 7. Multi-step task with error recovery
132
+
133
+ ```
134
+ doctor() # verify permissions first
135
+ list_apps()
136
+ focus_app("Notes")
137
+ find_element({ text: "New Note" }) → id
138
+ click_element({ elementId: id, captureAfter: true })
139
+
140
+ # if click_element throws ELEMENT_NOT_FOUND:
141
+ find_element({ text: "New Note" }) → id2 # refetch, id may have changed
142
+ click_element({ elementId: id2 })
143
+
144
+ type_in_element({ elementId: bodyId, text: "Hello" })
145
+ screenshot({}) # confirm content
146
+ ```