machine-bridge-mcp 0.13.0 → 0.14.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/src/local/cli.mjs CHANGED
@@ -10,7 +10,7 @@ import { runStdioServer } from "./stdio.mjs";
10
10
  import { assertCanonicalFullPolicy, DEFAULT_POLICY_PROFILE, DEFAULT_POLICY_REVISION, POLICY_PROFILES, normalizePolicy, policyProfile, toolsForPolicy } from "./tools.mjs";
11
11
  import { classifyOperationalError, createLogger, normalizeLogLevel, sanitizeLogText } from "./log.mjs";
12
12
  import { activeManagedJobs, inspectResourceFile, loadManagedJobPlan, ManagedJobManager, publicResourceRegistry, validateResourceName } from "./managed-jobs.mjs";
13
- import { runWrangler } from "./shell.mjs";
13
+ import { run, runWrangler } from "./shell.mjs";
14
14
  import { generateRegisteredSshKey } from "./resource-operations.mjs";
15
15
  import { runFullAccessTest } from "./full-access-test.mjs";
16
16
  import { readBoundedRegularFileSync } from "./secure-file.mjs";
@@ -1124,6 +1124,10 @@ async function doctorCommand(args) {
1124
1124
  const workspace = await chooseWorkspace(args, { promptOnFirstRun: false, save: false, allowPositional: true });
1125
1125
  const checks = [];
1126
1126
  checks.push({ name: "node", ok: isSupportedNodeVersion(), detail: process.version });
1127
+ const npmCommand = npmVersionCommand();
1128
+ const npm = await run(npmCommand.file, npmCommand.args, { capture: true, allowFailure: true, timeoutMs: 10_000 });
1129
+ const npmDetail = sanitizeLines(npm.stdout || npm.stderr);
1130
+ checks.push({ name: "npm", ok: npm.code === 0 && isSupportedNpmVersion(npmDetail), detail: npmDetail || "unavailable" });
1127
1131
  const wrangler = await runWrangler(["--version"], { capture: true, allowFailure: true });
1128
1132
  checks.push({ name: "wrangler", ok: wrangler.code === 0, detail: (wrangler.stdout || wrangler.stderr).trim() });
1129
1133
  const whoami = await runWrangler(["whoami"], { capture: true, allowFailure: true });
@@ -1557,10 +1561,22 @@ function validateWorkerName(value) {
1557
1561
  }
1558
1562
 
1559
1563
  export function isSupportedNodeVersion(version = process.versions.node) {
1560
- const major = Number(String(version || "").split(".")[0]);
1564
+ const major = Number(String(version || "").replace(/^v/, "").split(".")[0]);
1561
1565
  return Number.isInteger(major) && major >= 26;
1562
1566
  }
1563
1567
 
1568
+ export function isSupportedNpmVersion(version) {
1569
+ const major = Number(String(version || "").trim().replace(/^v/, "").split(".")[0]);
1570
+ return Number.isInteger(major) && major >= 12;
1571
+ }
1572
+
1573
+ export function npmVersionCommand(platform = process.platform, comspec = process.env.ComSpec) {
1574
+ if (platform === "win32") {
1575
+ return { file: comspec || "cmd.exe", args: ["/d", "/s", "/c", "npm --version"] };
1576
+ }
1577
+ return { file: "npm", args: ["--version"] };
1578
+ }
1579
+
1564
1580
  function assertNodeVersion() {
1565
1581
  if (!isSupportedNodeVersion()) throw new Error(`Node.js >=26 is required; current ${process.version}`);
1566
1582
  }
@@ -1568,8 +1584,10 @@ function assertNodeVersion() {
1568
1584
  function usage() {
1569
1585
  console.log(`machine-bridge-mcp
1570
1586
 
1587
+ Installation (run from a package-free temporary directory; Node.js >=26):
1588
+ npx --yes npm@12.0.1 install --global --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest && machine-mcp
1589
+
1571
1590
  Usage:
1572
- npm install -g --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest && machine-mcp
1573
1591
  npx machine-bridge-mcp@latest # no global install; autostart may rely on npm cache
1574
1592
  ./mbm # from source checkout
1575
1593
  .\\mbm.cmd # from source checkout on Windows cmd
@@ -46,6 +46,8 @@ const RUNTIME_TOOL_HANDLERS = Object.freeze({
46
46
  browser_status: (runtime, _args, context) => runtime.browserBridgeManager.status(context),
47
47
  pair_browser_extension: (runtime, args, context) => runtime.browserBridgeManager.pair(args, context),
48
48
  browser_list_tabs: (runtime, args, context) => runtime.browserBridgeManager.listTabs(args, context),
49
+ browser_manage_tabs: (runtime, args, context) => runtime.browserBridgeManager.manageTabs(args, context),
50
+ browser_wait: (runtime, args, context) => runtime.browserBridgeManager.wait(args, context),
49
51
  browser_get_source: (runtime, args, context) => runtime.browserBridgeManager.getSource(args, context),
50
52
  browser_inspect_page: (runtime, args, context) => runtime.browserBridgeManager.inspectPage(args, context),
51
53
  browser_action: (runtime, args, context) => runtime.browserBridgeManager.act(args, context),
@@ -385,6 +385,55 @@
385
385
  }
386
386
  }
387
387
  },
388
+ {
389
+ "name": "browser_manage_tabs",
390
+ "title": "Manage browser tabs",
391
+ "description": "Create, activate, or close tabs in the paired existing browser profile.",
392
+ "availability": "full",
393
+ "annotations": {
394
+ "readOnlyHint": false,
395
+ "destructiveHint": true,
396
+ "idempotentHint": false,
397
+ "openWorldHint": true
398
+ },
399
+ "inputSchema": {
400
+ "type": "object",
401
+ "additionalProperties": false,
402
+ "properties": {
403
+ "action": {
404
+ "type": "string",
405
+ "enum": [
406
+ "new",
407
+ "activate",
408
+ "close"
409
+ ]
410
+ },
411
+ "tab_id": {
412
+ "type": "integer",
413
+ "minimum": 1,
414
+ "description": "Required for activate and close."
415
+ },
416
+ "url": {
417
+ "type": "string",
418
+ "maxLength": 32768,
419
+ "description": "Optional absolute http, https, or file URL for a new tab."
420
+ },
421
+ "active": {
422
+ "type": "boolean",
423
+ "default": true
424
+ },
425
+ "timeout_seconds": {
426
+ "type": "integer",
427
+ "minimum": 1,
428
+ "maximum": 120,
429
+ "default": 30
430
+ }
431
+ },
432
+ "required": [
433
+ "action"
434
+ ]
435
+ }
436
+ },
388
437
  {
389
438
  "name": "browser_get_source",
390
439
  "title": "Read browser page source",
@@ -473,10 +522,115 @@
473
522
  }
474
523
  }
475
524
  },
525
+ {
526
+ "name": "browser_wait",
527
+ "title": "Wait for browser state",
528
+ "description": "Wait until all supplied URL, load, text, and element-state conditions are satisfied in an existing browser tab.",
529
+ "availability": "full",
530
+ "annotations": {
531
+ "readOnlyHint": true,
532
+ "destructiveHint": false,
533
+ "idempotentHint": true,
534
+ "openWorldHint": true
535
+ },
536
+ "inputSchema": {
537
+ "type": "object",
538
+ "additionalProperties": false,
539
+ "properties": {
540
+ "tab_id": {
541
+ "type": "integer",
542
+ "minimum": 1
543
+ },
544
+ "frame_id": {
545
+ "type": "integer",
546
+ "minimum": 0
547
+ },
548
+ "selector": {
549
+ "type": "object",
550
+ "properties": {
551
+ "ref": {
552
+ "type": "string",
553
+ "maxLength": 100,
554
+ "description": "Stable element reference returned by browser_inspect_page for the same document and frame."
555
+ },
556
+ "css": {
557
+ "type": "string",
558
+ "maxLength": 2000
559
+ },
560
+ "id": {
561
+ "type": "string",
562
+ "maxLength": 2000
563
+ },
564
+ "name": {
565
+ "type": "string",
566
+ "maxLength": 2000
567
+ },
568
+ "label": {
569
+ "type": "string",
570
+ "maxLength": 2000
571
+ },
572
+ "text": {
573
+ "type": "string",
574
+ "maxLength": 2000
575
+ },
576
+ "role": {
577
+ "type": "string",
578
+ "maxLength": 2000
579
+ },
580
+ "placeholder": {
581
+ "type": "string",
582
+ "maxLength": 2000
583
+ },
584
+ "index": {
585
+ "type": "integer",
586
+ "minimum": 0,
587
+ "maximum": 10000
588
+ }
589
+ },
590
+ "additionalProperties": false
591
+ },
592
+ "state": {
593
+ "type": "string",
594
+ "enum": [
595
+ "attached",
596
+ "detached",
597
+ "visible",
598
+ "hidden",
599
+ "enabled",
600
+ "editable",
601
+ "checked",
602
+ "unchecked"
603
+ ],
604
+ "description": "Element state to wait for. Defaults to visible only when selector is supplied."
605
+ },
606
+ "text": {
607
+ "type": "string",
608
+ "maxLength": 4000
609
+ },
610
+ "url_contains": {
611
+ "type": "string",
612
+ "maxLength": 32768
613
+ },
614
+ "load_state": {
615
+ "type": "string",
616
+ "enum": [
617
+ "domcontentloaded",
618
+ "complete"
619
+ ]
620
+ },
621
+ "timeout_seconds": {
622
+ "type": "integer",
623
+ "minimum": 1,
624
+ "maximum": 120,
625
+ "default": 30
626
+ }
627
+ }
628
+ }
629
+ },
476
630
  {
477
631
  "name": "browser_action",
478
632
  "title": "Operate browser page",
479
- "description": "Perform one structured navigation or DOM action in the user's existing browser tab without accepting arbitrary JavaScript.",
633
+ "description": "Perform one structured navigation or page action in the user's existing browser tab, with automatic actionability waits and optional fixed DevTools input, without accepting arbitrary JavaScript.",
480
634
  "availability": "full",
481
635
  "annotations": {
482
636
  "readOnlyHint": false,
@@ -506,12 +660,21 @@
506
660
  "submit",
507
661
  "reload",
508
662
  "back",
509
- "forward"
663
+ "forward",
664
+ "double_click",
665
+ "hover",
666
+ "type_text",
667
+ "scroll_into_view"
510
668
  ]
511
669
  },
512
670
  "selector": {
513
671
  "type": "object",
514
672
  "properties": {
673
+ "ref": {
674
+ "type": "string",
675
+ "maxLength": 100,
676
+ "description": "Stable element reference returned by browser_inspect_page for the same document and frame."
677
+ },
515
678
  "css": {
516
679
  "type": "string",
517
680
  "maxLength": 2000
@@ -582,6 +745,22 @@
582
745
  "frame_id": {
583
746
  "type": "integer",
584
747
  "minimum": 0
748
+ },
749
+ "input_mode": {
750
+ "type": "string",
751
+ "enum": [
752
+ "auto",
753
+ "trusted",
754
+ "dom"
755
+ ],
756
+ "default": "auto",
757
+ "description": "Use fixed DevTools Input commands when possible, require them, or use DOM events only."
758
+ },
759
+ "element_timeout_seconds": {
760
+ "type": "integer",
761
+ "minimum": 1,
762
+ "maximum": 60,
763
+ "default": 10
585
764
  }
586
765
  },
587
766
  "required": [
@@ -618,6 +797,11 @@
618
797
  "selector": {
619
798
  "type": "object",
620
799
  "properties": {
800
+ "ref": {
801
+ "type": "string",
802
+ "maxLength": 100,
803
+ "description": "Stable element reference returned by browser_inspect_page for the same document and frame."
804
+ },
621
805
  "css": {
622
806
  "type": "string",
623
807
  "maxLength": 2000
@@ -691,6 +875,11 @@
691
875
  "submit_selector": {
692
876
  "type": "object",
693
877
  "properties": {
878
+ "ref": {
879
+ "type": "string",
880
+ "maxLength": 100,
881
+ "description": "Stable element reference returned by browser_inspect_page for the same document and frame."
882
+ },
694
883
  "css": {
695
884
  "type": "string",
696
885
  "maxLength": 2000
@@ -745,6 +934,12 @@
745
934
  "frame_id": {
746
935
  "type": "integer",
747
936
  "minimum": 0
937
+ },
938
+ "element_timeout_seconds": {
939
+ "type": "integer",
940
+ "minimum": 1,
941
+ "maximum": 60,
942
+ "default": 10
748
943
  }
749
944
  },
750
945
  "required": [
@@ -815,6 +1010,11 @@
815
1010
  "selector": {
816
1011
  "type": "object",
817
1012
  "properties": {
1013
+ "ref": {
1014
+ "type": "string",
1015
+ "maxLength": 100,
1016
+ "description": "Stable element reference returned by browser_inspect_page for the same document and frame."
1017
+ },
818
1018
  "css": {
819
1019
  "type": "string",
820
1020
  "maxLength": 2000
@@ -885,6 +1085,12 @@
885
1085
  "frame_id": {
886
1086
  "type": "integer",
887
1087
  "minimum": 0
1088
+ },
1089
+ "element_timeout_seconds": {
1090
+ "type": "integer",
1091
+ "minimum": 1,
1092
+ "maximum": 60,
1093
+ "default": 10
888
1094
  }
889
1095
  },
890
1096
  "required": [
@@ -3,7 +3,7 @@ import toolCatalog from "../shared/tool-catalog.json";
3
3
  import serverMetadata from "../shared/server-metadata.json";
4
4
 
5
5
  const SERVER_NAME = String(serverMetadata.name);
6
- const SERVER_VERSION = "0.13.0";
6
+ const SERVER_VERSION = "0.14.0";
7
7
  const MCP_PROTOCOL_VERSION = String(serverMetadata.protocolVersion);
8
8
  const MCP_SUPPORTED_PROTOCOL_VERSIONS = serverMetadata.supportedProtocolVersions.map((value) => String(value));
9
9
  const JSONRPC_VERSION = "2.0";
@@ -817,6 +817,7 @@ export class BridgeRoom extends DurableObject<BridgeEnv> {
817
817
  client.last_used_at = now;
818
818
 
819
819
  const code = randomToken("mcp_code");
820
+ const redirectLocation = authorizationRedirectLocation(redirectUri, code, state);
820
821
  store.codes[code] = {
821
822
  client_id: clientId,
822
823
  redirect_uri: redirectUri,
@@ -829,9 +830,7 @@ export class BridgeRoom extends DurableObject<BridgeEnv> {
829
830
  pruneRecordByExpiry(store.codes, MAX_OAUTH_CODES);
830
831
  await this.ctx.storage.put("oauth", store);
831
832
 
832
- const params = new URLSearchParams({ code });
833
- if (state) params.set("state", state);
834
- return oauthRedirect(`${redirectUri}${redirectUri.includes("?") ? "&" : "?"}${params.toString()}`);
833
+ return oauthRedirect(redirectLocation);
835
834
  });
836
835
  }
837
836
 
@@ -996,11 +995,11 @@ function methodNotAllowed(allow: string): Response {
996
995
  });
997
996
  }
998
997
 
999
- function oauthRedirect(location: string): Response {
998
+ function oauthRedirect(location: URL): Response {
1000
999
  return new Response(null, {
1001
- status: 302,
1000
+ status: 303,
1002
1001
  headers: {
1003
- location,
1002
+ location: location.href,
1004
1003
  "cache-control": "no-store",
1005
1004
  "referrer-policy": "no-referrer",
1006
1005
  "x-content-type-options": "nosniff",
@@ -1008,6 +1007,13 @@ function oauthRedirect(location: string): Response {
1008
1007
  });
1009
1008
  }
1010
1009
 
1010
+ function authorizationRedirectLocation(redirectUri: string, code: string, state: string): URL {
1011
+ const location = new URL(redirectUri);
1012
+ location.searchParams.append("code", code);
1013
+ if (state) location.searchParams.append("state", state);
1014
+ return location;
1015
+ }
1016
+
1011
1017
  function isFreshDaemonCandidate(connectedAt: string): boolean {
1012
1018
  const timestamp = Date.parse(connectedAt);
1013
1019
  if (!Number.isFinite(timestamp)) return false;
@@ -1187,7 +1193,7 @@ function daemonToolTimeoutMs(name: string, args: Record<string, unknown>): numbe
1187
1193
  const configurable = new Set([
1188
1194
  "exec_command", "run_process", "run_local_command", "open_local_application",
1189
1195
  "inspect_local_application", "operate_local_application", "browser_list_tabs",
1190
- "browser_get_source", "browser_inspect_page", "browser_action", "browser_fill_form",
1196
+ "browser_manage_tabs", "browser_wait", "browser_get_source", "browser_inspect_page", "browser_action", "browser_fill_form",
1191
1197
  "browser_screenshot", "browser_upload_files",
1192
1198
  ]);
1193
1199
  if (!configurable.has(name)) return 60_000;