@remcp/runtime 0.2.150 → 0.2.151
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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/src/catalog.mjs +5 -829
- package/src/core-catalog-part1.mjs +373 -0
- package/src/core-catalog-part2.mjs +374 -0
- package/src/core-catalog-shared.mjs +92 -0
- package/src/extended/catalog-rules.mjs +253 -0
- package/src/extended/catalog-schemas.mjs +252 -0
- package/src/extended/catalog.mjs +2 -505
- package/src/extended/desktop-macos-windows.mjs +66 -0
- package/src/extended/desktop-macos.mjs +2 -64
- package/src/extended/desktop-windows-msaa.mjs +104 -0
- package/src/extended/desktop-windows.mjs +1 -103
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { requiredAny } from './catalog-schemas.mjs';
|
|
2
|
+
|
|
3
|
+
export function applySchemaRules(name, schema) {
|
|
4
|
+
const next = structuredClone(schema);
|
|
5
|
+
if (name === 'computer_action') {
|
|
6
|
+
const nativeTarget = requiredAny(['id'], ['label'], ['name'], ['role'], ['automation_id']);
|
|
7
|
+
const browserElementTarget = requiredAny(['selector'], ['browser_text'], ['name']);
|
|
8
|
+
const windowTarget = requiredAny(['window_id'], ['pid'], ['app'], ['window_title'], ['title']);
|
|
9
|
+
const semanticTarget = [...nativeTarget, ...browserElementTarget];
|
|
10
|
+
next.allOf = [
|
|
11
|
+
{
|
|
12
|
+
if:{ properties:{ target:{ const:'window' } }, required:['target'] },
|
|
13
|
+
then:{
|
|
14
|
+
properties:{ action:{ enum:['type','focus','scroll','drag'] } },
|
|
15
|
+
anyOf:windowTarget,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
if:{ properties:{ action:{ const:'click' } }, required:['action'] },
|
|
20
|
+
then:{ anyOf:[...semanticTarget, { required:['ocr_text'] }, { required:['x','y'] }] },
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
if:{ properties:{ action:{ enum:['invoke','set_value','select','toggle','expand','collapse','scroll_into_view','set_range_value','add_to_selection','remove_from_selection'] } }, required:['action'] },
|
|
24
|
+
then:{ anyOf:semanticTarget },
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
if:{ properties:{ action:{ const:'focus' } }, required:['action'] },
|
|
28
|
+
then:{ anyOf:[...semanticTarget, ...requiredAny(['window_id'], ['pid'], ['app'], ['window_title'], ['title'])] },
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
if:{ properties:{ action:{ const:'type' } }, required:['action'] },
|
|
32
|
+
then:{
|
|
33
|
+
required:['text'],
|
|
34
|
+
properties:{ target:{ enum:['auto','ui','ui_element','browser','window'] } },
|
|
35
|
+
allOf:[
|
|
36
|
+
{ anyOf:[...semanticTarget, ...windowTarget] },
|
|
37
|
+
{
|
|
38
|
+
if:{ properties:{ target:{ enum:['ui','ui_element'] } }, required:['target'] },
|
|
39
|
+
then:{ anyOf:nativeTarget },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
if:{ properties:{ target:{ const:'browser' } }, required:['target'] },
|
|
43
|
+
then:{ anyOf:browserElementTarget },
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
if:{ properties:{ action:{ const:'scroll' } }, required:['action'] },
|
|
50
|
+
then:{
|
|
51
|
+
properties:{ target:{ enum:['auto','ui','ui_element','browser','coordinates','window'] } },
|
|
52
|
+
allOf:[
|
|
53
|
+
{ anyOf:requiredAny(['delta_x'], ['delta_y'], ['delta'], ['direction']) },
|
|
54
|
+
{
|
|
55
|
+
if:{ properties:{ target:{ enum:['ui','ui_element'] } }, required:['target'] },
|
|
56
|
+
then:{ anyOf:nativeTarget },
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
if:{ properties:{ target:{ const:'coordinates' } }, required:['target'] },
|
|
60
|
+
then:{ required:['x','y'] },
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
if:{ properties:{ action:{ const:'drag' } }, required:['action'] },
|
|
67
|
+
then:{
|
|
68
|
+
properties:{ target:{ enum:['auto','ui','ui_element','coordinates','window'] } },
|
|
69
|
+
allOf:[
|
|
70
|
+
{ anyOf:requiredAny(['from_id'], ['from_x','from_y']) },
|
|
71
|
+
{ anyOf:requiredAny(['to_id'], ['to_x','to_y']) },
|
|
72
|
+
{
|
|
73
|
+
if:{ properties:{ target:{ enum:['ui','ui_element'] } }, required:['target'] },
|
|
74
|
+
then:{ required:['from_id','to_id'] },
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
if:{ properties:{ target:{ const:'coordinates' } }, required:['target'] },
|
|
78
|
+
then:{ required:['from_x','from_y','to_x','to_y'] },
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
if:{ properties:{ action:{ enum:['set_value','set_range_value'] } }, required:['action'] },
|
|
85
|
+
then:{ required:['value'] },
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
if:{ properties:{ action:{ const:'multi_select' } }, required:['action'] },
|
|
89
|
+
then:{ required:['targets'] },
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
if:{ properties:{ action:{ const:'multi_edit' } }, required:['action'] },
|
|
93
|
+
then:{ required:['edits'] },
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
} else if (name === 'ui_find') {
|
|
97
|
+
next.anyOf = requiredAny(['id'], ['label'], ['name'], ['role'], ['automation_id']);
|
|
98
|
+
} else if (name === 'ui_action') {
|
|
99
|
+
next.allOf = [
|
|
100
|
+
{ anyOf:requiredAny(['id'], ['label'], ['name'], ['role'], ['automation_id']) },
|
|
101
|
+
{
|
|
102
|
+
if:{ properties:{ action:{ enum:['set_value','set_range_value'] } }, required:['action'] },
|
|
103
|
+
then:{ required:['value'] },
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
} else if (name === 'wait_for_ui') {
|
|
107
|
+
next.anyOf = requiredAny(['state'], ['condition']);
|
|
108
|
+
next.allOf = [
|
|
109
|
+
{
|
|
110
|
+
if:{ properties:{ condition:{ const:'text_exists' } }, required:['condition'] },
|
|
111
|
+
then:{ required:['text'] },
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
if:{ properties:{ condition:{ const:'active_window' } }, required:['condition'] },
|
|
115
|
+
then:{ anyOf:requiredAny(['text'], ['name'], ['window_title']) },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
if:{ properties:{ condition:{ enum:['element_exists','element_enabled'] } }, required:['condition'] },
|
|
119
|
+
then:{ anyOf:requiredAny(['id'], ['label'], ['name'], ['role'], ['automation_id']) },
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
if:{ properties:{ state:{ enum:['present','absent'] } }, required:['state'] },
|
|
123
|
+
then:{ anyOf:requiredAny(['id'], ['label'], ['name'], ['role'], ['automation_id']) },
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
} else if (name === 'window_action') {
|
|
127
|
+
next.allOf = [
|
|
128
|
+
{ anyOf:requiredAny(['id'], ['pid'], ['app'], ['title']) },
|
|
129
|
+
{
|
|
130
|
+
if:{ properties:{ action:{ const:'move' } }, required:['action'] },
|
|
131
|
+
then:{ required:['x','y'] },
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
if:{ properties:{ action:{ const:'resize' } }, required:['action'] },
|
|
135
|
+
then:{ required:['width','height'] },
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
if:{ properties:{ action:{ const:'move_resize' } }, required:['action'] },
|
|
139
|
+
then:{ required:['x','y','width','height'] },
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
} else if (name === 'pointer') {
|
|
143
|
+
next.allOf = [{
|
|
144
|
+
if:{ properties:{ action:{ const:'move' } }, required:['action'] },
|
|
145
|
+
then:{ required:['x','y'] },
|
|
146
|
+
}];
|
|
147
|
+
} else if (name === 'scroll') {
|
|
148
|
+
next.anyOf = requiredAny(['delta_x'], ['delta_y'], ['delta'], ['direction']);
|
|
149
|
+
} else if (name === 'network') {
|
|
150
|
+
next.allOf = [{
|
|
151
|
+
if:{ properties:{ action:{ const:'test' } }, required:['action'] },
|
|
152
|
+
then:{ required:['host','port'] },
|
|
153
|
+
}];
|
|
154
|
+
} else if (name === 'launch_app') {
|
|
155
|
+
next.anyOf = requiredAny(['app'], ['path']);
|
|
156
|
+
} else if (name === 'keyboard') {
|
|
157
|
+
next.anyOf = requiredAny(['shortcut'], ['key'], ['keys']);
|
|
158
|
+
if (next.properties?.keys) next.properties.keys.minItems = 1;
|
|
159
|
+
} else if (name === 'drag_drop') {
|
|
160
|
+
next.allOf = [
|
|
161
|
+
{ anyOf: requiredAny(['from_id'], ['from_x','from_y']) },
|
|
162
|
+
{ anyOf: requiredAny(['to_id'], ['to_x','to_y']) },
|
|
163
|
+
];
|
|
164
|
+
} else if (name === 'screenshot_region') {
|
|
165
|
+
next.anyOf = [
|
|
166
|
+
{ required:['x','y','width','height'] },
|
|
167
|
+
...requiredAny(['window_id'], ['pid'], ['app'], ['title'], ['monitor'], ['monitor_index']),
|
|
168
|
+
];
|
|
169
|
+
} else if (name === 'browser_find') {
|
|
170
|
+
next.anyOf = requiredAny(['selector'], ['text'], ['role']);
|
|
171
|
+
} else if (name === 'browser_navigate') {
|
|
172
|
+
next.allOf = [{
|
|
173
|
+
if:{ properties:{ action:{ enum:['url','new_tab'] } }, required:['action'] },
|
|
174
|
+
then:{ required:['url'] },
|
|
175
|
+
}];
|
|
176
|
+
} else if (name === 'browser_action') {
|
|
177
|
+
next.allOf = [
|
|
178
|
+
{
|
|
179
|
+
if:{ properties:{ action:{ enum:['click','focus','set_value','select','scroll_into_view'] } }, required:['action'] },
|
|
180
|
+
then:{ anyOf:requiredAny(['selector'], ['text'], ['browser_text']) },
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
if:{ properties:{ action:{ const:'type' } }, required:['action'] },
|
|
184
|
+
then:{
|
|
185
|
+
required:['text'],
|
|
186
|
+
anyOf:requiredAny(['selector'], ['browser_text'], ['value'], ['text_value']),
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
if:{ properties:{ action:{ const:'scroll' } }, required:['action'] },
|
|
191
|
+
then:{ anyOf:requiredAny(['delta_x'], ['delta_y'], ['delta'], ['direction']) },
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
if:{ properties:{ action:{ const:'upload' } }, required:['action'] },
|
|
195
|
+
then:{ required:['selector'], anyOf:requiredAny(['path'], ['paths']) },
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
if:{ properties:{ action:{ const:'press' } }, required:['action'] },
|
|
199
|
+
then:{ required:['key'] },
|
|
200
|
+
},
|
|
201
|
+
];
|
|
202
|
+
if (next.properties?.paths) next.properties.paths.minItems = 1;
|
|
203
|
+
} else if (name === 'browser_wait') {
|
|
204
|
+
next.allOf = [
|
|
205
|
+
{
|
|
206
|
+
if:{ not:{ required:['condition'] } },
|
|
207
|
+
then:{ required:['selector'] },
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
if:{ properties:{ condition:{ const:'selector' } }, required:['condition'] },
|
|
211
|
+
then:{ required:['selector'] },
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
if:{ properties:{ condition:{ const:'text' } }, required:['condition'] },
|
|
215
|
+
then:{ required:['text'] },
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
if:{ properties:{ condition:{ const:'url_contains' } }, required:['condition'] },
|
|
219
|
+
then:{ anyOf:requiredAny(['value'], ['text']) },
|
|
220
|
+
},
|
|
221
|
+
];
|
|
222
|
+
} else if (name === 'service') {
|
|
223
|
+
next.allOf = [{
|
|
224
|
+
if:{ properties:{ action:{ enum:['status','start','stop','restart'] } }, required:['action'] },
|
|
225
|
+
then:{ required:['name'] },
|
|
226
|
+
}];
|
|
227
|
+
} else if (name === 'audio') {
|
|
228
|
+
next.allOf = [{
|
|
229
|
+
if:{ properties:{ action:{ const:'set_volume' } }, required:['action'] },
|
|
230
|
+
then:{ required:['volume'] },
|
|
231
|
+
}];
|
|
232
|
+
} else if (name === 'pdf_action') {
|
|
233
|
+
next.allOf = [
|
|
234
|
+
{
|
|
235
|
+
if:{ properties:{ action:{ enum:['annotations','info','split','extract_pages'] } }, required:['action'] },
|
|
236
|
+
then:{ required:['path'] },
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
if:{ properties:{ action:{ const:'merge' } }, required:['action'] },
|
|
240
|
+
then:{ required:['paths','output'] },
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
if:{ properties:{ action:{ const:'extract_pages' } }, required:['action'] },
|
|
244
|
+
then:{ required:['pages','output'] },
|
|
245
|
+
},
|
|
246
|
+
];
|
|
247
|
+
if (next.properties?.paths) {
|
|
248
|
+
next.properties.paths.minItems = 2;
|
|
249
|
+
next.properties.paths.maxItems = 100;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return next;
|
|
253
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
export const readOnly = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
2
|
+
export const readOnlyLive = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
3
|
+
export const readOnlyOpen = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
|
|
4
|
+
export const additive = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
5
|
+
export const mutatingNonDestructive = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
6
|
+
export const openMutatingNonDestructive = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
|
|
7
|
+
export const mutating = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
|
|
8
|
+
export const openMutating = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true };
|
|
9
|
+
|
|
10
|
+
export const s = description => ({ type: 'string', ...(description ? { description } : {}) });
|
|
11
|
+
export const n = description => ({ type: 'number', ...(description ? { description } : {}) });
|
|
12
|
+
export const b = description => ({ type: 'boolean', ...(description ? { description } : {}) });
|
|
13
|
+
export const e = (values, description) => ({ type: 'string', enum: values, ...(description ? { description } : {}) });
|
|
14
|
+
export const o = (properties = {}, required = []) => ({ type: 'object', properties, ...(required.length ? { required } : {}), additionalProperties: false });
|
|
15
|
+
|
|
16
|
+
export const AI_TOOL_DESCRIPTIONS = Object.freeze({
|
|
17
|
+
computer_snapshot: 'Use this for the first look at an unfamiliar desktop state when you need one compact bundle of windows, semantic UI, displays, clipboard metadata and optional pixels/browser/OCR. Prefer it over repeated screenshots; narrow later with ui_find, browser_find or screenshot_region.',
|
|
18
|
+
computer_action: 'Use this only when one semantic click/type/invoke/select/focus/scroll/drag action must work across an uncertain target backend and may need Accessibility → browser DOM/CDP → OCR → coordinate fallback, or when multi_select/multi_edit avoids many identical native UI calls. When the backend is already known, prefer the specialist tool (ui_action, browser_action, type_text, drag_drop, scroll or window_action) for its full controls. Keep keyboard, clipboard, launch/open/reveal/notification and raw pointer gestures in their dedicated tools.',
|
|
19
|
+
list_windows: 'Use this to discover top-level desktop windows or obtain a stable window id/PID/bounds before window_action, screenshot_region or targeted input. Use ui_snapshot instead when you need controls inside a window; prefer list_windows over screenshot-based window guessing.',
|
|
20
|
+
window_action: 'Use this to focus, minimize, maximize, restore, move, resize or close one known top-level window. Select it by id/PID/app/title from list_windows. Use ui_action for controls inside the window; do not use pointer coordinates for window management when this semantic tool can express the action.',
|
|
21
|
+
launch_app: 'Use this to start a desktop application by executable/path or friendly application name without a shell. Prefer it over start_process for GUI application launch. If the user wants to open a specific file or directory in its associated application, use open_path instead; use start_process for terminal commands, build tools and shell pipelines.',
|
|
22
|
+
ui_snapshot: 'Use this to inspect native desktop Accessibility/UI Automation semantics for the active app before clicking or typing. Prefer scope=active; use scope=desktop only for cross-application discovery. For a Chromium web page with CDP available, prefer browser_snapshot for page DOM semantics.',
|
|
23
|
+
ui_find: 'Use this after ui_snapshot when you need a small set of native UI elements by role/name/label/AutomationId. It requires an element criterion; use ui_snapshot, not an empty ui_find, to enumerate UI. Prefer returned id or compact label for subsequent ui_action; refresh only when the UI has changed.',
|
|
24
|
+
ui_action: 'Use this to invoke, focus, edit, select, toggle, expand/collapse, range-edit or scroll a native accessible UI element. Prefer this over pointer clicks because semantic actions survive layout changes; fall back to pointer only when accessibility cannot perform the requested action.',
|
|
25
|
+
type_text: 'Use this for reliable Unicode text entry into a native semantic target or focused native control. Prefer method=auto so Accessibility is used when the target supports verified semantic editing. For Chromium page DOM, use browser_action instead of forcing desktop accessibility text input; auto fails safely when a semantic element rejects the write rather than claiming an unverified paste. Use method=keys only for deliberate physical key-by-key input, and keyboard for shortcuts/navigation/control keys.',
|
|
26
|
+
keyboard: 'Use this for shortcuts and individual key presses such as Ctrl/Cmd+C, Tab, Enter or Escape. Do not use it to enter normal prose when type_text is available. On Wayland auto uses an already-authorized portal; backend=portal explicitly requests consent.',
|
|
27
|
+
pointer: 'Use this only when an action truly requires screen coordinates or a semantic UI/browser action is unavailable. Prefer ui_action for native controls and browser_action for web controls; pointer is the coordinate fallback for move/click/button-down/button-up.',
|
|
28
|
+
drag_drop: 'Use this for a real drag gesture between two UI element ids or coordinates. Prefer element ids from ui_find/ui_snapshot because their current bounds are resolved at execution time; use coordinates only when no semantic endpoints exist. On GNOME Wayland, real drag input requires the consent-backed Remote Desktop portal because XTEST/xdotool drag delivery is not reliable.',
|
|
29
|
+
scroll: 'Use this for native wheel scrolling of a desktop region or semantic UI target. Prefer browser_action scroll_into_view for a known web element and ui_action scroll_into_view for a known accessible desktop element.',
|
|
30
|
+
wait_for_ui: 'Use this after a desktop action to wait for an explicit native UI state or condition instead of sleeping or polling screenshots. Presence/absence waits need a semantic target; use state=changed for a broad tree change and set a bounded timeout.',
|
|
31
|
+
clipboard: 'Use this for explicit text clipboard read/write/clear operations. Do not use clipboard as a typing workaround directly; type_text already uses safe clipboard paste with restoration when needed.',
|
|
32
|
+
display_inventory: 'Use this before monitor-specific screenshots, cross-monitor geometry or coordinate work to obtain monitor bounds, scale and primary-display information.',
|
|
33
|
+
screenshot_region: 'Use this when static pixels are required for visual verification but only one window, monitor or rectangle matters. Prefer it over take_screenshot for a full desktop and over record_screen when motion/timing is not required; use ui_snapshot/browser_snapshot first when semantic structure is sufficient.',
|
|
34
|
+
open_path: 'Use this to open a permitted file or directory in its normal desktop application. Use launch_app when the task is to start an application itself without a particular file. Prefer open_path over shell-specific open/start/xdg-open commands because ReMCP applies filesystem confinement.',
|
|
35
|
+
reveal_path: 'Use this to reveal a permitted local file or directory in Finder/Explorer/file manager without opening the document itself.',
|
|
36
|
+
notification: 'Use this only when the user wants a visible native desktop notification on the paired computer. Do not use it as a substitute for replying in chat.',
|
|
37
|
+
browser_tabs: 'Use this first for CDP browser automation when the target page/tab is not already unambiguous. It lists debuggable Chromium pages from a loopback-only endpoint; use the returned target_id/title/url to scope later browser tools.',
|
|
38
|
+
browser_navigate: 'Use this to open a URL in a new debuggable tab or navigate a known Chromium page by URL, back, forward or reload. Prefer it over typing into the address bar; new_tab also bootstraps browser automation when no page target exists yet.',
|
|
39
|
+
browser_snapshot: 'Use this to inspect a web page structurally through CDP accessibility. Prefer it over desktop ui_snapshot for page content; add include_screenshot=true only when final pixel/layout verification is needed. A selector may be temporarily centered for capture, but the original page scroll position is restored before the tool returns.',
|
|
40
|
+
browser_find: 'Use this to locate visible web elements by CSS selector, text or ARIA role and obtain reusable selectors/bounds. It requires an element criterion; use browser_snapshot, not an empty browser_find, to enumerate page structure. Prefer this before browser_action rather than guessing selectors or coordinates.',
|
|
41
|
+
browser_action: 'Use this for deterministic DOM/CDP interaction with a known web element: click/focus/type/value/select/upload/key/scroll or viewport emulation. Prefer it over desktop pointer/ui_action for page content; use browser_evaluate only when the supported actions cannot express the task.',
|
|
42
|
+
browser_wait: 'Use this after browser navigation/action to wait for selector/text/URL/load/navigation/network-idle instead of fixed sleeps or screenshot polling. Use browser_evaluate separately for JavaScript predicates.',
|
|
43
|
+
browser_evaluate: 'Use this as the powerful browser escape hatch only when browser_snapshot/find/action/wait cannot express the required page operation or inspection. JavaScript executes in page context and page content is untrusted.',
|
|
44
|
+
service: 'Use this for operating-system service inventory/status/start/stop/restart. Prefer status before a mutation when service state matters; use start_process for ordinary commands that are not service-manager operations.',
|
|
45
|
+
event_log: 'Use this to read bounded recent OS logs (Windows Event Log, macOS unified log or systemd journal) when diagnosing a service/app/system problem. Prefer it over an unbounded shell log dump.',
|
|
46
|
+
network: 'Use this for a compact network summary, full interface inventory, DNS/routes/listeners inspection or a bounded TCP connectivity test. Prefer action=summary for orientation and action=interfaces only when the complete adapter/address inventory is required; prefer this tool over shell ip/netstat/nslookup commands for routine diagnostics.',
|
|
47
|
+
installed_apps: 'Use this to answer what software/packages are installed and their versions. Prefer a filter when looking for one product instead of returning the full inventory.',
|
|
48
|
+
environment: 'Use this for PATH, shell, Node/runtime environment paths, architecture and optionally sanitized environment variables. Use get_system_info for host health such as CPU/memory/disk/uptime, and get_runtime_info for ReMCP policy, roots and limits. Secret-looking values remain masked.',
|
|
49
|
+
audio: 'Use this to read or change the default output volume/mute state. Use action=status for inspection; set_volume requires a 0–100 value.',
|
|
50
|
+
power_action: 'Use this only when the user explicitly asks to lock, sleep, restart or shut down the computer. Prefer it over invoking those system actions through start_process; it is intentionally guarded by device command policy because restart/shutdown can interrupt work.',
|
|
51
|
+
record_screen: 'Use this for a short bounded screen recording when motion/timing is necessary to diagnose a UI issue. Prefer screenshot_region for static visual verification because it is cheaper and easier to inspect.',
|
|
52
|
+
read_document: 'Use this for structured document content, especially PDF, DOCX and XLSX, and for CSV/JSON/XML when document-style parsing is useful. Use read_file for ordinary source code, logs, configuration, Markdown, or other plain line-oriented text. Prefer read_document over shell extraction or launching Office when the goal is document content; PDF reading uses the built-in parser first and may fall back to local pdftotext when embedded fonts prevent direct decoding.',
|
|
53
|
+
edit_spreadsheet: 'Use this to create a new XLSX workbook or directly edit XLSX cells/ranges/formulas without launching Excel. Set create=true only for a new path; it fails rather than overwriting an existing workbook. Prefer explicit cells/ranges and write to output when preserving an existing original matters.',
|
|
54
|
+
edit_document: 'Use this to create a new DOCX document or make structured DOCX paragraph edits without launching Word. Set create=true only for a new path; it fails rather than overwriting an existing document. It is best for replace/insert/delete paragraph operations; use native UI automation only when document formatting/layout interaction is required.',
|
|
55
|
+
pdf_action: 'Use this for PDF metadata/annotations inspection or structural merge/split/page extraction. For reading PDF text use read_document; supply an output/output_dir for write actions when preserving originals matters.',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const COMMON_PARAM_DESCRIPTIONS = Object.freeze({
|
|
59
|
+
action:'Operation to perform; choose the narrowest action that directly matches the requested outcome.',
|
|
60
|
+
id:'Stable target id returned by the corresponding discovery tool.',
|
|
61
|
+
label:'Compact numeric accessibility label from the most recent semantic UI snapshot/find.',
|
|
62
|
+
pid:'Owning process id used to scope the target.',
|
|
63
|
+
app:'Application/process name or substring used to select the target.',
|
|
64
|
+
title:'Window/page title substring used to select the target.',
|
|
65
|
+
window_title:'Top-level accessibility window/frame title substring used to scope native UI matching.',
|
|
66
|
+
name:'Accessible element name/title substring.',
|
|
67
|
+
role:'Accessible or ARIA role used to narrow semantic matching.',
|
|
68
|
+
automation_id:'Windows UI Automation AutomationId when available.',
|
|
69
|
+
x:'Virtual-desktop X coordinate in pixels.',
|
|
70
|
+
y:'Virtual-desktop Y coordinate in pixels.',
|
|
71
|
+
width:'Width in pixels.',
|
|
72
|
+
height:'Height in pixels.',
|
|
73
|
+
value:'Value required by the selected action.',
|
|
74
|
+
text:'Text required by the selected action.',
|
|
75
|
+
selector:'CSS selector identifying a web element.',
|
|
76
|
+
endpoint:'Loopback Chrome DevTools Protocol endpoint; normally leave unset to use the default.',
|
|
77
|
+
target_id:'Browser page target id returned by browser_tabs.',
|
|
78
|
+
url_contains:'Substring used to select a browser page by URL.',
|
|
79
|
+
timeout_ms:'Bounded timeout in milliseconds.',
|
|
80
|
+
poll_ms:'Polling interval in milliseconds.',
|
|
81
|
+
path:'Permitted local filesystem path.',
|
|
82
|
+
output:'Optional permitted output path; when omitted the tool may update the input according to its action.',
|
|
83
|
+
output_dir:'Permitted directory for generated output files.',
|
|
84
|
+
paths:'List of permitted local filesystem paths.',
|
|
85
|
+
args:'Argument array passed directly to the launched application; no shell parsing.',
|
|
86
|
+
backend:'Input backend preference; auto is recommended unless a specific backend is required.',
|
|
87
|
+
key:'Single key name such as Enter, Escape, Tab or A.',
|
|
88
|
+
shortcut:'Keyboard shortcut such as CTRL+L, CMD+K or ALT+F4.',
|
|
89
|
+
delta_x:'Horizontal wheel/scroll delta; positive values move right.',
|
|
90
|
+
delta_y:'Vertical wheel/scroll delta; positive values move down.',
|
|
91
|
+
from_x:'Source X coordinate for a drag gesture.',
|
|
92
|
+
from_y:'Source Y coordinate for a drag gesture.',
|
|
93
|
+
to_x:'Destination X coordinate for a drag gesture.',
|
|
94
|
+
to_y:'Destination Y coordinate for a drag gesture.',
|
|
95
|
+
from_id:'Source semantic UI element id; preferred over source coordinates when available.',
|
|
96
|
+
to_id:'Destination semantic UI element id; preferred over destination coordinates when available.',
|
|
97
|
+
limit:'Maximum number of results to return.',
|
|
98
|
+
filter:'Optional bounded filter expression or text used to reduce returned results.',
|
|
99
|
+
query:'Optional platform-specific query used to reduce returned log results.',
|
|
100
|
+
scope:'Scope of the operation; choose user scope unless the task explicitly requires a system service.',
|
|
101
|
+
host:'Host name or IP address for a bounded connectivity test.',
|
|
102
|
+
port:'TCP port number for a connectivity test.',
|
|
103
|
+
duration_seconds:'Bounded recording/action duration in seconds.',
|
|
104
|
+
fps:'Frames per second for screen recording.',
|
|
105
|
+
sheet:'Worksheet name.',
|
|
106
|
+
max_cells:'Maximum number of spreadsheet cells to return.',
|
|
107
|
+
pattern:'Output filename pattern where supported.',
|
|
108
|
+
pages:'PDF page numbers/ranges such as 1-3,5.',
|
|
109
|
+
message:'User-visible notification text.',
|
|
110
|
+
max_nodes:'Maximum accessibility nodes to inspect or return; keep this bounded and raise it only when the target is missing.',
|
|
111
|
+
max_depth:'Maximum accessibility tree depth to traverse.',
|
|
112
|
+
method:'Text-entry strategy. auto is recommended; accessibility is semantic, clipboard preserves Unicode, and keys emits physical key events whose printable characters follow the computer current keyboard layout.',
|
|
113
|
+
caret_position:'Where text insertion should occur when the semantic editor supports caret placement.',
|
|
114
|
+
keys:'Ordered key names that together form the requested shortcut or key sequence.',
|
|
115
|
+
button:'Pointer button to use for click/down/up or drag gestures.',
|
|
116
|
+
hold_ms:'How long to hold the pointer button before movement during a drag.',
|
|
117
|
+
duration_ms:'Duration of the drag/movement gesture in milliseconds.',
|
|
118
|
+
replacement:'Replacement text for matching document content.',
|
|
119
|
+
direction:'Human-readable scroll direction used with wheel_times instead of explicit deltas.',
|
|
120
|
+
condition:'Semantic condition to wait for; choose the condition that directly proves the requested state transition.',
|
|
121
|
+
timeout_seconds:'Bounded native notification display timeout in seconds when the platform supports it.',
|
|
122
|
+
text_value:'Text value used by browser type/set-value actions; use text or value when possible unless the action specifically expects this compatibility alias.',
|
|
123
|
+
option:'Option value to select in a browser select element.',
|
|
124
|
+
expression:'JavaScript expression or predicate used only by the selected browser operation/wait condition.',
|
|
125
|
+
edits:'Bounded list of spreadsheet edits; each item names a cell/range and the value(s) or formula to write.',
|
|
126
|
+
operations:'Bounded list of structured document operations executed in order.',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
export function enrichSchemaDescriptions(schema) {
|
|
130
|
+
if (!schema || typeof schema !== 'object') return schema;
|
|
131
|
+
const copy = structuredClone(schema);
|
|
132
|
+
const visit = node => {
|
|
133
|
+
if (!node || typeof node !== 'object') return;
|
|
134
|
+
if (node.properties && typeof node.properties === 'object') {
|
|
135
|
+
for (const [key, property] of Object.entries(node.properties)) {
|
|
136
|
+
if (property && typeof property === 'object' && !property.description) {
|
|
137
|
+
property.description = COMMON_PARAM_DESCRIPTIONS[key] || ('Parameter ' + key.replaceAll('_',' ') + ' for this operation.');
|
|
138
|
+
}
|
|
139
|
+
visit(property);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (node.items) visit(node.items);
|
|
143
|
+
for (const key of ['oneOf','anyOf','allOf']) if (Array.isArray(node[key])) node[key].forEach(visit);
|
|
144
|
+
};
|
|
145
|
+
visit(copy);
|
|
146
|
+
return copy;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const outputString = description => ({ type:'string', ...(description ? { description } : {}) });
|
|
150
|
+
export const outputNumber = description => ({ type:'number', ...(description ? { description } : {}) });
|
|
151
|
+
export const outputBoolean = description => ({ type:'boolean', ...(description ? { description } : {}) });
|
|
152
|
+
export const outputObject = description => ({ type:'object', additionalProperties:true, ...(description ? { description } : {}) });
|
|
153
|
+
export const outputNullableObject = description => ({ type:['object','null'], additionalProperties:true, ...(description ? { description } : {}) });
|
|
154
|
+
export const outputRecord = (properties = {}, description) => ({ type:'object', properties, additionalProperties:true, ...(description ? { description } : {}) });
|
|
155
|
+
export const outputArray = (items = {}, description) => ({ type:'array', items, ...(description ? { description } : {}) });
|
|
156
|
+
|
|
157
|
+
export const OUTPUT_FIELDS = Object.freeze({
|
|
158
|
+
computer_snapshot: {
|
|
159
|
+
device:outputObject('Device/platform metadata for the captured computer state.'),
|
|
160
|
+
windows:outputArray(outputObject(), 'Top-level windows with stable ids/PIDs/bounds when available.'),
|
|
161
|
+
displays:outputArray(outputObject(), 'Display inventory used for virtual-desktop geometry.'),
|
|
162
|
+
active_window:outputNullableObject('Active top-level window when known; null when accessibility state cannot be joined to top-level window enumeration.'),
|
|
163
|
+
ui:outputObject('Native accessibility snapshot and semantic tree.'),
|
|
164
|
+
cursor:outputObject('Pointer position when available.'),
|
|
165
|
+
clipboard:outputObject('Clipboard metadata only unless clipboard content was explicitly requested.'),
|
|
166
|
+
browser:outputObject('Optional browser DOM/CDP snapshot.'),
|
|
167
|
+
ocr:outputObject('Optional OCR words/metadata.'),
|
|
168
|
+
fallback_chain:outputObject('Which semantic/DOM/OCR/coordinate layers were requested and available.'),
|
|
169
|
+
errors:outputArray(outputObject(), 'Non-fatal sub-capture errors.'),
|
|
170
|
+
},
|
|
171
|
+
computer_action: { action:outputString(), ok:outputBoolean(), completed:outputNumber(), results:outputArray(outputObject()), stopped_at:outputNumber(), count:outputNumber(), waited_ms:outputNumber(), capped:outputBoolean() },
|
|
172
|
+
list_windows: { data:outputArray(outputRecord({ id:outputString('Stable window id.'), pid:outputNumber('Owning process id.'), app:outputString(), title:outputString(), x:outputNumber(), y:outputNumber(), width:outputNumber(), height:outputNumber(), monitor:{ type:['string','number','null'], description:'Monitor name or platform index when available.' }, active:outputBoolean(), minimized:outputBoolean(), maximized:outputBoolean() }, 'Top-level window record.'), 'Top-level windows.') },
|
|
173
|
+
window_action: { action:outputString(), id:outputString(), pid:outputNumber(), title:outputString(), text:outputString() },
|
|
174
|
+
launch_app: { text:outputString('Human-readable launch result and optional ready-window confirmation.') },
|
|
175
|
+
ui_snapshot: { platform:outputString(), count:outputNumber(), nodes:outputArray(outputRecord({ id:outputString('Stable accessibility element id.'), label:outputNumber('Compact label valid for the current cached snapshot.'), role:outputString(), name:outputString(), value:{ type:['string','number','boolean','null'], description:'Accessible value in its native scalar type when available.' }, x:outputNumber(), y:outputNumber(), width:outputNumber(), height:outputNumber(), focused:outputBoolean(), enabled:outputBoolean() }, 'Accessible semantic node.')), semantic_tree:outputString(), label_count:outputNumber(), browser_dom:outputObject() },
|
|
176
|
+
ui_find: { count:outputNumber(), nodes:outputArray(outputRecord({ id:outputString('Stable accessibility element id.'), label:outputNumber('Compact snapshot label.'), role:outputString(), name:outputString(), value:{ type:['string','number','boolean','null'], description:'Accessible value in its native scalar type when available.' }, x:outputNumber(), y:outputNumber(), width:outputNumber(), height:outputNumber() }, 'Matched accessible node.')) },
|
|
177
|
+
ui_action: { action:outputString(), id:outputString(), label:outputNumber(), backend:outputString(), result:outputObject(), text:outputString() },
|
|
178
|
+
type_text: { length:outputNumber(), method:outputString(), target:outputObject(), clear:outputBoolean(), press_enter:outputBoolean(), clipboard_restored:outputBoolean(), backend:outputString() },
|
|
179
|
+
keyboard: { action:outputString(), backend:outputString(), text:outputString() },
|
|
180
|
+
pointer: { action:outputString(), x:outputNumber(), y:outputNumber(), button:outputString(), backend:outputString(), id:outputString() },
|
|
181
|
+
drag_drop: { from:outputArray(outputNumber()), to:outputArray(outputNumber()), from_element:outputString(), to_element:outputString(), backend:outputString() },
|
|
182
|
+
scroll: { delta_x:outputNumber(), delta_y:outputNumber(), backend:outputString(), text:outputString() },
|
|
183
|
+
wait_for_ui: { state:outputString(), wanted:outputString(), matched:{ type:['number','boolean'], description:'Match count for state waits or boolean result for condition waits.' }, attempts:outputNumber(), elapsed_ms:outputNumber(), nodes:outputArray(outputObject()), before:outputObject() },
|
|
184
|
+
clipboard: { action:outputString(), length:outputNumber(), backend:outputString(), text:outputString(), data:outputString() },
|
|
185
|
+
display_inventory: { data:outputArray(outputObject('Display with id/name/virtual bounds/scale/primary state.')) },
|
|
186
|
+
screenshot_region: { text:outputString('Capture summary; image bytes are returned as MCP image content.'), image:outputObject('Optional structured image metadata when the client mirrors image content.') },
|
|
187
|
+
open_path: { text:outputString() },
|
|
188
|
+
reveal_path: { text:outputString() },
|
|
189
|
+
notification: { text:outputString() },
|
|
190
|
+
browser_tabs: { data:outputArray(outputRecord({ id:outputString('Stable CDP page target id.'), title:outputString(), url:outputString(), type:outputString(), webSocketDebuggerUrl:outputString() }, 'Debuggable browser page target.')) },
|
|
191
|
+
browser_navigate: { target_id:outputString('Stable CDP page target id.'), action:outputString(), url:outputString(), title:outputString(), frame_id:{ type:['string','null'], description:'CDP frame id when navigation produced one.' }, error_text:{ type:['string','null'], description:'CDP navigation error text, or null when navigation succeeded.' } },
|
|
192
|
+
browser_snapshot: { target_id:outputString('Stable CDP page target id.'), url:outputString(), title:outputString(), count:outputNumber(), nodes:outputArray(outputObject('Accessibility node for the page.')), semantic_tree:outputString(), screenshot:outputObject() },
|
|
193
|
+
browser_find: { target_id:outputString(), count:outputNumber(), matches:outputArray(outputRecord({ selector:outputString('Reusable CSS selector when one can be derived.'), role:outputString(), text:outputString(), x:outputNumber(), y:outputNumber(), width:outputNumber(), height:outputNumber() }, 'Matched DOM element.')) },
|
|
194
|
+
browser_action: { target_id:outputString(), action:outputString(), selector:outputString(), key:outputString(), files:outputNumber(), width:outputNumber(), height:outputNumber(), device_scale_factor:outputNumber(), mobile:outputBoolean(), result:outputObject() },
|
|
195
|
+
browser_wait: { target_id:outputString(), condition:outputString(), matched:outputBoolean(), attempts:outputNumber(), elapsed_ms:outputNumber(), value:outputObject(), url:outputString(), text:outputString() },
|
|
196
|
+
browser_evaluate: { target_id:outputString(), value:{} },
|
|
197
|
+
service: { data:outputArray(outputObject('Service record.')), action:outputString(), name:outputString(), text:outputString() },
|
|
198
|
+
event_log: { text:outputString('Bounded log output.'), data:outputArray(outputObject()) },
|
|
199
|
+
network: { hostname:outputString(), interfaces:outputObject(), dns:outputArray(outputString()), servers:outputArray(outputString()), host:outputString(), port:outputNumber(), ok:outputBoolean(), latency_ms:outputNumber(), interface_count:outputNumber(), interface_count_returned:outputNumber(), address_count:outputNumber(), address_count_returned:outputNumber(), interfaces_truncated:outputBoolean(), addresses_truncated:outputBoolean(), data:outputArray(outputObject()) },
|
|
200
|
+
installed_apps: {
|
|
201
|
+
data:outputArray(outputRecord({
|
|
202
|
+
name:outputString('Installed application/package name.'),
|
|
203
|
+
version:{ type:['string','null'], description:'Installed version when available.' },
|
|
204
|
+
publisher:{ type:['string','null'], description:'Publisher/maintainer/vendor when available.' },
|
|
205
|
+
path:{ type:['string','null'], description:'Installation path when available.' },
|
|
206
|
+
signed_by:outputString('Optional macOS signing identity summary.'),
|
|
207
|
+
}, 'Normalized installed app/package record.')),
|
|
208
|
+
backend:outputString('Native inventory backend used on this platform.'),
|
|
209
|
+
count:outputNumber('Total matching records before the requested limit.'),
|
|
210
|
+
returned:outputNumber('Number of records returned in data.'),
|
|
211
|
+
truncated:outputBoolean('True when count is larger than returned because of the requested limit.'),
|
|
212
|
+
},
|
|
213
|
+
environment: {
|
|
214
|
+
platform:outputString(),
|
|
215
|
+
arch:outputString(),
|
|
216
|
+
node:outputString(),
|
|
217
|
+
cwd:outputString('Runtime working directory.'),
|
|
218
|
+
home:outputString('Current user home directory.'),
|
|
219
|
+
temp:outputString('Operating-system temporary directory.'),
|
|
220
|
+
shell:{ type:['string','null'], description:'Configured command shell when available.' },
|
|
221
|
+
path_entries:outputArray(outputString('One PATH entry in native search order.')),
|
|
222
|
+
environment:outputObject('Sanitized environment variables when include_env=true.'),
|
|
223
|
+
},
|
|
224
|
+
audio: { volume:outputNumber(), muted:outputBoolean(), action:outputString(), backend:outputString(), text:outputString() },
|
|
225
|
+
power_action: { text:outputString(), action:outputString() },
|
|
226
|
+
record_screen: { path:outputString(), bytes:outputNumber(), duration_seconds:outputNumber(), format:outputString() },
|
|
227
|
+
read_document: { text:outputString(), path:outputString(), data:{}, sheets:outputArray(outputObject()), sheet:outputString(), rows:outputArray({}), cells:outputArray({}) },
|
|
228
|
+
edit_spreadsheet: { path:outputString(), sheet:outputString(), edited_cells:outputNumber(), bytes:outputNumber(), created:outputBoolean() },
|
|
229
|
+
edit_document: { path:outputString(), operations:outputNumber(), changes:outputNumber(), bytes:outputNumber(), created:outputBoolean() },
|
|
230
|
+
pdf_action: { action:outputString(), path:outputString(), source:outputString(), output:outputString(), output_dir:outputString(), files:outputArray(outputString()), pages:outputString(), bytes:outputNumber(), annotations:outputArray(outputObject()), annotation_count:outputNumber('Number of PDF annotations found by the info action.'), inputs:outputNumber() },
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
export function outputSchemaFor(name, title) {
|
|
234
|
+
const properties = {
|
|
235
|
+
...(OUTPUT_FIELDS[name] || {}),
|
|
236
|
+
truncated:{ type:'boolean', description:'True when the structured mirror was bounded because the result was large.' },
|
|
237
|
+
bytes:{ type:'number', description:'Serialized structured-result size when truncation metadata is returned.' },
|
|
238
|
+
preview:{ type:'string', description:'Bounded JSON preview when the full structured mirror is too large.' },
|
|
239
|
+
data:(OUTPUT_FIELDS[name] || {}).data || { description:'Array/scalar payload wrapper used when the natural result is not an object.' },
|
|
240
|
+
text:(OUTPUT_FIELDS[name] || {}).text || { type:'string', description:'Human-readable structured mirror for text-only results.' },
|
|
241
|
+
};
|
|
242
|
+
return {
|
|
243
|
+
type:'object',
|
|
244
|
+
properties,
|
|
245
|
+
additionalProperties:true,
|
|
246
|
+
description:title + ' structured result for ' + name + '. Stable fields are declared explicitly; platform-specific fields may be added. Large results may return truncated/bytes/preview instead of the full payload.',
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function requiredAny(...groups) {
|
|
251
|
+
return groups.map(required => ({ required }));
|
|
252
|
+
}
|