agent-browser-bridge 2.0.0 → 2.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-browser-bridge",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Live multi-tab, multi-profile Chrome DevTools MCP bridge for AI agents (Antigravity, Claude, Cursor, Windsurf)",
5
5
  "author": "thisisdarkstar",
6
6
  "type": "module",
@@ -11,6 +11,8 @@
11
11
  "files": [
12
12
  "bin",
13
13
  "index.js",
14
+ "core",
15
+ "tools",
14
16
  "README.md"
15
17
  ],
16
18
  "scripts": {
@@ -0,0 +1,141 @@
1
+ // Agent Bridge MCP - Bookmarks, History, Notifications, Clipboard & Downloads Tools (ES Module)
2
+ import { z } from "zod";
3
+ import { jsonResult } from "../core/state.js";
4
+ import { callProfileExtension } from "../core/bridge.js";
5
+
6
+ export function registerBrowserDataTools(server) {
7
+ server.registerTool(
8
+ "browser_search_bookmarks",
9
+ {
10
+ description: "Search the user's personal browser bookmarks by query keyword or folder name.",
11
+ inputSchema: {
12
+ profile: z.string().optional().describe("Target browser profile"),
13
+ query: z.string().optional().describe("Search keyword to match in title or URL. Empty string returns all recent bookmarks."),
14
+ limit: z.number().optional().describe("Max bookmarks to return (default 50)"),
15
+ },
16
+ },
17
+ async ({ profile, query, limit }) => {
18
+ const res = await callProfileExtension(profile, "search_bookmarks", { query, limit });
19
+ return jsonResult(res);
20
+ }
21
+ );
22
+
23
+ server.registerTool(
24
+ "browser_add_bookmark",
25
+ {
26
+ description: "Create a new bookmark in the user's browser with title and URL.",
27
+ inputSchema: {
28
+ profile: z.string().optional().describe("Target browser profile"),
29
+ title: z.string().describe("Title for the bookmark"),
30
+ url: z.string().describe("Target webpage URL to bookmark"),
31
+ parentId: z.string().optional().describe("Optional parent bookmark folder ID (defaults to 'Other bookmarks')"),
32
+ },
33
+ },
34
+ async ({ profile, title, url, parentId }) => {
35
+ const res = await callProfileExtension(profile, "add_bookmark", { title, url, parentId });
36
+ return jsonResult(res);
37
+ }
38
+ );
39
+
40
+ server.registerTool(
41
+ "browser_search_history",
42
+ {
43
+ description: "Search the user's browsing history by query keyword, date range, and visit counts.",
44
+ inputSchema: {
45
+ profile: z.string().optional().describe("Target browser profile"),
46
+ query: z.string().optional().describe("Search keyword in history titles or URLs"),
47
+ limit: z.number().optional().describe("Max history items to return (default 50)"),
48
+ startTime: z.number().optional().describe("Milliseconds timestamp to search after (defaults to 7 days ago)"),
49
+ },
50
+ },
51
+ async ({ profile, query, limit, startTime }) => {
52
+ const res = await callProfileExtension(profile, "search_history", { query, limit, startTime });
53
+ return jsonResult(res);
54
+ }
55
+ );
56
+
57
+ server.registerTool(
58
+ "browser_show_notification",
59
+ {
60
+ description: "Display a native OS desktop notification from the browser (ideal for alerting user when a long-running agent task finishes).",
61
+ inputSchema: {
62
+ profile: z.string().optional().describe("Target browser profile"),
63
+ title: z.string().optional().describe("Notification title"),
64
+ message: z.string().describe("Notification body message text"),
65
+ },
66
+ },
67
+ async ({ profile, title, message }) => {
68
+ const res = await callProfileExtension(profile, "show_notification", { title, message });
69
+ return jsonResult(res);
70
+ }
71
+ );
72
+
73
+ server.registerTool(
74
+ "browser_download_file",
75
+ {
76
+ description: "Trigger a file download from a direct URL in the browser with customizable filename and conflict action.",
77
+ inputSchema: {
78
+ profile: z.string().optional().describe("Target browser profile"),
79
+ tabId: z.number().optional().describe("Optional tabId associated with the download"),
80
+ url: z.string().describe("Direct URL of the file to download"),
81
+ filename: z.string().optional().describe("Custom local filename or subpath"),
82
+ saveAs: z.boolean().optional().describe("Whether to prompt user with save file dialog (default false)"),
83
+ conflictAction: z.enum(["uniquify", "overwrite", "prompt"]).optional().describe("Action on duplicate filename (default 'uniquify')"),
84
+ },
85
+ },
86
+ async ({ profile, tabId, url, filename, saveAs, conflictAction }) => {
87
+ const res = await callProfileExtension(profile, "download_file", { tabId, url, filename, saveAs, conflictAction });
88
+ return jsonResult(res);
89
+ }
90
+ );
91
+
92
+ server.registerTool(
93
+ "browser_wait_for_download",
94
+ {
95
+ description: "Wait for a file download to complete, returning its final filename and file size.",
96
+ inputSchema: {
97
+ profile: z.string().optional().describe("Target browser profile"),
98
+ downloadId: z.number().optional().describe("Specific download ID to wait for"),
99
+ timeoutMs: z.number().optional().describe("Timeout in milliseconds (default 30000)"),
100
+ },
101
+ },
102
+ async ({ profile, downloadId, timeoutMs }) => {
103
+ const res = await callProfileExtension(profile, "wait_for_download", { downloadId, timeoutMs });
104
+ return jsonResult(res);
105
+ }
106
+ );
107
+
108
+ server.registerTool(
109
+ "browser_list_downloads",
110
+ {
111
+ description: "List recent downloads in the browser profile (status, file path, bytes received).",
112
+ inputSchema: {
113
+ profile: z.string().optional().describe("Target browser profile"),
114
+ limit: z.number().optional().describe("Max downloads to return (default 20)"),
115
+ state: z.enum(["in_progress", "interrupted", "complete"]).optional().describe("Filter by state"),
116
+ },
117
+ },
118
+ async ({ profile, limit, state }) => {
119
+ const res = await callProfileExtension(profile, "list_downloads", { limit, state });
120
+ return jsonResult(res);
121
+ }
122
+ );
123
+
124
+ server.registerTool(
125
+ "browser_clipboard",
126
+ {
127
+ description: "Read or copy text directly to/from the system clipboard in the active tab context.",
128
+ inputSchema: {
129
+ profile: z.string().optional().describe("Target browser profile"),
130
+ tabId: z.number().describe("Target tabId"),
131
+ operation: z.enum(["get", "set"]).optional().describe("'get' to read clipboard, 'set' to copy text (default 'get')"),
132
+ text: z.string().optional().describe("Text string to write when operation is 'set'"),
133
+ },
134
+ },
135
+ async ({ profile, tabId, operation, text }) => {
136
+ const res = await callProfileExtension(profile, "clipboard", { tabId, operation, text });
137
+ return jsonResult(res);
138
+ }
139
+ );
140
+ }
141
+
@@ -0,0 +1,170 @@
1
+ // Agent Bridge MCP - Diagnostics, Performance, Console & Raw CDP Tools (ES Module)
2
+ import { z } from "zod";
3
+ import { jsonResult } from "../core/state.js";
4
+ import { callProfileExtension } from "../core/bridge.js";
5
+
6
+ export function registerDiagnosticTools(server) {
7
+ server.registerTool(
8
+ "browser_wait_for",
9
+ {
10
+ description: "Wait until a selector appears in DOM or text appears in document body (replaces arbitrary sleep timers).",
11
+ inputSchema: {
12
+ profile: z.string().optional().describe("Target browser profile"),
13
+ tabId: z.number().describe("Target tabId"),
14
+ selector: z.string().optional().describe("CSS selector or ref to wait for"),
15
+ text: z.string().optional().describe("Text string to wait for on page"),
16
+ visible: z.boolean().optional().describe("Require element to be visible (default true)"),
17
+ timeout: z.number().optional().describe("Timeout in milliseconds (default 10000)"),
18
+ },
19
+ },
20
+ async ({ profile, tabId, selector, text, visible, timeout }) => {
21
+ const res = await callProfileExtension(profile, "wait_for", { tabId, selector, text, visible, timeout });
22
+ return jsonResult(res);
23
+ }
24
+ );
25
+
26
+ server.registerTool(
27
+ "browser_list_console_messages",
28
+ {
29
+ description: "List captured console messages (log, info, warn, error, uncaught exceptions) with arguments and stack traces.",
30
+ inputSchema: {
31
+ profile: z.string().optional().describe("Target browser profile"),
32
+ tabId: z.number().describe("Target tabId"),
33
+ types: z.array(z.string()).optional().describe("Filter by type (e.g. ['error', 'warn'])"),
34
+ limit: z.number().optional().describe("Max messages to return (default 100)"),
35
+ },
36
+ },
37
+ async ({ profile, tabId, types, limit }) => {
38
+ const res = await callProfileExtension(profile, "list_console_messages", { tabId, types, limit });
39
+ return jsonResult(res);
40
+ }
41
+ );
42
+
43
+ server.registerTool(
44
+ "browser_get_telemetry",
45
+ {
46
+ description: "Combined telemetry snapshot: console logs, recent network requests, and dialog events for a tab in one call.",
47
+ inputSchema: {
48
+ profile: z.string().optional().describe("Target browser profile"),
49
+ tabId: z.number().describe("Target tabId"),
50
+ },
51
+ },
52
+ async ({ profile, tabId }) => {
53
+ const res = await callProfileExtension(profile, "get_telemetry", { tabId });
54
+ return jsonResult(res);
55
+ }
56
+ );
57
+
58
+ server.registerTool(
59
+ "browser_take_heapsnapshot",
60
+ {
61
+ description: "Capture V8 heap snapshot via DevTools HeapProfiler.takeHeapSnapshot for JavaScript memory leak diagnosis.",
62
+ inputSchema: {
63
+ profile: z.string().optional().describe("Target browser profile"),
64
+ tabId: z.number().describe("Target tabId"),
65
+ },
66
+ },
67
+ async ({ profile, tabId }) => {
68
+ const res = await callProfileExtension(profile, "take_heapsnapshot", { tabId });
69
+ return jsonResult(res);
70
+ }
71
+ );
72
+
73
+ server.registerTool(
74
+ "browser_performance_trace",
75
+ {
76
+ description: "Start or stop Chrome DevTools Tracing performance profile.",
77
+ inputSchema: {
78
+ profile: z.string().optional().describe("Target browser profile"),
79
+ tabId: z.number().describe("Target tabId"),
80
+ action: z.enum(["start", "stop"]).describe("Action to perform"),
81
+ categories: z.string().optional().describe("Trace categories string"),
82
+ },
83
+ },
84
+ async ({ profile, tabId, action, categories }) => {
85
+ const res = await callProfileExtension(profile, "performance_trace", { tabId, action, categories });
86
+ return jsonResult(res);
87
+ }
88
+ );
89
+
90
+ server.registerTool(
91
+ "browser_performance_analyze_insight",
92
+ {
93
+ description: "Extract Core Web Vitals (TTFB, FCP, First Paint, DOMContentLoaded, Load Time, Transfer Size) for a tab.",
94
+ inputSchema: {
95
+ profile: z.string().optional().describe("Target browser profile"),
96
+ tabId: z.number().describe("Target tabId"),
97
+ },
98
+ },
99
+ async ({ profile, tabId }) => {
100
+ const res = await callProfileExtension(profile, "performance_analyze_insight", { tabId });
101
+ return jsonResult(res);
102
+ }
103
+ );
104
+
105
+ server.registerTool(
106
+ "browser_eval_js",
107
+ {
108
+ description: "Evaluate arbitrary JavaScript in the tab context with async/await support and return serialized result.",
109
+ inputSchema: {
110
+ profile: z.string().optional().describe("Target browser profile"),
111
+ tabId: z.number().describe("Target tabId"),
112
+ code: z.string().describe("JavaScript code string to evaluate"),
113
+ useDebugger: z.boolean().optional().describe("Use Runtime.evaluate over CDP (default false)"),
114
+ },
115
+ },
116
+ async ({ profile, tabId, code, useDebugger }) => {
117
+ const res = await callProfileExtension(profile, "eval_js", { tabId, code, useDebugger });
118
+ return jsonResult(res);
119
+ }
120
+ );
121
+
122
+ server.registerTool(
123
+ "browser_raw_cdp",
124
+ {
125
+ description: "Ultimate escape hatch: execute ANY raw Chrome DevTools Protocol command with arbitrary parameters on a tab.",
126
+ inputSchema: {
127
+ profile: z.string().optional().describe("Target browser profile"),
128
+ tabId: z.number().describe("Target tabId"),
129
+ method: z.string().describe("CDP method name (e.g. 'Page.captureScreenshot', 'DOM.getDocument', 'Network.clearBrowserCache')"),
130
+ commandParams: z.record(z.any()).optional().describe("Parameters for the CDP command"),
131
+ },
132
+ },
133
+ async ({ profile, tabId, method, commandParams }) => {
134
+ const res = await callProfileExtension(profile, "raw_cdp", { tabId, method, commandParams });
135
+ return jsonResult(res);
136
+ }
137
+ );
138
+
139
+ server.registerTool(
140
+ "browser_request_human_takeover",
141
+ {
142
+ description: "Pause agent execution and display a prominent interactive takeover banner in the browser for manual 2FA, CAPTCHA, or payment confirmation. Resumes automatically when user clicks 'Resume Agent' or timeout expires.",
143
+ inputSchema: {
144
+ profile: z.string().optional().describe("Target browser profile"),
145
+ tabId: z.number().describe("Target tabId"),
146
+ reason: z.string().optional().describe("Brief message shown to the user explaining what they need to complete (e.g. 'Please complete 2FA on screen')"),
147
+ timeoutSeconds: z.number().optional().describe("Maximum seconds to pause before timing out (default 300)"),
148
+ },
149
+ },
150
+ async ({ profile, tabId, reason, timeoutSeconds }) => {
151
+ const res = await callProfileExtension(profile, "request_human_takeover", { tabId, reason, timeoutSeconds });
152
+ return jsonResult(res);
153
+ }
154
+ );
155
+
156
+ server.registerTool(
157
+ "browser_reload_extension",
158
+ {
159
+ description: "Hot reload the Agent Bridge Chrome extension service worker in the browser profile.",
160
+ inputSchema: {
161
+ profile: z.string().optional().describe("Target browser profile"),
162
+ },
163
+ },
164
+ async ({ profile }) => {
165
+ const res = await callProfileExtension(profile, "reload_extension");
166
+ return jsonResult(res);
167
+ }
168
+ );
169
+ }
170
+
@@ -0,0 +1,295 @@
1
+ // Agent Bridge MCP - DOM Snapshots & Element Interaction Tools (ES Module)
2
+ import { z } from "zod";
3
+ import { jsonResult } from "../core/state.js";
4
+ import { callProfileExtension } from "../core/bridge.js";
5
+
6
+ export function registerDomTools(server) {
7
+ server.registerTool(
8
+ "browser_get_interactive_snapshot",
9
+ {
10
+ description: "Maps all interactive elements on the page (buttons, links, inputs, selects, tabs, clickable elements, shadow roots), assigning deterministic 'ref_1', 'ref_2' IDs with coordinates and bounding boxes. Supports viewportOnly filtering for token efficiency.",
11
+ inputSchema: {
12
+ profile: z.string().optional().describe("Target browser profile"),
13
+ tabId: z.number().describe("Target tabId"),
14
+ viewportOnly: z.boolean().optional().describe("If true, only returns elements visible within the current viewport (default false)"),
15
+ },
16
+ },
17
+ async ({ profile, tabId, viewportOnly }) => {
18
+ const res = await callProfileExtension(profile, "get_interactive_snapshot", { tabId, viewportOnly });
19
+ return jsonResult(res);
20
+ }
21
+ );
22
+
23
+ server.registerTool(
24
+ "browser_take_snapshot",
25
+ {
26
+ description: "Accessibility (a11y) tree text snapshot of the page matching standard Chrome DevTools MCP.",
27
+ inputSchema: {
28
+ profile: z.string().optional().describe("Target browser profile"),
29
+ tabId: z.number().describe("Target tabId"),
30
+ verbose: z.boolean().optional().describe("Include deeper accessibility details"),
31
+ },
32
+ },
33
+ async ({ profile, tabId, verbose }) => {
34
+ const res = await callProfileExtension(profile, "take_snapshot", { tabId, verbose });
35
+ return jsonResult(res);
36
+ }
37
+ );
38
+
39
+ server.registerTool(
40
+ "browser_get_css_styles",
41
+ {
42
+ description: "Inspect computed CSS styling for any element ref or CSS selector (display, visibility, opacity, colors, fonts, geometry, z-index).",
43
+ inputSchema: {
44
+ profile: z.string().optional().describe("Target browser profile"),
45
+ tabId: z.number().describe("Target tabId"),
46
+ target: z.string().describe("Target element data-agent-ref (e.g. 'ref_1') or CSS selector"),
47
+ properties: z.array(z.string()).optional().describe("Array of specific CSS property names (e.g. ['color', 'font-size']). Omit for essential styling."),
48
+ },
49
+ },
50
+ async ({ profile, tabId, target, properties }) => {
51
+ const res = await callProfileExtension(profile, "get_css_styles", { tabId, target, properties });
52
+ return jsonResult(res);
53
+ }
54
+ );
55
+
56
+ server.registerTool(
57
+ "browser_act_on_ref",
58
+ {
59
+ description: "Perform precision interaction on a data-agent-ref target or coordinates with visible virtual cursor and action HUD animations.",
60
+ inputSchema: {
61
+ profile: z.string().optional().describe("Target browser profile"),
62
+ tabId: z.number().describe("Target tabId"),
63
+ ref: z.string().optional().describe("The data-agent-ref ID (e.g. 'ref_1')"),
64
+ actionType: z.enum(["click", "type"]).optional().describe("Action to perform (default 'click')"),
65
+ text: z.string().optional().describe("Text to insert if actionType is 'type'"),
66
+ button: z.enum(["left", "right", "middle"]).optional().describe("Mouse button (default 'left')"),
67
+ clickCount: z.number().optional().describe("Number of clicks (default 1, 2 for double click)"),
68
+ clearFirst: z.boolean().optional().describe("Clear input field before typing (default false)"),
69
+ pressEnter: z.boolean().optional().describe("Press Enter after typing text (default false)"),
70
+ x: z.number().optional().describe("Direct X coordinate"),
71
+ y: z.number().optional().describe("Direct Y coordinate"),
72
+ },
73
+ },
74
+ async ({ profile, tabId, ref, actionType, text, button, clickCount, clearFirst, pressEnter, x, y }) => {
75
+ const res = await callProfileExtension(profile, "act_on_ref", {
76
+ tabId,
77
+ ref,
78
+ actionType,
79
+ text,
80
+ button,
81
+ clickCount,
82
+ clearFirst,
83
+ pressEnter,
84
+ x,
85
+ y,
86
+ });
87
+ return jsonResult(res);
88
+ }
89
+ );
90
+
91
+ server.registerTool(
92
+ "browser_click",
93
+ {
94
+ description: "Click an element by ref or CSS selector, or click exact screen coordinates (matching chrome-devtools click).",
95
+ inputSchema: {
96
+ profile: z.string().optional().describe("Target browser profile"),
97
+ tabId: z.number().describe("Target tabId"),
98
+ target: z.string().optional().describe("Target ref (e.g. 'ref_1') or CSS selector"),
99
+ x: z.number().optional().describe("Direct X coordinate"),
100
+ y: z.number().optional().describe("Direct Y coordinate"),
101
+ button: z.enum(["left", "right", "middle"]).optional().describe("Mouse button (default 'left')"),
102
+ clickCount: z.number().optional().describe("1 for single click, 2 for double click"),
103
+ },
104
+ },
105
+ async ({ profile, tabId, target, x, y, button, clickCount }) => {
106
+ const res = await callProfileExtension(profile, "act_on_ref", {
107
+ tabId,
108
+ ref: target,
109
+ actionType: "click",
110
+ x,
111
+ y,
112
+ button,
113
+ clickCount,
114
+ });
115
+ return jsonResult(res);
116
+ }
117
+ );
118
+
119
+ server.registerTool(
120
+ "browser_hover",
121
+ {
122
+ description: "Hover over an element by ref or CSS selector, or move mouse to coordinates.",
123
+ inputSchema: {
124
+ profile: z.string().optional().describe("Target browser profile"),
125
+ tabId: z.number().describe("Target tabId"),
126
+ target: z.string().optional().describe("Target ref (e.g. 'ref_1') or CSS selector"),
127
+ x: z.number().optional().describe("Direct X coordinate"),
128
+ y: z.number().optional().describe("Direct Y coordinate"),
129
+ },
130
+ },
131
+ async ({ profile, tabId, target, x, y }) => {
132
+ const res = await callProfileExtension(profile, "hover", { tabId, target, x, y });
133
+ return jsonResult(res);
134
+ }
135
+ );
136
+
137
+ server.registerTool(
138
+ "browser_drag",
139
+ {
140
+ description: "Drag and drop from one element/coordinate to another with visual laser trajectory.",
141
+ inputSchema: {
142
+ profile: z.string().optional().describe("Target browser profile"),
143
+ tabId: z.number().describe("Target tabId"),
144
+ fromTarget: z.string().optional().describe("Source element ref or selector"),
145
+ toTarget: z.string().optional().describe("Destination element ref or selector"),
146
+ fromX: z.number().optional().describe("Source X coordinate"),
147
+ fromY: z.number().optional().describe("Source Y coordinate"),
148
+ toX: z.number().optional().describe("Destination X coordinate"),
149
+ toY: z.number().optional().describe("Destination Y coordinate"),
150
+ },
151
+ },
152
+ async ({ profile, tabId, fromTarget, toTarget, fromX, fromY, toX, toY }) => {
153
+ const res = await callProfileExtension(profile, "drag", { tabId, fromTarget, toTarget, fromX, fromY, toX, toY });
154
+ return jsonResult(res);
155
+ }
156
+ );
157
+
158
+ server.registerTool(
159
+ "browser_fill",
160
+ {
161
+ description: "Focus an input/textarea and fill its value (matching chrome-devtools fill).",
162
+ inputSchema: {
163
+ profile: z.string().optional().describe("Target browser profile"),
164
+ tabId: z.number().describe("Target tabId"),
165
+ target: z.string().describe("Target ref (e.g. 'ref_1') or CSS selector"),
166
+ value: z.string().describe("Value to fill"),
167
+ },
168
+ },
169
+ async ({ profile, tabId, target, value }) => {
170
+ const res = await callProfileExtension(profile, "fill", { tabId, target, value });
171
+ return jsonResult(res);
172
+ }
173
+ );
174
+
175
+ server.registerTool(
176
+ "browser_fill_form",
177
+ {
178
+ description: "Fill multiple form fields in one batch call (e.g. { '#username': 'alice', '#password': 'secret' }).",
179
+ inputSchema: {
180
+ profile: z.string().optional().describe("Target browser profile"),
181
+ tabId: z.number().describe("Target tabId"),
182
+ fields: z.record(z.string()).describe("Key-value mapping of selector/ref to input string values"),
183
+ },
184
+ },
185
+ async ({ profile, tabId, fields }) => {
186
+ const res = await callProfileExtension(profile, "fill_form", { tabId, fields });
187
+ return jsonResult(res);
188
+ }
189
+ );
190
+
191
+ server.registerTool(
192
+ "browser_press_key",
193
+ {
194
+ description: "Dispatch a physical keyboard key press (e.g. 'Enter', 'Tab', 'Escape', 'ArrowDown', 'Backspace', 'a', '1') with 3D mechanical keycap HUD animation.",
195
+ inputSchema: {
196
+ profile: z.string().optional().describe("Target browser profile"),
197
+ tabId: z.number().describe("Target tabId"),
198
+ key: z.string().describe("Key identifier (e.g. 'Enter', 'Tab', 'Escape', 'ArrowDown', 'Space')"),
199
+ code: z.string().optional().describe("Optional DOM code (e.g. 'KeyA', 'Enter')"),
200
+ },
201
+ },
202
+ async ({ profile, tabId, key, code }) => {
203
+ const res = await callProfileExtension(profile, "press_key", { tabId, key, code });
204
+ return jsonResult(res);
205
+ }
206
+ );
207
+
208
+ server.registerTool(
209
+ "browser_type_text",
210
+ {
211
+ description: "Type raw text into the currently focused element or specified target.",
212
+ inputSchema: {
213
+ profile: z.string().optional().describe("Target browser profile"),
214
+ tabId: z.number().describe("Target tabId"),
215
+ text: z.string().describe("Text string to type"),
216
+ target: z.string().optional().describe("Optional ref or selector to click/focus before typing"),
217
+ },
218
+ },
219
+ async ({ profile, tabId, text, target }) => {
220
+ const res = await callProfileExtension(profile, "type_text", { tabId, text, target });
221
+ return jsonResult(res);
222
+ }
223
+ );
224
+
225
+ server.registerTool(
226
+ "browser_upload_file",
227
+ {
228
+ description: "Set files on an HTML file input element (<input type='file'>) via DevTools DOM.setFileInputFiles.",
229
+ inputSchema: {
230
+ profile: z.string().optional().describe("Target browser profile"),
231
+ tabId: z.number().describe("Target tabId"),
232
+ target: z.string().describe("Target input[type='file'] ref or selector"),
233
+ filePaths: z.union([z.string(), z.array(z.string())]).describe("Absolute local file path(s) to attach"),
234
+ },
235
+ },
236
+ async ({ profile, tabId, target, filePaths }) => {
237
+ const res = await callProfileExtension(profile, "upload_file", { tabId, target, filePaths });
238
+ return jsonResult(res);
239
+ }
240
+ );
241
+
242
+ server.registerTool(
243
+ "browser_handle_dialog",
244
+ {
245
+ description: "Accept or dismiss active JavaScript dialogs (alert, confirm, prompt, beforeunload).",
246
+ inputSchema: {
247
+ profile: z.string().optional().describe("Target browser profile"),
248
+ tabId: z.number().describe("Target tabId"),
249
+ action: z.enum(["accept", "dismiss"]).describe("Whether to accept or dismiss the dialog"),
250
+ promptText: z.string().optional().describe("Text response if the dialog is a prompt"),
251
+ },
252
+ },
253
+ async ({ profile, tabId, action, promptText }) => {
254
+ const res = await callProfileExtension(profile, "handle_dialog", { tabId, action, promptText });
255
+ return jsonResult(res);
256
+ }
257
+ );
258
+
259
+ server.registerTool(
260
+ "browser_select_option",
261
+ {
262
+ description: "Select an option from a dropdown (<select>) element by value, visible label text, or index. Handles shadow DOM & iframes.",
263
+ inputSchema: {
264
+ profile: z.string().optional().describe("Target browser profile"),
265
+ tabId: z.number().describe("Target tabId"),
266
+ target: z.string().describe("Target <select> ref (e.g. 'ref_1') or CSS selector"),
267
+ value: z.string().optional().describe("The option value attribute to select"),
268
+ label: z.string().optional().describe("The visible option text to select"),
269
+ index: z.number().optional().describe("The zero-based option index to select"),
270
+ },
271
+ },
272
+ async ({ profile, tabId, target, value, label, index }) => {
273
+ const res = await callProfileExtension(profile, "select_option", { tabId, target, value, label, index });
274
+ return jsonResult(res);
275
+ }
276
+ );
277
+
278
+ server.registerTool(
279
+ "browser_set_checked",
280
+ {
281
+ description: "Set the checked state of a checkbox or radio button element (true or false) with spring bloom visual animation.",
282
+ inputSchema: {
283
+ profile: z.string().optional().describe("Target browser profile"),
284
+ tabId: z.number().describe("Target tabId"),
285
+ target: z.string().describe("Target checkbox or radio ref (e.g. 'ref_1') or CSS selector"),
286
+ checked: z.boolean().describe("Whether the checkbox/radio should be checked (true) or unchecked (false)"),
287
+ },
288
+ },
289
+ async ({ profile, tabId, target, checked }) => {
290
+ const res = await callProfileExtension(profile, "set_checked", { tabId, target, checked });
291
+ return jsonResult(res);
292
+ }
293
+ );
294
+ }
295
+