@rebind.gg/mcp-server 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -55,7 +55,7 @@ Connects, takes a capture, and reports the display — the fastest way to catch
55
55
  | `type` | Type text into the focused element. |
56
56
  | `key` | Press a key or combo, e.g. `Enter`, `LCtrl+L`, `LAlt+Tab`. |
57
57
  | `scroll` | Scroll at the cursor. Positive scrolls down, negative up. `unit` is `wheel` (default, fine wheel clicks) or `page` (one PageDown or PageUp per unit, capped at 10). |
58
- | `read_page` | Extract the focused browser's current page as structured text (URL, forms, interactive elements, plain text) via an address-bar bookmarklet; the full dump, including cleaned HTML, lands in the clipboard. The result is read back through AppleScript (`URL of active tab of front window`), so the relay host must be macOS and the front browser must answer that query. Safari is detected but blocks the bookmarklet path. |
58
+ | `read_page` | Extract the focused browser's current page as structured text (URL, forms, interactive elements, plain text) via an address-bar bookmarklet; the full dump, including cleaned HTML, lands in the clipboard. The result is read back through AppleScript (`URL of active tab of front window`), so the tool is registered only when the relay host is macOS, and the front browser must answer that query. Safari is detected but blocks the bookmarklet path. |
59
59
  | `meditate` | Research a problem page until a reasoning model has the data to solve it: fetches the URL directly, pulls the script bundles behind a JS shell so its API endpoints are visible, fetches those endpoints and assets, and reads the live page with vision. The browser steps (rendering a JS shell, same-origin fetches through the page, and the vision pass) need a macOS relay; elsewhere it reads pages over HTTP only. Returns a markdown dossier plus JSON `{error, problem, requirements, plan, submittable, form, live_captcha_hint, confidence, sources, assets, pending_fetch, notes, cost_usd}`. `error` is non-null when no problem could be identified or the page could not be read; partial data is still returned. Gated behind `REBIND_ALLOW_DELEGATION=1` (spends OpenRouter credits). See the pipeline below. |
60
60
  | `run_lua` | Run a Luau script on the connected machine and return its `return` value as JSON — the fast path for any deterministic sequence (launch apps, place windows, shell commands, pixel search). |
61
61
  | `lua_docs` | Full Luau SDK type definitions for `run_lua`. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebind.gg/mcp-server",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "MCP server exposing Rebind as computer-use tools: screenshot, verified click, type, key, scroll, and window control over the Remote Control protocol.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -98,8 +98,6 @@ function versusMessages(
98
98
  // asked to re-emit the whole growing dossier blows the model's output cap and
99
99
  // truncates the JSON; splitting keeps every response small and bounds cost.
100
100
 
101
- /** stop the loop once the model reports at least this much confidence */
102
- const MEDITATE_CONFIDENT = 0.8;
103
101
  /** max distinct pages read (problem page + follows) before the loop must stop */
104
102
  const MEDITATE_MAX_PAGES = 10;
105
103
  // Budgets sized for a 1M-token context, not a 4k one. These were tight enough
@@ -980,9 +978,10 @@ return { ok = true, proc = proc }
980
978
  },
981
979
  async ({ handle }) => {
982
980
  // handle-based activation can raise without focusing on macOS; fall back
983
- // to app-name activation (App.Activate, macOS-only, no-op elsewhere via
984
- // pcall). Decided relay-side: this process may run on a different
985
- // machine than the relay, so process.platform is the wrong signal.
981
+ // to app-name activation (App.Activate, pcall-guarded because it raises
982
+ // where the relay has no App backend). Decided relay-side: this process
983
+ // may run on a different machine than the relay, so process.platform is
984
+ // the wrong signal.
986
985
  const r = (await client.luaExec(`
987
986
  local h = ${handle}
988
987
  Window.Activate(h)
@@ -1289,7 +1288,7 @@ return { ok = Window.IsActive(h), name = name }
1289
1288
  "meditate",
1290
1289
  {
1291
1290
  description:
1292
- "Research a problem page until enough data is collected to solve it. Fetches `url` directly (relay-side curl) and, when the response is a JS shell, pulls its script bundles so the API endpoints behind the page are visible; falls back to extracting the rendered page in a browser when a fetch cannot see the content. A reasoning model then iterates: identify the problem, fetch the URLs and API endpoints that close information gaps, download required assets, until it reaches confidence >= 0.8, has nothing left to fetch, or hits max_rounds. A final pass renders everything into a solving dossier. " +
1291
+ "Research a problem page until enough data is collected to solve it. Fetches `url` directly (relay-side curl) and, when the response is a JS shell, pulls its script bundles so the API endpoints behind the page are visible; falls back to extracting the rendered page in a browser when a fetch cannot see the content. A reasoning model then iterates: identify the problem, fetch the URLs and API endpoints that close information gaps, download required assets, until it has nothing left to fetch or hits max_rounds. A final pass renders everything into a solving dossier. " +
1293
1292
  "Returns the dossier as markdown plus a JSON plan {error, problem, requirements, plan, confidence, sources, assets, pending_fetch, notes, cost_usd}. `error` is non-null (with the partial data still returned, never thrown) when no clear problem is identifiable or the browser could not be driven. Downloaded `assets` paths are on the RELAY host. " +
1294
1293
  "On a macOS relay it also drives the real browser (takes over keyboard/clipboard for the duration) to render a JS shell, fetch same-origin URLs through the page, and read the live captcha; elsewhere it reads pages over HTTP only. Spends OpenRouter credits. Refuses non-public URLs (loopback/private/link-local). Safari cannot be driven — pass `browser` to name another.",
1295
1294
  inputSchema: {
package/src/rebind.d.luau CHANGED
@@ -35,6 +35,14 @@ export type ServerHandle = {
35
35
  Stop: (self: ServerHandle) -> (),
36
36
  }
37
37
 
38
+ -- UDP: socket handle returned by Net.UDPListen. SendTo sends from the bound
39
+ -- address and returns false when the send fails or the socket is stopped.
40
+ export type UdpHandle = {
41
+ port: number,
42
+ SendTo: (self: UdpHandle, ip: string, port: number, bytes: string) -> boolean,
43
+ Stop: (self: UdpHandle) -> (),
44
+ }
45
+
38
46
  -- WebSocket: server-side per-connection handle passed to OnConnect/OnMessage/OnClose.
39
47
  -- `id` is a monotonic identifier unique to this connection.
40
48
  export type WsClient = {
@@ -112,6 +120,19 @@ export type WindowPos = {
112
120
  height: number,
113
121
  }
114
122
 
123
+ -- the input device a key, button, move or scroll came from. hooks get nil
124
+ -- where the source cannot tell devices apart (Windows and macOS software mode)
125
+ export type InputDevice = {
126
+ id: string,
127
+ name: string,
128
+ nickname: string?,
129
+ kind: "keyboard" | "mouse" | "other",
130
+ vid: string,
131
+ pid: string,
132
+ source: "vm" | "host",
133
+ exact: boolean,
134
+ }
135
+
115
136
  export type MousePos = {
116
137
  x: number,
117
138
  y: number,
@@ -224,9 +245,10 @@ export type UiProxy = { [string]: any }
224
245
 
225
246
  -- bind config for table-style binding
226
247
  export type BindConfig = {
227
- when: (() -> boolean)?,
228
- action: (() -> boolean?)?,
229
- release: (() -> ())?,
248
+ device: string?, -- nickname or id: the bind answers only to that device
249
+ when: ((device: InputDevice?) -> boolean)?,
250
+ action: ((device: InputDevice?) -> boolean?)?,
251
+ release: ((device: InputDevice?) -> ())?,
230
252
  }
231
253
 
232
254
  -- runtime identity table
@@ -300,6 +322,20 @@ declare System: {
300
322
 
301
323
  -- hid namespace
302
324
 
325
+ -- a joypad state; every field is optional and defaults to neutral. buttons
326
+ -- lists pressed buttons 1-32, hat is 0 (up) to 7 clockwise (nil = centered),
327
+ -- x/y/z/rz are -1..1 (0 = center), slider and dial are 0..1.
328
+ export type JoypadState = {
329
+ buttons: { number }?,
330
+ hat: number?,
331
+ x: number?,
332
+ y: number?,
333
+ z: number?,
334
+ rz: number?,
335
+ slider: number?,
336
+ dial: number?,
337
+ }
338
+
303
339
  declare HID: {
304
340
  Down: (code: string) -> (),
305
341
  Up: (code: string) -> (),
@@ -319,6 +355,15 @@ declare HID: {
319
355
  Scroll: (amount: number) -> (),
320
356
  SetMouseMode: (mode: string) -> (),
321
357
  GetMouseMode: () -> string,
358
+ -- keyboard HID usage for a key name, or nil (media keys return nil). Down,
359
+ -- Up, Press and Combo also accept "usage:N" (decimal or 0x hex, 0x04-0xE7).
360
+ Usage: (name: string) -> number?,
361
+ -- set the whole joypad state (last write wins across scripts). Needs a
362
+ -- Rebind Link whose USB profile has a joypad; raises otherwise. Keyboard
363
+ -- and mouse calls raise the same way when the profile leaves them out.
364
+ Joypad: (state: JoypadState?) -> (),
365
+ -- back to neutral; the relay also does this when a script stops or errors
366
+ JoypadReset: () -> (),
322
367
  }
323
368
 
324
369
  -- input namespace
@@ -354,7 +399,7 @@ declare Bind: typeof(setmetatable({} :: {
354
399
  Toggle: (key: string, uiId: string) -> BindHandle,
355
400
  Remap: (from: string, to: string) -> BindHandle,
356
401
  }, {} :: {
357
- __call: (self: any, key: string, config: (() -> boolean?) | BindConfig) -> BindHandle,
402
+ __call: (self: any, key: string, config: ((device: InputDevice?) -> boolean?) | BindConfig) -> BindHandle,
358
403
  }))
359
404
 
360
405
  -- timer namespace
@@ -374,7 +419,8 @@ declare Macro: {
374
419
  StopAll: () -> (),
375
420
  Record: (options: RecordOpts?) -> (),
376
421
  Finish: () -> { MacroAction },
377
- -- the saved-macro format: modeline, actions table, top-level Macro.Play.
422
+ -- the saved-macro format: a `macro=true` modeline, then `return` of the
423
+ -- actions. require() of a saved macro returns the actions.
378
424
  ToScript: (macro: { MacroAction }, name: string) -> string,
379
425
  Stream: (macro: { MacroAction }) -> (),
380
426
  Abort: () -> (),
@@ -519,9 +565,12 @@ declare Window: {
519
565
  }
520
566
 
521
567
  -- app namespace: application-level control (Keyboard Maestro's Application
522
- -- Control). names match case-insensitively by app display name. Front/IsRunning/
523
- -- Activate are permission-free; Hide/Quit/IsHidden drive an AppleEvent and
524
- -- prompt for macOS Automation permission on first use.
568
+ -- Control). macOS: names match case-insensitively by app display name;
569
+ -- Front/IsRunning/Activate are permission-free; Hide/Quit/IsHidden drive an
570
+ -- AppleEvent and prompt for macOS Automation permission on first use.
571
+ -- Windows: names are executable names (case-insensitive, ".exe" optional);
572
+ -- Activate focuses a running app and does not launch one; Quit closes its
573
+ -- windows; Hide/IsHidden raise. Linux: every call raises.
525
574
 
526
575
  export type AppInfo = {
527
576
  name: string,
@@ -551,6 +600,10 @@ declare Net: {
551
600
  Listen: (port: number, handler: (request: HttpRequest) -> HttpResponseSpec | string) -> ServerHandle,
552
601
  WSListen: (port: number, handlers: WsListenHandlers) -> WsServerHandle,
553
602
  WSConnect: (url: string, handlers: WsConnectHandlers) -> WsConnectHandle,
603
+ -- bind a UDP socket; handler receives each datagram's raw bytes and sender.
604
+ UDPListen: (bindIp: string, port: number, handler: (data: string, ip: string, port: number) -> ()) -> UdpHandle,
605
+ -- this computer's IPv4 address on its default route, or nil when there is none.
606
+ LocalIP: () -> string?,
554
607
  }
555
608
 
556
609
  -- pipe namespace
@@ -752,14 +805,14 @@ declare Registry: {
752
805
  -- declare function OnFocus(window: WindowInfo): ()
753
806
  -- declare function OnBlur(window: WindowInfo): ()
754
807
  -- declare function OnTick(dtMs: number): () -- ms elapsed since the previous OnTick (0 on the first)
755
- -- declare function OnDown(key: string): boolean?
756
- -- declare function OnUp(key: string, durationMs: number): boolean?
757
- -- declare function OnMove(dx: number, dy: number): boolean? -- return value only used with mouse_block=true (hardware mode only)
758
- -- declare function OnScroll(delta: number): boolean?
808
+ -- declare function OnDown(key: string, device: InputDevice?): boolean?
809
+ -- declare function OnUp(key: string, durationMs: number, device: InputDevice?): boolean?
810
+ -- declare function OnMove(dx: number, dy: number, device: InputDevice?): boolean? -- return value only used with mouse_block=true (hardware mode only)
811
+ -- declare function OnScroll(delta: number, device: InputDevice?): boolean?
759
812
  -- declare function OnScrollH(delta: number): boolean?
760
- -- declare function OnInputDown(key: string): ()
761
- -- declare function OnInputUp(key: string, durationMs: number): ()
762
- -- declare function OnInputMove(dx: number, dy: number): ()
763
- -- declare function OnInputScroll(delta: number): ()
813
+ -- declare function OnInputDown(key: string, device: InputDevice?): ()
814
+ -- declare function OnInputUp(key: string, durationMs: number, device: InputDevice?): ()
815
+ -- declare function OnInputMove(dx: number, dy: number, device: InputDevice?): ()
816
+ -- declare function OnInputScroll(delta: number, device: InputDevice?): ()
764
817
  -- declare function OnError(message: string): ()
765
818
  -- declare function OnReload(): ()