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/CHANGELOG.md +19 -0
- package/README.md +38 -9
- package/SECURITY.md +3 -3
- package/browser-extension/devtools-input.js +136 -0
- package/browser-extension/manifest.json +3 -2
- package/browser-extension/page-automation.js +309 -29
- package/browser-extension/service-worker.js +108 -5
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/LOCAL_AUTOMATION.md +13 -9
- package/docs/OPERATIONS.md +12 -5
- package/docs/TESTING.md +4 -4
- package/package.json +8 -4
- package/src/local/agent-context.mjs +1 -1
- package/src/local/browser-bridge.mjs +186 -88
- package/src/local/browser-command.mjs +126 -0
- package/src/local/cli.mjs +21 -3
- package/src/local/runtime.mjs +2 -0
- package/src/shared/tool-catalog.json +208 -2
- package/src/worker/index.ts +14 -8
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
|
package/src/local/runtime.mjs
CHANGED
|
@@ -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
|
|
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": [
|
package/src/worker/index.ts
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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:
|
|
998
|
+
function oauthRedirect(location: URL): Response {
|
|
1000
999
|
return new Response(null, {
|
|
1001
|
-
status:
|
|
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;
|