@browserbridge/bbx 1.8.0 → 1.10.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/PRIVACY.md +29 -3
- package/README.md +10 -3
- package/SECURITY.md +12 -0
- package/package.json +7 -1
- package/packages/agent-client/src/cli-args.js +70 -6
- package/packages/agent-client/src/cli-batch.js +127 -59
- package/packages/agent-client/src/cli-helpers.js +8 -0
- package/packages/agent-client/src/cli-output.js +8 -1
- package/packages/agent-client/src/cli-setup-commands.js +3 -1
- package/packages/agent-client/src/cli.js +254 -26
- package/packages/agent-client/src/client.js +156 -3
- package/packages/agent-client/src/command-registry.js +14 -4
- package/packages/agent-client/src/config.js +195 -0
- package/packages/agent-client/src/mcp-config.js +8 -8
- package/packages/agent-client/src/npm-self-update.js +305 -0
- package/packages/agent-client/src/remotes.js +4 -2
- package/packages/agent-client/src/runtime.js +99 -7
- package/packages/agent-client/src/types.ts +38 -0
- package/packages/mcp-server/src/guidance.js +37 -6
- package/packages/mcp-server/src/handlers-capture.js +29 -2
- package/packages/mcp-server/src/handlers-dom.js +54 -3
- package/packages/mcp-server/src/handlers-page.js +333 -60
- package/packages/mcp-server/src/handlers-utils.js +141 -151
- package/packages/mcp-server/src/handlers.js +17 -4
- package/packages/mcp-server/src/server.js +269 -51
- package/packages/mcp-server/src/toolset.js +48 -0
- package/packages/native-host/src/artifact-store.js +353 -0
- package/packages/native-host/src/config.js +9 -1
- package/packages/native-host/src/daemon-logger.js +3 -1
- package/packages/native-host/src/daemon.js +457 -27
- package/packages/native-host/src/dom-baseline-owners.js +210 -0
- package/packages/native-host/src/native-host.js +38 -29
- package/packages/protocol/src/budget-presets.js +182 -0
- package/packages/protocol/src/capability-values.js +2 -0
- package/packages/protocol/src/defaults.js +28 -0
- package/packages/protocol/src/errors.js +48 -5
- package/packages/protocol/src/har.js +34 -0
- package/packages/protocol/src/incidental-sanitizer.js +197 -0
- package/packages/protocol/src/index.js +4 -0
- package/packages/protocol/src/json-lines.js +3 -2
- package/packages/protocol/src/protocol.js +316 -6
- package/packages/protocol/src/recovery-telemetry.js +302 -0
- package/packages/protocol/src/registry.js +226 -6
- package/packages/protocol/src/summary.js +116 -25
- package/packages/protocol/src/types.ts +490 -4
- package/skills/browser-bridge/SKILL.md +26 -8
- package/skills/browser-bridge/references/protocol.md +125 -15
- package/skills/browser-bridge/references/token-efficiency.md +17 -7
|
@@ -11,14 +11,17 @@ import * as z from 'zod/v4';
|
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
handleAccessTool,
|
|
14
|
+
handleArtifactTool,
|
|
14
15
|
handleCaptureTool,
|
|
15
16
|
handleBatchTool,
|
|
16
17
|
handleDomTool,
|
|
17
18
|
handleHealthTool,
|
|
18
19
|
handleInputTool,
|
|
20
|
+
handleInterceptTool,
|
|
19
21
|
handleLogTool,
|
|
20
22
|
handleNavigationTool,
|
|
21
23
|
handlePageTool,
|
|
24
|
+
handleSensitiveReadTool,
|
|
22
25
|
handlePatchTool,
|
|
23
26
|
handleRawCallTool,
|
|
24
27
|
handleSetupTool,
|
|
@@ -27,12 +30,15 @@ import {
|
|
|
27
30
|
handleStylesLayoutTool,
|
|
28
31
|
handleTabsTool,
|
|
29
32
|
handleInvestigateTool,
|
|
33
|
+
createToolResult,
|
|
30
34
|
MAX_BATCH_CALLS,
|
|
31
35
|
} from './handlers.js';
|
|
32
36
|
import {
|
|
37
|
+
ARTIFACT_CHUNK_BYTES,
|
|
33
38
|
BUDGET_PRESETS,
|
|
34
39
|
DEFAULT_CONSOLE_LIMIT,
|
|
35
40
|
DEFAULT_EVAL_TIMEOUT_MS,
|
|
41
|
+
DEFAULT_HAR_LIMIT,
|
|
36
42
|
DEFAULT_LOG_TAIL_LIMIT,
|
|
37
43
|
DEFAULT_MAX_DEPTH,
|
|
38
44
|
DEFAULT_MAX_HTML_LENGTH,
|
|
@@ -41,10 +47,12 @@ import {
|
|
|
41
47
|
DEFAULT_TEXT_BUDGET,
|
|
42
48
|
DEFAULT_NAV_TIMEOUT_MS,
|
|
43
49
|
DEFAULT_WAIT_TIMEOUT_MS,
|
|
50
|
+
MAX_HAR_ENTRIES,
|
|
44
51
|
getMethodsByMaxComplexity,
|
|
45
52
|
} from '../../protocol/src/index.js';
|
|
46
53
|
import { applyWindowsTcpTransportDefaults } from '../../native-host/src/config.js';
|
|
47
|
-
import {
|
|
54
|
+
import { getMcpServerInstructions } from './guidance.js';
|
|
55
|
+
import { isInitiallyEnabledTool, LOADABLE_TOOLSET_TOOLS } from './toolset.js';
|
|
48
56
|
|
|
49
57
|
export const BUDGET_PRESET_DESCRIPTION = `Budget preset: "quick", "normal", or "deep" (defaults: query ${BUDGET_PRESETS.normal.maxNodes} nodes / depth ${BUDGET_PRESETS.normal.maxDepth} / text ${BUDGET_PRESETS.normal.textBudget}). Numeric fields override the preset when both are provided.`;
|
|
50
58
|
export const TAB_ID_DESCRIPTION =
|
|
@@ -78,24 +86,52 @@ const INVESTIGATE_SUBAGENT_BRIDGE_METHODS = Object.freeze(
|
|
|
78
86
|
)
|
|
79
87
|
);
|
|
80
88
|
|
|
81
|
-
const
|
|
82
|
-
recommended: true,
|
|
83
|
-
costTier: 'low',
|
|
84
|
-
preferredAgentProfile: {
|
|
85
|
-
modelClass: 'small',
|
|
86
|
-
reasoningEffort: 'low',
|
|
87
|
-
},
|
|
88
|
-
preferredTools: ['browser_dom', 'browser_page', 'browser_styles_layout', 'browser_batch'],
|
|
89
|
-
escalationTools: ['browser_capture'],
|
|
90
|
-
preferredBridgeMethods: INVESTIGATE_SUBAGENT_BRIDGE_METHODS,
|
|
91
|
-
escalationTriggers: [
|
|
92
|
-
'Structured DOM or page reads are insufficient.',
|
|
93
|
-
'Visual confirmation is required.',
|
|
94
|
-
'Debugger-backed evidence is required.',
|
|
95
|
-
],
|
|
96
|
-
});
|
|
89
|
+
const INVESTIGATE_PREFERRED_TOOLS = Object.freeze(['browser_call', 'browser_batch']);
|
|
97
90
|
|
|
98
91
|
/**
|
|
92
|
+
* Build the subagent delegation hint for browser_investigate using tools that
|
|
93
|
+
* are available before any on-demand expansion.
|
|
94
|
+
*/
|
|
95
|
+
function createInvestigateDelegationHint() {
|
|
96
|
+
return Object.freeze({
|
|
97
|
+
recommended: true,
|
|
98
|
+
costTier: 'low',
|
|
99
|
+
preferredAgentProfile: {
|
|
100
|
+
modelClass: 'small',
|
|
101
|
+
reasoningEffort: 'low',
|
|
102
|
+
},
|
|
103
|
+
preferredTools: INVESTIGATE_PREFERRED_TOOLS,
|
|
104
|
+
escalationTools: Object.freeze(['browser_call']),
|
|
105
|
+
preferredBridgeMethods: INVESTIGATE_SUBAGENT_BRIDGE_METHODS,
|
|
106
|
+
escalationTriggers: [
|
|
107
|
+
'Structured DOM or page reads are insufficient.',
|
|
108
|
+
'Visual confirmation is required.',
|
|
109
|
+
'Debugger-backed evidence is required.',
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Build the browser_investigate description for the progressive tool surface.
|
|
116
|
+
*
|
|
117
|
+
* @returns {string}
|
|
118
|
+
*/
|
|
119
|
+
function getInvestigateToolDescription() {
|
|
120
|
+
return (
|
|
121
|
+
'Investigate a page to answer a question or verify a condition. ' +
|
|
122
|
+
'Pass a natural-language objective and an optional scope (quick/normal/deep). ' +
|
|
123
|
+
'DELEGATION HINT: Prefer delegating this to a smaller, low-cost subagent ' +
|
|
124
|
+
'that starts with structured reads via browser_call and browser_batch. ' +
|
|
125
|
+
'Escalate through browser_call only ' +
|
|
126
|
+
'when structured reads are insufficient. ' +
|
|
127
|
+
'If subagent delegation is not available, a deterministic heuristic fallback ' +
|
|
128
|
+
'runs a scripted inspection sequence and returns a best-effort summary.'
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create the MCP server with a compact initial surface and on-demand typed tools.
|
|
134
|
+
*
|
|
99
135
|
* @returns {McpServer}
|
|
100
136
|
*/
|
|
101
137
|
export function createBridgeMcpServer() {
|
|
@@ -105,11 +141,33 @@ export function createBridgeMcpServer() {
|
|
|
105
141
|
version: MCP_SERVER_VERSION,
|
|
106
142
|
},
|
|
107
143
|
{
|
|
108
|
-
instructions:
|
|
144
|
+
instructions: getMcpServerInstructions(),
|
|
109
145
|
}
|
|
110
146
|
);
|
|
111
147
|
|
|
112
|
-
server.registerTool
|
|
148
|
+
/** @type {Map<string, ReturnType<typeof server.registerTool>>} */
|
|
149
|
+
const registrations = new Map();
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Register a tool, then hide it when it is not part of the initial surface.
|
|
153
|
+
*
|
|
154
|
+
* Registering first keeps every call site checked against the SDK's own
|
|
155
|
+
* generic signature instead of a hand-written passthrough type. Disabling
|
|
156
|
+
* happens before `connect()`, so an excluded tool never reaches `tools/list`
|
|
157
|
+
* but can be enabled later by browser_toolset.
|
|
158
|
+
*
|
|
159
|
+
* @type {typeof server.registerTool}
|
|
160
|
+
*/
|
|
161
|
+
const registerTool = (name, config, handler) => {
|
|
162
|
+
const registration = server.registerTool(name, config, handler);
|
|
163
|
+
registrations.set(name, registration);
|
|
164
|
+
if (!isInitiallyEnabledTool(name)) {
|
|
165
|
+
registration.disable();
|
|
166
|
+
}
|
|
167
|
+
return registration;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
registerTool(
|
|
113
171
|
'browser_status',
|
|
114
172
|
{
|
|
115
173
|
title: 'Browser Bridge Status',
|
|
@@ -125,7 +183,7 @@ export function createBridgeMcpServer() {
|
|
|
125
183
|
handleStatusTool
|
|
126
184
|
);
|
|
127
185
|
|
|
128
|
-
|
|
186
|
+
registerTool(
|
|
129
187
|
'browser_setup',
|
|
130
188
|
{
|
|
131
189
|
title: 'Browser Bridge Setup Status',
|
|
@@ -140,7 +198,7 @@ export function createBridgeMcpServer() {
|
|
|
140
198
|
handleSetupTool
|
|
141
199
|
);
|
|
142
200
|
|
|
143
|
-
|
|
201
|
+
registerTool(
|
|
144
202
|
'browser_logs',
|
|
145
203
|
{
|
|
146
204
|
title: 'Browser Bridge Logs',
|
|
@@ -162,7 +220,7 @@ export function createBridgeMcpServer() {
|
|
|
162
220
|
handleLogTool
|
|
163
221
|
);
|
|
164
222
|
|
|
165
|
-
|
|
223
|
+
registerTool(
|
|
166
224
|
'browser_health',
|
|
167
225
|
{
|
|
168
226
|
title: 'Browser Bridge Health',
|
|
@@ -178,7 +236,7 @@ export function createBridgeMcpServer() {
|
|
|
178
236
|
handleHealthTool
|
|
179
237
|
);
|
|
180
238
|
|
|
181
|
-
|
|
239
|
+
registerTool(
|
|
182
240
|
'browser_tabs',
|
|
183
241
|
{
|
|
184
242
|
title: 'Browser Tabs',
|
|
@@ -205,7 +263,7 @@ export function createBridgeMcpServer() {
|
|
|
205
263
|
handleTabsTool
|
|
206
264
|
);
|
|
207
265
|
|
|
208
|
-
|
|
266
|
+
registerTool(
|
|
209
267
|
'browser_dom',
|
|
210
268
|
{
|
|
211
269
|
title: 'Browser DOM',
|
|
@@ -215,6 +273,10 @@ export function createBridgeMcpServer() {
|
|
|
215
273
|
action: z
|
|
216
274
|
.enum([
|
|
217
275
|
'query',
|
|
276
|
+
'baseline_create',
|
|
277
|
+
'baseline_compare',
|
|
278
|
+
'baseline_describe',
|
|
279
|
+
'baseline_release',
|
|
218
280
|
'describe',
|
|
219
281
|
'text',
|
|
220
282
|
'attributes',
|
|
@@ -237,6 +299,14 @@ export function createBridgeMcpServer() {
|
|
|
237
299
|
.describe(
|
|
238
300
|
'CSS selector (used if no elementRef; accessibility_tree requires exactly one match)'
|
|
239
301
|
),
|
|
302
|
+
baselineId: z.string().optional().describe('Opaque semantic DOM baseline identifier'),
|
|
303
|
+
maxChanges: z
|
|
304
|
+
.number()
|
|
305
|
+
.int()
|
|
306
|
+
.positive()
|
|
307
|
+
.max(200)
|
|
308
|
+
.optional()
|
|
309
|
+
.describe('Maximum returned change examples; exact counts are always retained'),
|
|
240
310
|
elementRef: z
|
|
241
311
|
.string()
|
|
242
312
|
.optional()
|
|
@@ -322,7 +392,7 @@ export function createBridgeMcpServer() {
|
|
|
322
392
|
handleDomTool
|
|
323
393
|
);
|
|
324
394
|
|
|
325
|
-
|
|
395
|
+
registerTool(
|
|
326
396
|
'browser_styles_layout',
|
|
327
397
|
{
|
|
328
398
|
title: 'Browser Styles And Layout',
|
|
@@ -361,12 +431,31 @@ export function createBridgeMcpServer() {
|
|
|
361
431
|
handleStylesLayoutTool
|
|
362
432
|
);
|
|
363
433
|
|
|
364
|
-
|
|
434
|
+
registerTool(
|
|
435
|
+
'browser_sensitive_read',
|
|
436
|
+
{
|
|
437
|
+
title: 'Sensitive Browser Read',
|
|
438
|
+
description:
|
|
439
|
+
'High-risk exact-value read for one local or session storage key. This operation is logged as Sensitive access, is never batched or retried, and returns the whole value or fails atomically.',
|
|
440
|
+
inputSchema: {
|
|
441
|
+
source: z.enum(['local_storage', 'session_storage']).describe('Exact storage source'),
|
|
442
|
+
key: z
|
|
443
|
+
.string()
|
|
444
|
+
.max(2_048)
|
|
445
|
+
.describe('Exact storage key, including an empty key if intended'),
|
|
446
|
+
tabId: z.number().int().positive().optional().describe(TAB_ID_DESCRIPTION),
|
|
447
|
+
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
handleSensitiveReadTool
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
registerTool(
|
|
365
454
|
'browser_page',
|
|
366
455
|
{
|
|
367
456
|
title: 'Browser Page State',
|
|
368
457
|
description:
|
|
369
|
-
'Read page-level data, wait for load/URL conditions, or explicitly inspect/handle JavaScript dialogs. For element-level reads, use browser_dom. evaluate, performance, handle_dialog, and source=cdp network capture are debugger-backed.',
|
|
458
|
+
'Read page-level data, export captured network evidence as HAR 1.2, wait for load/URL conditions, or explicitly inspect/handle JavaScript dialogs. The performance action returns a raw CDP Performance.getMetrics point sample: Chrome defines names and units, which vary, while BBX sets no navigation observation window and does not measure LCP, CLS, or INP. For element-level reads, use browser_dom. evaluate, performance, handle_dialog, and source=cdp network capture are debugger-backed.',
|
|
370
459
|
inputSchema: {
|
|
371
460
|
action: z
|
|
372
461
|
.enum([
|
|
@@ -379,9 +468,12 @@ export function createBridgeMcpServer() {
|
|
|
379
468
|
'text',
|
|
380
469
|
'extract_content',
|
|
381
470
|
'network',
|
|
471
|
+
'har',
|
|
382
472
|
'performance',
|
|
383
473
|
])
|
|
384
|
-
.describe(
|
|
474
|
+
.describe(
|
|
475
|
+
'Page operation to perform; performance reads raw Chrome/CDP counters, not LCP, CLS, or INP'
|
|
476
|
+
),
|
|
385
477
|
tabId: z.number().int().positive().optional().describe(TAB_ID_DESCRIPTION),
|
|
386
478
|
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
387
479
|
budgetPreset: z
|
|
@@ -434,7 +526,9 @@ export function createBridgeMcpServer() {
|
|
|
434
526
|
.int()
|
|
435
527
|
.positive()
|
|
436
528
|
.optional()
|
|
437
|
-
.describe(
|
|
529
|
+
.describe(
|
|
530
|
+
`Maximum entries to return (console default: ${DEFAULT_CONSOLE_LIMIT}; HAR default: ${DEFAULT_HAR_LIMIT}, max: ${MAX_HAR_ENTRIES})`
|
|
531
|
+
),
|
|
438
532
|
type: z
|
|
439
533
|
.enum(['local', 'session'])
|
|
440
534
|
.optional()
|
|
@@ -468,7 +562,11 @@ export function createBridgeMcpServer() {
|
|
|
468
562
|
.positive()
|
|
469
563
|
.optional()
|
|
470
564
|
.describe('Maximum semantic extraction settlement wait (default: 2000)'),
|
|
471
|
-
urlPattern: z
|
|
565
|
+
urlPattern: z
|
|
566
|
+
.string()
|
|
567
|
+
.max(2_048)
|
|
568
|
+
.optional()
|
|
569
|
+
.describe('Bounded URL pattern used to filter network entries or HAR export evidence'),
|
|
472
570
|
source: z
|
|
473
571
|
.enum(['fetch-xhr', 'cdp'])
|
|
474
572
|
.optional()
|
|
@@ -477,6 +575,10 @@ export function createBridgeMcpServer() {
|
|
|
477
575
|
.enum(['read', 'start', 'clear', 'stop'])
|
|
478
576
|
.optional()
|
|
479
577
|
.describe('Explicit CDP capture lifecycle action (default: read)'),
|
|
578
|
+
delivery: z
|
|
579
|
+
.enum(['auto', 'inline', 'artifact'])
|
|
580
|
+
.optional()
|
|
581
|
+
.describe('HAR delivery mode: auto chooses inline or a daemon-owned artifact'),
|
|
480
582
|
waitForLoad: z
|
|
481
583
|
.boolean()
|
|
482
584
|
.optional()
|
|
@@ -491,7 +593,7 @@ export function createBridgeMcpServer() {
|
|
|
491
593
|
handlePageTool
|
|
492
594
|
);
|
|
493
595
|
|
|
494
|
-
|
|
596
|
+
registerTool(
|
|
495
597
|
'browser_navigation',
|
|
496
598
|
{
|
|
497
599
|
title: 'Browser Navigation',
|
|
@@ -535,7 +637,7 @@ export function createBridgeMcpServer() {
|
|
|
535
637
|
handleNavigationTool
|
|
536
638
|
);
|
|
537
639
|
|
|
538
|
-
|
|
640
|
+
registerTool(
|
|
539
641
|
'browser_input',
|
|
540
642
|
{
|
|
541
643
|
title: 'Browser Input',
|
|
@@ -626,7 +728,7 @@ export function createBridgeMcpServer() {
|
|
|
626
728
|
.int()
|
|
627
729
|
.nonnegative()
|
|
628
730
|
.optional()
|
|
629
|
-
.describe('
|
|
731
|
+
.describe('Delay after hover dispatch in ms; does not end hover (default: 0)'),
|
|
630
732
|
sourceElementRef: z.string().optional().describe('Drag source element (for drag action)'),
|
|
631
733
|
sourceSelector: z
|
|
632
734
|
.string()
|
|
@@ -647,7 +749,7 @@ export function createBridgeMcpServer() {
|
|
|
647
749
|
handleInputTool
|
|
648
750
|
);
|
|
649
751
|
|
|
650
|
-
|
|
752
|
+
registerTool(
|
|
651
753
|
'browser_patch',
|
|
652
754
|
{
|
|
653
755
|
title: 'Browser Patch',
|
|
@@ -674,7 +776,14 @@ export function createBridgeMcpServer() {
|
|
|
674
776
|
.describe('CSS property: value pairs (for apply_styles)'),
|
|
675
777
|
important: z.boolean().optional().describe('Add !important flag (default: false)'),
|
|
676
778
|
operation: z
|
|
677
|
-
.enum([
|
|
779
|
+
.enum([
|
|
780
|
+
'setAttribute',
|
|
781
|
+
'removeAttribute',
|
|
782
|
+
'addClass',
|
|
783
|
+
'removeClass',
|
|
784
|
+
'toggleClass',
|
|
785
|
+
'setTextContent',
|
|
786
|
+
])
|
|
678
787
|
.optional()
|
|
679
788
|
.describe('DOM mutation type'),
|
|
680
789
|
value: z.unknown().optional().describe('Value for the DOM operation'),
|
|
@@ -691,7 +800,7 @@ export function createBridgeMcpServer() {
|
|
|
691
800
|
handlePatchTool
|
|
692
801
|
);
|
|
693
802
|
|
|
694
|
-
|
|
803
|
+
registerTool(
|
|
695
804
|
'browser_capture',
|
|
696
805
|
{
|
|
697
806
|
title: 'Browser Capture',
|
|
@@ -728,6 +837,11 @@ export function createBridgeMcpServer() {
|
|
|
728
837
|
.positive()
|
|
729
838
|
.optional()
|
|
730
839
|
.describe('CDP node id for cdp_box_model/cdp_computed_styles'),
|
|
840
|
+
computedStyles: z
|
|
841
|
+
.array(z.string().min(1).max(128))
|
|
842
|
+
.max(100)
|
|
843
|
+
.optional()
|
|
844
|
+
.describe('CSS property names to include for cdp_dom_snapshot'),
|
|
731
845
|
rect: z
|
|
732
846
|
.object({
|
|
733
847
|
x: z.number().nonnegative().describe('Region left edge (viewport pixels)'),
|
|
@@ -748,12 +862,101 @@ export function createBridgeMcpServer() {
|
|
|
748
862
|
.max(100)
|
|
749
863
|
.optional()
|
|
750
864
|
.describe('JPEG/WebP quality from 0 to 100; ignored for PNG'),
|
|
865
|
+
delivery: z
|
|
866
|
+
.enum(['auto', 'inline', 'artifact'])
|
|
867
|
+
.optional()
|
|
868
|
+
.describe('Delivery mode: auto chooses inline or a daemon-owned artifact'),
|
|
869
|
+
scale: z
|
|
870
|
+
.number()
|
|
871
|
+
.min(0.1)
|
|
872
|
+
.max(4)
|
|
873
|
+
.optional()
|
|
874
|
+
.describe('Screenshot capture scale (0.1 to 4)'),
|
|
751
875
|
},
|
|
752
876
|
},
|
|
753
877
|
handleCaptureTool
|
|
754
878
|
);
|
|
755
879
|
|
|
756
|
-
|
|
880
|
+
registerTool(
|
|
881
|
+
'browser_artifact',
|
|
882
|
+
{
|
|
883
|
+
title: 'Browser Artifact',
|
|
884
|
+
description:
|
|
885
|
+
'Read (chunked base64) or delete a daemon-owned artifact produced by artifact delivery (screenshots, HAR exports). Artifacts are scoped to this MCP session and expire quickly. Page reads with offset until nextOffset is null, then verify the reassembled bytes against sha256.',
|
|
886
|
+
inputSchema: {
|
|
887
|
+
action: z.enum(['read', 'delete']).describe('Artifact operation to perform'),
|
|
888
|
+
artifactId: z
|
|
889
|
+
.string()
|
|
890
|
+
.min(1)
|
|
891
|
+
.describe('Artifact identifier returned by a capture or HAR export'),
|
|
892
|
+
offset: z
|
|
893
|
+
.number()
|
|
894
|
+
.int()
|
|
895
|
+
.nonnegative()
|
|
896
|
+
.optional()
|
|
897
|
+
.describe('Byte offset to read from (default: 0)'),
|
|
898
|
+
limit: z
|
|
899
|
+
.number()
|
|
900
|
+
.int()
|
|
901
|
+
.positive()
|
|
902
|
+
.max(ARTIFACT_CHUNK_BYTES)
|
|
903
|
+
.optional()
|
|
904
|
+
.describe(`Maximum bytes to read in this chunk (default/max: ${ARTIFACT_CHUNK_BYTES})`),
|
|
905
|
+
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
906
|
+
budgetPreset: z
|
|
907
|
+
.enum(['quick', 'normal', 'deep'])
|
|
908
|
+
.optional()
|
|
909
|
+
.describe(BUDGET_PRESET_DESCRIPTION),
|
|
910
|
+
},
|
|
911
|
+
},
|
|
912
|
+
handleArtifactTool
|
|
913
|
+
);
|
|
914
|
+
|
|
915
|
+
registerTool(
|
|
916
|
+
'browser_intercept',
|
|
917
|
+
{
|
|
918
|
+
title: 'Browser Network Intercept',
|
|
919
|
+
description:
|
|
920
|
+
'Manage CDP Fetch request interception rules: add (block, fulfill, or continue matching requests), list, remove, or clear. Debugger-backed; rules persist until removed or cleared.',
|
|
921
|
+
inputSchema: {
|
|
922
|
+
action: z
|
|
923
|
+
.enum(['add', 'list', 'remove', 'clear'])
|
|
924
|
+
.describe('Intercept operation to perform'),
|
|
925
|
+
tabId: z.number().int().positive().optional().describe(TAB_ID_DESCRIPTION),
|
|
926
|
+
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
927
|
+
budgetPreset: z
|
|
928
|
+
.enum(['quick', 'normal', 'deep'])
|
|
929
|
+
.optional()
|
|
930
|
+
.describe(BUDGET_PRESET_DESCRIPTION),
|
|
931
|
+
urlPattern: z
|
|
932
|
+
.string()
|
|
933
|
+
.min(1)
|
|
934
|
+
.max(2_048)
|
|
935
|
+
.optional()
|
|
936
|
+
.describe('URL substring to match (required for add)'),
|
|
937
|
+
ruleAction: z
|
|
938
|
+
.enum(['block', 'fulfill', 'continue'])
|
|
939
|
+
.optional()
|
|
940
|
+
.describe('How to handle matched requests (default: continue)'),
|
|
941
|
+
statusCode: z
|
|
942
|
+
.number()
|
|
943
|
+
.int()
|
|
944
|
+
.min(100)
|
|
945
|
+
.max(599)
|
|
946
|
+
.optional()
|
|
947
|
+
.describe('HTTP status for fulfill responses'),
|
|
948
|
+
body: z.string().optional().describe('Response body for fulfill'),
|
|
949
|
+
headers: z
|
|
950
|
+
.record(z.string(), z.string())
|
|
951
|
+
.optional()
|
|
952
|
+
.describe('Response headers for fulfill'),
|
|
953
|
+
ruleId: z.string().min(1).optional().describe('Rule ID (required for remove)'),
|
|
954
|
+
},
|
|
955
|
+
},
|
|
956
|
+
handleInterceptTool
|
|
957
|
+
);
|
|
958
|
+
|
|
959
|
+
registerTool(
|
|
757
960
|
'browser_batch',
|
|
758
961
|
{
|
|
759
962
|
title: 'Browser Bridge Batch',
|
|
@@ -790,7 +993,7 @@ export function createBridgeMcpServer() {
|
|
|
790
993
|
handleBatchTool
|
|
791
994
|
);
|
|
792
995
|
|
|
793
|
-
|
|
996
|
+
registerTool(
|
|
794
997
|
'browser_call',
|
|
795
998
|
{
|
|
796
999
|
title: 'Raw Browser Bridge Call',
|
|
@@ -813,7 +1016,7 @@ export function createBridgeMcpServer() {
|
|
|
813
1016
|
handleRawCallTool
|
|
814
1017
|
);
|
|
815
1018
|
|
|
816
|
-
|
|
1019
|
+
registerTool(
|
|
817
1020
|
'browser_skill',
|
|
818
1021
|
{
|
|
819
1022
|
title: 'Browser Bridge Runtime Context',
|
|
@@ -826,7 +1029,7 @@ export function createBridgeMcpServer() {
|
|
|
826
1029
|
handleSkillTool
|
|
827
1030
|
);
|
|
828
1031
|
|
|
829
|
-
|
|
1032
|
+
registerTool(
|
|
830
1033
|
'browser_access',
|
|
831
1034
|
{
|
|
832
1035
|
title: 'Request Browser Bridge Access',
|
|
@@ -839,19 +1042,11 @@ export function createBridgeMcpServer() {
|
|
|
839
1042
|
handleAccessTool
|
|
840
1043
|
);
|
|
841
1044
|
|
|
842
|
-
|
|
1045
|
+
registerTool(
|
|
843
1046
|
'browser_investigate',
|
|
844
1047
|
{
|
|
845
1048
|
title: 'Browser Investigate',
|
|
846
|
-
description:
|
|
847
|
-
'Investigate a page to answer a question or verify a condition. ' +
|
|
848
|
-
'Pass a natural-language objective and an optional scope (quick/normal/deep). ' +
|
|
849
|
-
'DELEGATION HINT: Prefer delegating this to a smaller, low-cost subagent ' +
|
|
850
|
-
'that starts with structured reads via browser_dom, browser_page, ' +
|
|
851
|
-
'browser_styles_layout, and browser_batch. Escalate to browser_capture only ' +
|
|
852
|
-
'when structured reads are insufficient. ' +
|
|
853
|
-
'If subagent delegation is not available, a deterministic heuristic fallback ' +
|
|
854
|
-
'runs a scripted inspection sequence and returns a best-effort summary.',
|
|
1049
|
+
description: getInvestigateToolDescription(),
|
|
855
1050
|
annotations: {
|
|
856
1051
|
readOnlyHint: true,
|
|
857
1052
|
destructiveHint: false,
|
|
@@ -859,7 +1054,7 @@ export function createBridgeMcpServer() {
|
|
|
859
1054
|
openWorldHint: true,
|
|
860
1055
|
},
|
|
861
1056
|
_meta: {
|
|
862
|
-
delegationHint:
|
|
1057
|
+
delegationHint: createInvestigateDelegationHint(),
|
|
863
1058
|
},
|
|
864
1059
|
inputSchema: {
|
|
865
1060
|
objective: z
|
|
@@ -884,6 +1079,29 @@ export function createBridgeMcpServer() {
|
|
|
884
1079
|
handleInvestigateTool
|
|
885
1080
|
);
|
|
886
1081
|
|
|
1082
|
+
registerTool(
|
|
1083
|
+
'browser_toolset',
|
|
1084
|
+
{
|
|
1085
|
+
title: 'Load Browser Bridge Tool',
|
|
1086
|
+
description:
|
|
1087
|
+
'Load one specialized typed Browser Bridge tool by exact name. The tool appears in tools/list automatically and remains enabled for this MCP session.',
|
|
1088
|
+
inputSchema: {
|
|
1089
|
+
tool: z
|
|
1090
|
+
.enum(LOADABLE_TOOLSET_TOOLS)
|
|
1091
|
+
.describe('Exact specialized Browser Bridge tool to load.'),
|
|
1092
|
+
},
|
|
1093
|
+
},
|
|
1094
|
+
({ tool }) => {
|
|
1095
|
+
const registration = registrations.get(tool);
|
|
1096
|
+
const newlyEnabled = Boolean(registration && !registration.enabled);
|
|
1097
|
+
if (newlyEnabled) registration?.enable();
|
|
1098
|
+
return createToolResult(newlyEnabled ? `Loaded ${tool}.` : `${tool} is already loaded.`, {
|
|
1099
|
+
tool,
|
|
1100
|
+
newlyEnabled,
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
);
|
|
1104
|
+
|
|
887
1105
|
return server;
|
|
888
1106
|
}
|
|
889
1107
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Browser Bridge starts with a compact fixed tool surface. Specialized typed
|
|
5
|
+
* tools remain registered but disabled until browser_toolset enables one by
|
|
6
|
+
* exact name. browser_call always keeps the full bridge protocol reachable.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** @type {readonly string[]} */
|
|
10
|
+
export const INITIAL_TOOLSET_TOOLS = Object.freeze([
|
|
11
|
+
'browser_access',
|
|
12
|
+
'browser_batch',
|
|
13
|
+
'browser_call',
|
|
14
|
+
'browser_health',
|
|
15
|
+
'browser_status',
|
|
16
|
+
'browser_toolset',
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
/** @typedef {'browser_dom' | 'browser_styles_layout' | 'browser_page' | 'browser_logs' | 'browser_input' | 'browser_navigation' | 'browser_tabs' | 'browser_capture' | 'browser_artifact' | 'browser_patch' | 'browser_intercept' | 'browser_investigate' | 'browser_sensitive_read' | 'browser_setup' | 'browser_skill'} LoadableToolName */
|
|
20
|
+
|
|
21
|
+
/** @type {readonly LoadableToolName[]} */
|
|
22
|
+
export const LOADABLE_TOOLSET_TOOLS = Object.freeze([
|
|
23
|
+
'browser_dom',
|
|
24
|
+
'browser_styles_layout',
|
|
25
|
+
'browser_page',
|
|
26
|
+
'browser_logs',
|
|
27
|
+
'browser_input',
|
|
28
|
+
'browser_navigation',
|
|
29
|
+
'browser_tabs',
|
|
30
|
+
'browser_capture',
|
|
31
|
+
'browser_artifact',
|
|
32
|
+
'browser_patch',
|
|
33
|
+
'browser_intercept',
|
|
34
|
+
'browser_investigate',
|
|
35
|
+
'browser_sensitive_read',
|
|
36
|
+
'browser_setup',
|
|
37
|
+
'browser_skill',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const INITIAL_TOOLSET = new Set(INITIAL_TOOLSET_TOOLS);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} toolName
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
46
|
+
export function isInitiallyEnabledTool(toolName) {
|
|
47
|
+
return INITIAL_TOOLSET.has(toolName);
|
|
48
|
+
}
|