@browserbridge/bbx 1.9.0 → 1.11.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/README.md +1 -1
- package/package.json +8 -10
- package/packages/agent-client/src/cli-args.js +35 -6
- package/packages/agent-client/src/cli-batch.js +10 -5
- package/packages/agent-client/src/cli-helpers.js +8 -0
- package/packages/agent-client/src/cli-proxy-remote.js +7 -4
- package/packages/agent-client/src/cli-setup-commands.js +3 -1
- package/packages/agent-client/src/cli.js +35 -15
- package/packages/agent-client/src/command-registry.js +7 -3
- package/packages/agent-client/src/mcp-config.js +8 -8
- package/packages/agent-client/src/npm-self-update.js +9 -2
- package/packages/agent-client/src/runtime.js +14 -5
- package/packages/agent-client/src/types.ts +2 -0
- package/packages/mcp-server/src/bin.js +1 -1
- package/packages/mcp-server/src/guidance.js +43 -6
- package/packages/mcp-server/src/handlers-capture.js +1 -1
- package/packages/mcp-server/src/handlers-page.js +162 -9
- package/packages/mcp-server/src/handlers-utils.js +145 -169
- package/packages/mcp-server/src/handlers.js +17 -5
- package/packages/mcp-server/src/server.js +255 -91
- package/packages/mcp-server/src/toolset.js +73 -0
- package/packages/native-host/src/daemon.js +24 -107
- package/packages/native-host/src/dom-baseline-owners.js +210 -0
- package/packages/native-host/src/native-host.js +14 -28
- package/packages/protocol/src/budget-presets.js +182 -0
- package/packages/protocol/src/har.js +34 -0
- package/packages/protocol/src/index.js +2 -0
- package/packages/protocol/src/json-lines.js +3 -2
- package/packages/protocol/src/protocol.js +10 -0
- package/packages/protocol/src/registry.js +83 -0
- package/packages/protocol/src/types.ts +3 -0
- package/skills/browser-bridge/SKILL.md +4 -0
- package/skills/browser-bridge/references/protocol.md +11 -5
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
|
|
5
|
-
import { McpServer } from '@modelcontextprotocol/
|
|
6
|
-
import {
|
|
7
|
-
// zod is required at runtime by @modelcontextprotocol/
|
|
5
|
+
import { McpServer } from '@modelcontextprotocol/server';
|
|
6
|
+
import { serveStdio } from '@modelcontextprotocol/server/stdio';
|
|
7
|
+
// zod is required at runtime by @modelcontextprotocol/server for tool parameter schema
|
|
8
8
|
// declarations (z.object, z.string, etc.). It is not used for request/response
|
|
9
9
|
// validation - that is handled by the protocol package.
|
|
10
10
|
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,
|
|
@@ -28,9 +30,12 @@ import {
|
|
|
28
30
|
handleStylesLayoutTool,
|
|
29
31
|
handleTabsTool,
|
|
30
32
|
handleInvestigateTool,
|
|
33
|
+
createToolResult,
|
|
31
34
|
MAX_BATCH_CALLS,
|
|
35
|
+
runWithMcpRequestEra,
|
|
32
36
|
} from './handlers.js';
|
|
33
37
|
import {
|
|
38
|
+
ARTIFACT_CHUNK_BYTES,
|
|
34
39
|
BUDGET_PRESETS,
|
|
35
40
|
DEFAULT_CONSOLE_LIMIT,
|
|
36
41
|
DEFAULT_EVAL_TIMEOUT_MS,
|
|
@@ -47,7 +52,8 @@ import {
|
|
|
47
52
|
getMethodsByMaxComplexity,
|
|
48
53
|
} from '../../protocol/src/index.js';
|
|
49
54
|
import { applyWindowsTcpTransportDefaults } from '../../native-host/src/config.js';
|
|
50
|
-
import {
|
|
55
|
+
import { getMcpServerInstructions } from './guidance.js';
|
|
56
|
+
import { isToolEnabledForEra, LOADABLE_TOOLSET_TOOLS } from './toolset.js';
|
|
51
57
|
|
|
52
58
|
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.`;
|
|
53
59
|
export const TAB_ID_DESCRIPTION =
|
|
@@ -81,74 +87,138 @@ const INVESTIGATE_SUBAGENT_BRIDGE_METHODS = Object.freeze(
|
|
|
81
87
|
)
|
|
82
88
|
);
|
|
83
89
|
|
|
84
|
-
const
|
|
85
|
-
recommended: true,
|
|
86
|
-
costTier: 'low',
|
|
87
|
-
preferredAgentProfile: {
|
|
88
|
-
modelClass: 'small',
|
|
89
|
-
reasoningEffort: 'low',
|
|
90
|
-
},
|
|
91
|
-
preferredTools: ['browser_dom', 'browser_page', 'browser_styles_layout', 'browser_batch'],
|
|
92
|
-
escalationTools: ['browser_capture'],
|
|
93
|
-
preferredBridgeMethods: INVESTIGATE_SUBAGENT_BRIDGE_METHODS,
|
|
94
|
-
escalationTriggers: [
|
|
95
|
-
'Structured DOM or page reads are insufficient.',
|
|
96
|
-
'Visual confirmation is required.',
|
|
97
|
-
'Debugger-backed evidence is required.',
|
|
98
|
-
],
|
|
99
|
-
});
|
|
90
|
+
const INVESTIGATE_PREFERRED_TOOLS = Object.freeze(['browser_call', 'browser_batch']);
|
|
100
91
|
|
|
101
92
|
/**
|
|
93
|
+
* Build the subagent delegation hint for browser_investigate using tools that
|
|
94
|
+
* are available before any on-demand expansion.
|
|
95
|
+
*/
|
|
96
|
+
function createInvestigateDelegationHint() {
|
|
97
|
+
return Object.freeze({
|
|
98
|
+
recommended: true,
|
|
99
|
+
costTier: 'low',
|
|
100
|
+
preferredAgentProfile: {
|
|
101
|
+
modelClass: 'small',
|
|
102
|
+
reasoningEffort: 'low',
|
|
103
|
+
},
|
|
104
|
+
preferredTools: INVESTIGATE_PREFERRED_TOOLS,
|
|
105
|
+
escalationTools: Object.freeze(['browser_call']),
|
|
106
|
+
preferredBridgeMethods: INVESTIGATE_SUBAGENT_BRIDGE_METHODS,
|
|
107
|
+
escalationTriggers: [
|
|
108
|
+
'Structured DOM or page reads are insufficient.',
|
|
109
|
+
'Visual confirmation is required.',
|
|
110
|
+
'Debugger-backed evidence is required.',
|
|
111
|
+
],
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Build the browser_investigate description for the progressive tool surface.
|
|
117
|
+
*
|
|
118
|
+
* @returns {string}
|
|
119
|
+
*/
|
|
120
|
+
function getInvestigateToolDescription() {
|
|
121
|
+
return (
|
|
122
|
+
'Investigate a page to answer a question or verify a condition. ' +
|
|
123
|
+
'Pass a natural-language objective and an optional scope (quick/normal/deep). ' +
|
|
124
|
+
'DELEGATION HINT: Prefer delegating this to a smaller, low-cost subagent ' +
|
|
125
|
+
'that starts with structured reads via browser_call and browser_batch. ' +
|
|
126
|
+
'Escalate through browser_call only ' +
|
|
127
|
+
'when structured reads are insufficient. ' +
|
|
128
|
+
'If subagent delegation is not available, a deterministic heuristic fallback ' +
|
|
129
|
+
'runs a scripted inspection sequence and returns a best-effort summary.'
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Create the MCP server for one negotiated protocol era.
|
|
135
|
+
*
|
|
136
|
+
* @param {{ era?: 'legacy' | 'modern' }} [options]
|
|
102
137
|
* @returns {McpServer}
|
|
103
138
|
*/
|
|
104
|
-
export function createBridgeMcpServer() {
|
|
139
|
+
export function createBridgeMcpServer(options = {}) {
|
|
140
|
+
const { era = 'legacy' } = options;
|
|
105
141
|
const server = new McpServer(
|
|
106
142
|
{
|
|
107
143
|
name: 'browser-bridge',
|
|
108
144
|
version: MCP_SERVER_VERSION,
|
|
109
145
|
},
|
|
110
146
|
{
|
|
111
|
-
instructions:
|
|
147
|
+
instructions: getMcpServerInstructions(era),
|
|
112
148
|
}
|
|
113
149
|
);
|
|
114
150
|
|
|
115
|
-
server.registerTool
|
|
151
|
+
/** @type {Map<string, ReturnType<typeof server.registerTool>>} */
|
|
152
|
+
const registrations = new Map();
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Register a tool, then hide it when it is not part of the era's compact surface.
|
|
156
|
+
*
|
|
157
|
+
* Registering first keeps every call site checked against the SDK's own
|
|
158
|
+
* generic signature instead of a hand-written passthrough type. Disabling
|
|
159
|
+
* happens before `connect()`, so an excluded tool never reaches `tools/list`.
|
|
160
|
+
*
|
|
161
|
+
* @template {z.ZodObject} InputSchema
|
|
162
|
+
* @param {string} name
|
|
163
|
+
* @param {{
|
|
164
|
+
* title?: string,
|
|
165
|
+
* description?: string,
|
|
166
|
+
* inputSchema: InputSchema,
|
|
167
|
+
* annotations?: import('@modelcontextprotocol/server').ToolAnnotations,
|
|
168
|
+
* _meta?: Record<string, unknown>,
|
|
169
|
+
* }} config
|
|
170
|
+
* @param {import('@modelcontextprotocol/server').AnyToolHandler<InputSchema>} handler
|
|
171
|
+
* @returns {ReturnType<typeof server.registerTool>}
|
|
172
|
+
*/
|
|
173
|
+
const registerTool = (name, config, handler) => {
|
|
174
|
+
const wrappedHandler = /** @type {typeof handler} */ (
|
|
175
|
+
(args, context) => runWithMcpRequestEra(era, () => handler(args, context))
|
|
176
|
+
);
|
|
177
|
+
const registration = server.registerTool(name, config, wrappedHandler);
|
|
178
|
+
registrations.set(name, registration);
|
|
179
|
+
if (!isToolEnabledForEra(name, era)) {
|
|
180
|
+
registration.disable();
|
|
181
|
+
}
|
|
182
|
+
return registration;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
registerTool(
|
|
116
186
|
'browser_status',
|
|
117
187
|
{
|
|
118
188
|
title: 'Browser Bridge Status',
|
|
119
189
|
description:
|
|
120
190
|
'Check bridge readiness: daemon connectivity, extension state, and window access. Omit destinationId to inspect local and configured remote destinations. If access is not enabled, ask the user to click Enable in the extension popup or side panel, then retry.',
|
|
121
|
-
inputSchema: {
|
|
191
|
+
inputSchema: z.object({
|
|
122
192
|
destinationId: z
|
|
123
193
|
.string()
|
|
124
194
|
.optional()
|
|
125
195
|
.describe('Inspect one destination instead of aggregating all configured destinations.'),
|
|
126
|
-
},
|
|
196
|
+
}),
|
|
127
197
|
},
|
|
128
198
|
handleStatusTool
|
|
129
199
|
);
|
|
130
200
|
|
|
131
|
-
|
|
201
|
+
registerTool(
|
|
132
202
|
'browser_setup',
|
|
133
203
|
{
|
|
134
204
|
title: 'Browser Bridge Setup Status',
|
|
135
205
|
description: 'Check MCP and CLI skill installation status for agent integration.',
|
|
136
|
-
inputSchema: {
|
|
206
|
+
inputSchema: z.object({
|
|
137
207
|
global: z
|
|
138
208
|
.boolean()
|
|
139
209
|
.optional()
|
|
140
210
|
.describe('Check global (true) or local (false) config (default: true)'),
|
|
141
|
-
},
|
|
211
|
+
}),
|
|
142
212
|
},
|
|
143
213
|
handleSetupTool
|
|
144
214
|
);
|
|
145
215
|
|
|
146
|
-
|
|
216
|
+
registerTool(
|
|
147
217
|
'browser_logs',
|
|
148
218
|
{
|
|
149
219
|
title: 'Browser Bridge Logs',
|
|
150
220
|
description: 'Tail recent bridge request logs for debugging connection or routing issues.',
|
|
151
|
-
inputSchema: {
|
|
221
|
+
inputSchema: z.object({
|
|
152
222
|
limit: z
|
|
153
223
|
.number()
|
|
154
224
|
.int()
|
|
@@ -160,34 +230,34 @@ export function createBridgeMcpServer() {
|
|
|
160
230
|
.optional()
|
|
161
231
|
.describe(BUDGET_PRESET_DESCRIPTION),
|
|
162
232
|
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
163
|
-
},
|
|
233
|
+
}),
|
|
164
234
|
},
|
|
165
235
|
handleLogTool
|
|
166
236
|
);
|
|
167
237
|
|
|
168
|
-
|
|
238
|
+
registerTool(
|
|
169
239
|
'browser_health',
|
|
170
240
|
{
|
|
171
241
|
title: 'Browser Bridge Health',
|
|
172
242
|
description: 'Ping the bridge to verify daemon and extension connectivity.',
|
|
173
|
-
inputSchema: {
|
|
243
|
+
inputSchema: z.object({
|
|
174
244
|
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
175
245
|
intent: z
|
|
176
246
|
.enum(['inspect', 'interact', 'capture', 'navigate', 'debugger', 'general'])
|
|
177
247
|
.optional()
|
|
178
248
|
.describe('Reported operation family shown in the access prompt (defaults to general).'),
|
|
179
|
-
},
|
|
249
|
+
}),
|
|
180
250
|
},
|
|
181
251
|
handleHealthTool
|
|
182
252
|
);
|
|
183
253
|
|
|
184
|
-
|
|
254
|
+
registerTool(
|
|
185
255
|
'browser_tabs',
|
|
186
256
|
{
|
|
187
257
|
title: 'Browser Tabs',
|
|
188
258
|
description:
|
|
189
259
|
'List, create, close, or activate browser tabs. List without destinationId aggregates configured destinations; other actions default to local. Only create a page when the user explicitly requests it.',
|
|
190
|
-
inputSchema: {
|
|
260
|
+
inputSchema: z.object({
|
|
191
261
|
action: z
|
|
192
262
|
.enum(['list', 'create', 'close', 'activate'])
|
|
193
263
|
.describe('"list" (preferred), "create" (only when needed), "close", or "activate"'),
|
|
@@ -203,18 +273,18 @@ export function createBridgeMcpServer() {
|
|
|
203
273
|
.string()
|
|
204
274
|
.optional()
|
|
205
275
|
.describe('Target one destination; list without it aggregates configured destinations.'),
|
|
206
|
-
},
|
|
276
|
+
}),
|
|
207
277
|
},
|
|
208
278
|
handleTabsTool
|
|
209
279
|
);
|
|
210
280
|
|
|
211
|
-
|
|
281
|
+
registerTool(
|
|
212
282
|
'browser_dom',
|
|
213
283
|
{
|
|
214
284
|
title: 'Browser DOM',
|
|
215
285
|
description:
|
|
216
286
|
'Query, describe, read, search, or wait for DOM elements. Reuse elementRef from prior results. For full-page text, use browser_page action "text". accessibility_tree is debugger-backed and depth-limited - use query/find first.',
|
|
217
|
-
inputSchema: {
|
|
287
|
+
inputSchema: z.object({
|
|
218
288
|
action: z
|
|
219
289
|
.enum([
|
|
220
290
|
'query',
|
|
@@ -332,18 +402,18 @@ export function createBridgeMcpServer() {
|
|
|
332
402
|
.positive()
|
|
333
403
|
.optional()
|
|
334
404
|
.describe(`Max HTML chars to return (default: ${DEFAULT_MAX_HTML_LENGTH})`),
|
|
335
|
-
},
|
|
405
|
+
}),
|
|
336
406
|
},
|
|
337
407
|
handleDomTool
|
|
338
408
|
);
|
|
339
409
|
|
|
340
|
-
|
|
410
|
+
registerTool(
|
|
341
411
|
'browser_styles_layout',
|
|
342
412
|
{
|
|
343
413
|
title: 'Browser Styles And Layout',
|
|
344
414
|
description:
|
|
345
415
|
'Read computed styles, matched CSS rules, box model, or hit-test a viewport point. Reuse elementRef from prior queries. For DOM structure, use browser_dom.',
|
|
346
|
-
inputSchema: {
|
|
416
|
+
inputSchema: z.object({
|
|
347
417
|
action: z
|
|
348
418
|
.enum(['computed', 'matched_rules', 'box_model', 'hit_test'])
|
|
349
419
|
.describe('Style/layout operation to perform'),
|
|
@@ -371,18 +441,18 @@ export function createBridgeMcpServer() {
|
|
|
371
441
|
.nonnegative()
|
|
372
442
|
.optional()
|
|
373
443
|
.describe('Y coordinate for hit_test (viewport relative)'),
|
|
374
|
-
},
|
|
444
|
+
}),
|
|
375
445
|
},
|
|
376
446
|
handleStylesLayoutTool
|
|
377
447
|
);
|
|
378
448
|
|
|
379
|
-
|
|
449
|
+
registerTool(
|
|
380
450
|
'browser_sensitive_read',
|
|
381
451
|
{
|
|
382
452
|
title: 'Sensitive Browser Read',
|
|
383
453
|
description:
|
|
384
454
|
'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.',
|
|
385
|
-
inputSchema: {
|
|
455
|
+
inputSchema: z.object({
|
|
386
456
|
source: z.enum(['local_storage', 'session_storage']).describe('Exact storage source'),
|
|
387
457
|
key: z
|
|
388
458
|
.string()
|
|
@@ -390,18 +460,18 @@ export function createBridgeMcpServer() {
|
|
|
390
460
|
.describe('Exact storage key, including an empty key if intended'),
|
|
391
461
|
tabId: z.number().int().positive().optional().describe(TAB_ID_DESCRIPTION),
|
|
392
462
|
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
393
|
-
},
|
|
463
|
+
}),
|
|
394
464
|
},
|
|
395
465
|
handleSensitiveReadTool
|
|
396
466
|
);
|
|
397
467
|
|
|
398
|
-
|
|
468
|
+
registerTool(
|
|
399
469
|
'browser_page',
|
|
400
470
|
{
|
|
401
471
|
title: 'Browser Page State',
|
|
402
472
|
description:
|
|
403
473
|
'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.',
|
|
404
|
-
inputSchema: {
|
|
474
|
+
inputSchema: z.object({
|
|
405
475
|
action: z
|
|
406
476
|
.enum([
|
|
407
477
|
'state',
|
|
@@ -533,18 +603,18 @@ export function createBridgeMcpServer() {
|
|
|
533
603
|
.enum(['exact', 'contains', 'regex'])
|
|
534
604
|
.optional()
|
|
535
605
|
.describe('URL match mode for wait_for_load (default: exact)'),
|
|
536
|
-
},
|
|
606
|
+
}),
|
|
537
607
|
},
|
|
538
608
|
handlePageTool
|
|
539
609
|
);
|
|
540
610
|
|
|
541
|
-
|
|
611
|
+
registerTool(
|
|
542
612
|
'browser_navigation',
|
|
543
613
|
{
|
|
544
614
|
title: 'Browser Navigation',
|
|
545
615
|
description:
|
|
546
616
|
'Navigate to a URL, reload, go back/forward, scroll, or resize the viewport. resize is debugger-backed - use only for exact viewport overrides.',
|
|
547
|
-
inputSchema: {
|
|
617
|
+
inputSchema: z.object({
|
|
548
618
|
action: z
|
|
549
619
|
.enum(['navigate', 'reload', 'go_back', 'go_forward', 'scroll', 'resize'])
|
|
550
620
|
.describe('Navigation operation to perform'),
|
|
@@ -577,18 +647,18 @@ export function createBridgeMcpServer() {
|
|
|
577
647
|
.optional()
|
|
578
648
|
.describe('Viewport device scale factor (for resize)'),
|
|
579
649
|
reset: z.boolean().optional().describe('Reset viewport to original size (for resize)'),
|
|
580
|
-
},
|
|
650
|
+
}),
|
|
581
651
|
},
|
|
582
652
|
handleNavigationTool
|
|
583
653
|
);
|
|
584
654
|
|
|
585
|
-
|
|
655
|
+
registerTool(
|
|
586
656
|
'browser_input',
|
|
587
657
|
{
|
|
588
658
|
title: 'Browser Input',
|
|
589
659
|
description:
|
|
590
660
|
'Dispatch browser input. Targeted click, focus, type, fill, press_key, set_checked, select_option, hover, and drag actions perform actionability checks and report resolution/execution metadata. cdp_press_key and scroll_into_view use separate contracts. Reuse elementRef values and verify application state after mutations.',
|
|
591
|
-
inputSchema: {
|
|
661
|
+
inputSchema: z.object({
|
|
592
662
|
action: z
|
|
593
663
|
.enum([
|
|
594
664
|
'click',
|
|
@@ -689,18 +759,18 @@ export function createBridgeMcpServer() {
|
|
|
689
759
|
.describe('Drag destination selector (alternative to destinationElementRef)'),
|
|
690
760
|
offsetX: z.number().optional().describe('Drag drop offset X (default: 0)'),
|
|
691
761
|
offsetY: z.number().optional().describe('Drag drop offset Y (default: 0)'),
|
|
692
|
-
},
|
|
762
|
+
}),
|
|
693
763
|
},
|
|
694
764
|
handleInputTool
|
|
695
765
|
);
|
|
696
766
|
|
|
697
|
-
|
|
767
|
+
registerTool(
|
|
698
768
|
'browser_patch',
|
|
699
769
|
{
|
|
700
770
|
title: 'Browser Patch',
|
|
701
771
|
description:
|
|
702
772
|
'Apply or rollback document-local style and DOM patches for live prototyping before editing source. commit_baseline keeps current changes but discards rollback history. Set verify=true to get computed results inline.',
|
|
703
|
-
inputSchema: {
|
|
773
|
+
inputSchema: z.object({
|
|
704
774
|
action: z
|
|
705
775
|
.enum(['apply_styles', 'apply_dom', 'list', 'rollback', 'commit_baseline'])
|
|
706
776
|
.describe('Patch operation to perform'),
|
|
@@ -740,18 +810,18 @@ export function createBridgeMcpServer() {
|
|
|
740
810
|
.describe(
|
|
741
811
|
'Return computed result inline after applying, eliminating a verification round-trip'
|
|
742
812
|
),
|
|
743
|
-
},
|
|
813
|
+
}),
|
|
744
814
|
},
|
|
745
815
|
handlePatchTool
|
|
746
816
|
);
|
|
747
817
|
|
|
748
|
-
|
|
818
|
+
registerTool(
|
|
749
819
|
'browser_capture',
|
|
750
820
|
{
|
|
751
821
|
title: 'Browser Capture',
|
|
752
822
|
description:
|
|
753
823
|
'Capture screenshots or CDP snapshots. Debugger-backed and token-expensive - use only when structured reads (browser_dom, browser_styles_layout) are insufficient. Prefer element, then tight region; full_page only for document-level context.',
|
|
754
|
-
inputSchema: {
|
|
824
|
+
inputSchema: z.object({
|
|
755
825
|
action: z
|
|
756
826
|
.enum([
|
|
757
827
|
'element',
|
|
@@ -817,12 +887,91 @@ export function createBridgeMcpServer() {
|
|
|
817
887
|
.max(4)
|
|
818
888
|
.optional()
|
|
819
889
|
.describe('Screenshot capture scale (0.1 to 4)'),
|
|
820
|
-
},
|
|
890
|
+
}),
|
|
821
891
|
},
|
|
822
892
|
handleCaptureTool
|
|
823
893
|
);
|
|
824
894
|
|
|
825
|
-
|
|
895
|
+
registerTool(
|
|
896
|
+
'browser_artifact',
|
|
897
|
+
{
|
|
898
|
+
title: 'Browser Artifact',
|
|
899
|
+
description:
|
|
900
|
+
'Read (chunked base64) or delete a daemon-owned artifact produced by artifact delivery (screenshots, HAR exports). Artifacts are scoped to the current local MCP process and expire quickly. Page reads with offset until nextOffset is null, then verify the reassembled bytes against sha256.',
|
|
901
|
+
inputSchema: z.object({
|
|
902
|
+
action: z.enum(['read', 'delete']).describe('Artifact operation to perform'),
|
|
903
|
+
artifactId: z
|
|
904
|
+
.string()
|
|
905
|
+
.min(1)
|
|
906
|
+
.describe('Artifact identifier returned by a capture or HAR export'),
|
|
907
|
+
offset: z
|
|
908
|
+
.number()
|
|
909
|
+
.int()
|
|
910
|
+
.nonnegative()
|
|
911
|
+
.optional()
|
|
912
|
+
.describe('Byte offset to read from (default: 0)'),
|
|
913
|
+
limit: z
|
|
914
|
+
.number()
|
|
915
|
+
.int()
|
|
916
|
+
.positive()
|
|
917
|
+
.max(ARTIFACT_CHUNK_BYTES)
|
|
918
|
+
.optional()
|
|
919
|
+
.describe(`Maximum bytes to read in this chunk (default/max: ${ARTIFACT_CHUNK_BYTES})`),
|
|
920
|
+
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
921
|
+
budgetPreset: z
|
|
922
|
+
.enum(['quick', 'normal', 'deep'])
|
|
923
|
+
.optional()
|
|
924
|
+
.describe(BUDGET_PRESET_DESCRIPTION),
|
|
925
|
+
}),
|
|
926
|
+
},
|
|
927
|
+
handleArtifactTool
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
registerTool(
|
|
931
|
+
'browser_intercept',
|
|
932
|
+
{
|
|
933
|
+
title: 'Browser Network Intercept',
|
|
934
|
+
description:
|
|
935
|
+
'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.',
|
|
936
|
+
inputSchema: z.object({
|
|
937
|
+
action: z
|
|
938
|
+
.enum(['add', 'list', 'remove', 'clear'])
|
|
939
|
+
.describe('Intercept operation to perform'),
|
|
940
|
+
tabId: z.number().int().positive().optional().describe(TAB_ID_DESCRIPTION),
|
|
941
|
+
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
942
|
+
budgetPreset: z
|
|
943
|
+
.enum(['quick', 'normal', 'deep'])
|
|
944
|
+
.optional()
|
|
945
|
+
.describe(BUDGET_PRESET_DESCRIPTION),
|
|
946
|
+
urlPattern: z
|
|
947
|
+
.string()
|
|
948
|
+
.min(1)
|
|
949
|
+
.max(2_048)
|
|
950
|
+
.optional()
|
|
951
|
+
.describe('URL substring to match (required for add)'),
|
|
952
|
+
ruleAction: z
|
|
953
|
+
.enum(['block', 'fulfill', 'continue'])
|
|
954
|
+
.optional()
|
|
955
|
+
.describe('How to handle matched requests (default: continue)'),
|
|
956
|
+
statusCode: z
|
|
957
|
+
.number()
|
|
958
|
+
.int()
|
|
959
|
+
.min(100)
|
|
960
|
+
.max(599)
|
|
961
|
+
.optional()
|
|
962
|
+
.describe('HTTP status for fulfill responses'),
|
|
963
|
+
body: z.string().optional().describe('Response body for fulfill'),
|
|
964
|
+
headers: z
|
|
965
|
+
.record(z.string(), z.string())
|
|
966
|
+
.optional()
|
|
967
|
+
.describe('Response headers for fulfill'),
|
|
968
|
+
ruleId: z.string().min(1).optional().describe('Rule ID (required for remove)'),
|
|
969
|
+
}),
|
|
970
|
+
},
|
|
971
|
+
handleInterceptTool
|
|
972
|
+
);
|
|
973
|
+
|
|
974
|
+
registerTool(
|
|
826
975
|
'browser_batch',
|
|
827
976
|
{
|
|
828
977
|
title: 'Browser Bridge Batch',
|
|
@@ -834,7 +983,7 @@ export function createBridgeMcpServer() {
|
|
|
834
983
|
idempotentHint: true,
|
|
835
984
|
openWorldHint: true,
|
|
836
985
|
},
|
|
837
|
-
inputSchema: {
|
|
986
|
+
inputSchema: z.object({
|
|
838
987
|
calls: z
|
|
839
988
|
.array(
|
|
840
989
|
z.object({
|
|
@@ -854,18 +1003,18 @@ export function createBridgeMcpServer() {
|
|
|
854
1003
|
.min(1)
|
|
855
1004
|
.max(MAX_BATCH_CALLS)
|
|
856
1005
|
.describe('Calls to execute in parallel'),
|
|
857
|
-
},
|
|
1006
|
+
}),
|
|
858
1007
|
},
|
|
859
1008
|
handleBatchTool
|
|
860
1009
|
);
|
|
861
1010
|
|
|
862
|
-
|
|
1011
|
+
registerTool(
|
|
863
1012
|
'browser_call',
|
|
864
1013
|
{
|
|
865
1014
|
title: 'Raw Browser Bridge Call',
|
|
866
1015
|
description:
|
|
867
1016
|
'Primary Browser Bridge tool for permission-ask hosts: call any bridge method directly by name so the user can approve one BBX MCP tool instead of each specialized tool separately.',
|
|
868
|
-
inputSchema: {
|
|
1017
|
+
inputSchema: z.object({
|
|
869
1018
|
method: z.string().describe('Bridge method name (e.g., "dom.query", "input.click")'),
|
|
870
1019
|
params: z
|
|
871
1020
|
.record(z.string(), z.unknown())
|
|
@@ -877,50 +1026,42 @@ export function createBridgeMcpServer() {
|
|
|
877
1026
|
.enum(['quick', 'normal', 'deep'])
|
|
878
1027
|
.optional()
|
|
879
1028
|
.describe(BUDGET_PRESET_DESCRIPTION),
|
|
880
|
-
},
|
|
1029
|
+
}),
|
|
881
1030
|
},
|
|
882
1031
|
handleRawCallTool
|
|
883
1032
|
);
|
|
884
1033
|
|
|
885
|
-
|
|
1034
|
+
registerTool(
|
|
886
1035
|
'browser_skill',
|
|
887
1036
|
{
|
|
888
1037
|
title: 'Browser Bridge Runtime Context',
|
|
889
1038
|
description:
|
|
890
1039
|
'Return runtime context: budget presets, method groups, and active limits. Call to discover defaults before inspecting a page.',
|
|
891
|
-
inputSchema: {
|
|
1040
|
+
inputSchema: z.object({
|
|
892
1041
|
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
893
|
-
},
|
|
1042
|
+
}),
|
|
894
1043
|
},
|
|
895
1044
|
handleSkillTool
|
|
896
1045
|
);
|
|
897
1046
|
|
|
898
|
-
|
|
1047
|
+
registerTool(
|
|
899
1048
|
'browser_access',
|
|
900
1049
|
{
|
|
901
1050
|
title: 'Request Browser Bridge Access',
|
|
902
1051
|
description:
|
|
903
1052
|
'Request window access for Browser Bridge. Surfaces an Enable prompt in the extension popup or side panel. Use once per window; if access is already pending, ask the user to enable that window instead of requesting again.',
|
|
904
|
-
inputSchema: {
|
|
1053
|
+
inputSchema: z.object({
|
|
905
1054
|
destinationId: z.string().optional().describe(DESTINATION_ID_DESCRIPTION),
|
|
906
|
-
},
|
|
1055
|
+
}),
|
|
907
1056
|
},
|
|
908
1057
|
handleAccessTool
|
|
909
1058
|
);
|
|
910
1059
|
|
|
911
|
-
|
|
1060
|
+
registerTool(
|
|
912
1061
|
'browser_investigate',
|
|
913
1062
|
{
|
|
914
1063
|
title: 'Browser Investigate',
|
|
915
|
-
description:
|
|
916
|
-
'Investigate a page to answer a question or verify a condition. ' +
|
|
917
|
-
'Pass a natural-language objective and an optional scope (quick/normal/deep). ' +
|
|
918
|
-
'DELEGATION HINT: Prefer delegating this to a smaller, low-cost subagent ' +
|
|
919
|
-
'that starts with structured reads via browser_dom, browser_page, ' +
|
|
920
|
-
'browser_styles_layout, and browser_batch. Escalate to browser_capture only ' +
|
|
921
|
-
'when structured reads are insufficient. ' +
|
|
922
|
-
'If subagent delegation is not available, a deterministic heuristic fallback ' +
|
|
923
|
-
'runs a scripted inspection sequence and returns a best-effort summary.',
|
|
1064
|
+
description: getInvestigateToolDescription(),
|
|
924
1065
|
annotations: {
|
|
925
1066
|
readOnlyHint: true,
|
|
926
1067
|
destructiveHint: false,
|
|
@@ -928,9 +1069,9 @@ export function createBridgeMcpServer() {
|
|
|
928
1069
|
openWorldHint: true,
|
|
929
1070
|
},
|
|
930
1071
|
_meta: {
|
|
931
|
-
delegationHint:
|
|
1072
|
+
delegationHint: createInvestigateDelegationHint(),
|
|
932
1073
|
},
|
|
933
|
-
inputSchema: {
|
|
1074
|
+
inputSchema: z.object({
|
|
934
1075
|
objective: z
|
|
935
1076
|
.string()
|
|
936
1077
|
.describe('What to find, verify, or extract from the current page (natural language).'),
|
|
@@ -948,20 +1089,43 @@ export function createBridgeMcpServer() {
|
|
|
948
1089
|
.string()
|
|
949
1090
|
.optional()
|
|
950
1091
|
.describe('Optional CSS selector to scope the investigation to a subtree.'),
|
|
951
|
-
},
|
|
1092
|
+
}),
|
|
952
1093
|
},
|
|
953
1094
|
handleInvestigateTool
|
|
954
1095
|
);
|
|
955
1096
|
|
|
1097
|
+
registerTool(
|
|
1098
|
+
'browser_toolset',
|
|
1099
|
+
{
|
|
1100
|
+
title: 'Load Browser Bridge Tool',
|
|
1101
|
+
description:
|
|
1102
|
+
'Load one specialized typed Browser Bridge tool by exact name. The tool appears in tools/list automatically and remains enabled for this legacy MCP connection.',
|
|
1103
|
+
inputSchema: z.object({
|
|
1104
|
+
tool: z
|
|
1105
|
+
.enum(LOADABLE_TOOLSET_TOOLS)
|
|
1106
|
+
.describe('Exact specialized Browser Bridge tool to load.'),
|
|
1107
|
+
}),
|
|
1108
|
+
},
|
|
1109
|
+
({ tool }) => {
|
|
1110
|
+
const registration = registrations.get(tool);
|
|
1111
|
+
const newlyEnabled = Boolean(registration && !registration.enabled);
|
|
1112
|
+
if (newlyEnabled) registration?.enable();
|
|
1113
|
+
return createToolResult(newlyEnabled ? `Loaded ${tool}.` : `${tool} is already loaded.`, {
|
|
1114
|
+
tool,
|
|
1115
|
+
newlyEnabled,
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
);
|
|
1119
|
+
|
|
956
1120
|
return server;
|
|
957
1121
|
}
|
|
958
1122
|
|
|
959
1123
|
/**
|
|
960
|
-
* @
|
|
1124
|
+
* @param {{ serve?: typeof serveStdio }} [options]
|
|
1125
|
+
* @returns {void}
|
|
961
1126
|
*/
|
|
962
|
-
export
|
|
1127
|
+
export function startBridgeMcpServer(options = {}) {
|
|
1128
|
+
const { serve = serveStdio } = options;
|
|
963
1129
|
applyWindowsTcpTransportDefaults();
|
|
964
|
-
|
|
965
|
-
const transport = new StdioServerTransport();
|
|
966
|
-
await server.connect(transport);
|
|
1130
|
+
serve(({ era }) => createBridgeMcpServer({ era }));
|
|
967
1131
|
}
|