@browserbridge/bbx 1.3.0 → 1.4.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 +3 -1
- package/package.json +2 -2
- package/packages/agent-client/src/cli.js +32 -15
- package/packages/agent-client/src/client.js +74 -20
- package/packages/agent-client/src/command-registry.js +2 -3
- package/packages/agent-client/src/mcp-config.js +19 -2
- package/packages/agent-client/src/types.ts +8 -0
- package/packages/mcp-server/src/guidance.js +241 -0
- package/packages/mcp-server/src/handlers-capture.js +74 -11
- package/packages/mcp-server/src/handlers-dom.js +48 -0
- package/packages/mcp-server/src/handlers-navigation.js +22 -2
- package/packages/mcp-server/src/handlers-page.js +4 -9
- package/packages/mcp-server/src/handlers-utils.js +47 -1
- package/packages/mcp-server/src/server.js +111 -29
- package/packages/native-host/src/auth-token.js +92 -0
- package/packages/native-host/src/daemon.js +171 -27
- package/packages/native-host/src/native-host.js +18 -2
- package/packages/protocol/src/defaults.js +3 -0
- package/packages/protocol/src/json-lines.js +29 -1
- package/packages/protocol/src/protocol.js +3 -0
- package/packages/protocol/src/types.ts +2 -0
- package/skills/browser-bridge/SKILL.md +21 -5
- package/skills/browser-bridge/agents/openai.yaml +1 -1
- package/skills/browser-bridge/references/interaction.md +6 -6
- package/skills/browser-bridge/references/protocol.md +57 -54
- package/skills/browser-bridge/references/ui-workflows.md +1 -1
package/README.md
CHANGED
|
@@ -110,7 +110,9 @@ Browser Bridge is optimized for the opposite starting point: **inspect the state
|
|
|
110
110
|
3. Run `bbx install`, or target a specific browser with `bbx install --browser edge`, `bbx install --browser brave`, `bbx install --browser chromium`, or `bbx install --browser arc`
|
|
111
111
|
4. In the extension side panel, install MCP or CLI (skill) for your agent of choice
|
|
112
112
|
5. Enable Browser Bridge for the browser window you want to inspect/control with the AI agent
|
|
113
|
-
6. Ask your agent to use Browser Bridge via MCP (`BB MCP` or `Browser Bridge MCP`), or invoke the
|
|
113
|
+
6. Ask your agent to use Browser Bridge via MCP (`BB MCP` or `Browser Bridge MCP`), or invoke the installed Browser Bridge skill in CLI mode (`/browser-bridge`, `browser-bridge`, or the client-specific skill trigger)
|
|
114
|
+
|
|
115
|
+
MCP mode is self-contained: the server exposes tools, startup instructions, and prompt templates, so a separate CLI skill is not required for MCP guidance.
|
|
114
116
|
|
|
115
117
|
## How it works
|
|
116
118
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserbridge/bbx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-tools",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"postinstall": "node packages/native-host/bin/postinstall.js",
|
|
62
62
|
"package:extension": "node scripts/package-extension.mjs",
|
|
63
63
|
"check:extension-zip": "node scripts/check-extension-zip.mjs",
|
|
64
|
-
"release:check": "npm run lint && npm run typecheck && npm test && npm run coverage:check && npm run package:extension && npm pack --dry-run",
|
|
64
|
+
"release:check": "npm run lint && npm run typecheck && npm test && npm run coverage:check && npm run package:extension && npm run check:extension-zip && npm pack --dry-run",
|
|
65
65
|
"prepublishOnly": "npm run lint && npm run typecheck && npm test && npm run coverage:check",
|
|
66
66
|
"status": "node packages/agent-client/src/cli.js status",
|
|
67
67
|
"daemon": "node packages/native-host/bin/bridge-daemon.js",
|
|
@@ -128,15 +128,21 @@ if (command === 'install-skill') {
|
|
|
128
128
|
const positional = rest.filter((a) => !a.startsWith('--'));
|
|
129
129
|
|
|
130
130
|
if (positional.length === 0) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
let scopeOptions;
|
|
132
|
+
try {
|
|
133
|
+
scopeOptions = parseInstallAgentArgs(rest);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const isGlobal = scopeOptions.global !== false;
|
|
140
|
+
const projectPath = isGlobal ? os.homedir() : scopeOptions.projectPath;
|
|
135
141
|
|
|
136
142
|
const setupStatus = await collectSetupStatus({
|
|
137
143
|
global: isGlobal,
|
|
138
144
|
cwd: process.cwd(),
|
|
139
|
-
projectPath
|
|
145
|
+
projectPath,
|
|
140
146
|
...getSetupStatusTestOverrides(),
|
|
141
147
|
});
|
|
142
148
|
/** @type {import('./types.js').SupportedTarget[]} */
|
|
@@ -176,7 +182,6 @@ if (command === 'install-skill') {
|
|
|
176
182
|
targets = /** @type {import('./types.js').SupportedTarget[]} */ (selected);
|
|
177
183
|
}
|
|
178
184
|
|
|
179
|
-
const projectPath = isGlobal ? os.homedir() : process.cwd();
|
|
180
185
|
if (selected !== null) {
|
|
181
186
|
const deselectedTargets =
|
|
182
187
|
/** @type {import('./types.js').SupportedTarget[]} */ (
|
|
@@ -226,20 +231,32 @@ if (command === 'install-skill') {
|
|
|
226
231
|
}
|
|
227
232
|
|
|
228
233
|
if (command === 'install-mcp') {
|
|
229
|
-
const argsLeft = [...rest];
|
|
230
234
|
let isGlobal = true;
|
|
235
|
+
/** @type {string[]} */
|
|
236
|
+
const positionals = [];
|
|
231
237
|
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
238
|
+
for (const arg of rest) {
|
|
239
|
+
if (arg === '--local') {
|
|
240
|
+
isGlobal = false;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (arg === '--global') {
|
|
244
|
+
isGlobal = true;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (arg.startsWith('--')) {
|
|
248
|
+
process.stderr.write(`Unknown install-mcp option "${arg}".\n`);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
positionals.push(arg);
|
|
236
252
|
}
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
|
|
253
|
+
|
|
254
|
+
if (positionals.length > 1) {
|
|
255
|
+
process.stderr.write(`Unexpected extra argument "${positionals[1]}".\n`);
|
|
256
|
+
process.exit(1);
|
|
240
257
|
}
|
|
241
258
|
|
|
242
|
-
const clientArg =
|
|
259
|
+
const clientArg = positionals[0];
|
|
243
260
|
|
|
244
261
|
/** @type {import('./types.js').McpClientName[]} */
|
|
245
262
|
let clients;
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getBridgeTransport,
|
|
16
16
|
getSocketPath,
|
|
17
17
|
} from '../../native-host/src/config.js';
|
|
18
|
+
import { normalizeBridgeAuthToken, readBridgeAuthToken } from '../../native-host/src/auth-token.js';
|
|
18
19
|
import { restartBridgeDaemon } from '../../native-host/src/daemon-process.js';
|
|
19
20
|
|
|
20
21
|
/** @typedef {import('./types.js').BridgeMeta} BridgeMeta */
|
|
@@ -56,6 +57,22 @@ function createTimeoutError(method, timeoutMs) {
|
|
|
56
57
|
return error;
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
/**
|
|
61
|
+
* @param {net.Socket} socket
|
|
62
|
+
* @param {string} line
|
|
63
|
+
* @returns {Promise<void>}
|
|
64
|
+
*/
|
|
65
|
+
async function writeSocketLine(socket, line) {
|
|
66
|
+
if (!socket.write(line)) {
|
|
67
|
+
await Promise.race([
|
|
68
|
+
once(socket, 'drain'),
|
|
69
|
+
once(socket, 'close').then(() => {
|
|
70
|
+
throw new Error('Bridge socket closed while writing.');
|
|
71
|
+
}),
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
export class BridgeClient extends EventEmitter {
|
|
60
77
|
/**
|
|
61
78
|
* @param {BridgeClientOptions} [options={}]
|
|
@@ -68,6 +85,7 @@ export class BridgeClient extends EventEmitter {
|
|
|
68
85
|
autoReconnect = false,
|
|
69
86
|
restartDaemonOnVersionMismatch = true,
|
|
70
87
|
restartDaemonFn = restartBridgeDaemon,
|
|
88
|
+
authToken = undefined,
|
|
71
89
|
} = {}) {
|
|
72
90
|
super();
|
|
73
91
|
this.transport = socketPath ? createSocketBridgeTransport(socketPath) : transport;
|
|
@@ -78,6 +96,7 @@ export class BridgeClient extends EventEmitter {
|
|
|
78
96
|
this.autoReconnect = autoReconnect;
|
|
79
97
|
this.restartDaemonOnVersionMismatch = restartDaemonOnVersionMismatch;
|
|
80
98
|
this.restartDaemonFn = restartDaemonFn;
|
|
99
|
+
this.authToken = authToken;
|
|
81
100
|
this.socket = null;
|
|
82
101
|
this.connected = false;
|
|
83
102
|
this.protocolCompatibility = null;
|
|
@@ -111,6 +130,18 @@ export class BridgeClient extends EventEmitter {
|
|
|
111
130
|
throw error;
|
|
112
131
|
}
|
|
113
132
|
|
|
133
|
+
const registrationPromise = new Promise((resolve, reject) => {
|
|
134
|
+
const timeoutId = setTimeout(() => {
|
|
135
|
+
this.waiting.delete('registered');
|
|
136
|
+
reject(createTimeoutError('register', this.defaultTimeoutMs));
|
|
137
|
+
}, this.defaultTimeoutMs);
|
|
138
|
+
this.waiting.set('registered', {
|
|
139
|
+
resolve,
|
|
140
|
+
reject,
|
|
141
|
+
timeoutId,
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
114
145
|
parseJsonLines(socket, (raw) => {
|
|
115
146
|
const message = /** @type {ClientMessage} */ (raw);
|
|
116
147
|
if (message.type === 'registered') {
|
|
@@ -124,6 +155,16 @@ export class BridgeClient extends EventEmitter {
|
|
|
124
155
|
return;
|
|
125
156
|
}
|
|
126
157
|
|
|
158
|
+
if (message.type === 'registration_failed') {
|
|
159
|
+
const pending = this.waiting.get('registered');
|
|
160
|
+
if (pending) {
|
|
161
|
+
this.waiting.delete('registered');
|
|
162
|
+
clearTimeout(pending.timeoutId);
|
|
163
|
+
pending.reject(new Error(message.error?.message || 'Bridge daemon registration failed.'));
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
127
168
|
if (message.type === 'agent.response') {
|
|
128
169
|
const pending = this.waiting.get(message.response.id);
|
|
129
170
|
if (pending) {
|
|
@@ -148,20 +189,31 @@ export class BridgeClient extends EventEmitter {
|
|
|
148
189
|
// 'close' fires after 'error'; reconnect is triggered there.
|
|
149
190
|
});
|
|
150
191
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
192
|
+
const authToken =
|
|
193
|
+
this.authToken === undefined
|
|
194
|
+
? this.transport.type === 'tcp'
|
|
195
|
+
? await readBridgeAuthToken()
|
|
196
|
+
: null
|
|
197
|
+
: normalizeBridgeAuthToken(this.authToken);
|
|
198
|
+
try {
|
|
199
|
+
await writeSocketLine(
|
|
200
|
+
socket,
|
|
201
|
+
`${JSON.stringify({
|
|
202
|
+
type: 'register',
|
|
203
|
+
role: 'agent',
|
|
204
|
+
clientId: this.clientId,
|
|
205
|
+
...(authToken ? { authToken } : {}),
|
|
206
|
+
})}\n`
|
|
207
|
+
);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const pending = this.waiting.get('registered');
|
|
210
|
+
if (pending) {
|
|
211
|
+
clearTimeout(pending.timeoutId);
|
|
156
212
|
this.waiting.delete('registered');
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
reject,
|
|
162
|
-
timeoutId,
|
|
163
|
-
});
|
|
164
|
-
});
|
|
213
|
+
}
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
await registrationPromise;
|
|
165
217
|
|
|
166
218
|
this.protocolCompatibility = null;
|
|
167
219
|
this.protocolWarning = null;
|
|
@@ -244,13 +296,15 @@ export class BridgeClient extends EventEmitter {
|
|
|
244
296
|
});
|
|
245
297
|
});
|
|
246
298
|
|
|
247
|
-
|
|
248
|
-
await
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
299
|
+
try {
|
|
300
|
+
await writeSocketLine(this.socket, `${JSON.stringify({ type: 'agent.request', request })}\n`);
|
|
301
|
+
} catch (error) {
|
|
302
|
+
const pending = this.waiting.get(request.id);
|
|
303
|
+
if (pending) {
|
|
304
|
+
clearTimeout(pending.timeoutId);
|
|
305
|
+
this.waiting.delete(request.id);
|
|
306
|
+
}
|
|
307
|
+
throw error;
|
|
254
308
|
}
|
|
255
309
|
const response = /** @type {BridgeResponse} */ (await responsePromise);
|
|
256
310
|
return this.attachProtocolWarning(response);
|
|
@@ -237,11 +237,10 @@ export const CLI_HELP_SECTIONS = Object.freeze([
|
|
|
237
237
|
{
|
|
238
238
|
title: 'Setup',
|
|
239
239
|
lines: [
|
|
240
|
-
'bbx install [--browser chrome|edge|brave|chromium|arc] [
|
|
240
|
+
'bbx install [extension-id] [--browser chrome|edge|brave|chromium|arc] [--all] Install native messaging manifest',
|
|
241
241
|
'bbx uninstall Remove native host manifests, Browser Bridge runtime files, and managed MCP/skill installs',
|
|
242
|
-
'bbx install [--all] [--browser <name>] [extension-id] Install native host manifest (--all for all supported browsers)',
|
|
243
242
|
'bbx install-skill [targets|all] [--global] [--project <path>] Install/update the managed Browser Bridge CLI skill',
|
|
244
|
-
'bbx install-mcp [client|all] [--local] Write MCP config for codex|claude|cursor|copilot|opencode|antigravity|windsurf',
|
|
243
|
+
'bbx install-mcp [client|all] [--local] Write MCP config for codex|claude|cursor|copilot|opencode|antigravity|windsurf|agents',
|
|
245
244
|
'bbx status Check bridge connection',
|
|
246
245
|
'bbx doctor Diagnose install, daemon, extension, and access readiness',
|
|
247
246
|
'bbx restart Restart the local Browser Bridge daemon',
|
|
@@ -523,8 +523,13 @@ async function installJsonMcpConfig(clientName, configPath, stdout) {
|
|
|
523
523
|
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
524
524
|
existing = parsed;
|
|
525
525
|
}
|
|
526
|
-
} catch {
|
|
527
|
-
|
|
526
|
+
} catch (error) {
|
|
527
|
+
if (!isMissingFileError(error)) {
|
|
528
|
+
throw new Error(
|
|
529
|
+
`Cannot update ${configPath}: existing MCP config is not valid JSON. Fix or remove it first.`
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
// File missing - start fresh.
|
|
528
533
|
}
|
|
529
534
|
|
|
530
535
|
const shape = getMcpConfigShape(clientName);
|
|
@@ -661,3 +666,15 @@ export function parseInstalledMcpConfig(clientName, raw) {
|
|
|
661
666
|
return { configured: false };
|
|
662
667
|
}
|
|
663
668
|
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* @param {unknown} error
|
|
672
|
+
* @returns {boolean}
|
|
673
|
+
*/
|
|
674
|
+
function isMissingFileError(error) {
|
|
675
|
+
return Boolean(
|
|
676
|
+
error &&
|
|
677
|
+
typeof error === 'object' &&
|
|
678
|
+
/** @type {{ code?: unknown }} */ (error).code === 'ENOENT'
|
|
679
|
+
);
|
|
680
|
+
}
|
|
@@ -54,6 +54,13 @@ export type ClientMessage =
|
|
|
54
54
|
role: 'agent' | 'extension';
|
|
55
55
|
clientId?: string;
|
|
56
56
|
}
|
|
57
|
+
| {
|
|
58
|
+
type: 'registration_failed';
|
|
59
|
+
error?: {
|
|
60
|
+
code?: string;
|
|
61
|
+
message?: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
57
64
|
| {
|
|
58
65
|
type: 'agent.response';
|
|
59
66
|
response: BridgeResponse;
|
|
@@ -73,6 +80,7 @@ export interface BridgeClientOptions {
|
|
|
73
80
|
autoReconnect?: boolean;
|
|
74
81
|
restartDaemonOnVersionMismatch?: boolean;
|
|
75
82
|
restartDaemonFn?: typeof restartBridgeDaemon;
|
|
83
|
+
authToken?: string | null;
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
export interface ShortcutCommand {
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import * as z from 'zod/v4';
|
|
4
|
+
|
|
5
|
+
/** @typedef {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} McpServer */
|
|
6
|
+
/** @typedef {import('@modelcontextprotocol/sdk/types.js').GetPromptResult} GetPromptResult */
|
|
7
|
+
|
|
8
|
+
export const MCP_SERVER_INSTRUCTIONS = [
|
|
9
|
+
"Browser Bridge MCP inspects and interacts with the user's real Chrome tab through typed MCP tools.",
|
|
10
|
+
'Prefer Browser Bridge MCP tools over shelling out to bbx. Use bbx only for explicit CLI setup, doctor, logs, or raw debugging requests.',
|
|
11
|
+
'Call browser_status first. If window access is disabled, call browser_access once, ask the user to click Enable in the Browser Bridge popup or side panel, then retry once.',
|
|
12
|
+
'Use structured reads first: browser_page, browser_dom, browser_styles_layout, and browser_batch. Keep budgetPreset quick or normal before widening.',
|
|
13
|
+
'Reuse elementRef values returned by DOM tools. Use attribute allowlists for focused DOM reads.',
|
|
14
|
+
'Escalate to browser_capture, accessibility_tree, page evaluate, viewport resize, or CDP only when structured reads cannot answer the question.',
|
|
15
|
+
'Use browser_patch for temporary style or DOM experiments, and rollback patches before finishing unless the user asks to keep them.',
|
|
16
|
+
].join('\n');
|
|
17
|
+
|
|
18
|
+
export const MCP_GUIDANCE_PROMPT_NAMES = Object.freeze([
|
|
19
|
+
'browser_bridge_guide',
|
|
20
|
+
'browser_bridge_investigate',
|
|
21
|
+
'browser_bridge_debug_layout',
|
|
22
|
+
'browser_bridge_verify_flow',
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register Browser Bridge MCP prompt templates. These are the MCP-mode equivalent
|
|
27
|
+
* of a lightweight skill: discoverable by clients without requiring filesystem
|
|
28
|
+
* skill installation or shell access.
|
|
29
|
+
*
|
|
30
|
+
* @param {McpServer} server
|
|
31
|
+
* @returns {void}
|
|
32
|
+
*/
|
|
33
|
+
export function registerBridgeMcpGuidance(server) {
|
|
34
|
+
server.registerPrompt(
|
|
35
|
+
'browser_bridge_guide',
|
|
36
|
+
{
|
|
37
|
+
title: 'Use Browser Bridge MCP',
|
|
38
|
+
description:
|
|
39
|
+
'General Browser Bridge MCP workflow guidance. Prefer this over CLI skill setup.',
|
|
40
|
+
},
|
|
41
|
+
createGuidePrompt
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
server.registerPrompt(
|
|
45
|
+
'browser_bridge_investigate',
|
|
46
|
+
{
|
|
47
|
+
title: 'Investigate Current Page',
|
|
48
|
+
description:
|
|
49
|
+
'Inspect the current page with structured reads before screenshots or evaluation.',
|
|
50
|
+
argsSchema: {
|
|
51
|
+
objective: z.string().optional().describe('What to find, verify, or explain'),
|
|
52
|
+
selector: z
|
|
53
|
+
.string()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe('Optional CSS selector to scope the first DOM read'),
|
|
56
|
+
scope: z.enum(['quick', 'normal', 'deep']).optional().describe('Investigation depth'),
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
createInvestigatePrompt
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
server.registerPrompt(
|
|
63
|
+
'browser_bridge_debug_layout',
|
|
64
|
+
{
|
|
65
|
+
title: 'Debug Layout Or Styling',
|
|
66
|
+
description: 'Diagnose a visual, spacing, sizing, visibility, or CSS issue in the live tab.',
|
|
67
|
+
argsSchema: {
|
|
68
|
+
target: z.string().optional().describe('Element, component, text, or selector to inspect'),
|
|
69
|
+
symptom: z.string().optional().describe('Observed layout or styling problem'),
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
createDebugLayoutPrompt
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
server.registerPrompt(
|
|
76
|
+
'browser_bridge_verify_flow',
|
|
77
|
+
{
|
|
78
|
+
title: 'Verify User Flow',
|
|
79
|
+
description:
|
|
80
|
+
'Drive a user flow through MCP input tools and verify page, console, and network state.',
|
|
81
|
+
argsSchema: {
|
|
82
|
+
flow: z.string().optional().describe('User flow to exercise'),
|
|
83
|
+
successCriteria: z.string().optional().describe('Expected successful outcome'),
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
createVerifyFlowPrompt
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @returns {GetPromptResult}
|
|
92
|
+
*/
|
|
93
|
+
function createGuidePrompt() {
|
|
94
|
+
return createUserPrompt(
|
|
95
|
+
'Browser Bridge MCP workflow guide.',
|
|
96
|
+
[
|
|
97
|
+
'Use Browser Bridge MCP for this browser task.',
|
|
98
|
+
'',
|
|
99
|
+
'Rules:',
|
|
100
|
+
'1. Prefer MCP tools over `bbx`; do not shell out unless setup, doctor, logs, or raw CLI debugging is explicitly needed.',
|
|
101
|
+
'2. Call `browser_status` first. If access is disabled, call `browser_access` once, ask the user to click Enable, then retry once.',
|
|
102
|
+
'3. Start with structured reads: `browser_page` action `state`, `browser_dom` action `query`/`find_text`/`find_role`, `browser_styles_layout`, and `browser_batch`.',
|
|
103
|
+
'4. Keep budgets tight with `budgetPreset: "quick"` or `"normal"`; widen only when results are truncated.',
|
|
104
|
+
'5. Reuse `elementRef` values returned by DOM tools instead of rescanning.',
|
|
105
|
+
'6. Escalate to `browser_capture`, accessibility tree, `browser_page` evaluate, viewport resize, or CDP only when structured reads cannot answer.',
|
|
106
|
+
'7. Use `browser_patch` for temporary style/DOM experiments and rollback before finishing unless the user asks to keep patches.',
|
|
107
|
+
'',
|
|
108
|
+
'Return concise findings with evidence. Edit source code only after the live page behavior is understood.',
|
|
109
|
+
].join('\n')
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {{ objective?: string, selector?: string, scope?: 'quick' | 'normal' | 'deep' }} args
|
|
115
|
+
* @returns {GetPromptResult}
|
|
116
|
+
*/
|
|
117
|
+
function createInvestigatePrompt(args) {
|
|
118
|
+
const objective = normalizeTextArg(
|
|
119
|
+
args.objective,
|
|
120
|
+
'inspect the current page and report findings'
|
|
121
|
+
);
|
|
122
|
+
const selector = normalizeTextArg(
|
|
123
|
+
args.selector,
|
|
124
|
+
'none; start with main, body, or semantic search'
|
|
125
|
+
);
|
|
126
|
+
const scope = normalizeTextArg(args.scope, 'normal');
|
|
127
|
+
|
|
128
|
+
return createUserPrompt(
|
|
129
|
+
'Browser Bridge MCP page investigation workflow.',
|
|
130
|
+
[
|
|
131
|
+
'Investigate the current page with Browser Bridge MCP.',
|
|
132
|
+
'',
|
|
133
|
+
`Objective: ${objective}`,
|
|
134
|
+
`Scope: ${scope}`,
|
|
135
|
+
`Initial selector: ${selector}`,
|
|
136
|
+
'',
|
|
137
|
+
'Workflow:',
|
|
138
|
+
'1. Call `browser_status` to confirm daemon, extension, and access readiness.',
|
|
139
|
+
'2. If access is disabled, call `browser_access` once, ask the user to click Enable, then retry once.',
|
|
140
|
+
'3. Use `browser_batch` for independent structured reads, usually `page.get_state`, a scoped `dom.query`, and `page.get_text` when page copy matters.',
|
|
141
|
+
'4. Use `browser_dom` `find_text` or `find_role` when the target is known by label but not selector.',
|
|
142
|
+
'5. Add `browser_styles_layout`, `browser_page` console, or `browser_page` network only when they directly answer the objective.',
|
|
143
|
+
'6. Escalate to screenshots, accessibility tree, or evaluate only when structured reads are insufficient.',
|
|
144
|
+
'',
|
|
145
|
+
'Return concise findings, relevant evidence, and the next source-code action if a fix is needed.',
|
|
146
|
+
].join('\n')
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @param {{ target?: string, symptom?: string }} args
|
|
152
|
+
* @returns {GetPromptResult}
|
|
153
|
+
*/
|
|
154
|
+
function createDebugLayoutPrompt(args) {
|
|
155
|
+
const target = normalizeTextArg(args.target, 'the affected element or component');
|
|
156
|
+
const symptom = normalizeTextArg(args.symptom, 'the observed layout or styling problem');
|
|
157
|
+
|
|
158
|
+
return createUserPrompt(
|
|
159
|
+
'Browser Bridge MCP layout debugging workflow.',
|
|
160
|
+
[
|
|
161
|
+
'Debug a layout or styling issue in the live tab with Browser Bridge MCP.',
|
|
162
|
+
'',
|
|
163
|
+
`Target: ${target}`,
|
|
164
|
+
`Symptom: ${symptom}`,
|
|
165
|
+
'',
|
|
166
|
+
'Workflow:',
|
|
167
|
+
'1. Call `browser_status` first and resolve access if needed.',
|
|
168
|
+
'2. Locate the target with `browser_dom` `query`, `find_text`, or `find_role` using a quick budget.',
|
|
169
|
+
'3. Read only relevant computed styles with `browser_styles_layout` action `computed` and specific `properties`.',
|
|
170
|
+
'4. Read dimensions with `browser_styles_layout` action `box_model`; use matched rules only when the cascade is unclear.',
|
|
171
|
+
'5. Prototype the smallest visual fix with `browser_patch` action `apply_styles` and `verify: true` when useful.',
|
|
172
|
+
'6. Check `browser_page` console for new errors after interaction or patching.',
|
|
173
|
+
'7. Roll back temporary patches unless the user explicitly wants them kept, then edit source with the confirmed fix.',
|
|
174
|
+
'',
|
|
175
|
+
'Avoid screenshots until DOM, computed styles, and box model evidence are insufficient.',
|
|
176
|
+
].join('\n')
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @param {{ flow?: string, successCriteria?: string }} args
|
|
182
|
+
* @returns {GetPromptResult}
|
|
183
|
+
*/
|
|
184
|
+
function createVerifyFlowPrompt(args) {
|
|
185
|
+
const flow = normalizeTextArg(args.flow, 'the requested user flow');
|
|
186
|
+
const successCriteria = normalizeTextArg(
|
|
187
|
+
args.successCriteria,
|
|
188
|
+
'visible success state, no console errors, and expected network behavior'
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
return createUserPrompt(
|
|
192
|
+
'Browser Bridge MCP user-flow verification workflow.',
|
|
193
|
+
[
|
|
194
|
+
'Verify a user flow in the current real browser tab with Browser Bridge MCP.',
|
|
195
|
+
'',
|
|
196
|
+
`Flow: ${flow}`,
|
|
197
|
+
`Success criteria: ${successCriteria}`,
|
|
198
|
+
'',
|
|
199
|
+
'Workflow:',
|
|
200
|
+
'1. Call `browser_status` and resolve access if needed.',
|
|
201
|
+
'2. Read `browser_page` state so you know the current URL and title before interacting.',
|
|
202
|
+
'3. Locate controls semantically with `browser_dom` `find_role` or `find_text`; reuse returned `elementRef` values.',
|
|
203
|
+
'4. Interact with `browser_input` actions such as `click`, `type`, `set_checked`, `select_option`, and `press_key`.',
|
|
204
|
+
'5. After navigation or UI changes, wait with `browser_dom` action `wait` or `browser_page` action `wait_for_load`.',
|
|
205
|
+
'6. Verify final page text/DOM plus `browser_page` console and network if the flow depends on API calls.',
|
|
206
|
+
'7. Do not create new tabs unless the user requested a fresh page or the flow requires one.',
|
|
207
|
+
'',
|
|
208
|
+
'Report the verified result, evidence, and any blocking failures.',
|
|
209
|
+
].join('\n')
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @param {string} description
|
|
215
|
+
* @param {string} text
|
|
216
|
+
* @returns {GetPromptResult}
|
|
217
|
+
*/
|
|
218
|
+
function createUserPrompt(description, text) {
|
|
219
|
+
return {
|
|
220
|
+
description,
|
|
221
|
+
messages: [
|
|
222
|
+
{
|
|
223
|
+
role: 'user',
|
|
224
|
+
content: {
|
|
225
|
+
type: 'text',
|
|
226
|
+
text,
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @param {string | undefined} value
|
|
235
|
+
* @param {string} fallback
|
|
236
|
+
* @returns {string}
|
|
237
|
+
*/
|
|
238
|
+
function normalizeTextArg(value, fallback) {
|
|
239
|
+
const text = typeof value === 'string' ? value.trim() : '';
|
|
240
|
+
return text || fallback;
|
|
241
|
+
}
|